dvander@39: /* vim: set ts=4 sw=4 tw=99 et: dvander@39: * 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: #include "Interpreter.h" dvander@0: #include "Modules.h" dvander@0: #include "Zone.h" dvander@0: #include "Types.h" dvander@12: #include "Array.h" dvander@14: #include "Reference.h" dvander@28: #include "Heap-inl.h" dvander@0: dvander@59: using namespace ke; dvander@0: dvander@0: #define PUSH_I32(top, value) \ dvander@0: do { \ dvander@0: top.sp->i32 = value; \ dvander@0: top.sp++; \ dvander@0: } while (0) dvander@0: dvander@4: #define PUSH_F(top, value) \ dvander@4: do { \ dvander@4: top.sp->f = value; \ dvander@4: top.sp++; \ dvander@4: } while (0) dvander@4: dvander@0: static inline int dvander@0: POP_I32(Top &top) dvander@0: { dvander@0: int value = top.sp[-1].i32; dvander@0: top.sp--; dvander@0: return value; dvander@0: } dvander@0: dvander@4: static inline float dvander@4: POP_F(Top &top) dvander@4: { dvander@4: float value = top.sp[-1].f; dvander@4: top.sp--; dvander@4: return value; dvander@4: } dvander@4: dvander@0: static inline bool dvander@14: PushNewRef(Zone *zone, Top &top, void *location, Heap::Tenure tenure) dvander@14: { dvander@14: Reference *ref = Reference::NewStack(zone, location, tenure); dvander@14: if (!ref) dvander@14: return false; dvander@14: top.sp->ref = ref; dvander@14: top.sp++; dvander@14: return true; dvander@14: } dvander@14: dvander@14: static inline bool dvander@0: CheckDimensionSize(int size, unsigned width) dvander@0: { dvander@0: if (size <= 0) { dvander@0: assert(false); dvander@0: return false; dvander@0: } dvander@0: // One word, because we hide stuff below and need to add that in. dvander@0: if (unsigned(size) >= (INT_MAX / width) - sizeof(unsigned)) { dvander@0: assert(false); dvander@0: return false; dvander@0: } dvander@0: return true; dvander@0: } dvander@0: dvander@12: // Generate an array with completely static sizes. dvander@12: static Array * dvander@27: GenerateArray(Zone *zone, Handle map) dvander@0: { dvander@12: assert(map->type()->elements() != -1); dvander@0: dvander@43: Local array(zone, Array::New(zone, map, map->type()->elements(), Heap::Tenure_Stack)); dvander@12: if (!array) dvander@0: return NULL; dvander@0: dvander@12: if (map->type()->levels() == 1) dvander@12: return array; dvander@0: dvander@27: Local contained(zone, map->contained()); dvander@43: Local sub(zone); dvander@12: for (unsigned i = 0; i < unsigned(map->type()->elements()); i++) { dvander@43: sub = GenerateArray(zone, contained); dvander@12: if (!sub) dvander@0: return NULL; dvander@12: array->writeObject(zone, i, sub); dvander@0: } dvander@0: dvander@12: return array; dvander@0: } dvander@0: dvander@21: static bool dvander@21: CopyArray(Zone *zone, Array *source, Array *dest) dvander@21: { dvander@21: assert(source->type()->levels() == 1); dvander@21: assert(dest->type()->levels() == 1); dvander@21: assert(source->type()->contained()->primitive() == dvander@21: dest->type()->contained()->primitive()); dvander@21: assert(source->nelements() >= dest->nelements()); dvander@21: assert(dest->elementSize() == source->elementSize()); dvander@21: dvander@21: memcpy(source->vec(), dest->vec(), dest->nelements() * dest->elementSize()); dvander@21: return true; dvander@21: } dvander@21: dvander@68: static bool dvander@68: InvokeNative(Zone *zone, Handle native, void *identity, LocalSlot *args, unsigned argc, dvander@68: int *result) dvander@68: { dvander@68: NativeArgs guard; dvander@68: dvander@68: if (!zone->stack().pushNativeFrame(native, guard, argc + 1)) dvander@68: return false; dvander@68: dvander@68: intptr_t *cells = guard.args(); dvander@68: dvander@68: // params[0] == argc, in ISPE dvander@68: cells[0] = argc; dvander@68: dvander@68: // Pass the formal arguments. dvander@68: Local type(zone, native->type()); dvander@68: Local param(zone); dvander@68: for (unsigned i = 0; i < type->parameters()->length(); i++) { dvander@68: param = type->parameterAt(i); dvander@68: dvander@68: // For floats, we rely on the aliasing of |i32| and |f|. dvander@68: LocalSlot &arg = args[-int(i) - 1]; dvander@68: if (param->isInt32() || param->isFloat()) { dvander@68: cells[i + 1] = arg.i32; dvander@68: } else if (param->isReference()) { dvander@68: cells[i + 1] = reinterpret_cast(arg.ref->location()); dvander@68: } else { dvander@68: assert(param->isArray()); dvander@68: cells[i + 1] = reinterpret_cast(arg.array->vec()); dvander@68: } dvander@68: } dvander@68: dvander@68: // For variadic arguments, the compiler left either arrays or references. dvander@68: for (unsigned i = type->parameters()->length(); i < argc; i++) { dvander@68: LocalSlot &arg = args[-int(i) - 1]; dvander@68: dvander@68: if (arg.obj->is(MapKind_Reference)) { dvander@68: cells[i + 1] = reinterpret_cast(arg.ref->location()); dvander@68: } else { dvander@68: assert(arg.obj->is(MapKind_Array)); dvander@68: cells[i + 1] = reinterpret_cast(arg.array->vec()); dvander@68: } dvander@68: } dvander@68: dvander@68: typedef intptr_t (*INVOKE)(void *, intptr_t *); dvander@68: INVOKE invoke = (INVOKE)native->function(); dvander@68: *result = invoke(identity, cells); dvander@68: dvander@68: // :TODO: detect errors. dvander@68: return true; dvander@68: } dvander@68: dvander@0: bool dvander@59: ke::Interpret(Zone *zone, Frame *entryFrame, LocalSlot *result) dvander@0: { dvander@42: Top &top = *zone->stack().top(); dvander@47: LocalSlot *locals = entryFrame->locals(); dvander@0: dvander@0: for (;;) { dvander@0: Opcode op = Opcode(*top.pc); dvander@0: switch (op) { dvander@0: dvander@0: case OP_NOP: dvander@0: break; dvander@0: dvander@0: case OP_INT: dvander@0: PUSH_I32(top, READ_INT32(top.pc)); dvander@0: break; dvander@0: dvander@4: case OP_FLOAT: dvander@4: PUSH_F(top, READ_FLOAT(top.pc)); dvander@4: break; dvander@4: dvander@0: case OP_RETURN: dvander@0: { dvander@4: *result = *--top.sp; dvander@47: if (!top.fp->prev()) dvander@0: return true; dvander@0: dvander@65: zone->stack().popInlineFrame(); dvander@47: locals = top.fp->locals(); dvander@4: *top.sp++ = *result; dvander@0: top.pc += OpcodeInfo[*top.pc].length; dvander@0: continue; dvander@0: } dvander@0: dvander@0: #define RELATIONAL_JUMP(op) \ dvander@0: { \ dvander@0: int right = POP_I32(top); \ dvander@0: int left = POP_I32(top); \ dvander@0: if (left op right) { \ dvander@0: top.pc += READ_INT32(top.pc); \ dvander@0: continue; \ dvander@0: } \ dvander@0: } dvander@0: dvander@0: case OP_JL: dvander@0: RELATIONAL_JUMP(<); dvander@0: break; dvander@0: dvander@0: case OP_JLE: dvander@0: RELATIONAL_JUMP(<=); dvander@0: break; dvander@0: dvander@0: case OP_JG: dvander@0: RELATIONAL_JUMP(>); dvander@0: break; dvander@0: dvander@0: case OP_JGE: dvander@0: RELATIONAL_JUMP(>=); dvander@0: break; dvander@0: dvander@0: case OP_JEQ: dvander@0: RELATIONAL_JUMP(==); dvander@0: break; dvander@0: dvander@0: case OP_JNE: dvander@0: RELATIONAL_JUMP(!=); dvander@0: break; dvander@0: dvander@0: case OP_JT: dvander@0: { dvander@0: int value = POP_I32(top); dvander@0: if (value) { dvander@0: top.pc += READ_INT32(top.pc); dvander@0: continue; dvander@0: } dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_JF: dvander@0: { dvander@0: int value = POP_I32(top); dvander@0: if (!value) { dvander@0: top.pc += READ_INT32(top.pc); dvander@0: continue; dvander@0: } dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_JUMP: dvander@0: top.pc += READ_INT32(top.pc); dvander@0: continue; dvander@0: dvander@0: case OP_GETLOCAL: dvander@0: { dvander@0: int slot = READ_INT32(top.pc); dvander@0: *top.sp++ = locals[slot]; dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_SETLOCAL: dvander@0: { dvander@0: int slot = READ_INT32(top.pc); dvander@0: locals[slot] = top.sp[-1]; dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_ADD: dvander@0: { dvander@0: int right = POP_I32(top); dvander@0: int left = POP_I32(top); dvander@0: int result = left + right; dvander@0: PUSH_I32(top, result); dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_SUB: dvander@0: { dvander@0: int right = POP_I32(top); dvander@0: int left = POP_I32(top); dvander@0: int result = left - right; dvander@0: PUSH_I32(top, result); dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_MUL: dvander@0: { dvander@0: int right = POP_I32(top); dvander@0: int left = POP_I32(top); dvander@0: int result = left * right; dvander@22: PUSH_I32(top, result); dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_DIV: dvander@0: { dvander@0: int right = POP_I32(top); dvander@0: int left = POP_I32(top); dvander@0: if (!right) dvander@0: return false; dvander@0: int result = left / right; dvander@0: PUSH_I32(top, result); dvander@0: break; dvander@0: } dvander@0: dvander@4: case OP_ADD_F: dvander@4: { dvander@4: float right = POP_F(top); dvander@4: float left = POP_F(top); dvander@4: float result = left + right; dvander@4: PUSH_F(top, result); dvander@4: break; dvander@4: } dvander@4: dvander@4: case OP_SUB_F: dvander@4: { dvander@4: float right = POP_F(top); dvander@4: float left = POP_F(top); dvander@4: float result = left - right; dvander@4: PUSH_F(top, result); dvander@4: break; dvander@4: } dvander@4: dvander@4: case OP_MUL_F: dvander@4: { dvander@4: float right = POP_F(top); dvander@4: float left = POP_F(top); dvander@4: float result = left * right; dvander@4: PUSH_F(top, result); dvander@4: break; dvander@4: } dvander@4: dvander@4: case OP_DIV_F: dvander@4: { dvander@4: float right = POP_F(top); dvander@4: float left = POP_F(top); dvander@4: if (!right) dvander@4: return false; dvander@4: float result = left / right; dvander@4: PUSH_F(top, result); dvander@4: break; dvander@4: } dvander@4: dvander@8: case OP_SHL: dvander@8: { dvander@8: int right = POP_I32(top); dvander@8: int left = POP_I32(top); dvander@8: int result = left << right; dvander@8: PUSH_I32(top, result); dvander@8: break; dvander@8: } dvander@8: dvander@8: case OP_SHR: dvander@8: { dvander@8: int right = POP_I32(top); dvander@8: int left = POP_I32(top); dvander@8: int result = left >> right; dvander@8: PUSH_I32(top, result); dvander@8: break; dvander@8: } dvander@8: dvander@8: case OP_USHR: dvander@8: { dvander@8: int right = POP_I32(top); dvander@8: int left = POP_I32(top); dvander@8: int result = unsigned(left) >> unsigned(right); dvander@8: PUSH_I32(top, result); dvander@8: break; dvander@8: } dvander@8: dvander@8: case OP_BITAND: dvander@8: { dvander@8: int right = POP_I32(top); dvander@8: int left = POP_I32(top); dvander@8: int result = left & right; dvander@8: PUSH_I32(top, result); dvander@8: break; dvander@8: } dvander@8: dvander@8: case OP_BITOR: dvander@8: { dvander@8: int right = POP_I32(top); dvander@8: int left = POP_I32(top); dvander@8: int result = left | right; dvander@8: PUSH_I32(top, result); dvander@8: break; dvander@8: } dvander@8: dvander@8: case OP_BITXOR: dvander@8: { dvander@8: int right = POP_I32(top); dvander@8: int left = POP_I32(top); dvander@8: int result = left ^ right; dvander@8: PUSH_I32(top, result); dvander@8: break; dvander@8: } dvander@8: dvander@0: case OP_POP: dvander@0: top.sp--; dvander@0: break; dvander@0: dvander@0: case OP_STOP: dvander@0: assert(false); dvander@0: break; dvander@0: dvander@0: case OP_CALLGLOBAL: dvander@0: { dvander@53: zone->heap().reclaim(); dvander@46: unsigned index = READ_UINT32(top.pc); dvander@47: Local newFun(zone, top.fp->code()->module()->getFunction(index)); dvander@65: if (!zone->stack().pushInlineFrame(newFun)) { dvander@47: assert(false); dvander@47: return false; dvander@47: } dvander@0: dvander@47: locals = top.fp->locals(); dvander@0: continue; dvander@0: } dvander@0: dvander@0: case OP_GETARG: dvander@0: { dvander@0: unsigned slot = READ_UINT32(top.pc); dvander@47: *top.sp++ = top.fp->arg(slot); dvander@0: break; dvander@0: } dvander@0: dvander@0: case OP_SETARG: dvander@0: { dvander@0: unsigned slot = READ_UINT32(top.pc); dvander@47: top.fp->arg(slot) = *--top.sp; dvander@0: break; dvander@0: } dvander@0: dvander@19: case OP_BOUNDS: dvander@0: { dvander@12: Array *array = top.sp[-2].array; dvander@0: int index = top.sp[-1].i32; dvander@12: if (index < 0 || unsigned(index) >= array->nelements()) dvander@0: return false; dvander@0: break; dvander@0: } dvander@0: dvander@6: case OP_GETARRAY_I: dvander@0: { dvander@12: Array *array = top.sp[-2].array; dvander@0: int index = top.sp[-1].i32; dvander@0: top.sp[-2].i32 = array->readInt32(index); dvander@0: top.sp--; dvander@0: break; dvander@0: } dvander@0: dvander@7: case OP_GETARRAY_F: dvander@7: { dvander@12: Array *array = top.sp[-2].array; dvander@7: int index = top.sp[-1].i32; dvander@7: top.sp[-2].f = array->readFloat(index); dvander@7: top.sp--; dvander@7: break; dvander@7: } dvander@7: dvander@21: case OP_GETARRAY_C: dvander@21: { dvander@21: Array *array = top.sp[-2].array; dvander@21: int index = top.sp[-1].i32; dvander@21: top.sp[-2].i32 = array->readChar(index); dvander@21: top.sp--; dvander@21: break; dvander@21: } dvander@21: dvander@6: case OP_SETARRAY_I: dvander@0: { dvander@12: Array *array = top.sp[-3].array; dvander@0: int index = top.sp[-2].i32; dvander@0: array->writeInt32(index, top.sp[-1].i32); dvander@0: top.sp[-3] = top.sp[-1]; dvander@0: top.sp -= 2; dvander@0: break; dvander@0: } dvander@0: dvander@7: case OP_SETARRAY_F: dvander@7: { dvander@12: Array *array = top.sp[-3].array; dvander@7: int index = top.sp[-2].i32; dvander@7: array->writeFloat(index, top.sp[-1].f); dvander@7: top.sp[-3] = top.sp[-1]; dvander@7: top.sp -= 2; dvander@7: break; dvander@7: } dvander@7: dvander@21: case OP_SETARRAY_C: dvander@21: { dvander@21: Array *array = top.sp[-3].array; dvander@21: int index = top.sp[-2].i32; dvander@21: array->writeChar(index, top.sp[-1].i32); dvander@21: top.sp[-3] = top.sp[-1]; dvander@21: top.sp -= 2; dvander@21: break; dvander@21: } dvander@21: dvander@19: case OP_GETARRAY_O: dvander@0: { dvander@12: Array *array = top.sp[-2].array; dvander@0: int index = top.sp[-1].i32; dvander@12: top.sp[-2].array = Array::cast(array->readObject(index)); dvander@0: top.sp--; dvander@0: break; dvander@0: } dvander@0: dvander@4: case OP_CVT_I2F: dvander@4: { dvander@4: int ival = POP_I32(top); dvander@4: float fval = (float)ival; dvander@4: PUSH_F(top, fval); dvander@4: break; dvander@4: } dvander@4: dvander@19: case OP_NEWARRAY_LIFO: dvander@12: { dvander@47: Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc)))); dvander@42: Array *array = GenerateArray(zone, map); dvander@12: if (!array) { dvander@12: assert(false); dvander@12: return false; dvander@12: } dvander@12: top.sp->obj = array; dvander@12: top.sp++; dvander@12: break; dvander@12: } dvander@12: dvander@12: case OP_SAVELIFO: dvander@10: { dvander@10: unsigned slot = READ_UINT32(top.pc); dvander@42: locals[slot].address = zone->heap().lifoPosition(); dvander@12: break; dvander@12: } dvander@12: dvander@12: case OP_UNWINDLIFO: dvander@12: { dvander@12: unsigned slot = READ_UINT32(top.pc); dvander@42: zone->heap().unwindLifo(locals[slot].address); dvander@10: break; dvander@10: } dvander@10: dvander@14: case OP_VARREF_LIFO: dvander@14: { dvander@14: unsigned slot = READ_UINT32(top.pc); dvander@42: if (!PushNewRef(zone, top, &locals[slot], Heap::Tenure_Stack)) { dvander@14: assert(false); dvander@14: return false; dvander@14: } dvander@14: break; dvander@14: } dvander@14: dvander@14: case OP_ARGREF_LIFO: dvander@14: { dvander@14: unsigned slot = READ_UINT32(top.pc); dvander@47: if (!PushNewRef(zone, top, &top.fp->arg(slot), Heap::Tenure_Stack)) { dvander@14: assert(false); dvander@14: return false; dvander@14: } dvander@14: break; dvander@14: } dvander@14: dvander@17: case OP_ARRAYREF_LIFO: dvander@17: { dvander@42: Local array(zone, top.sp[-2].array); dvander@42: Local ref(zone, Reference::NewArray(zone, array, top.sp[-1].i32, Heap::Tenure_Stack)); dvander@17: if (!ref) { dvander@17: assert(false); dvander@17: return false; dvander@17: } dvander@17: top.sp[-2].ref = ref; dvander@17: top.sp--; dvander@17: break; dvander@17: } dvander@17: dvander@16: case OP_SLOTREF_LIFO: dvander@16: { dvander@42: Reference *ref = Reference::NewStack(zone, top.sp[-1], Heap::Tenure_Stack); dvander@16: if (!ref) { dvander@16: assert(false); dvander@16: return false; dvander@16: } dvander@16: top.sp[-1].ref = ref; dvander@16: break; dvander@16: } dvander@16: dvander@14: case OP_GETREF_I: dvander@14: { dvander@14: top.sp[-1].i32 = top.sp[-1].ref->getInt(); dvander@14: break; dvander@14: } dvander@14: dvander@14: case OP_SETREF_I: dvander@14: { dvander@14: top.sp[-2].ref->setInt(top.sp[-1].i32); dvander@14: top.sp[-2].i32 = top.sp[-1].i32; dvander@23: top.sp--; dvander@14: break; dvander@14: } dvander@14: dvander@14: case OP_GETREF_F: dvander@14: { dvander@14: top.sp[-1].f = top.sp[-1].ref->getFloat(); dvander@14: break; dvander@14: } dvander@14: dvander@14: case OP_SETREF_F: dvander@14: { dvander@14: top.sp[-2].ref->setFloat(top.sp[-1].f); dvander@14: top.sp[-2].f = top.sp[-1].f; dvander@14: break; dvander@14: } dvander@14: dvander@21: case OP_OBJECT: dvander@21: { dvander@47: top.sp->obj = top.fp->code()->object(READ_UINT32(top.pc)); dvander@21: top.sp++; dvander@21: break; dvander@21: } dvander@21: dvander@21: case OP_ARRAYCOPY: dvander@21: { dvander@21: // Note for this assignment opcode, we return the left-hand side, not the dvander@21: // right, since in this case the result of the expression could be different. dvander@42: if (!CopyArray(zone, top.sp[-2].array, top.sp[-1].array)) { dvander@21: assert(false); dvander@21: return false; dvander@21: } dvander@21: top.sp--; dvander@21: break; dvander@21: } dvander@21: dvander@22: case OP_DUP: dvander@22: { dvander@23: top.sp++; dvander@23: top.sp[-1] = top.sp[-2]; dvander@22: break; dvander@22: } dvander@22: dvander@22: case OP_DUP2: dvander@22: { dvander@23: top.sp += 2; dvander@23: top.sp[-2] = top.sp[-4]; dvander@23: top.sp[-1] = top.sp[-3]; dvander@23: break; dvander@23: } dvander@23: dvander@23: case OP_SWAP: dvander@23: { dvander@23: LocalSlot slot = top.sp[-2]; dvander@23: top.sp[-2] = top.sp[-1]; dvander@23: top.sp[-1] = slot; dvander@23: break; dvander@23: } dvander@23: dvander@23: case OP_ROLL3: dvander@23: { dvander@23: LocalSlot slot = top.sp[-3]; dvander@23: top.sp[-3] = top.sp[-2]; dvander@23: top.sp[-2] = top.sp[-1]; dvander@23: top.sp[-1] = slot; dvander@23: break; dvander@23: } dvander@23: dvander@23: case OP_PICK2: dvander@23: { dvander@23: top.sp++; dvander@23: top.sp[-1] = top.sp[-3]; dvander@23: break; dvander@23: } dvander@23: dvander@23: case OP_PICK3: dvander@23: { dvander@23: top.sp++; dvander@23: top.sp[-1] = top.sp[-4]; dvander@22: break; dvander@22: } dvander@22: dvander@68: case OP_CALLNATIVE: dvander@68: { dvander@68: unsigned index = READ_UINT32(top.pc); dvander@68: unsigned argc = READ_UINT32(top.pc + 4); dvander@68: dvander@68: Local native(zone, Native::cast(top.fp->code()->module()->natives()->at(index))); dvander@68: if (!native->function()) { dvander@68: // :TODO: error dvander@68: assert(false); dvander@68: return false; dvander@68: } dvander@68: dvander@68: int result; dvander@68: void *nativeIdentity = top.fp->code()->module()->nativeIdentity(); dvander@68: if (!InvokeNative(zone, native, nativeIdentity, top.sp, argc, &result)) { dvander@68: assert(false); dvander@68: return false; dvander@68: } dvander@68: dvander@68: top.sp -= argc; dvander@68: PUSH_I32(top, result); dvander@68: break; dvander@68: } dvander@68: dvander@0: default: dvander@0: assert(false); dvander@0: return false; dvander@0: dvander@0: } dvander@0: dvander@0: top.pc += OpcodeInfo[op].length; dvander@0: } dvander@0: dvander@0: return true; dvander@0: }