|
dvander@47
|
1 |
/* vim: set ts=4 sw=4 tw=99 et: |
|
dvander@47
|
2 |
* |
|
dvander@0
|
3 |
* Copyright (C) 2012 David Anderson |
|
dvander@0
|
4 |
* |
|
dvander@0
|
5 |
* This file is part of JITCraft. |
|
dvander@0
|
6 |
* |
|
dvander@0
|
7 |
* JITCraft is free software: you can redistribute it and/or modify it under |
|
dvander@0
|
8 |
* the terms of the GNU General Public License as published by the Free |
|
dvander@0
|
9 |
* Software Foundation, either version 3 of the License, or (at your option) |
|
dvander@0
|
10 |
* any later version. |
|
dvander@0
|
11 |
* |
|
dvander@0
|
12 |
* Foobar is distributed in the hope that it will be useful, but WITHOUT ANY |
|
dvander@0
|
13 |
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
dvander@0
|
14 |
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
|
dvander@0
|
15 |
* |
|
dvander@0
|
16 |
* You should have received a copy of the GNU General Public License along with |
|
dvander@0
|
17 |
* JITCraft. If not, see http://www.gnu.org/licenses/. |
|
dvander@0
|
18 |
*/ |
|
dvander@0
|
19 |
#include <stdlib.h> |
|
dvander@177
|
20 |
#include <string.h> |
|
dvander@195
|
21 |
#include "../Utility.h" |
|
dvander@195
|
22 |
#include "../Types.h" |
|
dvander@195
|
23 |
#include "../Zone.h" |
|
dvander@195
|
24 |
#include "../Strings.h" |
|
dvander@195
|
25 |
#include "../Zone.h" |
|
dvander@0
|
26 |
#include "BytecodeEmitter.h" |
|
dvander@195
|
27 |
#include "Symbols.h" |
|
dvander@195
|
28 |
#include "Scopes.h" |
|
dvander@195
|
29 |
#include "../Spaces-inl.h" |
|
dvander@195
|
30 |
#include "../Heap-inl.h" |
|
dvander@0
|
31 |
|
|
dvander@59
|
32 |
using namespace ke; |
|
dvander@0
|
33 |
|
|
dvander@195
|
34 |
StringTable::StringTable() |
|
dvander@195
|
35 |
: set_(ZoneAllocatorPolicy()), |
|
dvander@195
|
36 |
initialized_(false) |
|
dvander@195
|
37 |
{ |
|
dvander@195
|
38 |
} |
|
dvander@195
|
39 |
|
|
dvander@195
|
40 |
bool |
|
dvander@195
|
41 |
StringTable::initialize() |
|
dvander@195
|
42 |
{ |
|
dvander@195
|
43 |
assert(!initialized_); |
|
dvander@195
|
44 |
initialized_ = true; |
|
dvander@195
|
45 |
if (!set_.init(16)) |
|
dvander@195
|
46 |
return false; |
|
dvander@195
|
47 |
return true; |
|
dvander@195
|
48 |
} |
|
dvander@195
|
49 |
|
|
dvander@195
|
50 |
bool |
|
dvander@195
|
51 |
StringTable::add(Handle<String> name, unsigned *indexp) |
|
dvander@195
|
52 |
{ |
|
dvander@195
|
53 |
if (!initialized_ && !initialize()) |
|
dvander@195
|
54 |
return false; |
|
dvander@195
|
55 |
|
|
dvander@195
|
56 |
Set::Insert p = set_.findForAdd(*name); |
|
dvander@195
|
57 |
if (p.found()) { |
|
dvander@195
|
58 |
*indexp = p->index; |
|
dvander@195
|
59 |
return true; |
|
dvander@195
|
60 |
} |
|
dvander@195
|
61 |
|
|
dvander@195
|
62 |
if (strings_.length() > INT_MAX) { |
|
dvander@195
|
63 |
ZONE()->reportAllocationOverflow(); |
|
dvander@195
|
64 |
return false; |
|
dvander@195
|
65 |
} |
|
dvander@195
|
66 |
|
|
dvander@195
|
67 |
Entry e(name, strings_.length()); |
|
dvander@195
|
68 |
if (!strings_.append(e)) |
|
dvander@195
|
69 |
return false; |
|
dvander@195
|
70 |
|
|
dvander@195
|
71 |
if (!set_.add(p, e)) |
|
dvander@195
|
72 |
return false; |
|
dvander@195
|
73 |
|
|
dvander@195
|
74 |
*indexp = e.index; |
|
dvander@195
|
75 |
|
|
dvander@195
|
76 |
return true; |
|
dvander@195
|
77 |
} |
|
dvander@195
|
78 |
|
|
dvander@195
|
79 |
void |
|
dvander@195
|
80 |
StringTable::accept(VirtualObjectVisitor *visitor) |
|
dvander@195
|
81 |
{ |
|
dvander@195
|
82 |
for (size_t i = 0; i < strings_.length(); i++) |
|
dvander@195
|
83 |
visitor->visit(reinterpret_cast<Object **>(&strings_[i].string)); |
|
dvander@195
|
84 |
} |
|
dvander@195
|
85 |
|
|
dvander@47
|
86 |
BytecodeEmitter::BytecodeEmitter(PoolAllocator &pool) |
|
dvander@195
|
87 |
: AutoRooted(ZONE()), |
|
dvander@195
|
88 |
pool_(pool), |
|
dvander@47
|
89 |
buffer_(inline_buffer_), |
|
dvander@0
|
90 |
buffer_pos_(buffer_), |
|
dvander@0
|
91 |
buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), |
|
dvander@0
|
92 |
oom_(false), |
|
dvander@47
|
93 |
maxStackDepth_(0) |
|
dvander@0
|
94 |
{ |
|
dvander@70
|
95 |
lastCodePosition_ = 0; |
|
dvander@70
|
96 |
firstLine_ = 1; |
|
dvander@0
|
97 |
} |
|
dvander@0
|
98 |
|
|
dvander@195
|
99 |
BytecodeEmitter::~BytecodeEmitter() |
|
dvander@195
|
100 |
{ |
|
dvander@195
|
101 |
for (size_t i = 0; i < oopRecords_.length(); i++) |
|
dvander@195
|
102 |
delete oopRecords_[i]; |
|
dvander@195
|
103 |
} |
|
dvander@195
|
104 |
|
|
dvander@195
|
105 |
void |
|
dvander@195
|
106 |
BytecodeEmitter::accept(VirtualObjectVisitor *visitor) |
|
dvander@195
|
107 |
{ |
|
dvander@195
|
108 |
for (size_t i = 0; i < objects_.length(); i++) |
|
dvander@195
|
109 |
visitor->visit(&objects_[i]); |
|
dvander@195
|
110 |
|
|
dvander@195
|
111 |
strings_.accept(visitor); |
|
dvander@195
|
112 |
} |
|
dvander@195
|
113 |
|
|
dvander@0
|
114 |
bool |
|
dvander@0
|
115 |
BytecodeEmitter::growBuffer(size_t nbytes) |
|
dvander@0
|
116 |
{ |
|
dvander@0
|
117 |
if (oom_) { |
|
dvander@0
|
118 |
buffer_pos_ = buffer_; |
|
dvander@0
|
119 |
return false; |
|
dvander@0
|
120 |
} |
|
dvander@0
|
121 |
|
|
dvander@0
|
122 |
size_t newSize = size_t((buffer_end_ - buffer_) * 2); |
|
dvander@0
|
123 |
if (newSize >= MAX_LENGTH) { |
|
dvander@0
|
124 |
oom_ = true; |
|
dvander@0
|
125 |
buffer_pos_ = buffer_; |
|
dvander@69
|
126 |
ZONE()->reportOutOfMemory(); |
|
dvander@0
|
127 |
return false; |
|
dvander@0
|
128 |
} |
|
dvander@0
|
129 |
|
|
dvander@0
|
130 |
bytecode *newBuffer; |
|
dvander@0
|
131 |
if (buffer_ == inline_buffer_) |
|
dvander@177
|
132 |
newBuffer = (bytecode *)malloc(newSize); |
|
dvander@0
|
133 |
else |
|
dvander@0
|
134 |
newBuffer = (bytecode *)realloc(buffer_, newSize); |
|
dvander@0
|
135 |
|
|
dvander@0
|
136 |
// If we can't allocate a buffer, reset the buffer to the start and set |
|
dvander@0
|
137 |
// a flag to indicate out-of-memory. |
|
dvander@0
|
138 |
if (!newBuffer) { |
|
dvander@0
|
139 |
oom_ = true; |
|
dvander@0
|
140 |
buffer_pos_ = buffer_; |
|
dvander@69
|
141 |
ZONE()->reportOutOfMemory(); |
|
dvander@0
|
142 |
return false; |
|
dvander@0
|
143 |
} |
|
dvander@0
|
144 |
|
|
dvander@177
|
145 |
if (buffer_ == inline_buffer_) |
|
dvander@177
|
146 |
memcpy(newBuffer, inline_buffer_, (buffer_pos_ - inline_buffer_)); |
|
dvander@177
|
147 |
|
|
dvander@0
|
148 |
buffer_pos_ = newBuffer + size_t(buffer_pos_ - buffer_); |
|
dvander@0
|
149 |
buffer_ = newBuffer; |
|
dvander@0
|
150 |
buffer_end_ = buffer_ + newSize; |
|
dvander@177
|
151 |
assert(buffer_pos_ + nbytes < buffer_end_); |
|
dvander@177
|
152 |
|
|
dvander@0
|
153 |
return true; |
|
dvander@0
|
154 |
} |
|
dvander@0
|
155 |
|
|
dvander@47
|
156 |
void |
|
dvander@47
|
157 |
BytecodeEmitter::recordOops() |
|
dvander@47
|
158 |
{ |
|
dvander@195
|
159 |
OopRecord *record = new OopRecord(oopMap_.length(), position()); |
|
dvander@47
|
160 |
|
|
dvander@195
|
161 |
if (!record->slots.ensure(record->depth)) { |
|
dvander@47
|
162 |
oom_ = true; |
|
dvander@47
|
163 |
return; |
|
dvander@47
|
164 |
} |
|
dvander@47
|
165 |
|
|
dvander@47
|
166 |
bool hadOops = false; |
|
dvander@58
|
167 |
for (size_t i = 0; i < oopMap_.length(); i++) { |
|
dvander@47
|
168 |
bool bit = oopMap_[i]; |
|
dvander@195
|
169 |
record->slots.infallibleAppend(bit); |
|
dvander@47
|
170 |
hadOops |= bit; |
|
dvander@47
|
171 |
} |
|
dvander@47
|
172 |
|
|
dvander@195
|
173 |
if (!hadOops) { |
|
dvander@195
|
174 |
delete record; |
|
dvander@47
|
175 |
return; |
|
dvander@195
|
176 |
} |
|
dvander@47
|
177 |
|
|
dvander@58
|
178 |
oom_ &= oopRecords_.append(record); |
|
dvander@47
|
179 |
} |
|
dvander@47
|
180 |
|
|
dvander@70
|
181 |
void |
|
dvander@70
|
182 |
BytecodeEmitter::note_position(const SourcePosition &pos) |
|
dvander@70
|
183 |
{ |
|
dvander@70
|
184 |
assert(pos.line > 0); |
|
dvander@70
|
185 |
assert(unsigned(pos.line) >= firstLine_); |
|
dvander@70
|
186 |
|
|
dvander@70
|
187 |
unsigned lineDelta = unsigned(pos.line) - firstLine_; |
|
dvander@70
|
188 |
unsigned pcDelta = unsigned(position()) - lastCodePosition_; |
|
dvander@70
|
189 |
|
|
dvander@70
|
190 |
sourcemap_.writeUint32(pcDelta); |
|
dvander@70
|
191 |
sourcemap_.writeUint32(lineDelta); |
|
dvander@70
|
192 |
sourcemap_.writeUint32(pos.col); |
|
dvander@70
|
193 |
|
|
dvander@70
|
194 |
lastCodePosition_ = unsigned(position()); |
|
dvander@70
|
195 |
} |
|
dvander@195
|
196 |
|
|
dvander@195
|
197 |
void |
|
dvander@195
|
198 |
BytecodeEmitter::getglobal(Symbol *sym) |
|
dvander@195
|
199 |
{ |
|
dvander@195
|
200 |
ensure(OP_GETGLOBAL_LENGTH); |
|
dvander@195
|
201 |
writeOp(OP_GETGLOBAL); |
|
dvander@195
|
202 |
writeUint32(addString(sym->name())); |
|
dvander@195
|
203 |
if (sym->type()->isTraceable()) |
|
dvander@195
|
204 |
markOop(1); |
|
dvander@195
|
205 |
} |
|
dvander@195
|
206 |
void |
|
dvander@195
|
207 |
BytecodeEmitter::setglobal(Symbol *sym) |
|
dvander@195
|
208 |
{ |
|
dvander@195
|
209 |
ensure(OP_SETGLOBAL_LENGTH); |
|
dvander@195
|
210 |
writeOp(OP_SETGLOBAL); |
|
dvander@195
|
211 |
writeUint32(addString(sym->name())); |
|
dvander@195
|
212 |
if (sym->type()->isTraceable()) |
|
dvander@195
|
213 |
markOop(1); |
|
dvander@195
|
214 |
} |
|
dvander@195
|
215 |
|
|
dvander@195
|
216 |
void |
|
dvander@195
|
217 |
BytecodeEmitter::getlocal(VariableSymbol *var) |
|
dvander@195
|
218 |
{ |
|
dvander@195
|
219 |
ensure(OP_GETLOCAL_LENGTH); |
|
dvander@195
|
220 |
writeOp(OP_GETLOCAL); |
|
dvander@195
|
221 |
writeUint32(var->slot()); |
|
dvander@195
|
222 |
if (var->type()->isTraceable()) |
|
dvander@195
|
223 |
markOop(1); |
|
dvander@195
|
224 |
} |
|
dvander@195
|
225 |
|
|
dvander@195
|
226 |
void |
|
dvander@195
|
227 |
BytecodeEmitter::setlocal(VariableSymbol *var) |
|
dvander@195
|
228 |
{ |
|
dvander@195
|
229 |
ensure(OP_SETLOCAL_LENGTH); |
|
dvander@195
|
230 |
writeOp(OP_SETLOCAL); |
|
dvander@195
|
231 |
writeUint32(var->slot()); |
|
dvander@195
|
232 |
if (var->type()->isTraceable()) |
|
dvander@195
|
233 |
markOop(1); |
|
dvander@195
|
234 |
} |
|
dvander@195
|
235 |
|
|
dvander@195
|
236 |
bool |
|
dvander@195
|
237 |
BytecodeEmitter::allocate(VariableSymbol *sym) |
|
dvander@195
|
238 |
{ |
|
dvander@195
|
239 |
assert(sym->isVariable()); |
|
dvander@195
|
240 |
assert(sym->scope()->kind() == Scope::BLOCK); |
|
dvander@195
|
241 |
|
|
dvander@195
|
242 |
switch (sym->scope()->kind()) { |
|
dvander@195
|
243 |
case Scope::BLOCK: |
|
dvander@195
|
244 |
{ |
|
dvander@195
|
245 |
// See overall comment on overflows. |
|
dvander@195
|
246 |
assert(locals_.length() < UINT_MAX); |
|
dvander@195
|
247 |
sym->allocate(VariableSymbol::Local, locals_.length()); |
|
dvander@195
|
248 |
if (!locals_.append(sym)) |
|
dvander@195
|
249 |
return false; |
|
dvander@195
|
250 |
break; |
|
dvander@195
|
251 |
} |
|
dvander@195
|
252 |
|
|
dvander@195
|
253 |
default: |
|
dvander@195
|
254 |
assert(false); |
|
dvander@195
|
255 |
return false; |
|
dvander@195
|
256 |
} |
|
dvander@195
|
257 |
|
|
dvander@195
|
258 |
return true; |
|
dvander@195
|
259 |
} |
|
dvander@195
|
260 |
|
|
dvander@195
|
261 |
Code * |
|
dvander@195
|
262 |
BytecodeEmitter::finish() |
|
dvander@195
|
263 |
{ |
|
dvander@195
|
264 |
stop(); |
|
dvander@195
|
265 |
|
|
dvander@195
|
266 |
Zone *zone = ZONE(); |
|
dvander@195
|
267 |
if (oom() || zone->hasPendingException()) |
|
dvander@195
|
268 |
return NULL; |
|
dvander@195
|
269 |
|
|
dvander@195
|
270 |
Local<ByteArray> bytes(zone, ByteArray::New(zone, length(), Heap::Tenure_Old)); |
|
dvander@195
|
271 |
if (!bytes) |
|
dvander@195
|
272 |
return NULL; |
|
dvander@195
|
273 |
memcpy(bytes->raw(), buffer(), length()); |
|
dvander@195
|
274 |
|
|
dvander@195
|
275 |
Local<FixedArray> objects(zone, FixedArray::New(zone, objects_.length(), Heap::Tenure_Old)); |
|
dvander@195
|
276 |
if (!objects) |
|
dvander@195
|
277 |
return NULL; |
|
dvander@195
|
278 |
|
|
dvander@195
|
279 |
for (size_t i = 0; i < objects_.length(); i++) |
|
dvander@195
|
280 |
objects->set(zone, i, objects_[i]); |
|
dvander@195
|
281 |
|
|
dvander@195
|
282 |
// If any stack slots hold gc pointers at potential failure points in the |
|
dvander@195
|
283 |
// bytecode, record them now. We do this by building a list of tuples; |
|
dvander@195
|
284 |
// each tuple contains the pc offset, and a bitset containing which stack |
|
dvander@195
|
285 |
// slots must be scanned by the gc. |
|
dvander@195
|
286 |
Local<ByteArray> safepoints(zone); |
|
dvander@195
|
287 |
if (oopRecords_.length()) { |
|
dvander@195
|
288 |
size_t bytesPerSafepoint = Code::BytesPerSafepoint(maxStackDepth()); |
|
dvander@195
|
289 |
size_t gcmapSize = bytesPerSafepoint * oopRecords_.length(); |
|
dvander@195
|
290 |
safepoints = ByteArray::New(zone, gcmapSize, Heap::Tenure_Old); |
|
dvander@195
|
291 |
if (!safepoints) |
|
dvander@195
|
292 |
return NULL; |
|
dvander@195
|
293 |
|
|
dvander@195
|
294 |
Address cursor = safepoints->raw(); |
|
dvander@195
|
295 |
for (size_t i = 0; i < oopRecords_.length(); i++) { |
|
dvander@195
|
296 |
OopRecord *record = oopRecords_[i]; |
|
dvander@195
|
297 |
assert(record->depth <= maxStackDepth()); |
|
dvander@195
|
298 |
|
|
dvander@195
|
299 |
*reinterpret_cast<unsigned *>(cursor) = record->offset; |
|
dvander@195
|
300 |
|
|
dvander@195
|
301 |
unsigned char *bits = reinterpret_cast<unsigned char *>(cursor + sizeof(unsigned)); |
|
dvander@195
|
302 |
for (unsigned i = 0; i < record->depth; i++) |
|
dvander@195
|
303 |
bits[i / 8] |= (1 << (i % 8)); |
|
dvander@195
|
304 |
|
|
dvander@195
|
305 |
cursor += bytesPerSafepoint; |
|
dvander@195
|
306 |
} |
|
dvander@195
|
307 |
|
|
dvander@195
|
308 |
assert(cursor == safepoints->raw() + gcmapSize); |
|
dvander@195
|
309 |
} |
|
dvander@195
|
310 |
|
|
dvander@195
|
311 |
Local<ByteArray> sourcemap(zone); |
|
dvander@195
|
312 |
if (sourcemap_.length()) { |
|
dvander@195
|
313 |
sourcemap = ByteArray::New(zone, sourcemap_.length(), Heap::Tenure_Old); |
|
dvander@195
|
314 |
if (!sourcemap) |
|
dvander@195
|
315 |
return NULL; |
|
dvander@195
|
316 |
|
|
dvander@195
|
317 |
memcpy(sourcemap->raw(), sourcemap_.buffer(), sourcemap_.length()); |
|
dvander@195
|
318 |
} |
|
dvander@195
|
319 |
|
|
dvander@195
|
320 |
Local<FixedArray> strings(zone, FixedArray::New(zone, strings_.stringCount(), Heap::Tenure_Old)); |
|
dvander@195
|
321 |
if (!strings) |
|
dvander@195
|
322 |
return NULL; |
|
dvander@195
|
323 |
for (size_t i = 0; i < strings_.stringCount(); i++) |
|
dvander@195
|
324 |
strings->set(zone, i, strings_.stringAt(i)); |
|
dvander@195
|
325 |
|
|
dvander@195
|
326 |
Local<FixedArray> locals(zone, FixedArray::New(zone, locals_.length(), Heap::Tenure_Old)); |
|
dvander@195
|
327 |
if (!locals) |
|
dvander@195
|
328 |
return NULL; |
|
dvander@195
|
329 |
for (size_t i = 0; i < locals_.length(); i++) |
|
dvander@195
|
330 |
locals->set(zone, i, locals_[i]->type()); |
|
dvander@195
|
331 |
|
|
dvander@195
|
332 |
Local<Code> code(zone); |
|
dvander@195
|
333 |
|
|
dvander@195
|
334 |
code = Code::New(zone, |
|
dvander@195
|
335 |
bytes, |
|
dvander@195
|
336 |
locals, |
|
dvander@195
|
337 |
objects, |
|
dvander@195
|
338 |
strings, |
|
dvander@195
|
339 |
maxStackDepth(), |
|
dvander@195
|
340 |
safepoints, |
|
dvander@195
|
341 |
sourcemap); |
|
dvander@195
|
342 |
|
|
dvander@195
|
343 |
return code; |
|
dvander@195
|
344 |
} |