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