1 /* 2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #ifndef RegisterFile_h 30 #define RegisterFile_h 31 32 #include "Heap.h" 33 #include "ExecutableAllocator.h" 34 #include "Register.h" 35 #include "Weak.h" 36 #include <stdio.h> 37 #include <wtf/Noncopyable.h> 38 #include <wtf/PageReservation.h> 39 #include <wtf/VMTags.h> 40 41 namespace JSC { 42 43 /* 44 A register file is a stack of register frames. We represent a register 45 frame by its offset from "base", the logical first entry in the register 46 file. The bottom-most register frame's offset from base is 0. 47 48 In a program where function "a" calls function "b" (global code -> a -> b), 49 the register file might look like this: 50 51 | global frame | call frame | call frame | spare capacity | 52 ----------------------------------------------------------------------------------------------------- 53 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | | | | | | <-- index in buffer 54 ----------------------------------------------------------------------------------------------------- 55 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | | | | | | <-- index relative to base 56 ----------------------------------------------------------------------------------------------------- 57 | <-globals | temps-> | <-vars | temps-> | <-vars | 58 ^ ^ ^ ^ 59 | | | | 60 buffer base (frame 0) frame 1 frame 2 61 62 Since all variables, including globals, are accessed by negative offsets 63 from their register frame pointers, to keep old global offsets correct, new 64 globals must appear at the beginning of the register file, shifting base 65 to the right. 66 67 If we added one global variable to the register file depicted above, it 68 would look like this: 69 70 | global frame |< > 71 -------------------------------> < 72 | 0 | 1 | 2 | 3 | 4 | 5 |< >snip< > <-- index in buffer 73 -------------------------------> < 74 | -4 | -3 | -2 | -1 | 0 | 1 |< > <-- index relative to base 75 -------------------------------> < 76 | <-globals | temps-> | 77 ^ ^ 78 | | 79 buffer base (frame 0) 80 81 As you can see, global offsets relative to base have stayed constant, 82 but base itself has moved. To keep up with possible changes to base, 83 clients keep an indirect pointer, so their calculations update 84 automatically when base changes. 85 86 For client simplicity, the RegisterFile measures size and capacity from 87 "base", not "buffer". 88 */ 89 90 class JSGlobalObject; 91 92 class RegisterFile { 93 WTF_MAKE_NONCOPYABLE(RegisterFile); 94 public: 95 enum CallFrameHeaderEntry { 96 CallFrameHeaderSize = 6, 97 98 ArgumentCount = -6, 99 CallerFrame = -5, 100 Callee = -4, 101 ScopeChain = -3, 102 ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. 103 CodeBlock = -1, 104 }; 105 106 enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 }; 107 108 static const size_t defaultCapacity = 512 * 1024; 109 static const size_t defaultMaxGlobals = 8 * 1024; 110 static const size_t commitSize = 16 * 1024; 111 // Allow 8k of excess registers before we start trying to reap the registerfile 112 static const ptrdiff_t maxExcessCapacity = 8 * 1024; 113 114 RegisterFile(JSGlobalData&, size_t capacity = defaultCapacity, size_t maxGlobals = defaultMaxGlobals); 115 ~RegisterFile(); 116 117 void gatherConservativeRoots(ConservativeRoots&); 118 start()119 Register* start() const { return m_start; } end()120 Register* end() const { return m_end; } size()121 size_t size() const { return m_end - m_start; } 122 123 void setGlobalObject(JSGlobalObject*); 124 JSGlobalObject* globalObject(); 125 126 bool grow(Register* newEnd); 127 void shrink(Register* newEnd); 128 setNumGlobals(size_t numGlobals)129 void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; } numGlobals()130 int numGlobals() const { return m_numGlobals; } maxGlobals()131 size_t maxGlobals() const { return m_maxGlobals; } 132 lastGlobal()133 Register* lastGlobal() const { return m_start - m_numGlobals; } 134 135 static size_t committedByteCount(); 136 static void initializeThreading(); 137 addressOfEnd()138 Register* const * addressOfEnd() const 139 { 140 return &m_end; 141 } 142 143 private: 144 void releaseExcessCapacity(); 145 void addToCommittedByteCount(long); 146 size_t m_numGlobals; 147 const size_t m_maxGlobals; 148 Register* m_start; 149 Register* m_end; 150 Register* m_max; 151 Register* m_maxUsed; 152 Register* m_commitEnd; 153 PageReservation m_reservation; 154 155 Weak<JSGlobalObject> m_globalObject; // The global object whose vars are currently stored in the register file. 156 class GlobalObjectOwner : public WeakHandleOwner { finalize(Handle<Unknown>,void * context)157 virtual void finalize(Handle<Unknown>, void* context) 158 { 159 static_cast<RegisterFile*>(context)->setNumGlobals(0); 160 } 161 } m_globalObjectOwner; 162 }; 163 RegisterFile(JSGlobalData & globalData,size_t capacity,size_t maxGlobals)164 inline RegisterFile::RegisterFile(JSGlobalData& globalData, size_t capacity, size_t maxGlobals) 165 : m_numGlobals(0) 166 , m_maxGlobals(maxGlobals) 167 , m_start(0) 168 , m_end(0) 169 , m_max(0) 170 , m_globalObject(globalData, 0, &m_globalObjectOwner, this) 171 { 172 ASSERT(maxGlobals && isPageAligned(maxGlobals)); 173 ASSERT(capacity && isPageAligned(capacity)); 174 size_t bufferLength = (capacity + maxGlobals) * sizeof(Register); 175 m_reservation = PageReservation::reserve(roundUpAllocationSize(bufferLength, commitSize), OSAllocator::JSVMStackPages); 176 void* base = m_reservation.base(); 177 size_t committedSize = roundUpAllocationSize(maxGlobals * sizeof(Register), commitSize); 178 m_reservation.commit(base, committedSize); 179 addToCommittedByteCount(static_cast<long>(committedSize)); 180 m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(base) + committedSize); 181 m_start = static_cast<Register*>(base) + maxGlobals; 182 m_end = m_start; 183 m_maxUsed = m_end; 184 m_max = m_start + capacity; 185 } 186 shrink(Register * newEnd)187 inline void RegisterFile::shrink(Register* newEnd) 188 { 189 if (newEnd >= m_end) 190 return; 191 m_end = newEnd; 192 if (m_end == m_start && (m_maxUsed - m_start) > maxExcessCapacity) 193 releaseExcessCapacity(); 194 } 195 grow(Register * newEnd)196 inline bool RegisterFile::grow(Register* newEnd) 197 { 198 if (newEnd < m_end) 199 return true; 200 201 if (newEnd > m_max) 202 return false; 203 204 if (newEnd > m_commitEnd) { 205 size_t size = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize); 206 m_reservation.commit(m_commitEnd, size); 207 addToCommittedByteCount(static_cast<long>(size)); 208 m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + size); 209 } 210 211 if (newEnd > m_maxUsed) 212 m_maxUsed = newEnd; 213 214 m_end = newEnd; 215 return true; 216 } 217 218 } // namespace JSC 219 220 #endif // RegisterFile_h 221