asherkin@263: /* vim: set ts=4 sw=4 tw=99 et: asherkin@263: * asherkin@263: * Copyright (C) 2012 David Anderson asherkin@263: * asherkin@263: * This file is part of JITCraft. asherkin@263: * asherkin@263: * JITCraft is free software: you can redistribute it and/or modify it under asherkin@263: * the terms of the GNU General Public License as published by the Free asherkin@263: * Software Foundation, either version 3 of the License, or (at your option) asherkin@263: * any later version. asherkin@263: * asherkin@263: * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY asherkin@263: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS asherkin@263: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. asherkin@263: * asherkin@263: * You should have received a copy of the GNU General Public License along with asherkin@263: * JITCraft. If not, see http://www.gnu.org/licenses/. asherkin@263: */ asherkin@263: #include asherkin@263: #include "Opcodes.h" asherkin@263: #include "Utility.h" asherkin@263: #include "Code.h" asherkin@263: #include "Zone.h" asherkin@263: #include "Array.h" asherkin@263: #include "Strings.h" asherkin@263: #include "CompactBuffer.h" asherkin@263: #include "Structures.h" asherkin@263: #include "Modules.h" asherkin@263: #include "Heap-inl.h" asherkin@263: asherkin@263: using namespace ke; asherkin@263: asherkin@263: OpInfo ke::OpcodeInfo[] = asherkin@263: { asherkin@263: #define OPDEF(name, text, length, uses, defs) \ asherkin@263: {text, length, uses, defs}, asherkin@263: #include "Opcodes.tbl" asherkin@263: #undef OPDEF asherkin@263: }; asherkin@263: asherkin@263: Code * asherkin@263: Code::New(Zone *zone, Handle bytecode, Handle locals, asherkin@263: Handle objects, Handle strings, unsigned maxStackDepth, asherkin@263: unsigned maxLoopDepth, Handle safepoints, Handle sourcemap) asherkin@263: { asherkin@263: Local code(zone, Code::cast(zone->allocate(MapKind_Code, sizeof(Code), Heap::Tenure_Old))); asherkin@263: if (!code) asherkin@263: return NULL; asherkin@263: asherkin@263: code->bytecode_ = bytecode; asherkin@263: code->locals_ = locals; asherkin@263: code->objects_ = objects; asherkin@263: code->maxStackDepth_ = maxStackDepth; asherkin@263: code->module_ = NULL; asherkin@263: code->safepoints_ = safepoints; asherkin@263: code->sourcemap_ = sourcemap; asherkin@263: code->lineNumber_ = 0; asherkin@263: code->strings_ = strings; asherkin@263: code->maxLoopDepth_ = maxLoopDepth; asherkin@263: return code; asherkin@263: } asherkin@263: asherkin@263: static int asherkin@263: CompareSafepoints(const void *a, const void *b) asherkin@263: { asherkin@263: const unsigned *sa = reinterpret_cast(a); asherkin@263: const unsigned *sb = reinterpret_cast(b); asherkin@263: if (*sa > *sb) asherkin@263: return 1; asherkin@263: if (*sa == *sb) asherkin@263: return 0; asherkin@263: return -1; asherkin@263: } asherkin@263: asherkin@263: uint8 * asherkin@263: Code::safepointAt(unsigned pcOffset) asherkin@263: { asherkin@263: // Only specific opcodes may have safepoints. Asking for a safepoint at asherkin@263: // any other position indicates a failure to mark that position as a asherkin@263: // safepoint, which is a bug. asherkin@263: #ifndef NDEBUG asherkin@263: Opcode op = Opcode(*(start() + pcOffset)); asherkin@263: assert(op == OP_CALL || asherkin@263: op == OP_NEWFIXED || asherkin@263: op == OP_NEWEMPTY || asherkin@263: op == OP_NEWSIZED || asherkin@263: op == OP_VARREF_LIFO || asherkin@263: op == OP_ARGREF_LIFO || asherkin@263: op == OP_SLOTREF_LIFO || asherkin@263: op == OP_COPYARRAY || asherkin@263: op == OP_CALLNATIVE || asherkin@263: op == OP_PUBLIC); asherkin@263: #endif asherkin@263: asherkin@263: if (!safepoints_) asherkin@263: return NULL; asherkin@263: asherkin@263: size_t bytesPerSafepoint = BytesPerSafepoint(maxStackDepth()); asherkin@263: assert(!(safepoints_->length() % bytesPerSafepoint)); asherkin@263: asherkin@263: size_t count = safepoints_->length() / bytesPerSafepoint; asherkin@263: asherkin@263: void *result = bsearch(&pcOffset, asherkin@263: safepoints_->raw(), asherkin@263: count, asherkin@263: bytesPerSafepoint, asherkin@263: CompareSafepoints); asherkin@263: asherkin@263: if (!result) asherkin@263: return NULL; asherkin@263: asherkin@263: assert(*reinterpret_cast(result) == pcOffset); asherkin@263: asherkin@263: return Address(result) + sizeof(unsigned); asherkin@263: } asherkin@263: asherkin@263: SourcePosition asherkin@263: Code::pcToSource(unsigned pc) asherkin@263: { asherkin@263: assert(pc < bytecode_->length()); asherkin@263: asherkin@263: CompactBufferReader reader(sourcemap_->raw(), sourcemap_->raw() + sourcemap_->length()); asherkin@263: asherkin@263: unsigned line = 1; asherkin@263: unsigned col = 0; asherkin@263: unsigned curPc = 0; asherkin@263: while (!reader.done()) { asherkin@263: curPc += reader.readUint32(); asherkin@263: if (curPc > pc) asherkin@263: break; asherkin@263: asherkin@263: line = reader.readUint32(); asherkin@263: col = reader.readUint32(); asherkin@263: } asherkin@263: asherkin@263: return SourcePosition(lineNumber_ + line, col); asherkin@263: } asherkin@263: asherkin@263: // All jumps must be the same length. asherkin@263: STATIC_ASSERT(OP_JT_LENGTH == OP_JUMP_LENGTH); asherkin@263: STATIC_ASSERT(OP_JF_LENGTH == OP_JUMP_LENGTH); asherkin@263: asherkin@263: void asherkin@263: ke::Disassemble(Code *code, FILE *fp) asherkin@263: { asherkin@263: bytecode *pc = code->start(); asherkin@263: for (;;) { asherkin@263: Opcode op = Opcode(*pc); asherkin@263: asherkin@263: fprintf(fp, "%-4d %s ", int(pc - code->start()), OpcodeInfo[op].name); asherkin@263: asherkin@263: switch (op) { asherkin@263: case OP_NOP: asherkin@263: case OP_RETURN: asherkin@263: case OP_RETURNVOID: asherkin@263: case OP_ADD: asherkin@263: case OP_SUB: asherkin@263: case OP_MUL: asherkin@263: case OP_DIV: asherkin@263: case OP_ADD_F: asherkin@263: case OP_SUB_F: asherkin@263: case OP_MUL_F: asherkin@263: case OP_DIV_F: asherkin@263: case OP_CVT_I2F: asherkin@263: case OP_CVT_I2B: asherkin@263: case OP_POP: asherkin@263: case OP_GETELEM: asherkin@263: case OP_SETELEM: asherkin@263: case OP_SHL: asherkin@263: case OP_SHR: asherkin@263: case OP_USHR: asherkin@263: case OP_BITAND: asherkin@263: case OP_BITOR: asherkin@263: case OP_BITXOR: asherkin@263: case OP_GETREF_I: asherkin@263: case OP_SETREF_I: asherkin@263: case OP_GETREF_F: asherkin@263: case OP_SETREF_F: asherkin@263: case OP_SLOTREF_LIFO: asherkin@263: case OP_COPYARRAY: asherkin@263: case OP_COPYSTRUCT: asherkin@263: case OP_DUP: asherkin@263: case OP_DUP2: asherkin@263: case OP_SWAP: asherkin@263: case OP_ROLL3: asherkin@263: case OP_PICK2: asherkin@263: case OP_PICK3: asherkin@263: case OP_CALL: asherkin@263: case OP_LT: asherkin@263: case OP_LE: asherkin@263: case OP_GE: asherkin@263: case OP_GT: asherkin@263: case OP_EQ: asherkin@263: case OP_NE: asherkin@263: case OP_LT_F: asherkin@263: case OP_LE_F: asherkin@263: case OP_GE_F: asherkin@263: case OP_GT_F: asherkin@263: case OP_EQ_F: asherkin@263: case OP_NE_F: asherkin@263: case OP_BITNOT: asherkin@263: case OP_NOT: asherkin@263: case OP_NOT_F: asherkin@263: case OP_NEG: asherkin@263: case OP_NEG_F: asherkin@263: case OP_MOD: asherkin@263: case OP_BITCAST: asherkin@263: case OP_TRUE: asherkin@263: case OP_FALSE: asherkin@263: case OP_SIZEOF: asherkin@263: case OP_PUBLIC: asherkin@263: case OP_LEAVELOOP: asherkin@263: case OP_DUPARRAY: asherkin@263: case OP_DEPARRAY: asherkin@263: break; asherkin@263: asherkin@263: case OP_ENTERLOOP: asherkin@263: if (pc[1]) asherkin@263: fprintf(fp, "; lifo region"); asherkin@263: break; asherkin@263: asherkin@263: case OP_STOP: asherkin@263: fprintf(fp, "\n"); asherkin@263: break; asherkin@263: asherkin@263: case OP_INT: asherkin@263: fprintf(fp, "%d", READ_INT32(pc)); asherkin@263: break; asherkin@263: asherkin@263: case OP_FLOAT: asherkin@263: fprintf(fp, "%f", READ_FLOAT(pc)); asherkin@263: break; asherkin@263: asherkin@263: case OP_JT: asherkin@263: case OP_JF: asherkin@263: case OP_JUMP: asherkin@263: case OP_CONTINUE: asherkin@263: case OP_BREAK: asherkin@263: fprintf(fp, "%u (%+d)", unsigned((pc + READ_INT32(pc)) - code->start()), READ_INT32(pc)); asherkin@263: break; asherkin@263: asherkin@263: case OP_NEWFIXED: asherkin@263: case OP_NEWEMPTY: asherkin@263: case OP_NEWSIZED: asherkin@263: { asherkin@263: Heap::Tenure tenure = (Heap::Tenure)pc[1]; asherkin@263: //ArrayMap *map = ArrayMap::cast(code->map(READ_UINT32(pc + 1))); asherkin@263: if (tenure == Heap::Tenure_Stack) asherkin@263: fprintf(fp, " ; lifo"); asherkin@263: else if (tenure == Heap::Tenure_Old) asherkin@263: fprintf(fp, " ; pretenured"); asherkin@263: else if (tenure == Heap::Tenure_Default) asherkin@263: fprintf(fp, " ; newborn"); asherkin@263: break; asherkin@263: } asherkin@263: asherkin@263: case OP_NEWSTRUCT: asherkin@263: { asherkin@263: Heap::Tenure tenure = (Heap::Tenure)pc[1]; asherkin@263: StructMap *map = StructMap::cast(code->map(READ_UINT32(pc + 1))); asherkin@263: fprintf(fp, "%s", StructType::cast(map->type())->name()->chars()); asherkin@263: if (tenure == Heap::Tenure_Stack) asherkin@263: fprintf(fp, " ; lifo"); asherkin@263: else if (tenure == Heap::Tenure_Old) asherkin@263: fprintf(fp, " ; pretenured"); asherkin@263: else if (tenure == Heap::Tenure_Default) asherkin@263: fprintf(fp, " ; newborn"); asherkin@263: break; asherkin@263: } asherkin@263: asherkin@263: case OP_GETLOCAL: asherkin@263: case OP_SETLOCAL: asherkin@263: case OP_GETARG: asherkin@263: case OP_SETARG: asherkin@263: case OP_SAVELIFO: asherkin@263: case OP_UNWINDLIFO: asherkin@263: case OP_VARREF_LIFO: asherkin@263: case OP_ARGREF_LIFO: asherkin@263: case OP_OBJECT: asherkin@263: case OP_GETFIELD: asherkin@263: case OP_SETFIELD: asherkin@263: fprintf(fp, "%u", READ_UINT32(pc)); asherkin@263: break; asherkin@263: asherkin@263: case OP_CALLNATIVE: asherkin@263: { asherkin@263: unsigned argc = READ_UINT32(pc); asherkin@263: fprintf(fp, " ; %u args", argc); asherkin@263: break; asherkin@263: } asherkin@263: asherkin@263: case OP_TABLESWITCH: asherkin@263: { asherkin@263: int low = READ_TABLESWITCH_LOW(pc); asherkin@263: int high = READ_TABLESWITCH_HIGH(pc); asherkin@263: fprintf(fp, "%d, %d\n", low, high); asherkin@263: break; asherkin@263: } asherkin@263: asherkin@263: case OP_GETGLOBAL: asherkin@263: case OP_SETGLOBAL: asherkin@263: { asherkin@263: unsigned index = READ_UINT32(pc); asherkin@263: fprintf(fp, "%s", String::cast(code->strings()->at(index))->chars()); asherkin@263: break; asherkin@263: } asherkin@263: asherkin@263: case OP_IMPORT: asherkin@263: { asherkin@263: unsigned index = READ_UINT32(pc); asherkin@263: FixedArray *path = code->module()->importPathAt(index); asherkin@263: fprintf(fp, " ; "); asherkin@263: for (unsigned i = 0; i < path->length(); i++) { asherkin@263: fprintf(fp, "%s", String::cast(path->at(i))->chars()); asherkin@263: if (i != path->length() - 1) asherkin@263: fprintf(fp, "."); asherkin@263: } asherkin@263: break; asherkin@263: } asherkin@263: asherkin@263: default: asherkin@263: // Unknown opcode! asherkin@263: assert(false); asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: if (op == OP_STOP) asherkin@263: break; asherkin@263: asherkin@263: fprintf(fp, "\n"); asherkin@263: pc += OpcodeInfo[op].length; asherkin@263: assert(pc < code->start() + code->length()); asherkin@263: } asherkin@263: asherkin@263: fprintf(fp, "\nSafepoints:\n"); asherkin@263: ByteArray *safepoints = code->safepoints(); asherkin@263: if (safepoints) { asherkin@263: size_t nsafepoints = safepoints->length() / Code::BytesPerSafepoint(code->maxStackDepth()); asherkin@263: for (size_t i = 0; i < nsafepoints; i++) { asherkin@263: Address address = safepoints->raw() + Code::BytesPerSafepoint(code->maxStackDepth()) * i; asherkin@263: fprintf(fp, "%4d: ", *reinterpret_cast(address)); asherkin@263: unsigned char *bits = (unsigned char *)(address + sizeof(unsigned)); asherkin@263: for (unsigned j = 0; j < code->maxStackDepth(); j++) { asherkin@263: if (bits[j / 8] & (1 << (j % 8))) asherkin@263: fprintf(fp, "%d ", j); asherkin@263: } asherkin@263: fprintf(fp, "\n"); asherkin@263: } asherkin@263: } asherkin@263: }