summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2016-06-18 16:22:56 +0100
committerAlon Zakai <alonzakai@gmail.com>2016-06-18 17:46:57 +0100
commitd82816166d9cfb7912368af20dffe8b8ffa5e7cf (patch)
tree6f26cb2bb6f8b8de5a0920ed10616d06f6ccd66d /src
parent6fdd9dd2137a563bc41a85cf45178cc734e499a2 (diff)
downloadbinaryen-d82816166d9cfb7912368af20dffe8b8ffa5e7cf.tar.gz
binaryen-d82816166d9cfb7912368af20dffe8b8ffa5e7cf.tar.bz2
binaryen-d82816166d9cfb7912368af20dffe8b8ffa5e7cf.zip
refactor interpreter code to provide expression executors for both standalone and full funtime execution
Diffstat (limited to 'src')
-rw-r--r--src/shell-interface.h2
-rw-r--r--src/tools/binaryen-shell.cpp4
-rw-r--r--src/wasm-interpreter.h874
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();