diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2020-03-05 15:52:44 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-05 15:52:44 -0800 |
commit | 9e3dbb1f668a14341e9aebae479537d4a26095a5 (patch) | |
tree | 9857d50c3373c3f6320ca3f2586bd499bc40b224 /src/wasm-interpreter.h | |
parent | 3a275d0627da443cce93631a64d78e7b3f441416 (diff) | |
download | binaryen-9e3dbb1f668a14341e9aebae479537d4a26095a5.tar.gz binaryen-9e3dbb1f668a14341e9aebae479537d4a26095a5.tar.bz2 binaryen-9e3dbb1f668a14341e9aebae479537d4a26095a5.zip |
Initial multivalue support (#2675)
Implements parsing and emitting of tuple creation and extraction and tuple-typed control flow for both the text and binary formats.
TODO:
- Extend Precompute/interpreter to handle tuple values
- C and JS API support/testing
- Figure out how to lower in stack IR
- Fuzzing
Diffstat (limited to 'src/wasm-interpreter.h')
-rw-r--r-- | src/wasm-interpreter.h | 223 |
1 files changed, 130 insertions, 93 deletions
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index cbed38b2f..153453f2b 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -49,13 +49,19 @@ extern Name WASM, RETURN_FLOW; // in control flow. class Flow { public: - Flow() = default; - Flow(Literal value) : value(value) {} - Flow(Name breakTo) : breakTo(breakTo) {} + Flow() : values{Literal()} {} + Flow(Literal value) : values{value} {} + Flow(Name breakTo) : values{Literal()}, breakTo(breakTo) {} - Literal value; + SmallVector<Literal, 1> values; Name breakTo; // if non-null, a break is going on + // A helper function for the common case where there is only one value + Literal& getSingleValue() { + assert(values.size() == 1); + return values[0]; + } + bool breaking() { return breakTo.is(); } void clearIf(Name target) { @@ -65,8 +71,14 @@ public: } friend std::ostream& operator<<(std::ostream& o, Flow& flow) { - o << "(flow " << (flow.breakTo.is() ? flow.breakTo.str : "-") << " : " - << flow.value << ')'; + o << "(flow " << (flow.breakTo.is() ? flow.breakTo.str : "-") << " : {"; + for (size_t i = 0; i < flow.values.size(); ++i) { + if (i > 0) { + o << ", "; + } + o << flow.values[i]; + } + o << "})"; return o; } }; @@ -131,6 +143,21 @@ protected: Index depth = 0; + Flow generateArguments(const ExpressionList& operands, + LiteralList& arguments) { + NOTE_ENTER_("generateArguments"); + arguments.reserve(operands.size()); + for (auto expression : operands) { + Flow flow = this->visit(expression); + if (flow.breaking()) { + return flow; + } + NOTE_EVAL1(flow.getSingleValue()); + arguments.push_back(flow.getSingleValue()); + } + return Flow(); + } + public: ExpressionRunner(Index maxDepth) : maxDepth(maxDepth) {} @@ -141,15 +168,15 @@ public: } auto ret = OverriddenVisitor<SubType, Flow>::visit(curr); if (!ret.breaking() && - (curr->type.isConcrete() || ret.value.type.isConcrete())) { + (curr->type.isConcrete() || ret.getSingleValue().type.isConcrete())) { #if 1 // def WASM_INTERPRETER_DEBUG - if (!Type::isSubType(ret.value.type, curr->type)) { - std::cerr << "expected " << curr->type << ", seeing " << ret.value.type - << " from\n" + if (!Type::isSubType(ret.getSingleValue().type, curr->type)) { + std::cerr << "expected " << curr->type << ", seeing " + << ret.getSingleValue().type << " from\n" << curr << '\n'; } #endif - assert(Type::isSubType(ret.value.type, curr->type)); + assert(Type::isSubType(ret.getSingleValue().type, curr->type)); } depth--; return ret; @@ -195,11 +222,11 @@ public: if (flow.breaking()) { return flow; } - NOTE_EVAL1(flow.value); - if (flow.value.geti32()) { + NOTE_EVAL1(flow.getSingleValue()); + if (flow.getSingleValue().geti32()) { Flow flow = visit(curr->ifTrue); if (!flow.breaking() && !curr->ifFalse) { - flow.value = Literal(); // if_else returns a value, but if does not + flow = Flow(); // if_else returns a value, but if does not } return flow; } @@ -236,7 +263,7 @@ public: if (conditionFlow.breaking()) { return conditionFlow; } - condition = conditionFlow.value.getInteger() != 0; + condition = conditionFlow.getSingleValue().getInteger() != 0; if (!condition) { return flow; } @@ -253,20 +280,20 @@ public: if (flow.breaking()) { return flow; } - value = flow.value; + value = flow.getSingleValue(); NOTE_EVAL1(value); } flow = visit(curr->condition); if (flow.breaking()) { return flow; } - int64_t index = flow.value.getInteger(); + int64_t index = flow.getSingleValue().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; + flow.getSingleValue() = value; return flow; } @@ -285,7 +312,7 @@ public: if (flow.breaking()) { return flow; } - Literal value = flow.value; + Literal value = flow.getSingleValue(); NOTE_EVAL1(value); switch (curr->op) { case ClzInt32: @@ -475,12 +502,12 @@ public: if (flow.breaking()) { return flow; } - Literal left = flow.value; + Literal left = flow.getSingleValue(); flow = visit(curr->right); if (flow.breaking()) { return flow; } - Literal right = flow.value; + Literal right = flow.getSingleValue(); NOTE_EVAL2(left, right); assert(curr->left->type.isConcrete() ? left.type == curr->left->type : true); @@ -860,7 +887,7 @@ public: if (flow.breaking()) { return flow; } - Literal vec = flow.value; + Literal vec = flow.getSingleValue(); switch (curr->op) { case ExtractLaneSVecI8x16: return vec.extractLaneSI8x16(curr->index); @@ -887,12 +914,12 @@ public: if (flow.breaking()) { return flow; } - Literal vec = flow.value; + Literal vec = flow.getSingleValue(); flow = this->visit(curr->value); if (flow.breaking()) { return flow; } - Literal value = flow.value; + Literal value = flow.getSingleValue(); switch (curr->op) { case ReplaceLaneVecI8x16: return vec.replaceLaneI8x16(value, curr->index); @@ -915,12 +942,12 @@ public: if (flow.breaking()) { return flow; } - Literal left = flow.value; + Literal left = flow.getSingleValue(); flow = this->visit(curr->right); if (flow.breaking()) { return flow; } - Literal right = flow.value; + Literal right = flow.getSingleValue(); return left.shuffleV8x16(right, curr->mask); } Flow visitSIMDTernary(SIMDTernary* curr) { @@ -929,17 +956,17 @@ public: if (flow.breaking()) { return flow; } - Literal a = flow.value; + Literal a = flow.getSingleValue(); flow = this->visit(curr->b); if (flow.breaking()) { return flow; } - Literal b = flow.value; + Literal b = flow.getSingleValue(); flow = this->visit(curr->c); if (flow.breaking()) { return flow; } - Literal c = flow.value; + Literal c = flow.getSingleValue(); switch (curr->op) { case Bitselect: return c.bitselectV128(a, b); @@ -954,12 +981,12 @@ public: if (flow.breaking()) { return flow; } - Literal vec = flow.value; + Literal vec = flow.getSingleValue(); flow = this->visit(curr->shift); if (flow.breaking()) { return flow; } - Literal shift = flow.value; + Literal shift = flow.getSingleValue(); switch (curr->op) { case ShlVecI8x16: return vec.shlI8x16(shift); @@ -1002,8 +1029,8 @@ public: if (condition.breaking()) { return condition; } - NOTE_EVAL1(condition.value); - return condition.value.geti32() ? ifTrue : ifFalse; // ;-) + NOTE_EVAL1(condition.getSingleValue()); + return condition.getSingleValue().geti32() ? ifTrue : ifFalse; // ;-) } Flow visitDrop(Drop* curr) { NOTE_ENTER("Drop"); @@ -1021,7 +1048,7 @@ public: if (flow.breaking()) { return flow; } - NOTE_EVAL1(flow.value); + NOTE_EVAL1(flow.getSingleValue()); } flow.breakTo = RETURN_FLOW; return flow; @@ -1101,6 +1128,27 @@ public: NOTE_ENTER("AtomicFence"); return Flow(); } + Flow visitTupleMake(TupleMake* curr) { + NOTE_ENTER("tuple.make"); + LiteralList arguments; + Flow flow = generateArguments(curr->operands, arguments); + if (flow.breaking()) { + return flow; + } + for (auto arg : arguments) { + flow.values.push_back(arg); + } + return flow; + } + Flow visitTupleExtract(TupleExtract* curr) { + NOTE_ENTER("tuple.extract"); + Flow flow = visit(curr->tuple); + if (flow.breaking()) { + return flow; + } + assert(flow.values.size() > curr->index); + return Flow(flow.values[curr->index]); + } Flow visitCall(Call*) { WASM_UNREACHABLE("unimp"); } Flow visitCallIndirect(CallIndirect*) { WASM_UNREACHABLE("unimp"); } @@ -1133,7 +1181,7 @@ public: if (flow.breaking()) { return flow; } - Literal value = flow.value; + Literal value = flow.getSingleValue(); NOTE_EVAL1(value); return Literal(value.type == Type::nullref); } @@ -1359,7 +1407,7 @@ public: globals[global->name] = ConstantExpressionRunner<GlobalManager>(globals, maxDepth) .visit(global->init) - .value; + .getSingleValue(); }); // initialize the rest of the external interface @@ -1424,7 +1472,8 @@ private: Address offset = (uint32_t)ConstantExpressionRunner<GlobalManager>(globals, maxDepth) .visit(segment.offset) - .value.geti32(); + .getSingleValue() + .geti32(); if (offset + segment.data.size() > wasm.table.initial) { externalInterface->trap("invalid offset when initializing table"); } @@ -1518,26 +1567,11 @@ private: : ExpressionRunner<RuntimeExpressionRunner>(maxDepth), instance(instance), scope(scope) {} - Flow generateArguments(const ExpressionList& operands, - LiteralList& arguments) { - NOTE_ENTER_("generateArguments"); - arguments.reserve(operands.size()); - for (auto expression : operands) { - Flow flow = this->visit(expression); - if (flow.breaking()) { - return flow; - } - NOTE_EVAL1(flow.value); - arguments.push_back(flow.value); - } - return Flow(); - } - Flow visitCall(Call* curr) { NOTE_ENTER("Call"); NOTE_NAME(curr->target); LiteralList arguments; - Flow flow = generateArguments(curr->operands, arguments); + Flow flow = this->generateArguments(curr->operands, arguments); if (flow.breaking()) { return flow; } @@ -1552,9 +1586,10 @@ private: std::cout << "(returned to " << scope.function->name << ")\n"; #endif // TODO: make this a proper tail call (return first) + // TODO: handle multivalue return_calls if (curr->isReturn) { Const c; - c.value = ret.value; + c.value = ret.getSingleValue(); c.finalize(); Return return_; return_.value = &c; @@ -1565,7 +1600,7 @@ private: Flow visitCallIndirect(CallIndirect* curr) { NOTE_ENTER("CallIndirect"); LiteralList arguments; - Flow flow = generateArguments(curr->operands, arguments); + Flow flow = this->generateArguments(curr->operands, arguments); if (flow.breaking()) { return flow; } @@ -1573,14 +1608,15 @@ private: if (target.breaking()) { return target; } - Index index = target.value.geti32(); + Index index = target.getSingleValue().geti32(); Type type = curr->isReturn ? scope.function->sig.results : curr->type; Flow ret = instance.externalInterface->callTable( index, curr->sig, arguments, type, *instance.self()); // TODO: make this a proper tail call (return first) + // TODO: handle multivalue return_call_indirects if (curr->isReturn) { Const c; - c.value = ret.value; + c.value = ret.getSingleValue(); c.finalize(); Return return_; return_.value = &c; @@ -1604,10 +1640,11 @@ private: return flow; } NOTE_EVAL1(index); - NOTE_EVAL1(flow.value); - assert(curr->isTee() ? Type::isSubType(flow.value.type, curr->type) - : true); - scope.locals[index] = flow.value; + NOTE_EVAL1(flow.getSingleValue()); + assert(curr->isTee() + ? Type::isSubType(flow.getSingleValue().type, curr->type) + : true); + scope.locals[index] = flow.getSingleValue(); return curr->isTee() ? flow : Flow(); } @@ -1627,8 +1664,8 @@ private: return flow; } NOTE_EVAL1(name); - NOTE_EVAL1(flow.value); - instance.globals[name] = flow.value; + NOTE_EVAL1(flow.getSingleValue()); + instance.globals[name] = flow.getSingleValue(); return Flow(); } @@ -1639,7 +1676,7 @@ private: return flow; } NOTE_EVAL1(flow); - auto addr = instance.getFinalAddress(curr, flow.value); + auto addr = instance.getFinalAddress(curr, flow.getSingleValue()); auto ret = instance.externalInterface->load(curr, addr); NOTE_EVAL1(addr); NOTE_EVAL1(ret); @@ -1655,10 +1692,10 @@ private: if (value.breaking()) { return value; } - auto addr = instance.getFinalAddress(curr, ptr.value); + auto addr = instance.getFinalAddress(curr, ptr.getSingleValue()); NOTE_EVAL1(addr); NOTE_EVAL1(value); - instance.externalInterface->store(curr, addr, value.value); + instance.externalInterface->store(curr, addr, value.getSingleValue()); return Flow(); } @@ -1673,30 +1710,30 @@ private: return value; } NOTE_EVAL1(ptr); - auto addr = instance.getFinalAddress(curr, ptr.value); + auto addr = instance.getFinalAddress(curr, ptr.getSingleValue()); NOTE_EVAL1(addr); NOTE_EVAL1(value); auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type); NOTE_EVAL1(loaded); - auto computed = value.value; + auto computed = value.getSingleValue(); switch (curr->op) { case Add: - computed = computed.add(value.value); + computed = computed.add(value.getSingleValue()); break; case Sub: - computed = computed.sub(value.value); + computed = computed.sub(value.getSingleValue()); break; case And: - computed = computed.and_(value.value); + computed = computed.and_(value.getSingleValue()); break; case Or: - computed = computed.or_(value.value); + computed = computed.or_(value.getSingleValue()); break; case Xor: - computed = computed.xor_(value.value); + computed = computed.xor_(value.getSingleValue()); break; case Xchg: - computed = value.value; + computed = value.getSingleValue(); break; } instance.doAtomicStore(addr, curr->bytes, computed); @@ -1717,14 +1754,14 @@ private: if (replacement.breaking()) { return replacement; } - auto addr = instance.getFinalAddress(curr, ptr.value); + auto addr = instance.getFinalAddress(curr, ptr.getSingleValue()); NOTE_EVAL1(addr); NOTE_EVAL1(expected); NOTE_EVAL1(replacement); auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type); NOTE_EVAL1(loaded); - if (loaded == expected.value) { - instance.doAtomicStore(addr, curr->bytes, replacement.value); + if (loaded == expected.getSingleValue()) { + instance.doAtomicStore(addr, curr->bytes, replacement.getSingleValue()); } return loaded; } @@ -1746,10 +1783,10 @@ private: return timeout; } auto bytes = curr->expectedType.getByteSize(); - auto addr = instance.getFinalAddress(ptr.value, bytes); + auto addr = instance.getFinalAddress(ptr.getSingleValue(), bytes); auto loaded = instance.doAtomicLoad(addr, bytes, curr->expectedType); NOTE_EVAL1(loaded); - if (loaded != expected.value) { + if (loaded != expected.getSingleValue()) { return Literal(int32_t(1)); // not equal } // TODO: add threads support! @@ -1821,7 +1858,7 @@ private: if (flow.breaking()) { return flow; } - return (flow.value.*splat)(); + return (flow.getSingleValue().*splat)(); } Flow visitSIMDLoadExtend(SIMDLoad* curr) { Flow flow = this->visit(curr->ptr); @@ -1829,7 +1866,7 @@ private: return flow; } NOTE_EVAL1(flow); - Address src(uint32_t(flow.value.geti32())); + Address src(uint32_t(flow.getSingleValue().geti32())); auto loadLane = [&](Address addr) { switch (curr->op) { case LoadExtSVec8x8ToVecI16x8: @@ -1890,7 +1927,7 @@ private: return flow; } int32_t ret = instance.memorySize; - uint32_t delta = flow.value.geti32(); + uint32_t delta = flow.getSingleValue().geti32(); if (delta > uint32_t(-1) / Memory::kPageSize) { return fail; } @@ -1931,9 +1968,9 @@ private: assert(curr->segment < instance.wasm.memory.segments.size()); Memory::Segment& segment = instance.wasm.memory.segments[curr->segment]; - Address destVal(uint32_t(dest.value.geti32())); - Address offsetVal(uint32_t(offset.value.geti32())); - Address sizeVal(uint32_t(size.value.geti32())); + Address destVal(uint32_t(dest.getSingleValue().geti32())); + Address offsetVal(uint32_t(offset.getSingleValue().geti32())); + Address sizeVal(uint32_t(size.getSingleValue().geti32())); if (offsetVal + sizeVal > 0 && instance.droppedSegments.count(curr->segment)) { @@ -1975,9 +2012,9 @@ private: NOTE_EVAL1(dest); NOTE_EVAL1(source); NOTE_EVAL1(size); - Address destVal(uint32_t(dest.value.geti32())); - Address sourceVal(uint32_t(source.value.geti32())); - Address sizeVal(uint32_t(size.value.geti32())); + Address destVal(uint32_t(dest.getSingleValue().geti32())); + Address sourceVal(uint32_t(source.getSingleValue().geti32())); + Address sizeVal(uint32_t(size.getSingleValue().geti32())); if ((uint64_t)sourceVal + sizeVal > (uint64_t)instance.memorySize * Memory::kPageSize || @@ -2020,14 +2057,14 @@ private: NOTE_EVAL1(dest); NOTE_EVAL1(value); NOTE_EVAL1(size); - Address destVal(uint32_t(dest.value.geti32())); - Address sizeVal(uint32_t(size.value.geti32())); + Address destVal(uint32_t(dest.getSingleValue().geti32())); + Address sizeVal(uint32_t(size.getSingleValue().geti32())); if ((uint64_t)destVal + sizeVal > (uint64_t)instance.memorySize * Memory::kPageSize) { trap("out of bounds memory access in memory.fill"); } - uint8_t val(value.value.geti32()); + uint8_t val(value.getSingleValue().geti32()); for (size_t i = 0; i < sizeVal; ++i) { instance.externalInterface->store8( instance.getFinalAddress(Literal(uint32_t(destVal + i)), 1), val); @@ -2040,7 +2077,7 @@ private: if (value.breaking()) { return value; } - instance.multiValues.push_back(value.value); + instance.multiValues.push_back(value.getSingleValue()); return Flow(); } Flow visitPop(Pop* curr) { @@ -2092,7 +2129,7 @@ public: RuntimeExpressionRunner(*this, scope, maxDepth).visit(function->body); // cannot still be breaking, it means we missed our stop assert(!flow.breaking() || flow.breakTo == RETURN_FLOW); - Literal ret = flow.value; + Literal ret = flow.getSingleValue(); if (!Type::isSubType(ret.type, function->sig.results)) { std::cerr << "calling " << function->name << " resulted in " << ret << " but the function type is " << function->sig.results |