|
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 "Collector.h" |
|
33 #include "ExecutableAllocator.h" |
|
34 #include "Register.h" |
|
35 #include "WeakGCPtr.h" |
|
36 #include <stdio.h> |
|
37 #include <wtf/Noncopyable.h> |
|
38 #include <wtf/VMTags.h> |
|
39 |
|
40 #if HAVE(MMAP) |
|
41 #include <errno.h> |
|
42 #include <sys/mman.h> |
|
43 #endif |
|
44 |
|
45 namespace JSC { |
|
46 |
|
47 /* |
|
48 A register file is a stack of register frames. We represent a register |
|
49 frame by its offset from "base", the logical first entry in the register |
|
50 file. The bottom-most register frame's offset from base is 0. |
|
51 |
|
52 In a program where function "a" calls function "b" (global code -> a -> b), |
|
53 the register file might look like this: |
|
54 |
|
55 | global frame | call frame | call frame | spare capacity | |
|
56 ----------------------------------------------------------------------------------------------------- |
|
57 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | | | | | | <-- index in buffer |
|
58 ----------------------------------------------------------------------------------------------------- |
|
59 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | | | | | | <-- index relative to base |
|
60 ----------------------------------------------------------------------------------------------------- |
|
61 | <-globals | temps-> | <-vars | temps-> | <-vars | |
|
62 ^ ^ ^ ^ |
|
63 | | | | |
|
64 buffer base (frame 0) frame 1 frame 2 |
|
65 |
|
66 Since all variables, including globals, are accessed by negative offsets |
|
67 from their register frame pointers, to keep old global offsets correct, new |
|
68 globals must appear at the beginning of the register file, shifting base |
|
69 to the right. |
|
70 |
|
71 If we added one global variable to the register file depicted above, it |
|
72 would look like this: |
|
73 |
|
74 | global frame |< > |
|
75 -------------------------------> < |
|
76 | 0 | 1 | 2 | 3 | 4 | 5 |< >snip< > <-- index in buffer |
|
77 -------------------------------> < |
|
78 | -4 | -3 | -2 | -1 | 0 | 1 |< > <-- index relative to base |
|
79 -------------------------------> < |
|
80 | <-globals | temps-> | |
|
81 ^ ^ |
|
82 | | |
|
83 buffer base (frame 0) |
|
84 |
|
85 As you can see, global offsets relative to base have stayed constant, |
|
86 but base itself has moved. To keep up with possible changes to base, |
|
87 clients keep an indirect pointer, so their calculations update |
|
88 automatically when base changes. |
|
89 |
|
90 For client simplicity, the RegisterFile measures size and capacity from |
|
91 "base", not "buffer". |
|
92 */ |
|
93 |
|
94 class JSGlobalObject; |
|
95 |
|
96 class RegisterFile : public Noncopyable { |
|
97 friend class JIT; |
|
98 public: |
|
99 enum CallFrameHeaderEntry { |
|
100 CallFrameHeaderSize = 6, |
|
101 |
|
102 ArgumentCount = -6, |
|
103 CallerFrame = -5, |
|
104 Callee = -4, |
|
105 ScopeChain = -3, |
|
106 ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. |
|
107 CodeBlock = -1, |
|
108 }; |
|
109 |
|
110 enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 }; |
|
111 |
|
112 static const size_t defaultCapacity = 524288; |
|
113 static const size_t defaultMaxGlobals = 8192; |
|
114 static const size_t commitSize = 1 << 14; |
|
115 // Allow 8k of excess registers before we start trying to reap the registerfile |
|
116 static const ptrdiff_t maxExcessCapacity = 8 * 1024; |
|
117 |
|
118 RegisterFile(size_t capacity = defaultCapacity, size_t maxGlobals = defaultMaxGlobals); |
|
119 ~RegisterFile(); |
|
120 |
|
121 Register* start() const { return m_start; } |
|
122 Register* end() const { return m_end; } |
|
123 size_t size() const { return m_end - m_start; } |
|
124 |
|
125 void setGlobalObject(JSGlobalObject*); |
|
126 bool clearGlobalObject(JSGlobalObject*); |
|
127 JSGlobalObject* globalObject(); |
|
128 |
|
129 bool grow(Register* newEnd); |
|
130 void shrink(Register* newEnd); |
|
131 |
|
132 void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; } |
|
133 int numGlobals() const { return m_numGlobals; } |
|
134 size_t maxGlobals() const { return m_maxGlobals; } |
|
135 |
|
136 Register* lastGlobal() const { return m_start - m_numGlobals; } |
|
137 |
|
138 void markGlobals(MarkStack& markStack, Heap* heap) { heap->markConservatively(markStack, lastGlobal(), m_start); } |
|
139 void markCallFrames(MarkStack& markStack, Heap* heap) { heap->markConservatively(markStack, m_start, m_end); } |
|
140 |
|
141 private: |
|
142 void releaseExcessCapacity(); |
|
143 size_t m_numGlobals; |
|
144 const size_t m_maxGlobals; |
|
145 Register* m_start; |
|
146 Register* m_end; |
|
147 Register* m_max; |
|
148 Register* m_buffer; |
|
149 Register* m_maxUsed; |
|
150 |
|
151 #if HAVE(VIRTUALALLOC) |
|
152 Register* m_commitEnd; |
|
153 #endif |
|
154 |
|
155 WeakGCPtr<JSGlobalObject> m_globalObject; // The global object whose vars are currently stored in the register file. |
|
156 }; |
|
157 |
|
158 // FIXME: Add a generic getpagesize() to WTF, then move this function to WTF as well. |
|
159 inline bool isPageAligned(size_t size) { return size != 0 && size % (8 * 1024) == 0; } |
|
160 |
|
161 inline RegisterFile::RegisterFile(size_t capacity, size_t maxGlobals) |
|
162 : m_numGlobals(0) |
|
163 , m_maxGlobals(maxGlobals) |
|
164 , m_start(0) |
|
165 , m_end(0) |
|
166 , m_max(0) |
|
167 , m_buffer(0) |
|
168 { |
|
169 // Verify that our values will play nice with mmap and VirtualAlloc. |
|
170 ASSERT(isPageAligned(maxGlobals)); |
|
171 ASSERT(isPageAligned(capacity)); |
|
172 |
|
173 size_t bufferLength = (capacity + maxGlobals) * sizeof(Register); |
|
174 #if HAVE(MMAP) |
|
175 m_buffer = static_cast<Register*>(mmap(0, bufferLength, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, VM_TAG_FOR_REGISTERFILE_MEMORY, 0)); |
|
176 if (m_buffer == MAP_FAILED) { |
|
177 #if OS(WINCE) |
|
178 fprintf(stderr, "Could not allocate register file: %d\n", GetLastError()); |
|
179 #else |
|
180 fprintf(stderr, "Could not allocate register file: %d\n", errno); |
|
181 #endif |
|
182 CRASH(); |
|
183 } |
|
184 #elif HAVE(VIRTUALALLOC) |
|
185 m_buffer = static_cast<Register*>(VirtualAlloc(0, roundUpAllocationSize(bufferLength, commitSize), MEM_RESERVE, PAGE_READWRITE)); |
|
186 if (!m_buffer) { |
|
187 #if OS(WINCE) |
|
188 fprintf(stderr, "Could not allocate register file: %d\n", GetLastError()); |
|
189 #else |
|
190 fprintf(stderr, "Could not allocate register file: %d\n", errno); |
|
191 #endif |
|
192 CRASH(); |
|
193 } |
|
194 size_t committedSize = roundUpAllocationSize(maxGlobals * sizeof(Register), commitSize); |
|
195 void* commitCheck = VirtualAlloc(m_buffer, committedSize, MEM_COMMIT, PAGE_READWRITE); |
|
196 if (commitCheck != m_buffer) { |
|
197 #if OS(WINCE) |
|
198 fprintf(stderr, "Could not allocate register file: %d\n", GetLastError()); |
|
199 #else |
|
200 fprintf(stderr, "Could not allocate register file: %d\n", errno); |
|
201 #endif |
|
202 CRASH(); |
|
203 } |
|
204 m_commitEnd = reinterpret_cast<Register*>(reinterpret_cast<char*>(m_buffer) + committedSize); |
|
205 #else |
|
206 /* |
|
207 * If neither MMAP nor VIRTUALALLOC are available - use fastMalloc instead. |
|
208 * |
|
209 * Please note that this is the fallback case, which is non-optimal. |
|
210 * If any possible, the platform should provide for a better memory |
|
211 * allocation mechanism that allows for "lazy commit" or dynamic |
|
212 * pre-allocation, similar to mmap or VirtualAlloc, to avoid waste of memory. |
|
213 */ |
|
214 m_buffer = static_cast<Register*>(fastMalloc(bufferLength)); |
|
215 #endif |
|
216 m_start = m_buffer + maxGlobals; |
|
217 m_end = m_start; |
|
218 m_maxUsed = m_end; |
|
219 m_max = m_start + capacity; |
|
220 } |
|
221 |
|
222 inline void RegisterFile::shrink(Register* newEnd) |
|
223 { |
|
224 if (newEnd >= m_end) |
|
225 return; |
|
226 m_end = newEnd; |
|
227 if (m_end == m_start && (m_maxUsed - m_start) > maxExcessCapacity) |
|
228 releaseExcessCapacity(); |
|
229 } |
|
230 |
|
231 inline bool RegisterFile::grow(Register* newEnd) |
|
232 { |
|
233 if (newEnd < m_end) |
|
234 return true; |
|
235 |
|
236 if (newEnd > m_max) |
|
237 return false; |
|
238 |
|
239 #if !HAVE(MMAP) && HAVE(VIRTUALALLOC) |
|
240 if (newEnd > m_commitEnd) { |
|
241 size_t size = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize); |
|
242 if (!VirtualAlloc(m_commitEnd, size, MEM_COMMIT, PAGE_READWRITE)) { |
|
243 #if OS(WINCE) |
|
244 fprintf(stderr, "Could not allocate register file: %d\n", GetLastError()); |
|
245 #else |
|
246 fprintf(stderr, "Could not allocate register file: %d\n", errno); |
|
247 #endif |
|
248 CRASH(); |
|
249 } |
|
250 m_commitEnd = reinterpret_cast<Register*>(reinterpret_cast<char*>(m_commitEnd) + size); |
|
251 } |
|
252 #endif |
|
253 |
|
254 if (newEnd > m_maxUsed) |
|
255 m_maxUsed = newEnd; |
|
256 |
|
257 m_end = newEnd; |
|
258 return true; |
|
259 } |
|
260 |
|
261 } // namespace JSC |
|
262 |
|
263 #endif // RegisterFile_h |