diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 10 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 13 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 293 |
3 files changed, 178 insertions, 138 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 51a401a3c..a9bc9e2b6 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -4998,16 +4998,18 @@ BinaryenExpressionRef RelooperRenderAndDispose(RelooperRef relooper, namespace wasm { -class CExpressionRunner final : public ExpressionRunner<CExpressionRunner> { +// Evaluates a suspected constant expression via the C-API. Inherits most of its +// functionality from ConstantExpressionRunner, which it shares with the +// precompute pass, but must be `final` so we can `delete` its instances. +class CExpressionRunner final + : public ConstantExpressionRunner<CExpressionRunner> { public: CExpressionRunner(Module* module, CExpressionRunner::Flags flags, Index maxDepth, Index maxLoopIterations) - : ExpressionRunner<CExpressionRunner>( + : ConstantExpressionRunner<CExpressionRunner>( module, flags, maxDepth, maxLoopIterations) {} - - void trap(const char* why) override { throw NonconstantException(); } }; } // namespace wasm diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index 7d3e691e1..695b62ab2 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -42,9 +42,11 @@ namespace wasm { typedef std::unordered_map<LocalGet*, Literals> GetValues; // Precomputes an expression. Errors if we hit anything that can't be -// precomputed. +// precomputed. Inherits most of its functionality from +// ConstantExpressionRunner, which it shares with the C-API, but adds handling +// of GetValues computed during the precompute pass. class PrecomputingExpressionRunner - : public ExpressionRunner<PrecomputingExpressionRunner> { + : public ConstantExpressionRunner<PrecomputingExpressionRunner> { // Concrete values of gets computed during the pass, which the runner does not // know about since it only records values of sets it visits. @@ -66,7 +68,7 @@ public: PrecomputingExpressionRunner(Module* module, GetValues& getValues, bool replaceExpression) - : ExpressionRunner<PrecomputingExpressionRunner>( + : ConstantExpressionRunner<PrecomputingExpressionRunner>( module, replaceExpression ? FlagValues::PRESERVE_SIDEEFFECTS : FlagValues::DEFAULT, @@ -82,10 +84,9 @@ public: return Flow(values); } } - return ExpressionRunner<PrecomputingExpressionRunner>::visitLocalGet(curr); + return ConstantExpressionRunner< + PrecomputingExpressionRunner>::visitLocalGet(curr); } - - void trap(const char* why) override { throw NonconstantException(); } }; struct Precompute diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 393c33b75..0124055ad 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -149,52 +149,13 @@ public: // Execute an expression template<typename SubType> class ExpressionRunner : public OverriddenVisitor<SubType, Flow> { -public: - enum FlagValues { - // By default, just evaluate the expression, i.e. all we want to know is - // whether it computes down to a concrete value, where it is not necessary - // to preserve side effects like those of a `local.tee`. - DEFAULT = 0, - // Be very careful to preserve any side effects. For example, if we are - // intending to replace the expression with a constant afterwards, even if - // we can technically evaluate down to a constant, we still cannot replace - // the expression if it also sets a local, which must be preserved in this - // scenario so subsequent code keeps functioning. - PRESERVE_SIDEEFFECTS = 1 << 0, - // Traverse through function calls, attempting to compute their concrete - // value. Must not be used in function-parallel scenarios, where the called - // function might be concurrently modified, leading to undefined behavior. - TRAVERSE_CALLS = 1 << 1 - }; - - // Flags indicating special requirements, for example whether we are just - // evaluating (default), also going to replace the expression afterwards or - // executing in a function-parallel scenario. See FlagValues. - typedef uint32_t Flags; - - // Indicates no limit of maxDepth or maxLoopIterations. - static const Index NO_LIMIT = 0; - protected: - // Optional module context to search for globals and called functions. NULL if - // we are not interested in any context or if the subclass uses an alternative - // mechanism, like RuntimeExpressionRunner does. - Module* module = nullptr; - - // Flags indicating special requirements. See FlagValues. - Flags flags = FlagValues::DEFAULT; - // Maximum depth before giving up. - Index maxDepth = NO_LIMIT; + Index maxDepth; Index depth = 0; // Maximum iterations before giving up on a loop. - Index maxLoopIterations = NO_LIMIT; - - // Map remembering concrete local values set in the context of this flow. - std::unordered_map<Index, Literals> localValues; - // Map remembering concrete global values set in the context of this flow. - std::unordered_map<Name, Literals> globalValues; + Index maxLoopIterations; Flow generateArguments(const ExpressionList& operands, LiteralList& arguments) { @@ -212,32 +173,12 @@ protected: } public: - ExpressionRunner() {} - ExpressionRunner(Index maxDepth) : maxDepth(maxDepth) {} - ExpressionRunner(Module* module, - Flags flags, - Index maxDepth, - Index maxLoopIterations) - : module(module), flags(flags), maxDepth(maxDepth), - maxLoopIterations(maxLoopIterations) {} - - struct NonconstantException { - }; // TODO: use a flow with a special name, as this is likely very slow - - // Gets the module this runner is operating on. - Module* getModule() { return module; } - - // Sets a known local value to use. - void setLocalValue(Index index, Literals& values) { - assert(values.isConcrete()); - localValues[index] = values; - } + // Indicates no limit of maxDepth or maxLoopIterations. + static const Index NO_LIMIT = 0; - // Sets a known global value to use. - void setGlobalValue(Name name, Literals& values) { - assert(values.isConcrete()); - globalValues[name] = values; - } + ExpressionRunner(Index maxDepth = NO_LIMIT, + Index maxLoopIterations = NO_LIMIT) + : maxDepth(maxDepth), maxLoopIterations(maxLoopIterations) {} Flow visit(Expression* curr) { depth++; @@ -1246,6 +1187,150 @@ public: assert(flow.values.size() > curr->index); return Flow(flow.values[curr->index]); } + Flow visitLocalGet(LocalGet* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitLocalSet(LocalSet* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitGlobalGet(GlobalGet* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitGlobalSet(GlobalSet* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitCall(Call* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitCallIndirect(CallIndirect* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitLoad(Load* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitStore(Store* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitHost(Host* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitMemoryInit(MemoryInit* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitDataDrop(DataDrop* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitMemoryCopy(MemoryCopy* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitMemoryFill(MemoryFill* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitAtomicRMW(AtomicRMW* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitAtomicCmpxchg(AtomicCmpxchg* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitAtomicWait(AtomicWait* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitAtomicNotify(AtomicNotify* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitSIMDLoad(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitSIMDLoadSplat(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitSIMDLoadExtend(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitPush(Push* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitPop(Pop* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitRefNull(RefNull* curr) { + NOTE_ENTER("RefNull"); + return Literal::makeNullref(); + } + Flow visitRefIsNull(RefIsNull* curr) { + NOTE_ENTER("RefIsNull"); + Flow flow = visit(curr->value); + if (flow.breaking()) { + return flow; + } + Literal value = flow.getSingleValue(); + NOTE_EVAL1(value); + return Literal(value.type == Type::nullref); + } + Flow visitRefFunc(RefFunc* curr) { + NOTE_ENTER("RefFunc"); + NOTE_NAME(curr->func); + return Literal::makeFuncref(curr->func); + } + Flow visitTry(Try* curr) { + NOTE_ENTER("Try"); + // FIXME This currently only executes 'try' part. Correctly implement this. + return visit(curr->body); + } + Flow visitThrow(Throw* curr) { + NOTE_ENTER("Throw"); + LiteralList arguments; + Flow flow = generateArguments(curr->operands, arguments); + if (flow.breaking()) { + return flow; + } + NOTE_EVAL1(curr->event); + // FIXME This currently traps. Correctly implement throw. + trap("throw"); + WASM_UNREACHABLE("throw"); + } + Flow visitRethrow(Rethrow* curr) { + NOTE_ENTER("Rethrow"); + Flow flow = visit(curr->exnref); + if (flow.breaking()) { + return flow; + } + if (flow.getType() == Type::nullref) { + trap("rethrow: argument is null"); + } + // FIXME This currently traps. Correctly implement rethrow. + trap("rethrow"); + WASM_UNREACHABLE("rethrow"); + } + Flow visitBrOnExn(BrOnExn* curr) { WASM_UNREACHABLE("unimp"); } + + virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); } +}; + +// Execute a suspected constant expression (precompute and C-API). +template<typename SubType> +class ConstantExpressionRunner : public ExpressionRunner<SubType> { +public: + enum FlagValues { + // By default, just evaluate the expression, i.e. all we want to know is + // whether it computes down to a concrete value, where it is not necessary + // to preserve side effects like those of a `local.tee`. + DEFAULT = 0, + // Be very careful to preserve any side effects. For example, if we are + // intending to replace the expression with a constant afterwards, even if + // we can technically evaluate down to a constant, we still cannot replace + // the expression if it also sets a local, which must be preserved in this + // scenario so subsequent code keeps functioning. + PRESERVE_SIDEEFFECTS = 1 << 0, + // Traverse through function calls, attempting to compute their concrete + // value. Must not be used in function-parallel scenarios, where the called + // function might be concurrently modified, leading to undefined behavior. + TRAVERSE_CALLS = 1 << 1 + }; + + // Flags indicating special requirements, for example whether we are just + // evaluating (default), also going to replace the expression afterwards or + // executing in a function-parallel scenario. See FlagValues. + typedef uint32_t Flags; + + // Indicates no limit of maxDepth or maxLoopIterations. + static const Index NO_LIMIT = 0; + +protected: + // Optional module context to search for globals and called functions. NULL if + // we are not interested in any context. + Module* module = nullptr; + + // Flags indicating special requirements. See FlagValues. + Flags flags = FlagValues::DEFAULT; + + // Map remembering concrete local values set in the context of this flow. + std::unordered_map<Index, Literals> localValues; + // Map remembering concrete global values set in the context of this flow. + std::unordered_map<Name, Literals> globalValues; + +public: + struct NonconstantException { + }; // TODO: use a flow with a special name, as this is likely very slow + + ConstantExpressionRunner(Module* module, + Flags flags, + Index maxDepth, + Index maxLoopIterations) + : ExpressionRunner<SubType>(maxDepth, maxLoopIterations), module(module), + flags(flags) {} + + // Gets the module this runner is operating on. + Module* getModule() { return module; } + + // Sets a known local value to use. + void setLocalValue(Index index, Literals& values) { + assert(values.isConcrete()); + localValues[index] = values; + } + + // Sets a known global value to use. + void setGlobalValue(Name name, Literals& values) { + assert(values.isConcrete()); + globalValues[name] = values; + } + Flow visitLocalGet(LocalGet* curr) { NOTE_ENTER("LocalGet"); NOTE_EVAL1(curr->index); @@ -1263,7 +1348,7 @@ public: // If we are evaluating and not replacing the expression, remember the // constant value set, if any, and see if there is a value flowing through // a tee. - auto setFlow = visit(curr->value); + auto setFlow = ExpressionRunner<SubType>::visit(curr->value); if (!setFlow.breaking()) { setLocalValue(curr->index, setFlow.values); if (curr->type.isConcrete()) { @@ -1282,7 +1367,7 @@ public: auto* global = module->getGlobal(curr->name); // Check if the global has an immutable value anyway if (!global->imported() && !global->mutable_) { - return visit(global->init); + return ExpressionRunner<SubType>::visit(global->init); } } // Check if a constant value has been set in the context of this runner. @@ -1300,7 +1385,7 @@ public: // constant value set, if any, for subsequent gets. auto* global = module->getGlobal(curr->name); assert(global->mutable_); - auto setFlow = visit(curr->value); + auto setFlow = ExpressionRunner<SubType>::visit(curr->value); if (!setFlow.breaking()) { setGlobalValue(curr->name, setFlow.values); return Flow(); @@ -1324,13 +1409,13 @@ public: auto prevLocalValues = localValues; localValues.clear(); for (Index i = 0; i < numOperands; ++i) { - auto argFlow = visit(curr->operands[i]); + auto argFlow = ExpressionRunner<SubType>::visit(curr->operands[i]); if (!argFlow.breaking()) { assert(argFlow.values.isConcrete()); localValues[i] = argFlow.values; } } - auto retFlow = visit(func->body); + auto retFlow = ExpressionRunner<SubType>::visit(func->body); localValues = prevLocalValues; if (retFlow.breakTo == RETURN_FLOW) { return Flow(retFlow.values); @@ -1411,72 +1496,24 @@ public: NOTE_ENTER("Pop"); return Flow(NONCONSTANT_FLOW); } - Flow visitRefNull(RefNull* curr) { - NOTE_ENTER("RefNull"); - return Literal::makeNullref(); - } - Flow visitRefIsNull(RefIsNull* curr) { - NOTE_ENTER("RefIsNull"); - Flow flow = visit(curr->value); - if (flow.breaking()) { - return flow; - } - Literal value = flow.getSingleValue(); - NOTE_EVAL1(value); - return Literal(value.type == Type::nullref); - } - Flow visitRefFunc(RefFunc* curr) { - NOTE_ENTER("RefFunc"); - NOTE_NAME(curr->func); - return Literal::makeFuncref(curr->func); - } - Flow visitTry(Try* curr) { - NOTE_ENTER("Try"); - // FIXME This currently only executes 'try' part. Correctly implement this. - return visit(curr->body); - } - Flow visitThrow(Throw* curr) { - NOTE_ENTER("Throw"); - LiteralList arguments; - Flow flow = generateArguments(curr->operands, arguments); - if (flow.breaking()) { - return flow; - } - NOTE_EVAL1(curr->event); - // FIXME This currently traps. Correctly implement throw. - trap("throw"); - WASM_UNREACHABLE("throw"); - } - Flow visitRethrow(Rethrow* curr) { - NOTE_ENTER("Rethrow"); - Flow flow = visit(curr->exnref); - if (flow.breaking()) { - return flow; - } - if (flow.getType() == Type::nullref) { - trap("rethrow: argument is null"); - } - // FIXME This currently traps. Correctly implement rethrow. - trap("rethrow"); - WASM_UNREACHABLE("rethrow"); - } Flow visitBrOnExn(BrOnExn* curr) { NOTE_ENTER("BrOnExn"); return Flow(NONCONSTANT_FLOW); } - virtual void trap(const char* why) { WASM_UNREACHABLE(why); } + void trap(const char* why) override { throw NonconstantException(); } }; -// Execute an constant expression in a global init or memory offset. +// Execute an initializer expression of a global, data or element segment. +// see: https://webassembly.org/docs/modules/#initializer-expression template<typename GlobalManager> -class ConstantExpressionRunner - : public ExpressionRunner<ConstantExpressionRunner<GlobalManager>> { +class InitializerExpressionRunner + : public ExpressionRunner<InitializerExpressionRunner<GlobalManager>> { GlobalManager& globals; public: - ConstantExpressionRunner(GlobalManager& globals, Index maxDepth) - : ExpressionRunner<ConstantExpressionRunner<GlobalManager>>(maxDepth), + InitializerExpressionRunner(GlobalManager& globals, Index maxDepth) + : ExpressionRunner<InitializerExpressionRunner<GlobalManager>>(maxDepth), globals(globals) {} Flow visitGlobalGet(GlobalGet* curr) { return Flow(globals[curr->name]); } @@ -1674,7 +1711,7 @@ public: // generate internal (non-imported) globals ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { globals[global->name] = - ConstantExpressionRunner<GlobalManager>(globals, maxDepth) + InitializerExpressionRunner<GlobalManager>(globals, maxDepth) .visit(global->init) .values; }); @@ -1739,7 +1776,7 @@ private: void initializeTableContents() { for (auto& segment : wasm.table.segments) { Address offset = - (uint32_t)ConstantExpressionRunner<GlobalManager>(globals, maxDepth) + (uint32_t)InitializerExpressionRunner<GlobalManager>(globals, maxDepth) .visit(segment.offset) .getSingleValue() .geti32(); |