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

source code of qtscript/src/3rdparty/javascriptcore/JavaScriptCore/interpreter/RegisterFile.h