diff options
Diffstat (limited to 'src/wasm-interpreter.h')
-rw-r--r-- | src/wasm-interpreter.h | 136 |
1 files changed, 83 insertions, 53 deletions
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 2a841f6fd..e7a5d6d14 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -29,6 +29,10 @@ #include "support/bits.h" #include "wasm.h" +#ifdef WASM_INTERPRETER_DEBUG +#include "wasm-printing.h" +#endif + namespace wasm { using namespace cashew; @@ -39,7 +43,6 @@ IString WASM("wasm"), RETURN_FLOW("*return:)*"); enum { - pageSize = 64*1024, maxCallDepth = 250 }; @@ -112,6 +115,15 @@ public: return callFunction(export_->value, arguments); } + std::string printFunctionStack() { + std::string ret = "/== (binaryen interpreter stack trace)\n"; + for (int i = int(functionStack.size()) - 1; i >= 0; i--) { + ret += std::string("|: ") + functionStack[i].str + "\n"; + } + ret += std::string("\\==\n"); + return ret; + } + private: size_t callDepth = 0; @@ -120,6 +132,10 @@ private: int indent = 0; #endif + // Function stack. We maintain this explicitly to allow printing of + // stack traces. + std::vector<Name> functionStack; + // // Calls a function. This can be used both internally (calls from // the interpreter to another method), or when you want to call into @@ -166,7 +182,7 @@ private: indent++; #if WASM_INTERPRETER_DEBUG == 2 doIndent(std::cout, indent); - expression->print(std::cout, indent) << '\n'; + std::cout << "\n" << expression << '\n'; indent++; #endif } @@ -200,12 +216,33 @@ private: Flow visitBlock(Block *curr) { NOTE_ENTER("Block"); + // special-case Block, because Block nesting (in their first element) can be incredibly deep + std::vector<Block*> stack; + stack.push_back(curr); + while (curr->list.size() > 0 && curr->list[0]->is<Block>()) { + curr = curr->list[0]->cast<Block>(); + stack.push_back(curr); + } Flow flow; - for (auto expression : curr->list) { - flow = visit(expression); + auto* top = stack.back(); + while (stack.size() > 0) { + curr = stack.back(); + stack.pop_back(); if (flow.breaking()) { flow.clearIf(curr->name); - return flow; + continue; + } + auto& list = curr->list; + for (size_t i = 0; i < list.size(); i++) { + if (curr != top && i == 0) { + // one of the block recursions we already handled + continue; + } + flow = visit(list[i]); + if (flow.breaking()) { + flow.clearIf(curr->name); + break; + } } } return flow; @@ -246,46 +283,30 @@ private: if (curr->condition) { Flow conditionFlow = visit(curr->condition); if (conditionFlow.breaking()) return conditionFlow; - condition = conditionFlow.value.getInteger(); + condition = conditionFlow.value.getInteger() != 0; } return condition ? flow : Flow(); } Flow visitSwitch(Switch *curr) { NOTE_ENTER("Switch"); - Flow flow = visit(curr->value); - if (flow.breaking()) { - flow.clearIf(curr->name); - return flow; - } + Flow flow = visit(curr->condition); + if (flow.breaking()) return flow; NOTE_EVAL1(flow.value); int64_t index = flow.value.getInteger(); + + if (curr->value) { + flow = visit(curr->value); + if (flow.breaking()) return flow; + NOTE_EVAL1(flow.value); + } else { + flow = Flow(); + } + Name target = curr->default_; if (index >= 0 && (size_t)index < curr->targets.size()) { - target = curr->targets[index]; - } - // This is obviously very inefficient. This should be a cached data structure - std::map<Name, size_t> caseMap; // name => index in cases - for (size_t i = 0; i < curr->cases.size(); i++) { - caseMap[curr->cases[i].name] = i; - } - auto iter = caseMap.find(target); - if (iter == caseMap.end()) { - // not in the cases, so this is a break - Flow flow(target); - flow.clearIf(curr->name); - return flow; - } - size_t caseIndex = iter->second; - assert(caseIndex < curr->cases.size()); - while (caseIndex < curr->cases.size()) { - Switch::Case& c = curr->cases[caseIndex]; - flow = visit(c.body); - if (flow.breaking()) { - flow.clearIf(curr->name); - break; - } - caseIndex++; + target = curr->targets[(size_t)index]; } + flow.breakTo = target; return flow; } @@ -382,6 +403,7 @@ private: case Clz: return value.countLeadingZeroes(); case Ctz: return value.countTrailingZeroes(); case Popcnt: return value.popCount(); + case EqZ: return Literal(int32_t(value == Literal(int32_t(0)))); case ReinterpretInt: return value.castToF32(); case ExtendSInt32: return value.extendToSI64(); case ExtendUInt32: return value.extendToUI64(); @@ -395,6 +417,7 @@ private: case Clz: return value.countLeadingZeroes(); case Ctz: return value.countTrailingZeroes(); case Popcnt: return value.popCount(); + case EqZ: return Literal(int32_t(value == Literal(int64_t(0)))); case WrapInt64: return value.truncateToI32(); case ReinterpretInt: return value.castToF64(); case ConvertUInt64: return curr->type == f32 ? value.convertUToF32() : value.convertUToF64(); @@ -445,8 +468,8 @@ private: if (flow.breaking()) return flow; Literal right = flow.value; NOTE_EVAL2(left, right); - assert(left.type == curr->left->type); - assert(right.type == curr->right->type); + assert(isConcreteWasmType(curr->left->type) ? left.type == curr->left->type : true); + assert(isConcreteWasmType(curr->right->type) ? right.type == curr->right->type : true); if (left.type == i32) { switch (curr->op) { case Add: return left.add(right); @@ -476,6 +499,8 @@ private: case Shl: return left.shl(right.and_(Literal(int32_t(31)))); case ShrU: return left.shrU(right.and_(Literal(int32_t(31)))); case ShrS: return left.shrS(right.and_(Literal(int32_t(31)))); + case RotL: return left.rotL(right); + case RotR: return left.rotR(right); case Eq: return left.eq(right); case Ne: return left.ne(right); case LtS: return left.ltS(right); @@ -517,6 +542,8 @@ private: case Shl: return left.shl(right.and_(Literal(int64_t(63)))); case ShrU: return left.shrU(right.and_(Literal(int64_t(63)))); case ShrS: return left.shrS(right.and_(Literal(int64_t(63)))); + case RotL: return left.rotL(right); + case RotR: return left.rotR(right); case Eq: return left.eq(right); case Ne: return left.ne(right); case LtS: return left.ltS(right); @@ -574,21 +601,20 @@ private: Flow visitHost(Host *curr) { NOTE_ENTER("Host"); switch (curr->op) { - case PageSize: return Literal((int32_t)pageSize); - case MemorySize: return Literal((int32_t)instance.memorySize); + case PageSize: return Literal((int32_t)Memory::kPageSize); + case MemorySize: return Literal(int32_t(instance.memorySize * Memory::kPageSize)); case GrowMemory: { Flow flow = visit(curr->operands[0]); if (flow.breaking()) return flow; int32_t ret = instance.memorySize; uint32_t delta = flow.value.geti32(); - if (delta % pageSize != 0) trap("growMemory: delta not multiple"); - if (delta > uint32_t(-1) - pageSize) trap("growMemory: delta relatively too big"); + if (delta > uint32_t(-1) /Memory::kPageSize) trap("growMemory: delta relatively too big"); if (instance.memorySize >= uint32_t(-1) - delta) trap("growMemory: delta objectively too big"); uint32_t newSize = instance.memorySize + delta; if (newSize > instance.wasm.memory.max) trap("growMemory: exceeds max"); - instance.externalInterface->growMemory(instance.memorySize, newSize); + instance.externalInterface->growMemory(instance.memorySize * Memory::kPageSize, newSize * Memory::kPageSize); instance.memorySize = newSize; - return Literal(ret); + return Literal(int32_t(ret * Memory::kPageSize)); } case HasFeature: { IString id = curr->nameOperand; @@ -615,8 +641,8 @@ private: if (val > (double)std::numeric_limits<int32_t>::max() || val < (double)std::numeric_limits<int32_t>::min()) trap("i32.truncSFloat overflow"); return Literal(int32_t(val)); } else { - int64_t converted = val; - if ((val >= 1 && converted <= 0) || val < (double)LLONG_MIN) trap("i32.truncSFloat overflow"); + int64_t converted = (int64_t)val; + if ((val >= 1 && converted <= 0) || val < (double)LLONG_MIN) trap("i64.truncSFloat overflow"); return Literal(converted); } } @@ -625,10 +651,10 @@ private: double val = value.getFloat(); if (isnan(val)) trap("truncUFloat of nan"); if (curr->type == i32) { - if (val > (double)std::numeric_limits<uint32_t>::max() || val <= (double)-1) trap("i64.truncUFloat overflow"); + if (val > (double)std::numeric_limits<uint32_t>::max() || val <= (double)-1) trap("i32.truncUFloat overflow"); return Literal(uint32_t(val)); } else { - uint64_t converted = val; + uint64_t converted = (uint64_t)val; if (converted < val - 1 || val <= (double)-1) trap("i64.truncUFloat overflow"); return Literal(converted); } @@ -641,6 +667,7 @@ private: if (callDepth > maxCallDepth) externalInterface->trap("stack limit"); callDepth++; + functionStack.push_back(name); Function *function = wasm.functionsMap[name]; assert(function); @@ -656,29 +683,32 @@ private: if (function->result == none) ret = Literal(); assert(function->result == ret.type); callDepth--; + assert(functionStack.back() == name); + functionStack.pop_back(); #ifdef WASM_INTERPRETER_DEBUG std::cout << "exiting " << function->name << " with " << ret << '\n'; #endif return ret; } - size_t memorySize; + size_t memorySize; // in pages template <class LS> size_t getFinalAddress(LS* curr, Literal ptr) { - auto trapIfGt = [this](size_t lhs, size_t rhs, const char* msg) { + auto trapIfGt = [this](uint64_t lhs, uint64_t rhs, const char* msg) { if (lhs > rhs) { std::stringstream ss; ss << msg << ": " << lhs << " > " << rhs; externalInterface->trap(ss.str().c_str()); } }; + uint32_t memorySizeBytes = memorySize * Memory::kPageSize; uint64_t addr = ptr.type == i32 ? ptr.geti32() : ptr.geti64(); - trapIfGt(curr->offset, memorySize, "offset > memory"); - trapIfGt(addr, memorySize - curr->offset, "final > memory"); + trapIfGt(curr->offset, memorySizeBytes, "offset > memory"); + trapIfGt(addr, memorySizeBytes - curr->offset, "final > memory"); addr += curr->offset; - trapIfGt(curr->bytes, memorySize, "bytes > memory"); - trapIfGt(addr, memorySize - curr->bytes, "highest > memory"); + trapIfGt(curr->bytes, memorySizeBytes, "bytes > memory"); + trapIfGt(addr, memorySizeBytes - curr->bytes, "highest > memory"); return addr; } |