dvander@33: /* vim: set ts=4 sw=4 tw=99 et: dvander@33: * dvander@0: * Copyright (C) 2012 David Anderson dvander@0: * dvander@0: * This file is part of JITCraft. dvander@0: * dvander@0: * JITCraft is free software: you can redistribute it and/or modify it under dvander@0: * the terms of the GNU General Public License as published by the Free dvander@0: * Software Foundation, either version 3 of the License, or (at your option) dvander@0: * any later version. dvander@0: * dvander@0: * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY dvander@0: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@0: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. dvander@0: * dvander@0: * You should have received a copy of the GNU General Public License along with dvander@0: * JITCraft. If not, see http://www.gnu.org/licenses/. dvander@0: */ dvander@0: #ifndef _include_bytecode_emitter_h_ dvander@0: #define _include_bytecode_emitter_h_ dvander@0: dvander@0: #include dvander@0: #include dvander@0: #include "Opcodes.h" dvander@14: #include "NameBinding.h" dvander@33: #include "Handles.h" dvander@70: #include "CompactBuffer.h" dvander@0: dvander@59: namespace ke { dvander@0: dvander@0: class Type; dvander@12: class Map; dvander@0: dvander@64: typedef PoolList> ObjectList; dvander@33: dvander@47: struct OopRecord : public PoolObject dvander@47: { dvander@47: unsigned depth; dvander@47: unsigned offset; dvander@47: bool *slots; dvander@47: dvander@47: OopRecord(unsigned length, unsigned offset) dvander@47: : depth(length), dvander@47: offset(offset), dvander@47: slots(NULL) dvander@47: { dvander@47: } dvander@47: }; dvander@47: dvander@0: class BytecodeEmitter dvander@0: { dvander@47: private: dvander@47: PoolAllocator &pool_; dvander@47: dvander@0: bytecode *buffer_; dvander@0: bytecode *buffer_pos_; dvander@0: bytecode *buffer_end_; dvander@0: bytecode inline_buffer_[512]; dvander@0: bool oom_; dvander@0: unsigned maxStackDepth_; dvander@33: ObjectList objects_; dvander@58: PoolList oopMap_; dvander@58: PoolList oopRecords_; dvander@70: CompactBufferWriter sourcemap_; dvander@70: unsigned lastCodePosition_; dvander@70: unsigned firstLine_; dvander@0: dvander@0: private: dvander@47: // The maximum size of a code object must be such that the distance between dvander@47: // two instructions fits into a signed, 31-bit integer. This ensures that dvander@47: // all code-bounded buffers (like the number of object literals or dvander@47: // safepoints) does not need to be bounds checked. It also means we can add dvander@47: // any two offsets and be guaranteed that they don't overflow. dvander@0: static const size_t MAX_LENGTH = INT_MAX / 2; dvander@0: dvander@0: private: dvander@0: bool growBuffer(size_t bytes); dvander@0: dvander@0: // Checking the return value is not necessary, since the inline buffer is dvander@0: // used as a ballast. dvander@0: bool ensure(size_t bytes) { dvander@0: if (buffer_pos_ + bytes > buffer_end_) dvander@0: return growBuffer(bytes); dvander@0: return true; dvander@0: } dvander@0: dvander@0: void writeUint8(unsigned value) { dvander@47: assert(value <= UCHAR_MAX); dvander@0: *buffer_pos_++ = value; dvander@0: } dvander@4: void writeFloat(float value) { dvander@4: *(float *)buffer_pos_ = value; dvander@4: buffer_pos_ += sizeof(float); dvander@4: } dvander@0: void writeInt32(int value) { dvander@0: *(int *)buffer_pos_ = value; dvander@0: buffer_pos_ += sizeof(int); dvander@0: } dvander@0: void writeUint32(unsigned value) { dvander@0: *(unsigned *)buffer_pos_ = value; dvander@0: buffer_pos_ += sizeof(unsigned); dvander@0: } dvander@0: void writeOp(Opcode op) { dvander@47: if (oom_) dvander@47: return; dvander@47: dvander@0: writeUint8(op); dvander@47: for (int i = 0; i < OpcodeInfo[op].nuses; i++) dvander@58: oopMap_.pop(); dvander@47: for (int i = 0; i < OpcodeInfo[op].ndefs; i++) dvander@58: oopMap_.append(false); dvander@58: if (oopMap_.length() > maxStackDepth_) dvander@58: maxStackDepth_ = oopMap_.length(); dvander@0: } dvander@21: void writeObject(Object *obj) { dvander@58: writeUint32(objects_.length()); dvander@64: ScopedRoot root(obj); dvander@70: if (!objects_.append(root)) dvander@70: oom_ = true; dvander@12: } dvander@0: dvander@0: int position() const { dvander@0: assert(buffer_pos_ - buffer_ < INT_MAX / 2); dvander@0: return int(buffer_pos_ - buffer_); dvander@0: } dvander@0: dvander@0: public: dvander@47: BytecodeEmitter(PoolAllocator &pool); dvander@0: dvander@0: bytecode *buffer() const { dvander@0: return buffer_; dvander@0: } dvander@0: unsigned length() const { dvander@0: return unsigned(buffer_pos_ - buffer_); dvander@0: } dvander@0: bool oom() const { dvander@70: return oom_ || sourcemap_.oom(); dvander@0: } dvander@0: unsigned stackDepth() const { dvander@58: return oopMap_.length(); dvander@0: } dvander@0: unsigned maxStackDepth() const { dvander@0: return maxStackDepth_; dvander@0: } dvander@33: const ObjectList *objects() const { dvander@21: return &objects_; dvander@12: } dvander@58: PoolList *oopRecords() { dvander@47: return &oopRecords_; dvander@47: } dvander@0: dvander@46: void drop(unsigned amt) { dvander@47: assert(amt <= stackDepth()); dvander@47: for (unsigned i = 0; i < amt; i++) dvander@58: oopMap_.pop(); dvander@46: } dvander@46: dvander@70: void note_position(const SourcePosition &pos); dvander@70: uint8 *sourcemap() const { dvander@70: return sourcemap_.buffer(); dvander@70: } dvander@70: size_t sourcemapLength() const { dvander@70: return sourcemap_.length(); dvander@70: } dvander@70: void setFirstLine(unsigned line) { dvander@70: assert(!sourcemap_.length()); dvander@70: firstLine_ = line; dvander@70: } dvander@70: dvander@0: public: dvander@0: // A label represents a local control-flow target in the bytecode. A label dvander@0: // may be in three states: dvander@0: // dvander@0: // (1) Empty - the label is not used yet. dvander@0: // (2) Pending - the label has incoming jumps, but the actual location of dvander@0: // is not yet known. The list of jumps is threaded through dvander@0: // the four bytes reserved for each emitted jump target. In dvander@0: // this case, the offset field is the location of the most dvander@0: // recently emitted incoming jump. dvander@0: // (3) Bound - the label's position has been pinned. dvander@0: // dvander@0: // A jump can be emitted to a label in any state. Binding a label dvander@0: // immediately backpatches all pending incoming jumps. A label must be dvander@0: // bound if it is used. dvander@0: class Label dvander@0: { dvander@0: int offset_ : 31; dvander@0: bool bound_ : 1; dvander@0: dvander@0: void setOffset(int offset) { dvander@0: offset_ = offset; dvander@0: assert(offset_ == offset); dvander@0: } dvander@0: dvander@0: public: dvander@0: static const int INVALID_OFFSET = -1; dvander@0: dvander@0: public: dvander@0: Label() dvander@0: : offset_(INVALID_OFFSET), dvander@0: bound_(false) dvander@0: { dvander@0: } dvander@0: ~Label() dvander@0: { dvander@0: assert(bound_ || offset_ == INVALID_OFFSET); dvander@0: } dvander@0: dvander@0: bool bound() const { dvander@0: return bound_; dvander@0: } dvander@0: int offset() const { dvander@0: return offset_; dvander@0: } dvander@0: void bind(int offset) { dvander@0: assert(!bound_); dvander@0: setOffset(offset); dvander@0: bound_ = true; dvander@0: } dvander@0: int link(int offset) { dvander@0: assert(!bound_); dvander@0: int old = offset_; dvander@0: setOffset(offset); dvander@0: return old; dvander@0: } dvander@0: }; dvander@0: dvander@0: private: dvander@0: int patchJumpAt(int at, int to) { dvander@0: assert(at >= 0 && at < position()); dvander@0: int old = *(int *)(buffer_ + at + 1); dvander@0: *(int *)(buffer_ + at + 1) = (to - at); dvander@0: return old; dvander@0: } dvander@0: dvander@0: void singleByteOp(Opcode op) { dvander@0: assert(OpcodeInfo[op].length == 1); dvander@0: ensure(OpcodeInfo[op].length); dvander@0: writeOp(op); dvander@0: } dvander@0: dvander@47: // Mark a stack slot as having an object pointer. dvander@47: void markOop(unsigned depth, bool oop = true) { dvander@47: if (oom_) dvander@47: return; dvander@58: oopMap_[oopMap_.length() - depth] = oop; dvander@47: } dvander@47: bool getOop(unsigned depth) { dvander@47: if (oom_) dvander@47: return false; dvander@58: return oopMap_[oopMap_.length() - depth]; dvander@47: } dvander@47: dvander@47: // Mark the current bytecode position as either re-entrant (meaning, it dvander@47: // can leave the interpreter or call to another function), or causing GC. dvander@47: // This will record which stack slots have objects and associate that dvander@47: // information with the current pc. dvander@47: void recordOops(); dvander@47: dvander@0: public: dvander@0: void int_(int value) { dvander@0: ensure(OP_INT); dvander@0: writeOp(OP_INT); dvander@0: writeInt32(value); dvander@0: } dvander@0: void return_() { dvander@0: ensure(OP_RETURN); dvander@0: writeOp(OP_RETURN); dvander@0: } dvander@47: void getlocal(Variable *var) { dvander@0: ensure(OP_GETLOCAL_LENGTH); dvander@0: writeOp(OP_GETLOCAL); dvander@47: writeUint32(var->slot()); dvander@47: dvander@47: if (var->type()->isTraceable()) dvander@47: markOop(1); dvander@0: } dvander@47: void setlocal(Variable *var) { dvander@0: ensure(OP_SETLOCAL_LENGTH); dvander@0: writeOp(OP_SETLOCAL); dvander@47: writeUint32(var->slot()); dvander@47: dvander@47: if (var->type()->isTraceable()) dvander@47: markOop(1); dvander@0: } dvander@47: void getarg(Variable *var) { dvander@0: ensure(OP_GETARG_LENGTH); dvander@0: writeOp(OP_GETARG); dvander@47: writeUint32(var->slot()); dvander@47: dvander@47: if (var->type()->isTraceable()) dvander@47: markOop(1); dvander@0: } dvander@47: void setarg(Variable *var) { dvander@0: ensure(OP_SETARG_LENGTH); dvander@0: writeOp(OP_SETARG); dvander@47: writeUint32(var->slot()); dvander@47: dvander@47: if (var->type()->isTraceable()) dvander@47: markOop(1); dvander@0: } dvander@0: dvander@0: void j(Opcode op, Label *label) { dvander@0: // Since jumps can skip around the stream, do not try to do anything dvander@0: // in a potentially truncated state. dvander@0: if (oom()) dvander@0: return; dvander@0: dvander@0: assert(op >= OP_JL && op <= OP_JUMP); dvander@0: ensure(OP_JUMP_LENGTH); dvander@0: dvander@0: if (label->bound()) { dvander@0: // We can immediately emit this jump. dvander@0: int distance = label->offset() - position(); dvander@0: writeOp(op); dvander@0: writeInt32(distance); dvander@0: } else { dvander@0: // We can't emit this jump yet. Add a placeholder. dvander@0: int old = label->link(position()); dvander@0: writeOp(op); dvander@0: writeInt32(old); dvander@0: } dvander@0: } dvander@0: void bind(Label *label) { dvander@0: if (oom()) dvander@0: return; dvander@0: dvander@0: assert(!label->bound()); dvander@0: dvander@0: int offset = label->offset(); dvander@0: while (offset != Label::INVALID_OFFSET) dvander@0: offset = patchJumpAt(offset, position()); dvander@0: dvander@0: label->bind(position()); dvander@0: } dvander@0: void jump(Label *label) { dvander@0: j(OP_JUMP, label); dvander@0: } dvander@0: dvander@0: void binary(Opcode op) { dvander@0: singleByteOp(op); dvander@0: } dvander@0: dvander@0: void pop() { dvander@0: singleByteOp(OP_POP); dvander@0: } dvander@0: void stop() { dvander@0: singleByteOp(OP_STOP); dvander@0: } dvander@46: void callglobal(unsigned index) { dvander@47: recordOops(); dvander@0: ensure(OP_CALLGLOBAL_LENGTH); dvander@0: writeOp(OP_CALLGLOBAL); dvander@0: writeUint32(index); dvander@0: } dvander@68: void callnative(unsigned index, unsigned argc) { dvander@68: recordOops(); dvander@68: ensure(OP_CALLNATIVE_LENGTH); dvander@68: writeOp(OP_CALLNATIVE); dvander@68: writeUint32(index); dvander@68: writeUint32(argc); dvander@68: } dvander@21: void getarray(PrimitiveType type) { dvander@21: switch (type) { dvander@21: case PrimitiveType_Float: dvander@21: singleByteOp(OP_GETARRAY_F); dvander@21: break; dvander@21: case PrimitiveType_Int32: dvander@21: singleByteOp(OP_GETARRAY_I); dvander@21: break; dvander@21: default: dvander@21: assert(type == PrimitiveType_Char); dvander@21: singleByteOp(OP_GETARRAY_C); dvander@21: break; dvander@21: } dvander@0: } dvander@21: void setarray(PrimitiveType type) { dvander@21: switch (type) { dvander@21: case PrimitiveType_Float: dvander@21: singleByteOp(OP_SETARRAY_F); dvander@21: break; dvander@21: case PrimitiveType_Int32: dvander@21: singleByteOp(OP_SETARRAY_I); dvander@21: break; dvander@21: default: dvander@21: assert(type == PrimitiveType_Char); dvander@21: singleByteOp(OP_SETARRAY_C); dvander@21: break; dvander@21: } dvander@7: } dvander@19: void getarray_o() { dvander@19: singleByteOp(OP_GETARRAY_O); dvander@47: markOop(1); dvander@0: } dvander@19: void bounds() { dvander@19: singleByteOp(OP_BOUNDS); dvander@47: markOop(2); dvander@0: } dvander@4: void float_(float value) { dvander@4: ensure(OP_FLOAT_LENGTH); dvander@4: writeOp(OP_FLOAT); dvander@4: writeFloat(value); dvander@4: } dvander@4: void cvt_i2f() { dvander@4: singleByteOp(OP_CVT_I2F); dvander@4: } dvander@33: void newarray_lifo(Handle map) { dvander@47: recordOops(); dvander@19: ensure(OP_NEWARRAY_LIFO_LENGTH); dvander@19: writeOp(OP_NEWARRAY_LIFO); dvander@21: writeObject(map); dvander@47: markOop(1); dvander@12: } dvander@12: void savelifo(unsigned slot) { dvander@12: ensure(OP_SAVELIFO_LENGTH); dvander@12: writeOp(OP_SAVELIFO); dvander@12: writeUint32(slot); dvander@12: } dvander@12: void unwindlifo(unsigned slot) { dvander@12: ensure(OP_UNWINDLIFO_LENGTH); dvander@12: writeOp(OP_UNWINDLIFO); dvander@12: writeUint32(slot); dvander@12: } dvander@14: void newref(Variable *var) { dvander@47: recordOops(); dvander@14: if (var->location() == Variable::STACK) { dvander@14: ensure(OP_VARREF_LIFO_LENGTH); dvander@14: writeOp(OP_VARREF_LIFO); dvander@14: } else { dvander@14: assert(var->location() == Variable::PARAMETER); dvander@14: ensure(OP_ARGREF_LIFO_LENGTH); dvander@14: writeOp(OP_ARGREF_LIFO); dvander@14: } dvander@14: writeUint32(var->slot()); dvander@47: markOop(1); dvander@16: } dvander@47: dvander@14: void slotref_lifo() { dvander@47: recordOops(); dvander@14: singleByteOp(OP_SLOTREF_LIFO); dvander@47: markOop(1); dvander@14: } dvander@17: void arrayref_lifo() { dvander@47: recordOops(); dvander@17: singleByteOp(OP_ARRAYREF_LIFO); dvander@47: markOop(1); dvander@17: } dvander@22: void getref(PrimitiveType type) { dvander@22: if (type == PrimitiveType_Int32) { dvander@22: singleByteOp(OP_GETREF_I); dvander@22: } else { dvander@22: assert(type == PrimitiveType_Float); dvander@22: singleByteOp(OP_GETREF_F); dvander@22: } dvander@14: } dvander@22: void setref(PrimitiveType type) { dvander@22: if (type == PrimitiveType_Int32) { dvander@22: singleByteOp(OP_SETREF_I); dvander@22: } else { dvander@22: assert(type == PrimitiveType_Float); dvander@22: singleByteOp(OP_SETREF_F); dvander@22: } dvander@14: } dvander@33: void object(Handle obj) { dvander@21: ensure(OP_OBJECT_LENGTH); dvander@21: writeOp(OP_OBJECT); dvander@21: writeObject(obj); dvander@47: markOop(1); dvander@21: } dvander@21: void arraycopy() { dvander@21: singleByteOp(OP_ARRAYCOPY); dvander@47: markOop(1); dvander@21: } dvander@47: dvander@22: void dup() { dvander@47: bool oop = getOop(1); dvander@22: singleByteOp(OP_DUP); dvander@47: markOop(1, oop); dvander@22: } dvander@22: void dup2() { dvander@47: bool oop2 = getOop(2); dvander@47: bool oop1 = getOop(1); dvander@47: dvander@22: singleByteOp(OP_DUP2); dvander@47: dvander@47: markOop(2, oop2); dvander@47: markOop(1, oop1); dvander@22: } dvander@23: void swap() { dvander@47: bool oop2 = getOop(2); dvander@47: bool oop1 = getOop(1); dvander@47: dvander@23: singleByteOp(OP_SWAP); dvander@47: dvander@47: markOop(2, oop1); dvander@47: markOop(1, oop2); dvander@23: } dvander@23: void roll3() { dvander@47: bool oop3 = getOop(3); dvander@47: bool oop2 = getOop(2); dvander@47: bool oop1 = getOop(1); dvander@47: dvander@23: singleByteOp(OP_ROLL3); dvander@47: dvander@47: markOop(3, oop2); dvander@47: markOop(2, oop1); dvander@47: markOop(1, oop3); dvander@23: } dvander@23: void pick2() { dvander@47: bool oop2 = getOop(2); dvander@47: bool oop1 = getOop(1); dvander@47: dvander@23: singleByteOp(OP_PICK2); dvander@47: dvander@47: markOop(3, oop2); dvander@47: markOop(2, oop1); dvander@47: markOop(1, oop2); dvander@23: } dvander@23: void pick3() { dvander@47: bool oop3 = getOop(3); dvander@47: bool oop2 = getOop(2); dvander@47: bool oop1 = getOop(1); dvander@47: dvander@23: singleByteOp(OP_PICK3); dvander@47: dvander@47: markOop(4, oop3); dvander@47: markOop(3, oop2); dvander@47: markOop(2, oop1); dvander@47: markOop(1, oop3); dvander@47: } dvander@47: dvander@47: // Helpers. dvander@47: void get(Variable *var) { dvander@47: if (var->location() == Variable::STACK) { dvander@47: getlocal(var); dvander@47: } else { dvander@47: assert(var->location() == Variable::PARAMETER); dvander@47: getarg(var); dvander@47: } dvander@47: } dvander@47: void set(Variable *var) { dvander@47: assert(!var->type()->isReference()); dvander@47: if (var->location() == Variable::STACK) { dvander@47: setlocal(var); dvander@47: } else { dvander@47: assert(var->location() == Variable::PARAMETER); dvander@47: setarg(var); dvander@47: } dvander@23: } dvander@0: }; dvander@0: dvander@0: } dvander@0: dvander@0: #endif // _include_bytecode_emitter_h_