diff -r 735503e448a0 -r 050939c6181e jitcraft/BytecodeEmitter.h --- a/jitcraft/BytecodeEmitter.h Tue Jul 03 13:53:34 2012 -0400 +++ b/jitcraft/BytecodeEmitter.h Wed Jul 04 19:39:00 2012 -0400 @@ -33,18 +33,41 @@ typedef std::vector *> ObjectList; +struct OopRecord : public PoolObject +{ + unsigned depth; + unsigned offset; + bool *slots; + + OopRecord(unsigned length, unsigned offset) + : depth(length), + offset(offset), + slots(NULL) + { + } +}; + class BytecodeEmitter { + private: + PoolAllocator &pool_; + bytecode *buffer_; bytecode *buffer_pos_; bytecode *buffer_end_; bytecode inline_buffer_[512]; bool oom_; unsigned maxStackDepth_; - unsigned stackDepth_; ObjectList objects_; + std::vector oopMap_; + std::vector oopRecords_; private: + // The maximum size of a code object must be such that the distance between + // two instructions fits into a signed, 31-bit integer. This ensures that + // all code-bounded buffers (like the number of object literals or + // safepoints) does not need to be bounds checked. It also means we can add + // any two offsets and be guaranteed that they don't overflow. static const size_t MAX_LENGTH = INT_MAX / 2; private: @@ -59,7 +82,7 @@ } void writeUint8(unsigned value) { - assert(value >= 0 && value <= UCHAR_MAX); + assert(value <= UCHAR_MAX); *buffer_pos_++ = value; } void writeFloat(float value) { @@ -75,12 +98,16 @@ buffer_pos_ += sizeof(unsigned); } void writeOp(Opcode op) { + if (oom_) + return; + writeUint8(op); - stackDepth_ += OpcodeInfo[op].ndefs; - if (OpcodeInfo[op].nuses != -1) - stackDepth_ -= OpcodeInfo[op].nuses; - if (stackDepth_ > maxStackDepth_) - maxStackDepth_ = stackDepth_; + for (int i = 0; i < OpcodeInfo[op].nuses; i++) + oopMap_.pop_back(); + for (int i = 0; i < OpcodeInfo[op].ndefs; i++) + oopMap_.push_back(false); + if (oopMap_.size() > maxStackDepth_) + maxStackDepth_ = oopMap_.size(); } void writeObject(Object *obj) { writeUint32(objects_.size()); @@ -96,7 +123,7 @@ } public: - BytecodeEmitter(); + BytecodeEmitter(PoolAllocator &pool); bytecode *buffer() const { return buffer_; @@ -108,7 +135,7 @@ return oom_; } unsigned stackDepth() const { - return stackDepth_; + return oopMap_.size(); } unsigned maxStackDepth() const { return maxStackDepth_; @@ -116,10 +143,14 @@ const ObjectList *objects() const { return &objects_; } + std::vector *oopRecords() { + return &oopRecords_; + } void drop(unsigned amt) { - assert(amt <= stackDepth_); - stackDepth_ -= amt; + assert(amt <= stackDepth()); + for (unsigned i = 0; i < amt; i++) + oopMap_.pop_back(); } public: @@ -194,6 +225,24 @@ writeOp(op); } + // Mark a stack slot as having an object pointer. + void markOop(unsigned depth, bool oop = true) { + if (oom_) + return; + oopMap_[oopMap_.size() - depth] = oop; + } + bool getOop(unsigned depth) { + if (oom_) + return false; + return oopMap_[oopMap_.size() - depth]; + } + + // Mark the current bytecode position as either re-entrant (meaning, it + // can leave the interpreter or call to another function), or causing GC. + // This will record which stack slots have objects and associate that + // information with the current pc. + void recordOops(); + public: void int_(int value) { ensure(OP_INT); @@ -204,25 +253,37 @@ ensure(OP_RETURN); writeOp(OP_RETURN); } - void getlocal(unsigned local) { + void getlocal(Variable *var) { ensure(OP_GETLOCAL_LENGTH); writeOp(OP_GETLOCAL); - writeUint32(local); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); } - void setlocal(unsigned local) { + void setlocal(Variable *var) { ensure(OP_SETLOCAL_LENGTH); writeOp(OP_SETLOCAL); - writeUint32(local); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); } - void getarg(unsigned slot) { + void getarg(Variable *var) { ensure(OP_GETARG_LENGTH); writeOp(OP_GETARG); - writeUint32(slot); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); } - void setarg(unsigned slot) { + void setarg(Variable *var) { ensure(OP_SETARG_LENGTH); writeOp(OP_SETARG); - writeUint32(slot); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); } void j(Opcode op, Label *label) { @@ -273,6 +334,7 @@ singleByteOp(OP_STOP); } void callglobal(unsigned index) { + recordOops(); ensure(OP_CALLGLOBAL_LENGTH); writeOp(OP_CALLGLOBAL); writeUint32(index); @@ -307,9 +369,11 @@ } void getarray_o() { singleByteOp(OP_GETARRAY_O); + markOop(1); } void bounds() { singleByteOp(OP_BOUNDS); + markOop(2); } void float_(float value) { ensure(OP_FLOAT_LENGTH); @@ -320,9 +384,11 @@ singleByteOp(OP_CVT_I2F); } void newarray_lifo(Handle map) { + recordOops(); ensure(OP_NEWARRAY_LIFO_LENGTH); writeOp(OP_NEWARRAY_LIFO); writeObject(map); + markOop(1); } void savelifo(unsigned slot) { ensure(OP_SAVELIFO_LENGTH); @@ -335,6 +401,7 @@ writeUint32(slot); } void newref(Variable *var) { + recordOops(); if (var->location() == Variable::STACK) { ensure(OP_VARREF_LIFO_LENGTH); writeOp(OP_VARREF_LIFO); @@ -344,29 +411,18 @@ writeOp(OP_ARGREF_LIFO); } writeUint32(var->slot()); - } - void get(Variable *var) { - if (var->location() == Variable::STACK) { - getlocal(var->slot()); - } else { - assert(var->location() == Variable::PARAMETER); - getarg(var->slot()); - } + markOop(1); } - void set(Variable *var) { - assert(!var->type()->isReference()); - if (var->location() == Variable::STACK) { - setlocal(var->slot()); - } else { - assert(var->location() == Variable::PARAMETER); - setarg(var->slot()); - } - } + void slotref_lifo() { + recordOops(); singleByteOp(OP_SLOTREF_LIFO); + markOop(1); } void arrayref_lifo() { + recordOops(); singleByteOp(OP_ARRAYREF_LIFO); + markOop(1); } void getref(PrimitiveType type) { if (type == PrimitiveType_Int32) { @@ -388,27 +444,87 @@ ensure(OP_OBJECT_LENGTH); writeOp(OP_OBJECT); writeObject(obj); + markOop(1); } void arraycopy() { singleByteOp(OP_ARRAYCOPY); + markOop(1); } + void dup() { + bool oop = getOop(1); singleByteOp(OP_DUP); + markOop(1, oop); } void dup2() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + singleByteOp(OP_DUP2); + + markOop(2, oop2); + markOop(1, oop1); } void swap() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + singleByteOp(OP_SWAP); + + markOop(2, oop1); + markOop(1, oop2); } void roll3() { + bool oop3 = getOop(3); + bool oop2 = getOop(2); + bool oop1 = getOop(1); + singleByteOp(OP_ROLL3); + + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop3); } void pick2() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + singleByteOp(OP_PICK2); + + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop2); } void pick3() { + bool oop3 = getOop(3); + bool oop2 = getOop(2); + bool oop1 = getOop(1); + singleByteOp(OP_PICK3); + + markOop(4, oop3); + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop3); + } + + // Helpers. + void get(Variable *var) { + if (var->location() == Variable::STACK) { + getlocal(var); + } else { + assert(var->location() == Variable::PARAMETER); + getarg(var); + } + } + void set(Variable *var) { + assert(!var->type()->isReference()); + if (var->location() == Variable::STACK) { + setlocal(var); + } else { + assert(var->location() == Variable::PARAMETER); + setarg(var); + } } };