diff options
-rw-r--r-- | src/shell-interface.h | 2 | ||||
-rw-r--r-- | src/tools/binaryen-shell.cpp | 4 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 874 |
3 files changed, 466 insertions, 414 deletions
diff --git a/src/shell-interface.h b/src/shell-interface.h index b87460a48..62fc2432a 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -98,7 +98,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { } } - Literal callImport(Import *import, ModuleInstance::LiteralList& arguments) override { + Literal callImport(Import *import, LiteralList& arguments) override { if (import->module == SPECTEST && import->base == PRINT) { for (auto argument : arguments) { std::cout << argument << '\n'; diff --git a/src/tools/binaryen-shell.cpp b/src/tools/binaryen-shell.cpp index ebc0138ee..fdc933fa6 100644 --- a/src/tools/binaryen-shell.cpp +++ b/src/tools/binaryen-shell.cpp @@ -41,7 +41,7 @@ using namespace wasm; struct Invocation { ModuleInstance* instance; IString name; - ModuleInstance::LiteralList arguments; + LiteralList arguments; Invocation(Element& invoke, ModuleInstance* instance, SExpressionWasmBuilder& builder) : instance(instance) { assert(invoke[0]->str() == INVOKE); @@ -84,7 +84,7 @@ static void run_asserts(size_t* i, bool* checked, Module* wasm, if (!function) { std::cerr << "Unknown entry " << entry << std::endl; } else { - ModuleInstance::LiteralList arguments; + LiteralList arguments; for (WasmType param : function->params) { arguments.push_back(Literal(param)); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 9024031d2..55edbdfa0 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -70,6 +70,463 @@ public: } }; +// A list of literals, for function calls +typedef std::vector<Literal> LiteralList; + +// Debugging helpers +#ifdef WASM_INTERPRETER_DEBUG +struct IndentHandler { + int& indent; + const char *name; + IndentHandler(int& indent, const char *name, Expression *expression) : indent(indent), name(name) { + doIndent(std::cout, indent); + std::cout << "visit " << name << " :\n"; + indent++; +#if WASM_INTERPRETER_DEBUG == 2 + doIndent(std::cout, indent); + std::cout << "\n" << expression << '\n'; + indent++; +#endif + } + ~IndentHandler() { +#if WASM_INTERPRETER_DEBUG == 2 + indent--; +#endif + indent--; + doIndent(std::cout, indent); + std::cout << "exit " << name << '\n'; + } +}; +#define NOTE_ENTER(x) IndentHandler indentHandler(instance.indent, x, curr); +#define NOTE_NAME(p0) { doIndent(std::cout, instance.indent); std::cout << "name in " << indentHandler.name << '(' << Name(p0) << ")\n"; } +#define NOTE_EVAL1(p0) { doIndent(std::cout, instance.indent); std::cout << "eval in " << indentHandler.name << '(' << p0 << ")\n"; } +#define NOTE_EVAL2(p0, p1) { doIndent(std::cout, instance.indent); std::cout << "eval in " << indentHandler.name << '(' << p0 << ", " << p1 << ")\n"; } +#else // WASM_INTERPRETER_DEBUG +#define NOTE_ENTER(x) +#define NOTE_NAME(p0) +#define NOTE_EVAL1(p0) +#define NOTE_EVAL2(p0, p1) +#endif // WASM_INTERPRETER_DEBUG + +// Execute an expression +template<typename SubType> +class ExpressionRunner : public Visitor<SubType, Flow> { +public: + Flow visit(Expression *curr) { + return Visitor<SubType, Flow>::visit(curr); + } + + 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; + auto* top = stack.back(); + while (stack.size() > 0) { + curr = stack.back(); + stack.pop_back(); + if (flow.breaking()) { + flow.clearIf(curr->name); + 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; + } + Flow visitIf(If *curr) { + NOTE_ENTER("If"); + Flow flow = visit(curr->condition); + if (flow.breaking()) return flow; + NOTE_EVAL1(flow.value); + if (flow.value.geti32()) { + Flow flow = visit(curr->ifTrue); + if (!flow.breaking() && !curr->ifFalse) flow.value = Literal(); // if_else returns a value, but if does not + return flow; + } + if (curr->ifFalse) return visit(curr->ifFalse); + return Flow(); + } + Flow visitLoop(Loop *curr) { + NOTE_ENTER("Loop"); + while (1) { + Flow flow = visit(curr->body); + if (flow.breaking()) { + if (flow.breakTo == curr->in) continue; // lol + flow.clearIf(curr->out); + } + return flow; // loop does not loop automatically, only continue achieves that + } + } + Flow visitBreak(Break *curr) { + NOTE_ENTER("Break"); + bool condition = true; + Flow flow(curr->name); + if (curr->value) { + flow = visit(curr->value); + if (flow.breaking()) return flow; + flow.breakTo = curr->name; + } + if (curr->condition) { + Flow conditionFlow = visit(curr->condition); + if (conditionFlow.breaking()) return conditionFlow; + condition = conditionFlow.value.getInteger() != 0; + } + return condition ? flow : Flow(); + } + Flow visitSwitch(Switch *curr) { + NOTE_ENTER("Switch"); + Flow flow; + Literal value; + if (curr->value) { + flow = visit(curr->value); + if (flow.breaking()) return flow; + value = flow.value; + NOTE_EVAL1(value); + } + flow = visit(curr->condition); + if (flow.breaking()) return flow; + int64_t index = flow.value.getInteger(); + Name target = curr->default_; + if (index >= 0 && (size_t)index < curr->targets.size()) { + target = curr->targets[(size_t)index]; + } + flow.breakTo = target; + flow.value = value; + return flow; + } + + Flow visitConst(Const *curr) { + NOTE_ENTER("Const"); + NOTE_EVAL1(curr->value); + return Flow(curr->value); // heh + } + Flow visitUnary(Unary *curr) { + NOTE_ENTER("Unary"); + Flow flow = visit(curr->value); + if (flow.breaking()) return flow; + Literal value = flow.value; + NOTE_EVAL1(value); + if (value.type == i32) { + switch (curr->op) { + case ClzInt32: return value.countLeadingZeroes(); + case CtzInt32: return value.countTrailingZeroes(); + case PopcntInt32: return value.popCount(); + case EqZInt32: return Literal(int32_t(value == Literal(int32_t(0)))); + case ReinterpretInt32: return value.castToF32(); + case ExtendSInt32: return value.extendToSI64(); + case ExtendUInt32: return value.extendToUI64(); + case ConvertUInt32ToFloat32: return value.convertUToF32(); + case ConvertUInt32ToFloat64: return value.convertUToF64(); + case ConvertSInt32ToFloat32: return value.convertSToF32(); + case ConvertSInt32ToFloat64: return value.convertSToF64(); + default: abort(); + } + } + if (value.type == i64) { + switch (curr->op) { + case ClzInt64: return value.countLeadingZeroes(); + case CtzInt64: return value.countTrailingZeroes(); + case PopcntInt64: return value.popCount(); + case EqZInt64: return Literal(int32_t(value == Literal(int64_t(0)))); + case WrapInt64: return value.truncateToI32(); + case ReinterpretInt64: return value.castToF64(); + case ConvertUInt64ToFloat32: return value.convertUToF32(); + case ConvertUInt64ToFloat64: return value.convertUToF64(); + case ConvertSInt64ToFloat32: return value.convertSToF32(); + case ConvertSInt64ToFloat64: return value.convertSToF64(); + default: abort(); + } + } + if (value.type == f32) { + switch (curr->op) { + case NegFloat32: return value.neg(); + case AbsFloat32: return value.abs(); + case CeilFloat32: return value.ceil(); + case FloorFloat32: return value.floor(); + case TruncFloat32: return value.trunc(); + case NearestFloat32: return value.nearbyint(); + case SqrtFloat32: return value.sqrt(); + case TruncSFloat32ToInt32: + case TruncSFloat32ToInt64: return truncSFloat(curr, value); + case TruncUFloat32ToInt32: + case TruncUFloat32ToInt64: return truncUFloat(curr, value); + case ReinterpretFloat32: return value.castToI32(); + case PromoteFloat32: return value.extendToF64(); + default: abort(); + } + } + if (value.type == f64) { + switch (curr->op) { + case NegFloat64: return value.neg(); + case AbsFloat64: return value.abs(); + case CeilFloat64: return value.ceil(); + case FloorFloat64: return value.floor(); + case TruncFloat64: return value.trunc(); + case NearestFloat64: return value.nearbyint(); + case SqrtFloat64: return value.sqrt(); + case TruncSFloat64ToInt32: + case TruncSFloat64ToInt64: return truncSFloat(curr, value); + case TruncUFloat64ToInt32: + case TruncUFloat64ToInt64: return truncUFloat(curr, value); + case ReinterpretFloat64: return value.castToI64(); + case DemoteFloat64: { + double val = value.getFloat(); + if (std::isnan(val)) return Literal(float(val)); + if (std::isinf(val)) return Literal(float(val)); + if (val < -std::numeric_limits<float>::max()) return Literal(-std::numeric_limits<float>::infinity()); + if (val > std::numeric_limits<float>::max()) return Literal(std::numeric_limits<float>::infinity()); + return value.truncateToF32(); + } + default: abort(); + } + } + abort(); + } + Flow visitBinary(Binary *curr) { + NOTE_ENTER("Binary"); + Flow flow = visit(curr->left); + if (flow.breaking()) return flow; + Literal left = flow.value; + flow = visit(curr->right); + if (flow.breaking()) return flow; + Literal right = flow.value; + NOTE_EVAL2(left, right); + 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 AddInt32: return left.add(right); + case SubInt32: return left.sub(right); + case MulInt32: return left.mul(right); + case DivSInt32: { + if (right.getInteger() == 0) trap("i32.div_s by 0"); + if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) trap("i32.div_s overflow"); // signed division overflow + return left.divS(right); + } + case DivUInt32: { + if (right.getInteger() == 0) trap("i32.div_u by 0"); + return left.divU(right); + } + case RemSInt32: { + if (right.getInteger() == 0) trap("i32.rem_s by 0"); + if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) return Literal(int32_t(0)); + return left.remS(right); + } + case RemUInt32: { + if (right.getInteger() == 0) trap("i32.rem_u by 0"); + return left.remU(right); + } + case AndInt32: return left.and_(right); + case OrInt32: return left.or_(right); + case XorInt32: return left.xor_(right); + case ShlInt32: return left.shl(right.and_(Literal(int32_t(31)))); + case ShrUInt32: return left.shrU(right.and_(Literal(int32_t(31)))); + case ShrSInt32: return left.shrS(right.and_(Literal(int32_t(31)))); + case RotLInt32: return left.rotL(right); + case RotRInt32: return left.rotR(right); + case EqInt32: return left.eq(right); + case NeInt32: return left.ne(right); + case LtSInt32: return left.ltS(right); + case LtUInt32: return left.ltU(right); + case LeSInt32: return left.leS(right); + case LeUInt32: return left.leU(right); + case GtSInt32: return left.gtS(right); + case GtUInt32: return left.gtU(right); + case GeSInt32: return left.geS(right); + case GeUInt32: return left.geU(right); + default: abort(); + } + } else if (left.type == i64) { + switch (curr->op) { + case AddInt64: return left.add(right); + case SubInt64: return left.sub(right); + case MulInt64: return left.mul(right); + case DivSInt64: { + if (right.getInteger() == 0) trap("i64.div_s by 0"); + if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) trap("i64.div_s overflow"); // signed division overflow + return left.divS(right); + } + case DivUInt64: { + if (right.getInteger() == 0) trap("i64.div_u by 0"); + return left.divU(right); + } + case RemSInt64: { + if (right.getInteger() == 0) trap("i64.rem_s by 0"); + if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) return Literal(int64_t(0)); + return left.remS(right); + } + case RemUInt64: { + if (right.getInteger() == 0) trap("i64.rem_u by 0"); + return left.remU(right); + } + case AndInt64: return left.and_(right); + case OrInt64: return left.or_(right); + case XorInt64: return left.xor_(right); + case ShlInt64: return left.shl(right.and_(Literal(int64_t(63)))); + case ShrUInt64: return left.shrU(right.and_(Literal(int64_t(63)))); + case ShrSInt64: return left.shrS(right.and_(Literal(int64_t(63)))); + case RotLInt64: return left.rotL(right); + case RotRInt64: return left.rotR(right); + case EqInt64: return left.eq(right); + case NeInt64: return left.ne(right); + case LtSInt64: return left.ltS(right); + case LtUInt64: return left.ltU(right); + case LeSInt64: return left.leS(right); + case LeUInt64: return left.leU(right); + case GtSInt64: return left.gtS(right); + case GtUInt64: return left.gtU(right); + case GeSInt64: return left.geS(right); + case GeUInt64: return left.geU(right); + default: abort(); + } + } else if (left.type == f32 || left.type == f64) { + switch (curr->op) { + case AddFloat32: case AddFloat64: return left.add(right); + case SubFloat32: case SubFloat64: return left.sub(right); + case MulFloat32: case MulFloat64: return left.mul(right); + case DivFloat32: case DivFloat64: return left.div(right); + case CopySignFloat32: case CopySignFloat64: return left.copysign(right); + case MinFloat32: case MinFloat64: return left.min(right); + case MaxFloat32: case MaxFloat64: return left.max(right); + case EqFloat32: case EqFloat64: return left.eq(right); + case NeFloat32: case NeFloat64: return left.ne(right); + case LtFloat32: case LtFloat64: return left.lt(right); + case LeFloat32: case LeFloat64: return left.le(right); + case GtFloat32: case GtFloat64: return left.gt(right); + case GeFloat32: case GeFloat64: return left.ge(right); + default: abort(); + } + } + abort(); + } + Flow visitSelect(Select *curr) { + NOTE_ENTER("Select"); + Flow ifTrue = visit(curr->ifTrue); + if (ifTrue.breaking()) return ifTrue; + Flow ifFalse = visit(curr->ifFalse); + if (ifFalse.breaking()) return ifFalse; + Flow condition = visit(curr->condition); + if (condition.breaking()) return condition; + NOTE_EVAL1(condition.value); + return condition.value.geti32() ? ifTrue : ifFalse; // ;-) + } + Flow visitReturn(Return *curr) { + NOTE_ENTER("Return"); + Flow flow; + if (curr->value) { + flow = visit(curr->value); + if (flow.breaking()) return flow; + NOTE_EVAL1(flow.value); + } + flow.breakTo = RETURN_FLOW; + return flow; + } + Flow visitNop(Nop *curr) { + NOTE_ENTER("Nop"); + return Flow(); + } + Flow visitUnreachable(Unreachable *curr) { + NOTE_ENTER("Unreachable"); + trap("unreachable"); + return Flow(); + } + + Literal truncSFloat(Unary* curr, Literal value) { + double val = value.getFloat(); + if (std::isnan(val)) trap("truncSFloat of nan"); + if (curr->type == i32) { + if (value.type == f32) { + if (!isInRangeI32TruncS(value.reinterpreti32())) trap("i32.truncSFloat overflow"); + } else { + if (!isInRangeI32TruncS(value.reinterpreti64())) trap("i32.truncSFloat overflow"); + } + return Literal(int32_t(val)); + } else { + if (value.type == f32) { + if (!isInRangeI64TruncS(value.reinterpreti32())) trap("i64.truncSFloat overflow"); + } else { + if (!isInRangeI64TruncS(value.reinterpreti64())) trap("i64.truncSFloat overflow"); + } + return Literal(int64_t(val)); + } + } + + Literal truncUFloat(Unary* curr, Literal value) { + double val = value.getFloat(); + if (std::isnan(val)) trap("truncUFloat of nan"); + if (curr->type == i32) { + if (value.type == f32) { + if (!isInRangeI32TruncU(value.reinterpreti32())) trap("i32.truncUFloat overflow"); + } else { + if (!isInRangeI32TruncU(value.reinterpreti64())) trap("i32.truncUFloat overflow"); + } + return Literal(uint32_t(val)); + } else { + if (value.type == f32) { + if (!isInRangeI64TruncU(value.reinterpreti32())) trap("i64.truncUFloat overflow"); + } else { + if (!isInRangeI64TruncU(value.reinterpreti64())) trap("i64.truncUFloat overflow"); + } + return Literal(uint64_t(val)); + } + } + + virtual void trap(const char* why) { + WASM_UNREACHABLE(); + } +}; + +// Execute an expression by itself. Errors if we hit anything we need anything not in the expression itself standalone. +class StandaloneExpressionRunner : public ExpressionRunner<StandaloneExpressionRunner> { +public: + struct NonstandaloneException {}; + + Flow visitCall(Call* curr) { + throw NonstandaloneException(); + } + Flow visitCallImport(CallImport* curr) { + throw NonstandaloneException(); + } + Flow visitCallIndirect(CallIndirect* curr) { + throw NonstandaloneException(); + } + Flow visitGetLocal(GetLocal *curr) { + throw NonstandaloneException(); + } + Flow visitSetLocal(SetLocal *curr) { + throw NonstandaloneException(); + } + Flow visitLoad(Load *curr) { + throw NonstandaloneException(); + } + Flow visitStore(Store *curr) { + throw NonstandaloneException(); + } + Flow visitHost(Host *curr) { + throw NonstandaloneException(); + } + + void trap(const char* why) { + throw NonstandaloneException(); + } +}; + // // An instance of a WebAssembly module, which can execute it via AST interpretation. // @@ -82,8 +539,6 @@ public: class ModuleInstance { public: - typedef std::vector<Literal> LiteralList; - // // You need to implement one of these to create a concrete interpreter. The // ExternalInterface provides embedding-specific functionality like calling @@ -181,142 +636,13 @@ private: } }; -#ifdef WASM_INTERPRETER_DEBUG - struct IndentHandler { - int& indent; - const char *name; - IndentHandler(int& indent, const char *name, Expression *expression) : indent(indent), name(name) { - doIndent(std::cout, indent); - std::cout << "visit " << name << " :\n"; - indent++; -#if WASM_INTERPRETER_DEBUG == 2 - doIndent(std::cout, indent); - std::cout << "\n" << expression << '\n'; - indent++; -#endif - } - ~IndentHandler() { -#if WASM_INTERPRETER_DEBUG == 2 - indent--; -#endif - indent--; - doIndent(std::cout, indent); - std::cout << "exit " << name << '\n'; - } - }; - #define NOTE_ENTER(x) IndentHandler indentHandler(instance.indent, x, curr); - #define NOTE_NAME(p0) { doIndent(std::cout, instance.indent); std::cout << "name in " << indentHandler.name << '(' << Name(p0) << ")\n"; } - #define NOTE_EVAL1(p0) { doIndent(std::cout, instance.indent); std::cout << "eval in " << indentHandler.name << '(' << p0 << ")\n"; } - #define NOTE_EVAL2(p0, p1) { doIndent(std::cout, instance.indent); std::cout << "eval in " << indentHandler.name << '(' << p0 << ", " << p1 << ")\n"; } -#else - #define NOTE_ENTER(x) - #define NOTE_NAME(p0) - #define NOTE_EVAL1(p0) - #define NOTE_EVAL2(p0, p1) -#endif - - // Execute a statement - class ExpressionRunner : public Visitor<ExpressionRunner, Flow> { + // Executes expresions with concrete runtime info, the function and module at runtime + class RuntimeExpressionRunner : public ExpressionRunner<RuntimeExpressionRunner> { ModuleInstance& instance; FunctionScope& scope; public: - ExpressionRunner(ModuleInstance& instance, FunctionScope& scope) : instance(instance), scope(scope) {} - - 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; - auto* top = stack.back(); - while (stack.size() > 0) { - curr = stack.back(); - stack.pop_back(); - if (flow.breaking()) { - flow.clearIf(curr->name); - 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; - } - Flow visitIf(If *curr) { - NOTE_ENTER("If"); - Flow flow = visit(curr->condition); - if (flow.breaking()) return flow; - NOTE_EVAL1(flow.value); - if (flow.value.geti32()) { - Flow flow = visit(curr->ifTrue); - if (!flow.breaking() && !curr->ifFalse) flow.value = Literal(); // if_else returns a value, but if does not - return flow; - } - if (curr->ifFalse) return visit(curr->ifFalse); - return Flow(); - } - Flow visitLoop(Loop *curr) { - NOTE_ENTER("Loop"); - while (1) { - Flow flow = visit(curr->body); - if (flow.breaking()) { - if (flow.breakTo == curr->in) continue; // lol - flow.clearIf(curr->out); - } - return flow; // loop does not loop automatically, only continue achieves that - } - } - Flow visitBreak(Break *curr) { - NOTE_ENTER("Break"); - bool condition = true; - Flow flow(curr->name); - if (curr->value) { - flow = visit(curr->value); - if (flow.breaking()) return flow; - flow.breakTo = curr->name; - } - if (curr->condition) { - Flow conditionFlow = visit(curr->condition); - if (conditionFlow.breaking()) return conditionFlow; - condition = conditionFlow.value.getInteger() != 0; - } - return condition ? flow : Flow(); - } - Flow visitSwitch(Switch *curr) { - NOTE_ENTER("Switch"); - Flow flow; - Literal value; - if (curr->value) { - flow = visit(curr->value); - if (flow.breaking()) return flow; - value = flow.value; - NOTE_EVAL1(value); - } - flow = visit(curr->condition); - if (flow.breaking()) return flow; - int64_t index = flow.value.getInteger(); - Name target = curr->default_; - if (index >= 0 && (size_t)index < curr->targets.size()) { - target = curr->targets[(size_t)index]; - } - flow.breakTo = target; - flow.value = value; - return flow; - } + RuntimeExpressionRunner(ModuleInstance& instance, FunctionScope& scope) : instance(instance), scope(scope) {} Flow generateArguments(const ExpressionList& operands, LiteralList& arguments) { arguments.reserve(operands.size()); @@ -399,232 +725,7 @@ private: instance.externalInterface->store(curr, instance.getFinalAddress(curr, ptr.value), value.value); return value; } - Flow visitConst(Const *curr) { - NOTE_ENTER("Const"); - NOTE_EVAL1(curr->value); - return Flow(curr->value); // heh - } - Flow visitUnary(Unary *curr) { - NOTE_ENTER("Unary"); - Flow flow = visit(curr->value); - if (flow.breaking()) return flow; - Literal value = flow.value; - NOTE_EVAL1(value); - if (value.type == i32) { - switch (curr->op) { - case ClzInt32: return value.countLeadingZeroes(); - case CtzInt32: return value.countTrailingZeroes(); - case PopcntInt32: return value.popCount(); - case EqZInt32: return Literal(int32_t(value == Literal(int32_t(0)))); - case ReinterpretInt32: return value.castToF32(); - case ExtendSInt32: return value.extendToSI64(); - case ExtendUInt32: return value.extendToUI64(); - case ConvertUInt32ToFloat32: return value.convertUToF32(); - case ConvertUInt32ToFloat64: return value.convertUToF64(); - case ConvertSInt32ToFloat32: return value.convertSToF32(); - case ConvertSInt32ToFloat64: return value.convertSToF64(); - default: abort(); - } - } - if (value.type == i64) { - switch (curr->op) { - case ClzInt64: return value.countLeadingZeroes(); - case CtzInt64: return value.countTrailingZeroes(); - case PopcntInt64: return value.popCount(); - case EqZInt64: return Literal(int32_t(value == Literal(int64_t(0)))); - case WrapInt64: return value.truncateToI32(); - case ReinterpretInt64: return value.castToF64(); - case ConvertUInt64ToFloat32: return value.convertUToF32(); - case ConvertUInt64ToFloat64: return value.convertUToF64(); - case ConvertSInt64ToFloat32: return value.convertSToF32(); - case ConvertSInt64ToFloat64: return value.convertSToF64(); - default: abort(); - } - } - if (value.type == f32) { - switch (curr->op) { - case NegFloat32: return value.neg(); - case AbsFloat32: return value.abs(); - case CeilFloat32: return value.ceil(); - case FloorFloat32: return value.floor(); - case TruncFloat32: return value.trunc(); - case NearestFloat32: return value.nearbyint(); - case SqrtFloat32: return value.sqrt(); - case TruncSFloat32ToInt32: - case TruncSFloat32ToInt64: return truncSFloat(curr, value); - case TruncUFloat32ToInt32: - case TruncUFloat32ToInt64: return truncUFloat(curr, value); - case ReinterpretFloat32: return value.castToI32(); - case PromoteFloat32: return value.extendToF64(); - default: abort(); - } - } - if (value.type == f64) { - switch (curr->op) { - case NegFloat64: return value.neg(); - case AbsFloat64: return value.abs(); - case CeilFloat64: return value.ceil(); - case FloorFloat64: return value.floor(); - case TruncFloat64: return value.trunc(); - case NearestFloat64: return value.nearbyint(); - case SqrtFloat64: return value.sqrt(); - case TruncSFloat64ToInt32: - case TruncSFloat64ToInt64: return truncSFloat(curr, value); - case TruncUFloat64ToInt32: - case TruncUFloat64ToInt64: return truncUFloat(curr, value); - case ReinterpretFloat64: return value.castToI64(); - case DemoteFloat64: { - double val = value.getFloat(); - if (std::isnan(val)) return Literal(float(val)); - if (std::isinf(val)) return Literal(float(val)); - if (val < -std::numeric_limits<float>::max()) return Literal(-std::numeric_limits<float>::infinity()); - if (val > std::numeric_limits<float>::max()) return Literal(std::numeric_limits<float>::infinity()); - return value.truncateToF32(); - } - default: abort(); - } - } - abort(); - } - Flow visitBinary(Binary *curr) { - NOTE_ENTER("Binary"); - Flow flow = visit(curr->left); - if (flow.breaking()) return flow; - Literal left = flow.value; - flow = visit(curr->right); - if (flow.breaking()) return flow; - Literal right = flow.value; - NOTE_EVAL2(left, right); - 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 AddInt32: return left.add(right); - case SubInt32: return left.sub(right); - case MulInt32: return left.mul(right); - case DivSInt32: { - if (right.getInteger() == 0) trap("i32.div_s by 0"); - if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) trap("i32.div_s overflow"); // signed division overflow - return left.divS(right); - } - case DivUInt32: { - if (right.getInteger() == 0) trap("i32.div_u by 0"); - return left.divU(right); - } - case RemSInt32: { - if (right.getInteger() == 0) trap("i32.rem_s by 0"); - if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) return Literal(int32_t(0)); - return left.remS(right); - } - case RemUInt32: { - if (right.getInteger() == 0) trap("i32.rem_u by 0"); - return left.remU(right); - } - case AndInt32: return left.and_(right); - case OrInt32: return left.or_(right); - case XorInt32: return left.xor_(right); - case ShlInt32: return left.shl(right.and_(Literal(int32_t(31)))); - case ShrUInt32: return left.shrU(right.and_(Literal(int32_t(31)))); - case ShrSInt32: return left.shrS(right.and_(Literal(int32_t(31)))); - case RotLInt32: return left.rotL(right); - case RotRInt32: return left.rotR(right); - case EqInt32: return left.eq(right); - case NeInt32: return left.ne(right); - case LtSInt32: return left.ltS(right); - case LtUInt32: return left.ltU(right); - case LeSInt32: return left.leS(right); - case LeUInt32: return left.leU(right); - case GtSInt32: return left.gtS(right); - case GtUInt32: return left.gtU(right); - case GeSInt32: return left.geS(right); - case GeUInt32: return left.geU(right); - default: abort(); - } - } else if (left.type == i64) { - switch (curr->op) { - case AddInt64: return left.add(right); - case SubInt64: return left.sub(right); - case MulInt64: return left.mul(right); - case DivSInt64: { - if (right.getInteger() == 0) trap("i64.div_s by 0"); - if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) trap("i64.div_s overflow"); // signed division overflow - return left.divS(right); - } - case DivUInt64: { - if (right.getInteger() == 0) trap("i64.div_u by 0"); - return left.divU(right); - } - case RemSInt64: { - if (right.getInteger() == 0) trap("i64.rem_s by 0"); - if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) return Literal(int64_t(0)); - return left.remS(right); - } - case RemUInt64: { - if (right.getInteger() == 0) trap("i64.rem_u by 0"); - return left.remU(right); - } - case AndInt64: return left.and_(right); - case OrInt64: return left.or_(right); - case XorInt64: return left.xor_(right); - case ShlInt64: return left.shl(right.and_(Literal(int64_t(63)))); - case ShrUInt64: return left.shrU(right.and_(Literal(int64_t(63)))); - case ShrSInt64: return left.shrS(right.and_(Literal(int64_t(63)))); - case RotLInt64: return left.rotL(right); - case RotRInt64: return left.rotR(right); - case EqInt64: return left.eq(right); - case NeInt64: return left.ne(right); - case LtSInt64: return left.ltS(right); - case LtUInt64: return left.ltU(right); - case LeSInt64: return left.leS(right); - case LeUInt64: return left.leU(right); - case GtSInt64: return left.gtS(right); - case GtUInt64: return left.gtU(right); - case GeSInt64: return left.geS(right); - case GeUInt64: return left.geU(right); - default: abort(); - } - } else if (left.type == f32 || left.type == f64) { - switch (curr->op) { - case AddFloat32: case AddFloat64: return left.add(right); - case SubFloat32: case SubFloat64: return left.sub(right); - case MulFloat32: case MulFloat64: return left.mul(right); - case DivFloat32: case DivFloat64: return left.div(right); - case CopySignFloat32: case CopySignFloat64: return left.copysign(right); - case MinFloat32: case MinFloat64: return left.min(right); - case MaxFloat32: case MaxFloat64: return left.max(right); - case EqFloat32: case EqFloat64: return left.eq(right); - case NeFloat32: case NeFloat64: return left.ne(right); - case LtFloat32: case LtFloat64: return left.lt(right); - case LeFloat32: case LeFloat64: return left.le(right); - case GtFloat32: case GtFloat64: return left.gt(right); - case GeFloat32: case GeFloat64: return left.ge(right); - default: abort(); - } - } - abort(); - } - Flow visitSelect(Select *curr) { - NOTE_ENTER("Select"); - Flow ifTrue = visit(curr->ifTrue); - if (ifTrue.breaking()) return ifTrue; - Flow ifFalse = visit(curr->ifFalse); - if (ifFalse.breaking()) return ifFalse; - Flow condition = visit(curr->condition); - if (condition.breaking()) return condition; - NOTE_EVAL1(condition.value); - return condition.value.geti32() ? ifTrue : ifFalse; // ;-) - } - Flow visitReturn(Return *curr) { - NOTE_ENTER("Return"); - Flow flow; - if (curr->value) { - flow = visit(curr->value); - if (flow.breaking()) return flow; - NOTE_EVAL1(flow.value); - } - flow.breakTo = RETURN_FLOW; - return flow; - } + Flow visitHost(Host *curr) { NOTE_ENTER("Host"); switch (curr->op) { @@ -651,57 +752,8 @@ private: default: abort(); } } - Flow visitNop(Nop *curr) { - NOTE_ENTER("Nop"); - return Flow(); - } - Flow visitUnreachable(Unreachable *curr) { - NOTE_ENTER("Unreachable"); - trap("unreachable"); - return Flow(); - } - - Literal truncSFloat(Unary* curr, Literal value) { - double val = value.getFloat(); - if (std::isnan(val)) trap("truncSFloat of nan"); - if (curr->type == i32) { - if (value.type == f32) { - if (!isInRangeI32TruncS(value.reinterpreti32())) trap("i32.truncSFloat overflow"); - } else { - if (!isInRangeI32TruncS(value.reinterpreti64())) trap("i32.truncSFloat overflow"); - } - return Literal(int32_t(val)); - } else { - if (value.type == f32) { - if (!isInRangeI64TruncS(value.reinterpreti32())) trap("i64.truncSFloat overflow"); - } else { - if (!isInRangeI64TruncS(value.reinterpreti64())) trap("i64.truncSFloat overflow"); - } - return Literal(int64_t(val)); - } - } - - Literal truncUFloat(Unary* curr, Literal value) { - double val = value.getFloat(); - if (std::isnan(val)) trap("truncUFloat of nan"); - if (curr->type == i32) { - if (value.type == f32) { - if (!isInRangeI32TruncU(value.reinterpreti32())) trap("i32.truncUFloat overflow"); - } else { - if (!isInRangeI32TruncU(value.reinterpreti64())) trap("i32.truncUFloat overflow"); - } - return Literal(uint32_t(val)); - } else { - if (value.type == f32) { - if (!isInRangeI64TruncU(value.reinterpreti32())) trap("i64.truncUFloat overflow"); - } else { - if (!isInRangeI64TruncU(value.reinterpreti64())) trap("i64.truncUFloat overflow"); - } - return Literal(uint64_t(val)); - } - } - void trap(const char* why) { + void trap(const char* why) override { instance.externalInterface->trap(why); } }; @@ -720,7 +772,7 @@ private: std::cout << "entering " << function->name << '\n'; #endif - Flow flow = ExpressionRunner(*this, scope).visit(function->body); + Flow flow = RuntimeExpressionRunner(*this, scope).visit(function->body); assert(!flow.breaking() || flow.breakTo == RETURN_FLOW); // cannot still be breaking, it means we missed our stop Literal ret = flow.value; if (function->result == none) ret = Literal(); |