/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#ifndef RegisterFile_h
#define RegisterFile_h
+#include "Heap.h"
+#include "ExecutableAllocator.h"
#include "Register.h"
-#include "Collector.h"
-#include <wtf/Noncopyable.h>
-
-#if HAVE(MMAP)
-#include <errno.h>
+#include "Weak.h"
#include <stdio.h>
-#include <sys/mman.h>
-#endif
+#include <wtf/Noncopyable.h>
+#include <wtf/PageReservation.h>
+#include <wtf/VMTags.h>
namespace JSC {
class JSGlobalObject;
- class RegisterFile : Noncopyable {
- friend class JIT;
+ class RegisterFile {
+ WTF_MAKE_NONCOPYABLE(RegisterFile);
public:
enum CallFrameHeaderEntry {
- CallFrameHeaderSize = 8,
-
- CodeBlock = -8,
- ScopeChain = -7,
- CallerFrame = -6,
- ReturnPC = -5, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
- ReturnValueRegister = -4,
- ArgumentCount = -3,
- Callee = -2,
- OptionalCalleeArguments = -1,
+ CallFrameHeaderSize = 6,
+
+ ArgumentCount = -6,
+ CallerFrame = -5,
+ Callee = -4,
+ ScopeChain = -3,
+ ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
+ CodeBlock = -1,
};
enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 };
- enum { ArgumentsRegister = 0 };
-
- static const size_t defaultCapacity = 524288;
- static const size_t defaultMaxGlobals = 8192;
- static const size_t allocationSize = 1 << 14;
- static const size_t allocationSizeMask = allocationSize - 1;
-
- RegisterFile(size_t capacity = defaultCapacity, size_t maxGlobals = defaultMaxGlobals)
- : m_numGlobals(0)
- , m_maxGlobals(maxGlobals)
- , m_start(0)
- , m_end(0)
- , m_max(0)
- , m_buffer(0)
- , m_globalObject(0)
- {
- size_t bufferLength = (capacity + maxGlobals) * sizeof(Register);
-#if HAVE(MMAP)
- m_buffer = static_cast<Register*>(mmap(0, bufferLength, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0));
- if (m_buffer == MAP_FAILED) {
- fprintf(stderr, "Could not allocate register file: %d\n", errno);
- CRASH();
- }
-#elif HAVE(VIRTUALALLOC)
- // Ensure bufferLength is a multiple of allocation size
- bufferLength = (bufferLength + allocationSizeMask) & ~allocationSizeMask;
- m_buffer = static_cast<Register*>(VirtualAlloc(0, bufferLength, MEM_RESERVE, PAGE_READWRITE));
- if (!m_buffer) {
- fprintf(stderr, "Could not allocate register file: %d\n", errno);
- CRASH();
- }
- int initialAllocation = (maxGlobals * sizeof(Register) + allocationSizeMask) & ~allocationSizeMask;
- void* commitCheck = VirtualAlloc(m_buffer, initialAllocation, MEM_COMMIT, PAGE_READWRITE);
- if (commitCheck != m_buffer) {
- fprintf(stderr, "Could not allocate register file: %d\n", errno);
- CRASH();
- }
- m_maxCommitted = reinterpret_cast<Register*>(reinterpret_cast<char*>(m_buffer) + initialAllocation);
-#else
- #error "Don't know how to reserve virtual memory on this platform."
-#endif
- m_start = m_buffer + maxGlobals;
- m_end = m_start;
- m_max = m_start + capacity;
- }
+ static const size_t defaultCapacity = 512 * 1024;
+ static const size_t defaultMaxGlobals = 8 * 1024;
+ static const size_t commitSize = 16 * 1024;
+ // Allow 8k of excess registers before we start trying to reap the registerfile
+ static const ptrdiff_t maxExcessCapacity = 8 * 1024;
+
+ RegisterFile(JSGlobalData&, size_t capacity = defaultCapacity, size_t maxGlobals = defaultMaxGlobals);
~RegisterFile();
+
+ void gatherConservativeRoots(ConservativeRoots&);
Register* start() const { return m_start; }
Register* end() const { return m_end; }
size_t size() const { return m_end - m_start; }
- void setGlobalObject(JSGlobalObject* globalObject) { m_globalObject = globalObject; }
- JSGlobalObject* globalObject() { return m_globalObject; }
+ void setGlobalObject(JSGlobalObject*);
+ JSGlobalObject* globalObject();
- void shrink(Register* newEnd)
- {
- if (newEnd < m_end)
- m_end = newEnd;
- }
-
- bool grow(Register* newEnd)
- {
- if (newEnd > m_end) {
- if (newEnd > m_max)
- return false;
-#if !HAVE(MMAP) && HAVE(VIRTUALALLOC)
- if (newEnd > m_maxCommitted) {
- ptrdiff_t additionalAllocation = ((reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_maxCommitted)) + allocationSizeMask) & ~allocationSizeMask;
- if (!VirtualAlloc(m_maxCommitted, additionalAllocation, MEM_COMMIT, PAGE_READWRITE)) {
- fprintf(stderr, "Could not allocate register file: %d\n", errno);
- CRASH();
- }
- m_maxCommitted = reinterpret_cast<Register*>(reinterpret_cast<char*>(m_maxCommitted) + additionalAllocation);
- }
-#endif
- m_end = newEnd;
- }
- return true;
- }
+ bool grow(Register* newEnd);
+ void shrink(Register* newEnd);
void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; }
int numGlobals() const { return m_numGlobals; }
Register* lastGlobal() const { return m_start - m_numGlobals; }
- void markGlobals(Heap* heap) { heap->markConservatively(lastGlobal(), m_start); }
- void markCallFrames(Heap* heap) { heap->markConservatively(m_start, m_end); }
+ static size_t committedByteCount();
+ static void initializeThreading();
+
+ Register* const * addressOfEnd() const
+ {
+ return &m_end;
+ }
private:
+ void releaseExcessCapacity();
+ void addToCommittedByteCount(long);
size_t m_numGlobals;
const size_t m_maxGlobals;
Register* m_start;
Register* m_end;
Register* m_max;
- Register* m_buffer;
-#if HAVE(VIRTUALALLOC)
- Register* m_maxCommitted;
-#endif
-
- JSGlobalObject* m_globalObject; // The global object whose vars are currently stored in the register file.
+ Register* m_maxUsed;
+ Register* m_commitEnd;
+ PageReservation m_reservation;
+
+ Weak<JSGlobalObject> m_globalObject; // The global object whose vars are currently stored in the register file.
+ class GlobalObjectOwner : public WeakHandleOwner {
+ virtual void finalize(Handle<Unknown>, void* context)
+ {
+ static_cast<RegisterFile*>(context)->setNumGlobals(0);
+ }
+ } m_globalObjectOwner;
};
+ inline RegisterFile::RegisterFile(JSGlobalData& globalData, size_t capacity, size_t maxGlobals)
+ : m_numGlobals(0)
+ , m_maxGlobals(maxGlobals)
+ , m_start(0)
+ , m_end(0)
+ , m_max(0)
+ , m_globalObject(globalData, 0, &m_globalObjectOwner, this)
+ {
+ ASSERT(maxGlobals && isPageAligned(maxGlobals));
+ ASSERT(capacity && isPageAligned(capacity));
+ size_t bufferLength = (capacity + maxGlobals) * sizeof(Register);
+ m_reservation = PageReservation::reserve(roundUpAllocationSize(bufferLength, commitSize), OSAllocator::JSVMStackPages);
+ void* base = m_reservation.base();
+ size_t committedSize = roundUpAllocationSize(maxGlobals * sizeof(Register), commitSize);
+ m_reservation.commit(base, committedSize);
+ addToCommittedByteCount(static_cast<long>(committedSize));
+ m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(base) + committedSize);
+ m_start = static_cast<Register*>(base) + maxGlobals;
+ m_end = m_start;
+ m_maxUsed = m_end;
+ m_max = m_start + capacity;
+ }
+
+ inline void RegisterFile::shrink(Register* newEnd)
+ {
+ if (newEnd >= m_end)
+ return;
+ m_end = newEnd;
+ if (m_end == m_start && (m_maxUsed - m_start) > maxExcessCapacity)
+ releaseExcessCapacity();
+ }
+
+ inline bool RegisterFile::grow(Register* newEnd)
+ {
+ if (newEnd < m_end)
+ return true;
+
+ if (newEnd > m_max)
+ return false;
+
+ if (newEnd > m_commitEnd) {
+ size_t size = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize);
+ m_reservation.commit(m_commitEnd, size);
+ addToCommittedByteCount(static_cast<long>(size));
+ m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + size);
+ }
+
+ if (newEnd > m_maxUsed)
+ m_maxUsed = newEnd;
+
+ m_end = newEnd;
+ return true;
+ }
+
} // namespace JSC
#endif // RegisterFile_h