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@195: #include "../Opcodes.h" dvander@195: #include "../Handles.h" dvander@195: #include "../CompactBuffer.h" dvander@195: #include "../Array.h" dvander@195: #include "../HashTable.h" dvander@195: #include "../VirtualObjectVisitor.h" dvander@0: dvander@59: namespace ke { dvander@0: dvander@195: // An overarching note on overflows: Though we should use CheckedInt, we rely dvander@195: // on a source-length cap covering all possible overflow checks. As long as dvander@195: // the source length is capped to INT_MAX, the number of AST nodes will be dvander@195: // strictly beneath that cap, and so to will most things generated by the dvander@195: // AST. For example, local and argument counts, number of functions, fields dvander@195: // in a struct, et cetera. Not everything can be covered by this cap: the dvander@195: // stack depth, number of bytecodes for example. In these cases we explicitly dvander@195: // test for overflow of INT_MAX and in other cases we assert. dvander@195: // dvander@195: // In the future, we should of course use a checked int wrapper. dvander@195: dvander@0: class Type; dvander@12: class Map; dvander@187: class ArrayMap; dvander@195: class Symbol; dvander@195: class VariableSymbol; dvander@0: dvander@195: struct OopRecord dvander@47: { dvander@47: unsigned depth; dvander@47: unsigned offset; dvander@195: Vector slots; dvander@47: dvander@47: OopRecord(unsigned length, unsigned offset) dvander@47: : depth(length), dvander@195: offset(offset) dvander@47: { dvander@47: } dvander@47: }; dvander@47: dvander@195: class StringTable dvander@195: { dvander@195: struct Entry dvander@195: { dvander@195: String *string; dvander@195: unsigned index; dvander@195: dvander@195: Entry() dvander@195: : string(NULL), dvander@195: index(0) dvander@195: { dvander@195: } dvander@195: dvander@195: Entry(String *string, unsigned index) dvander@195: : string(string), dvander@195: index(index) dvander@195: { dvander@195: } dvander@195: }; dvander@195: dvander@195: struct Policy dvander@195: { dvander@195: typedef Entry Payload; dvander@195: dvander@195: static uint32 hash(String *string) { dvander@195: return HashPointer(string); dvander@195: } dvander@195: dvander@195: static bool matches(String *key, Entry &e) { dvander@195: return key == e.string; dvander@195: } dvander@195: }; dvander@195: dvander@195: typedef HashTable Set; dvander@195: dvander@195: public: dvander@195: StringTable(); dvander@195: bool initialize(); dvander@195: void accept(VirtualObjectVisitor *visitor); dvander@195: dvander@195: bool add(Handle name, unsigned *indexp); dvander@195: size_t stringCount() const { dvander@195: return strings_.length(); dvander@195: } dvander@195: String *stringAt(size_t i) const { dvander@195: return strings_[i].string; dvander@195: } dvander@195: dvander@195: private: dvander@195: Set set_; dvander@195: Vector strings_; dvander@195: bool initialized_; dvander@195: }; dvander@195: dvander@195: // The bytecode emitter assists in building Code objects, by emitting opcodes dvander@195: // and tracking stack information. Since the SemA creates temporary pool/root dvander@195: // scopes, we cannot use ScopedRoot or PoolObjects. Instead, BCE gets a dvander@195: // special marking function. dvander@195: class BytecodeEmitter : public AutoRooted 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@195: Vector oopMap_; dvander@195: Vector oopRecords_; dvander@70: CompactBufferWriter sourcemap_; dvander@70: unsigned lastCodePosition_; dvander@70: unsigned firstLine_; dvander@195: StringTable strings_; dvander@195: Vector locals_; dvander@195: Vector objects_; dvander@230: unsigned loopDepth_; dvander@230: unsigned maxLoopDepth_; 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@234: assert(obj); dvander@234: dvander@58: writeUint32(objects_.length()); dvander@234: if (!objects_.append(obj)) 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@195: ~BytecodeEmitter(); dvander@195: dvander@195: void accept(VirtualObjectVisitor *visitor); 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@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: 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@195: void getlocal(VariableSymbol *var); dvander@195: void setlocal(VariableSymbol *var); dvander@220: void getarg(VariableSymbol *var); dvander@220: void setarg(VariableSymbol *var); 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@230: assert(op == OP_JT || op == OP_JF || op == OP_JUMP || dvander@230: op == OP_CONTINUE || op == OP_BREAK); 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@195: void unary(Opcode op) { dvander@195: singleByteOp(op); dvander@195: } 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@195: void call(Handle fun, unsigned argc) { dvander@47: recordOops(); dvander@72: dvander@195: if (fun->isNative()) { dvander@195: ensure(OP_CALLNATIVE_LENGTH); dvander@195: writeOp(OP_CALLNATIVE); dvander@195: writeUint32(argc); dvander@195: drop(argc + 1); dvander@195: } else { dvander@195: ensure(OP_CALL_LENGTH); dvander@195: writeOp(OP_CALL); dvander@195: drop(argc + 1); dvander@195: dvander@195: if (fun->returnType()->isVoid()) dvander@195: drop(1); dvander@195: else if (fun->returnType()->isTraceable()) dvander@195: markOop(1); dvander@195: } dvander@68: } dvander@85: dvander@157: void getelem(Type *type) { dvander@157: singleByteOp(OP_GETELEM); dvander@154: dvander@157: if (type->isTraceable()) dvander@157: markOop(1); dvander@0: } dvander@157: void setelem(Type *type) { dvander@157: singleByteOp(OP_SETELEM); dvander@7: } dvander@85: dvander@147: void getfield(unsigned index, Type *type) { dvander@147: ensure(OP_GETFIELD_LENGTH); dvander@147: writeOp(OP_GETFIELD); dvander@147: writeUint32(index); dvander@147: dvander@147: if (type->isTraceable()) dvander@147: markOop(1); dvander@147: } dvander@147: void setfield(unsigned index, Type *type) { dvander@147: ensure(OP_SETFIELD_LENGTH); dvander@147: writeOp(OP_SETFIELD); dvander@147: writeUint32(index); dvander@147: dvander@147: if (type->isTraceable()) dvander@147: markOop(1); dvander@147: } dvander@147: dvander@230: int enterloop() { dvander@230: int offset = position(); dvander@230: dvander@230: ensure(OP_ENTERLOOP); dvander@230: writeOp(OP_ENTERLOOP); dvander@230: writeUint8(0); dvander@230: dvander@230: loopDepth_++; dvander@230: if (loopDepth_ > maxLoopDepth_) dvander@230: maxLoopDepth_ = loopDepth_; dvander@230: dvander@230: return offset; dvander@85: } dvander@230: void leaveloop() { dvander@230: singleByteOp(OP_LEAVELOOP); dvander@230: dvander@230: assert(loopDepth_); dvander@230: loopDepth_--; dvander@85: } dvander@230: void setLoopUsesLifoStack(int pos) { dvander@230: assert(pos >= 0 && pos < position()); dvander@230: assert(*(buffer() + pos) == OP_ENTERLOOP); dvander@230: *(buffer() + pos + 1) = 1; dvander@230: } dvander@85: 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@187: void newarray(Opcode op, Handle map, Heap::Tenure tenure) { dvander@185: assert(op == OP_NEWFIXED || dvander@185: op == OP_NEWEMPTY || dvander@185: op == OP_NEWSIZED); dvander@185: dvander@47: recordOops(); dvander@185: ensure(OpcodeInfo[op].length); dvander@185: writeOp(op); dvander@85: writeUint8(tenure); dvander@21: writeObject(map); dvander@187: dvander@187: if (op == OP_NEWSIZED) dvander@187: drop(map->type()->levels()); dvander@187: dvander@47: markOop(1); dvander@12: } dvander@147: void newstruct(Handle map, Heap::Tenure tenure) { dvander@147: recordOops(); dvander@147: ensure(OP_NEWSTRUCT_LENGTH); dvander@147: writeOp(OP_NEWSTRUCT); dvander@147: writeUint8(tenure); dvander@147: writeObject(map); dvander@147: markOop(1); dvander@147: } 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@222: dvander@222: void newref(VariableSymbol *var); dvander@47: dvander@14: void slotref_lifo() { dvander@47: recordOops(); dvander@14: singleByteOp(OP_SLOTREF_LIFO); dvander@47: markOop(1); dvander@14: } dvander@145: void getref(Type *type) { dvander@145: if (type->pod() == PrimitiveType_Int32) { dvander@22: singleByteOp(OP_GETREF_I); dvander@22: } else { dvander@145: assert(type->pod() == PrimitiveType_Float); dvander@22: singleByteOp(OP_GETREF_F); dvander@22: } dvander@14: } dvander@145: void setref(Type *type) { dvander@145: if (type->pod() == PrimitiveType_Int32) { dvander@22: singleByteOp(OP_SETREF_I); dvander@22: } else { dvander@145: assert(type->pod() == PrimitiveType_Float); dvander@22: singleByteOp(OP_SETREF_F); dvander@22: } dvander@14: } dvander@33: void object(Handle obj) { dvander@234: assert(obj); dvander@234: dvander@21: ensure(OP_OBJECT_LENGTH); dvander@21: writeOp(OP_OBJECT); dvander@21: writeObject(obj); dvander@47: markOop(1); dvander@21: } dvander@234: void duparray() { dvander@234: singleByteOp(OP_DUPARRAY); dvander@234: markOop(1); dvander@234: } dvander@152: void copyarray() { dvander@152: singleByteOp(OP_COPYARRAY); dvander@152: markOop(1); dvander@152: } dvander@152: void copystruct() { dvander@152: singleByteOp(OP_COPYSTRUCT); dvander@47: markOop(1); dvander@21: } dvander@194: void sizeof_() { dvander@194: singleByteOp(OP_SIZEOF); dvander@194: } 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@72: void returnvoid() { dvander@72: singleByteOp(OP_RETURNVOID); dvander@72: } dvander@78: void bitnot() { dvander@78: singleByteOp(OP_BITNOT); dvander@78: } dvander@79: void not_() { dvander@79: singleByteOp(OP_NOT); dvander@79: } dvander@79: void not_f() { dvander@79: singleByteOp(OP_NOT_F); dvander@79: } dvander@80: void neg() { dvander@80: singleByteOp(OP_NEG); dvander@80: } dvander@80: void neg_f() { dvander@80: singleByteOp(OP_NEG_F); dvander@80: } dvander@142: void bits_i2f() { dvander@142: singleByteOp(OP_BITS_I2F); dvander@142: } dvander@142: void bits_f2i() { dvander@142: singleByteOp(OP_BITS_F2I); dvander@142: } dvander@143: void true_() { dvander@143: singleByteOp(OP_TRUE); dvander@143: } dvander@143: void false_() { dvander@143: singleByteOp(OP_FALSE); dvander@143: } dvander@195: dvander@195: unsigned addString(Handle name) { dvander@195: unsigned index; dvander@195: if (!strings_.add(name, &index)) { dvander@195: oom_ |= true; dvander@195: return 0; dvander@195: } dvander@195: return index; dvander@195: } dvander@195: dvander@195: void getglobal(Symbol *sym); dvander@195: void setglobal(Symbol *sym); dvander@195: dvander@195: void public_() { dvander@195: recordOops(); dvander@195: singleByteOp(OP_PUBLIC); dvander@195: } dvander@72: dvander@172: void tableswitch(int low, int high, Label *def, Label **labels) { dvander@172: unsigned tablesize = TableSwitchEntries(low, high); dvander@172: assert(tablesize <= MAX_TABLESWITCH_ENTRIES); dvander@172: dvander@172: ensure(OP_TABLESWITCH_LENGTH); dvander@172: writeOp(OP_TABLESWITCH); dvander@172: writeInt32(low); dvander@172: writeInt32(high); dvander@172: jump(def); dvander@172: dvander@172: // When labels are patched, we assume each position is a jump. Rather dvander@172: // than hack position offsets to make labels work without actual dvander@172: // incoming jumps, we emit a jump inline. dvander@172: for (unsigned i = 0; i < tablesize; i++) dvander@172: jump(labels[i]); dvander@172: } dvander@172: dvander@195: bool allocate(VariableSymbol *sym); dvander@195: dvander@195: Code *finish(); dvander@195: dvander@47: // Helpers. dvander@195: #if 0 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@195: #endif dvander@0: }; dvander@0: dvander@0: } dvander@0: dvander@0: #endif // _include_bytecode_emitter_h_