Implemented gcmaps and stack frame tracing.
1 /* vim: set ts=4 sw=4 tw=99 et:
3 * Copyright (C) 2012 David Anderson
5 * This file is part of JITCraft.
7 * JITCraft is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation, either version 3 of the License, or (at your option)
12 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with
17 * JITCraft. If not, see http://www.gnu.org/licenses/.
19 #ifndef _include_bytecode_emitter_h_
20 #define _include_bytecode_emitter_h_
26 #include "NameBinding.h"
34 typedef std::vector<ScopedRoot<Object> *> ObjectList;
36 struct OopRecord : public PoolObject
42 OopRecord(unsigned length, unsigned offset)
56 bytecode *buffer_pos_;
57 bytecode *buffer_end_;
58 bytecode inline_buffer_[512];
60 unsigned maxStackDepth_;
62 std::vector<bool> oopMap_;
63 std::vector<OopRecord *> oopRecords_;
66 // The maximum size of a code object must be such that the distance between
67 // two instructions fits into a signed, 31-bit integer. This ensures that
68 // all code-bounded buffers (like the number of object literals or
69 // safepoints) does not need to be bounds checked. It also means we can add
70 // any two offsets and be guaranteed that they don't overflow.
71 static const size_t MAX_LENGTH = INT_MAX / 2;
74 bool growBuffer(size_t bytes);
76 // Checking the return value is not necessary, since the inline buffer is
78 bool ensure(size_t bytes) {
79 if (buffer_pos_ + bytes > buffer_end_)
80 return growBuffer(bytes);
84 void writeUint8(unsigned value) {
85 assert(value <= UCHAR_MAX);
86 *buffer_pos_++ = value;
88 void writeFloat(float value) {
89 *(float *)buffer_pos_ = value;
90 buffer_pos_ += sizeof(float);
92 void writeInt32(int value) {
93 *(int *)buffer_pos_ = value;
94 buffer_pos_ += sizeof(int);
96 void writeUint32(unsigned value) {
97 *(unsigned *)buffer_pos_ = value;
98 buffer_pos_ += sizeof(unsigned);
100 void writeOp(Opcode op) {
105 for (int i = 0; i < OpcodeInfo[op].nuses; i++)
107 for (int i = 0; i < OpcodeInfo[op].ndefs; i++)
108 oopMap_.push_back(false);
109 if (oopMap_.size() > maxStackDepth_)
110 maxStackDepth_ = oopMap_.size();
112 void writeObject(Object *obj) {
113 writeUint32(objects_.size());
114 ScopedRoot<Object> *root = new ScopedRoot<Object>(obj);
117 objects_.push_back(root);
120 int position() const {
121 assert(buffer_pos_ - buffer_ < INT_MAX / 2);
122 return int(buffer_pos_ - buffer_);
126 BytecodeEmitter(PoolAllocator &pool);
128 bytecode *buffer() const {
131 unsigned length() const {
132 return unsigned(buffer_pos_ - buffer_);
137 unsigned stackDepth() const {
138 return oopMap_.size();
140 unsigned maxStackDepth() const {
141 return maxStackDepth_;
143 const ObjectList *objects() const {
146 std::vector<OopRecord *> *oopRecords() {
150 void drop(unsigned amt) {
151 assert(amt <= stackDepth());
152 for (unsigned i = 0; i < amt; i++)
157 // A label represents a local control-flow target in the bytecode. A label
158 // may be in three states:
160 // (1) Empty - the label is not used yet.
161 // (2) Pending - the label has incoming jumps, but the actual location of
162 // is not yet known. The list of jumps is threaded through
163 // the four bytes reserved for each emitted jump target. In
164 // this case, the offset field is the location of the most
165 // recently emitted incoming jump.
166 // (3) Bound - the label's position has been pinned.
168 // A jump can be emitted to a label in any state. Binding a label
169 // immediately backpatches all pending incoming jumps. A label must be
170 // bound if it is used.
176 void setOffset(int offset) {
178 assert(offset_ == offset);
182 static const int INVALID_OFFSET = -1;
186 : offset_(INVALID_OFFSET),
192 assert(bound_ || offset_ == INVALID_OFFSET);
201 void bind(int offset) {
206 int link(int offset) {
215 int patchJumpAt(int at, int to) {
216 assert(at >= 0 && at < position());
217 int old = *(int *)(buffer_ + at + 1);
218 *(int *)(buffer_ + at + 1) = (to - at);
222 void singleByteOp(Opcode op) {
223 assert(OpcodeInfo[op].length == 1);
224 ensure(OpcodeInfo[op].length);
228 // Mark a stack slot as having an object pointer.
229 void markOop(unsigned depth, bool oop = true) {
232 oopMap_[oopMap_.size() - depth] = oop;
234 bool getOop(unsigned depth) {
237 return oopMap_[oopMap_.size() - depth];
240 // Mark the current bytecode position as either re-entrant (meaning, it
241 // can leave the interpreter or call to another function), or causing GC.
242 // This will record which stack slots have objects and associate that
243 // information with the current pc.
247 void int_(int value) {
256 void getlocal(Variable *var) {
257 ensure(OP_GETLOCAL_LENGTH);
258 writeOp(OP_GETLOCAL);
259 writeUint32(var->slot());
261 if (var->type()->isTraceable())
264 void setlocal(Variable *var) {
265 ensure(OP_SETLOCAL_LENGTH);
266 writeOp(OP_SETLOCAL);
267 writeUint32(var->slot());
269 if (var->type()->isTraceable())
272 void getarg(Variable *var) {
273 ensure(OP_GETARG_LENGTH);
275 writeUint32(var->slot());
277 if (var->type()->isTraceable())
280 void setarg(Variable *var) {
281 ensure(OP_SETARG_LENGTH);
283 writeUint32(var->slot());
285 if (var->type()->isTraceable())
289 void j(Opcode op, Label *label) {
290 // Since jumps can skip around the stream, do not try to do anything
291 // in a potentially truncated state.
295 assert(op >= OP_JL && op <= OP_JUMP);
296 ensure(OP_JUMP_LENGTH);
298 if (label->bound()) {
299 // We can immediately emit this jump.
300 int distance = label->offset() - position();
302 writeInt32(distance);
304 // We can't emit this jump yet. Add a placeholder.
305 int old = label->link(position());
310 void bind(Label *label) {
314 assert(!label->bound());
316 int offset = label->offset();
317 while (offset != Label::INVALID_OFFSET)
318 offset = patchJumpAt(offset, position());
320 label->bind(position());
322 void jump(Label *label) {
326 void binary(Opcode op) {
331 singleByteOp(OP_POP);
334 singleByteOp(OP_STOP);
336 void callglobal(unsigned index) {
338 ensure(OP_CALLGLOBAL_LENGTH);
339 writeOp(OP_CALLGLOBAL);
342 void getarray(PrimitiveType type) {
344 case PrimitiveType_Float:
345 singleByteOp(OP_GETARRAY_F);
347 case PrimitiveType_Int32:
348 singleByteOp(OP_GETARRAY_I);
351 assert(type == PrimitiveType_Char);
352 singleByteOp(OP_GETARRAY_C);
356 void setarray(PrimitiveType type) {
358 case PrimitiveType_Float:
359 singleByteOp(OP_SETARRAY_F);
361 case PrimitiveType_Int32:
362 singleByteOp(OP_SETARRAY_I);
365 assert(type == PrimitiveType_Char);
366 singleByteOp(OP_SETARRAY_C);
371 singleByteOp(OP_GETARRAY_O);
375 singleByteOp(OP_BOUNDS);
378 void float_(float value) {
379 ensure(OP_FLOAT_LENGTH);
384 singleByteOp(OP_CVT_I2F);
386 void newarray_lifo(Handle<Map> map) {
388 ensure(OP_NEWARRAY_LIFO_LENGTH);
389 writeOp(OP_NEWARRAY_LIFO);
393 void savelifo(unsigned slot) {
394 ensure(OP_SAVELIFO_LENGTH);
395 writeOp(OP_SAVELIFO);
398 void unwindlifo(unsigned slot) {
399 ensure(OP_UNWINDLIFO_LENGTH);
400 writeOp(OP_UNWINDLIFO);
403 void newref(Variable *var) {
405 if (var->location() == Variable::STACK) {
406 ensure(OP_VARREF_LIFO_LENGTH);
407 writeOp(OP_VARREF_LIFO);
409 assert(var->location() == Variable::PARAMETER);
410 ensure(OP_ARGREF_LIFO_LENGTH);
411 writeOp(OP_ARGREF_LIFO);
413 writeUint32(var->slot());
417 void slotref_lifo() {
419 singleByteOp(OP_SLOTREF_LIFO);
422 void arrayref_lifo() {
424 singleByteOp(OP_ARRAYREF_LIFO);
427 void getref(PrimitiveType type) {
428 if (type == PrimitiveType_Int32) {
429 singleByteOp(OP_GETREF_I);
431 assert(type == PrimitiveType_Float);
432 singleByteOp(OP_GETREF_F);
435 void setref(PrimitiveType type) {
436 if (type == PrimitiveType_Int32) {
437 singleByteOp(OP_SETREF_I);
439 assert(type == PrimitiveType_Float);
440 singleByteOp(OP_SETREF_F);
443 void object(Handle<Object> obj) {
444 ensure(OP_OBJECT_LENGTH);
450 singleByteOp(OP_ARRAYCOPY);
455 bool oop = getOop(1);
456 singleByteOp(OP_DUP);
460 bool oop2 = getOop(2);
461 bool oop1 = getOop(1);
463 singleByteOp(OP_DUP2);
469 bool oop2 = getOop(2);
470 bool oop1 = getOop(1);
472 singleByteOp(OP_SWAP);
478 bool oop3 = getOop(3);
479 bool oop2 = getOop(2);
480 bool oop1 = getOop(1);
482 singleByteOp(OP_ROLL3);
489 bool oop2 = getOop(2);
490 bool oop1 = getOop(1);
492 singleByteOp(OP_PICK2);
499 bool oop3 = getOop(3);
500 bool oop2 = getOop(2);
501 bool oop1 = getOop(1);
503 singleByteOp(OP_PICK3);
512 void get(Variable *var) {
513 if (var->location() == Variable::STACK) {
516 assert(var->location() == Variable::PARAMETER);
520 void set(Variable *var) {
521 assert(!var->type()->isReference());
522 if (var->location() == Variable::STACK) {
525 assert(var->location() == Variable::PARAMETER);
533 #endif // _include_bytecode_emitter_h_