diff options
28 files changed, 4125 insertions, 237 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 92f0054dc..baee36fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ full changeset diff at the end of each section. Current Trunk ------------- +- `BinaryenExpressionGetSideEffects` (C API) and `getSideEffects` (JS API) now + takes an additional `features` parameter. - Reference type support is added. Supported instructions are `ref.null`, `ref.is_null`, `ref.func`, and typed `select`. Table instructions are not supported yet. For typed `select`, C/JS API can take an additional 'type' diff --git a/scripts/test/wasm2js.py b/scripts/test/wasm2js.py index 31df227cf..975262efe 100755 --- a/scripts/test/wasm2js.py +++ b/scripts/test/wasm2js.py @@ -50,7 +50,10 @@ def test_wasm2js_output(): for module, asserts in support.split_wast(t): support.write_wast('split.wast', module, asserts) - cmd = shared.WASM2JS + ['split.wast', '-all'] + # wasm2js does not yet support EH, and enabling it can reduce + # optimization opportunities + cmd = shared.WASM2JS + ['split.wast', '-all', + '--disable-exception-handling'] if opt: cmd += ['-O'] if 'emscripten' in t: @@ -99,7 +102,8 @@ def test_asserts_output(): traps_expected_file = os.path.join(shared.options.binaryen_test, traps) wasm = os.path.join(shared.get_test_dir('wasm2js'), wasm) - cmd = shared.WASM2JS + [wasm, '--allow-asserts', '-all'] + cmd = shared.WASM2JS + [wasm, '--allow-asserts', '-all', + '--disable-exception-handling'] out = support.run_command(cmd) shared.fail_if_not_identical_to_file(out, asserts_expected_file) @@ -146,7 +150,10 @@ def update_wasm2js_tests(): for module, asserts in support.split_wast(t): support.write_wast('split.wast', module, asserts) - cmd = shared.WASM2JS + ['split.wast', '-all'] + # wasm2js does not yet support EH, and enable it can reduce + # optimization opportunities + cmd = shared.WASM2JS + ['split.wast', '-all', + '--disable-exception-handling'] if opt: cmd += ['-O'] if 'emscripten' in wasm: @@ -165,7 +172,7 @@ def update_wasm2js_tests(): asserts_expected_file = os.path.join(shared.options.binaryen_test, asserts) traps_expected_file = os.path.join(shared.options.binaryen_test, traps) - cmd = shared.WASM2JS + [os.path.join(shared.get_test_dir('wasm2js'), wasm), '--allow-asserts', '-all'] + cmd = shared.WASM2JS + [os.path.join(shared.get_test_dir('wasm2js'), wasm), '--allow-asserts', '-all', '--disable-exception-handling'] out = support.run_command(cmd) with open(asserts_expected_file, 'w') as o: o.write(out) diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 3d311f5bd..1362e0960 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -4508,17 +4508,22 @@ BinaryenSideEffects BinaryenSideEffectIsAtomic(void) { return static_cast<BinaryenSideEffects>( EffectAnalyzer::SideEffects::IsAtomic); } +BinaryenSideEffects BinaryenSideEffectThrows(void) { + return static_cast<BinaryenSideEffects>(EffectAnalyzer::SideEffects::Throws); +} BinaryenSideEffects BinaryenSideEffectAny(void) { return static_cast<BinaryenSideEffects>(EffectAnalyzer::SideEffects::Any); } BinaryenSideEffects -BinaryenExpressionGetSideEffects(BinaryenExpressionRef expr) { +BinaryenExpressionGetSideEffects(BinaryenExpressionRef expr, + BinaryenFeatures features) { if (tracing) { std::cout << " BinaryenExpressionGetSideEffects(expressions[" - << expressions[expr] << "]);\n"; + << expressions[expr] << "], " << features << ");\n"; } - return EffectAnalyzer(globalPassOptions, (Expression*)expr).getSideEffects(); + return EffectAnalyzer(globalPassOptions, features, (Expression*)expr) + .getSideEffects(); } // diff --git a/src/binaryen-c.h b/src/binaryen-c.h index d4ea9ccfa..0a9a70f4d 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1497,10 +1497,11 @@ BINARYEN_API BinaryenSideEffects BinaryenSideEffectReadsMemory(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectWritesMemory(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectImplicitTrap(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectIsAtomic(void); +BINARYEN_API BinaryenSideEffects BinaryenSideEffectThrows(void); BINARYEN_API BinaryenSideEffects BinaryenSideEffectAny(void); -BINARYEN_API BinaryenSideEffects -BinaryenExpressionGetSideEffects(BinaryenExpressionRef expr); +BINARYEN_API BinaryenSideEffects BinaryenExpressionGetSideEffects( + BinaryenExpressionRef expr, BinaryenFeatures features); // // ========== CFG / Relooper ========== diff --git a/src/ir/block-utils.h b/src/ir/block-utils.h index d3a4e0a64..f8224186f 100644 --- a/src/ir/block-utils.h +++ b/src/ir/block-utils.h @@ -37,8 +37,10 @@ simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) { !BranchUtils::BranchSeeker::has(list[0], block->name)) { // just one element. try to replace the block auto* singleton = list[0]; - auto sideEffects = - EffectAnalyzer(parent->getPassOptions(), singleton).hasSideEffects(); + auto sideEffects = EffectAnalyzer(parent->getPassOptions(), + parent->getModule()->features, + singleton) + .hasSideEffects(); if (!sideEffects && !singleton->type.isConcrete()) { // no side effects, and singleton is not returning a value, so we can // throw away the block and its contents, basically diff --git a/src/ir/effects.h b/src/ir/effects.h index 0d1e2c2b1..9c1ed18f2 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -27,9 +27,11 @@ namespace wasm { struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>> { - EffectAnalyzer(const PassOptions& passOptions, Expression* ast = nullptr) { - ignoreImplicitTraps = passOptions.ignoreImplicitTraps; - debugInfo = passOptions.debugInfo; + EffectAnalyzer(const PassOptions& passOptions, + FeatureSet features, + Expression* ast = nullptr) + : ignoreImplicitTraps(passOptions.ignoreImplicitTraps), + debugInfo(passOptions.debugInfo), features(features) { if (ast) { analyze(ast); } @@ -37,6 +39,7 @@ struct EffectAnalyzer bool ignoreImplicitTraps; bool debugInfo; + FeatureSet features; void analyze(Expression* ast) { breakNames.clear(); @@ -45,6 +48,7 @@ struct EffectAnalyzer if (breakNames.size() > 0) { branches = true; } + assert(tryDepth == 0); } // Core effect tracking @@ -66,6 +70,36 @@ struct EffectAnalyzer // An atomic load/store/RMW/Cmpxchg or an operator that has a defined ordering // wrt atomics (e.g. memory.grow) bool isAtomic = false; + bool throws = false; + // The nested depth of try. If an instruction that may throw is inside an + // inner try, we don't mark it as 'throws', because it will be caught by an + // inner catch. + size_t tryDepth = 0; + + static void scan(EffectAnalyzer* self, Expression** currp) { + Expression* curr = *currp; + // We need to decrement try depth before catch starts, so handle it + // separately + if (curr->is<Try>()) { + self->pushTask(doVisitTry, currp); + self->pushTask(scan, &curr->cast<Try>()->catchBody); + self->pushTask(doStartCatch, currp); + self->pushTask(scan, &curr->cast<Try>()->body); + self->pushTask(doStartTry, currp); + return; + } + PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>>::scan(self, + currp); + } + + static void doStartTry(EffectAnalyzer* self, Expression** currp) { + self->tryDepth++; + } + + static void doStartCatch(EffectAnalyzer* self, Expression** currp) { + assert(self->tryDepth > 0 && "try depth cannot be negative"); + self->tryDepth--; + } // Helper functions to check for various effect types @@ -76,17 +110,20 @@ struct EffectAnalyzer return globalsRead.size() + globalsWritten.size() > 0; } bool accessesMemory() const { return calls || readsMemory || writesMemory; } + bool transfersControlFlow() const { return branches || throws; } bool hasGlobalSideEffects() const { - return calls || globalsWritten.size() > 0 || writesMemory || isAtomic; + return calls || globalsWritten.size() > 0 || writesMemory || isAtomic || + throws; } bool hasSideEffects() const { return hasGlobalSideEffects() || localsWritten.size() > 0 || branches || implicitTrap; } bool hasAnything() const { - return branches || calls || accessesLocal() || readsMemory || - writesMemory || accessesGlobal() || implicitTrap || isAtomic; + return calls || accessesLocal() || readsMemory || writesMemory || + accessesGlobal() || implicitTrap || isAtomic || + transfersControlFlow(); } bool noticesGlobalSideEffects() { @@ -99,8 +136,8 @@ struct EffectAnalyzer // checks if these effects would invalidate another set (e.g., if we write, we // invalidate someone that reads, they can't be moved past us) bool invalidates(const EffectAnalyzer& other) { - if ((branches && other.hasSideEffects()) || - (other.branches && hasSideEffects()) || + if ((transfersControlFlow() && other.hasSideEffects()) || + (other.transfersControlFlow() && hasSideEffects()) || ((writesMemory || calls) && other.accessesMemory()) || (accessesMemory() && (other.writesMemory || other.calls))) { return true; @@ -137,7 +174,8 @@ struct EffectAnalyzer } } // we are ok to reorder implicit traps, but not conditionalize them - if ((implicitTrap && other.branches) || (other.implicitTrap && branches)) { + if ((implicitTrap && other.transfersControlFlow()) || + (other.implicitTrap && transfersControlFlow())) { return true; } // we can't reorder an implicit trap in a way that alters global state @@ -155,6 +193,7 @@ struct EffectAnalyzer writesMemory = writesMemory || other.writesMemory; implicitTrap = implicitTrap || other.implicitTrap; isAtomic = isAtomic || other.isAtomic; + throws = throws || other.throws; for (auto i : other.localsRead) { localsRead.insert(i); } @@ -223,6 +262,10 @@ struct EffectAnalyzer void visitCall(Call* curr) { calls = true; + // When EH is enabled, any call can throw. + if (features.hasExceptionHandling() && tryDepth == 0) { + throws = true; + } if (curr->isReturn) { branches = true; } @@ -235,6 +278,9 @@ struct EffectAnalyzer } void visitCallIndirect(CallIndirect* curr) { calls = true; + if (features.hasExceptionHandling() && tryDepth == 0) { + throws = true; + } if (curr->isReturn) { branches = true; } @@ -391,9 +437,16 @@ struct EffectAnalyzer void visitRefIsNull(RefIsNull* curr) {} void visitRefFunc(RefFunc* curr) {} void visitTry(Try* curr) {} - // We safely model throws as branches - void visitThrow(Throw* curr) { branches = true; } - void visitRethrow(Rethrow* curr) { branches = true; } + void visitThrow(Throw* curr) { + if (tryDepth == 0) { + throws = true; + } + } + void visitRethrow(Rethrow* curr) { + if (tryDepth == 0) { + throws = true; + } + } void visitBrOnExn(BrOnExn* curr) { breakNames.insert(curr->name); } void visitNop(Nop* curr) {} void visitUnreachable(Unreachable* curr) { branches = true; } @@ -402,10 +455,12 @@ struct EffectAnalyzer // Helpers - static bool - canReorder(const PassOptions& passOptions, Expression* a, Expression* b) { - EffectAnalyzer aEffects(passOptions, a); - EffectAnalyzer bEffects(passOptions, b); + static bool canReorder(const PassOptions& passOptions, + FeatureSet features, + Expression* a, + Expression* b) { + EffectAnalyzer aEffects(passOptions, features, a); + EffectAnalyzer bEffects(passOptions, features, b); return !aEffects.invalidates(bEffects); } @@ -423,7 +478,8 @@ struct EffectAnalyzer WritesMemory = 1 << 7, ImplicitTrap = 1 << 8, IsAtomic = 1 << 9, - Any = (1 << 10) - 1 + Throws = 1 << 10, + Any = (1 << 11) - 1 }; uint32_t getSideEffects() const { uint32_t effects = 0; @@ -457,6 +513,9 @@ struct EffectAnalyzer if (isAtomic) { effects |= SideEffects::IsAtomic; } + if (throws) { + effects |= SideEffects::Throws; + } return effects; } }; diff --git a/src/ir/local-utils.h b/src/ir/local-utils.h index c2a99b5af..0eba31889 100644 --- a/src/ir/local-utils.h +++ b/src/ir/local-utils.h @@ -45,18 +45,23 @@ struct UnneededSetRemover : public PostWalker<UnneededSetRemover> { PassOptions& passOptions; LocalGetCounter* localGetCounter = nullptr; + FeatureSet features; - UnneededSetRemover(Function* func, PassOptions& passOptions) - : passOptions(passOptions) { + UnneededSetRemover(Function* func, + PassOptions& passOptions, + FeatureSet features) + : passOptions(passOptions), features(features) { LocalGetCounter counter(func); - UnneededSetRemover inner(counter, func, passOptions); + UnneededSetRemover inner(counter, func, passOptions, features); removed = inner.removed; } UnneededSetRemover(LocalGetCounter& localGetCounter, Function* func, - PassOptions& passOptions) - : passOptions(passOptions), localGetCounter(&localGetCounter) { + PassOptions& passOptions, + FeatureSet features) + : passOptions(passOptions), localGetCounter(&localGetCounter), + features(features) { walk(func->body); } @@ -91,7 +96,8 @@ struct UnneededSetRemover : public PostWalker<UnneededSetRemover> { auto* value = set->value; if (set->isTee()) { replaceCurrent(value); - } else if (EffectAnalyzer(passOptions, set->value).hasSideEffects()) { + } else if (EffectAnalyzer(passOptions, features, set->value) + .hasSideEffects()) { Drop* drop = ExpressionManipulator::convert<LocalSet, Drop>(set); drop->value = value; drop->finalize(); diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 5857c21c9..fd2832bf2 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -471,6 +471,7 @@ function initializeConstants() { 'WritesMemory', 'ImplicitTrap', 'IsAtomic', + 'Throws', 'Any' ].forEach(function(name) { Module['SideEffects'][name] = Module['_BinaryenSideEffect' + name](); @@ -2759,8 +2760,8 @@ Module['getExpressionInfo'] = function(expr) { }; // Gets the side effects of the specified expression -Module['getSideEffects'] = function(expr) { - return Module['_BinaryenExpressionGetSideEffects'](expr); +Module['getSideEffects'] = function(expr, features) { + return Module['_BinaryenExpressionGetSideEffects'](expr, features); }; Module['createType'] = function(types) { diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp index 9b6e1143d..e4d24e33d 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -569,7 +569,9 @@ private: // TODO: this should not be a problem in // *non*-terminating tails, but // double-verify that - if (EffectAnalyzer(getPassOptions(), newItem) + if (EffectAnalyzer(getPassOptions(), + getModule()->features, + newItem) .hasExternalBreakTargets()) { return true; } diff --git a/src/passes/CodePushing.cpp b/src/passes/CodePushing.cpp index 4eb69b92e..6907f4fdc 100644 --- a/src/passes/CodePushing.cpp +++ b/src/passes/CodePushing.cpp @@ -82,14 +82,16 @@ class Pusher { LocalAnalyzer& analyzer; std::vector<Index>& numGetsSoFar; PassOptions& passOptions; + FeatureSet features; public: Pusher(Block* block, LocalAnalyzer& analyzer, std::vector<Index>& numGetsSoFar, - PassOptions& passOptions) + PassOptions& passOptions, + FeatureSet features) : list(block->list), analyzer(analyzer), numGetsSoFar(numGetsSoFar), - passOptions(passOptions) { + passOptions(passOptions), features(features) { // Find an optimization segment: from the first pushable thing, to the first // point past which we want to push. We then push in that range before // continuing forward. @@ -126,7 +128,7 @@ private: // but also have no side effects, as it may not execute if pushed. if (analyzer.isSFA(index) && numGetsSoFar[index] == analyzer.getNumGets(index) && - !EffectAnalyzer(passOptions, set->value).hasSideEffects()) { + !EffectAnalyzer(passOptions, features, set->value).hasSideEffects()) { return set; } return nullptr; @@ -157,7 +159,7 @@ private: assert(firstPushable != Index(-1) && pushPoint != Index(-1) && firstPushable < pushPoint); // everything that matters if you want to be pushed past the pushPoint - EffectAnalyzer cumulativeEffects(passOptions); + EffectAnalyzer cumulativeEffects(passOptions, features); cumulativeEffects.analyze(list[pushPoint]); // it is ok to ignore the branching here, that is the crucial point of this // opt @@ -169,11 +171,12 @@ private: if (pushable) { auto iter = pushableEffects.find(pushable); if (iter == pushableEffects.end()) { - iter = pushableEffects - .emplace(std::piecewise_construct, - std::forward_as_tuple(pushable), - std::forward_as_tuple(passOptions, pushable)) - .first; + iter = + pushableEffects + .emplace(std::piecewise_construct, + std::forward_as_tuple(pushable), + std::forward_as_tuple(passOptions, features, pushable)) + .first; } auto& effects = iter->second; if (cumulativeEffects.invalidates(effects)) { @@ -263,7 +266,8 @@ struct CodePushing : public WalkerPass<PostWalker<CodePushing>> { // don't hit a non-control-flow ordering invalidation issue, since if this // isn't a loop, it's fine (we're not used outside), and if it is, we hit // the assign before any use (as we can't push it past a use). - Pusher pusher(curr, analyzer, numGetsSoFar, getPassOptions()); + Pusher pusher( + curr, analyzer, numGetsSoFar, getPassOptions(), getModule()->features); } }; diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 43ebc6721..a20ff1fb8 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -341,7 +341,8 @@ struct DAE : public Pass { bool canRemove = std::none_of(calls.begin(), calls.end(), [&](Call* call) { auto* operand = call->operands[i]; - return EffectAnalyzer(runner->options, operand).hasSideEffects(); + return EffectAnalyzer(runner->options, module->features, operand) + .hasSideEffects(); }); if (canRemove) { // Wonderful, nothing stands in our way! Do it. diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp index b49c92310..f7c1cda26 100644 --- a/src/passes/LocalCSE.cpp +++ b/src/passes/LocalCSE.cpp @@ -60,8 +60,11 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { Index index; // the local we are assigned to, local.get that to reuse us EffectAnalyzer effects; - UsableInfo(Expression* value, Index index, PassOptions& passOptions) - : value(value), index(index), effects(passOptions, value) {} + UsableInfo(Expression* value, + Index index, + PassOptions& passOptions, + FeatureSet features) + : value(value), index(index), effects(passOptions, features, value) {} }; // a list of usables in a linear execution trace @@ -136,7 +139,7 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { // pre operations Expression* curr = *currp; - EffectAnalyzer effects(self->getPassOptions()); + EffectAnalyzer effects(self->getPassOptions(), self->getModule()->features); if (effects.checkPre(curr)) { self->checkInvalidations(effects); } @@ -152,7 +155,7 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { // post operations - EffectAnalyzer effects(self->getPassOptions()); + EffectAnalyzer effects(self->getPassOptions(), self->getModule()->features); if (effects.checkPost(curr)) { self->checkInvalidations(effects, curr); } @@ -194,7 +197,9 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { } else { // not in table, add this, maybe we can help others later usables.emplace(std::make_pair( - hashed, UsableInfo(value, set->index, getPassOptions()))); + hashed, + UsableInfo( + value, set->index, getPassOptions(), getModule()->features))); } } } else if (auto* get = curr->dynCast<LocalGet>()) { @@ -215,7 +220,8 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { if (!value->type.isConcrete()) { return false; // don't bother with unreachable etc. } - if (EffectAnalyzer(getPassOptions(), value).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), getModule()->features, value) + .hasSideEffects()) { return false; // we can't combine things with side effects } auto& options = getPassRunner()->options; diff --git a/src/passes/LoopInvariantCodeMotion.cpp b/src/passes/LoopInvariantCodeMotion.cpp index a95f4c8eb..26e2a0844 100644 --- a/src/passes/LoopInvariantCodeMotion.cpp +++ b/src/passes/LoopInvariantCodeMotion.cpp @@ -60,13 +60,14 @@ struct LoopInvariantCodeMotion // Accumulate effects of things we can't move out - things // we move out later must cross them, so we must verify it // is ok to do so. - EffectAnalyzer effectsSoFar(getPassOptions()); + FeatureSet features = getModule()->features; + EffectAnalyzer effectsSoFar(getPassOptions(), features); // The loop's total effects also matter. For example, a store // in the loop means we can't move a load outside. // FIXME: we look at the loop "tail" area too, after the last // possible branch back, which can cause false positives // for bad effect interactions. - EffectAnalyzer loopEffects(getPassOptions(), loop); + EffectAnalyzer loopEffects(getPassOptions(), features, loop); // Note all the sets in each loop, and how many per index. Currently // EffectAnalyzer can't do that, and we need it to know if we // can move a set out of the loop (if there is another set @@ -107,8 +108,8 @@ struct LoopInvariantCodeMotion // a branch to it anyhow, so we would stop before that point anyhow. } // If this may branch, we are done. - EffectAnalyzer effects(getPassOptions(), curr); - if (effects.branches) { + EffectAnalyzer effects(getPassOptions(), features, curr); + if (effects.transfersControlFlow()) { break; } if (interestingToMove(curr)) { diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 276c50d9e..ab368429f 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -101,7 +101,8 @@ struct ProblemFinder : public ControlFlowWalker<ProblemFinder> { brIfs++; } // if the value has side effects, we can't remove it - if (EffectAnalyzer(passOptions, curr->value).hasSideEffects()) { + if (EffectAnalyzer(passOptions, getModule()->features, curr->value) + .hasSideEffects()) { foundProblem = true; } } @@ -224,6 +225,7 @@ optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) { Expression* expression = childBlock; // check if it's ok to remove the value from all breaks to us ProblemFinder finder(passOptions); + finder.setModule(module); finder.origin = childBlock->name; finder.walk(expression); if (finder.found()) { @@ -418,17 +420,18 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { if (!child) { return outer; } + FeatureSet features = getModule()->features; if ((dependency1 && *dependency1) || (dependency2 && *dependency2)) { // there are dependencies, things we must be reordered through. make sure // no problems there - EffectAnalyzer childEffects(getPassOptions(), child); + EffectAnalyzer childEffects(getPassOptions(), features, child); if (dependency1 && *dependency1 && - EffectAnalyzer(getPassOptions(), *dependency1) + EffectAnalyzer(getPassOptions(), features, *dependency1) .invalidates(childEffects)) { return outer; } if (dependency2 && *dependency2 && - EffectAnalyzer(getPassOptions(), *dependency2) + EffectAnalyzer(getPassOptions(), features, *dependency2) .invalidates(childEffects)) { return outer; } @@ -495,16 +498,17 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { Expression*& third) { // TODO: for now, just stop when we see any side effect. instead, we could // check effects carefully for reordering + FeatureSet features = getModule()->features; Block* outer = nullptr; - if (EffectAnalyzer(getPassOptions(), first).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), features, first).hasSideEffects()) { return; } outer = optimize(curr, first, outer); - if (EffectAnalyzer(getPassOptions(), second).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), features, second).hasSideEffects()) { return; } outer = optimize(curr, second, outer); - if (EffectAnalyzer(getPassOptions(), third).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), features, third).hasSideEffects()) { return; } optimize(curr, third, outer); @@ -529,7 +533,8 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { template<typename T> void handleCall(T* curr) { Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { - if (EffectAnalyzer(getPassOptions(), curr->operands[i]) + if (EffectAnalyzer( + getPassOptions(), getModule()->features, curr->operands[i]) .hasSideEffects()) { return; } @@ -541,15 +546,17 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { void visitCall(Call* curr) { handleCall(curr); } void visitCallIndirect(CallIndirect* curr) { + FeatureSet features = getModule()->features; Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { - if (EffectAnalyzer(getPassOptions(), curr->operands[i]) + if (EffectAnalyzer(getPassOptions(), features, curr->operands[i]) .hasSideEffects()) { return; } outer = optimize(curr, curr->operands[i], outer); } - if (EffectAnalyzer(getPassOptions(), curr->target).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), features, curr->target) + .hasSideEffects()) { return; } optimize(curr, curr->target, outer); diff --git a/src/passes/OptimizeAddedConstants.cpp b/src/passes/OptimizeAddedConstants.cpp index f0c1ec2d9..e5cbf108a 100644 --- a/src/passes/OptimizeAddedConstants.cpp +++ b/src/passes/OptimizeAddedConstants.cpp @@ -360,7 +360,8 @@ private: void cleanUpAfterPropagation() { // Remove sets that no longer have uses. This allows further propagation by // letting us see the accurate amount of uses of each set. - UnneededSetRemover remover(getFunction(), getPassOptions()); + UnneededSetRemover remover( + getFunction(), getPassOptions(), getModule()->features); } std::map<LocalSet*, Index> helperIndexes; diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index ef7c17000..90abed825 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -330,6 +330,7 @@ struct OptimizeInstructions // Optimizations that don't yet fit in the pattern DSL, but could be // eventually maybe Expression* handOptimize(Expression* curr) { + FeatureSet features = getModule()->features; // if this contains dead code, don't bother trying to optimize it, the type // might change (if might not be unreachable if just one arm is, for // example). this optimization pass focuses on actually executing code. the @@ -472,7 +473,7 @@ struct OptimizeInstructions if (auto* subZero = sub->left->dynCast<Const>()) { if (subZero->value.geti32() == 0) { if (EffectAnalyzer::canReorder( - getPassOptions(), sub->right, binary->right)) { + getPassOptions(), features, sub->right, binary->right)) { sub->left = binary->right; return sub; } @@ -630,7 +631,8 @@ struct OptimizeInstructions } // finally, try more expensive operations on the binary in // the case that they have no side effects - if (!EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) { + if (!EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { if (ExpressionAnalyzer::equal(binary->left, binary->right)) { return optimizeBinaryWithEqualEffectlessChildren(binary); } @@ -750,7 +752,8 @@ struct OptimizeInstructions // if we can replace the if with one arm, and no side effects in the // condition, do that auto needCondition = - EffectAnalyzer(getPassOptions(), iff->condition).hasSideEffects(); + EffectAnalyzer(getPassOptions(), features, iff->condition) + .hasSideEffects(); auto isSubType = Type::isSubType(iff->ifTrue->type, iff->type); if (isSubType && !needCondition) { return iff->ifTrue; @@ -781,8 +784,8 @@ struct OptimizeInstructions auto* condition = select->condition->dynCast<Unary>(); if (condition && condition->op == EqZInt32) { // flip select to remove eqz, if we can reorder - EffectAnalyzer ifTrue(getPassOptions(), select->ifTrue); - EffectAnalyzer ifFalse(getPassOptions(), select->ifFalse); + EffectAnalyzer ifTrue(getPassOptions(), features, select->ifTrue); + EffectAnalyzer ifFalse(getPassOptions(), features, select->ifFalse); if (!ifTrue.invalidates(ifFalse)) { select->condition = condition->value; std::swap(select->ifTrue, select->ifFalse); @@ -792,7 +795,7 @@ struct OptimizeInstructions // constant condition, we can just pick the right side (barring side // effects) if (c->value.getInteger()) { - if (!EffectAnalyzer(getPassOptions(), select->ifFalse) + if (!EffectAnalyzer(getPassOptions(), features, select->ifFalse) .hasSideEffects()) { return select->ifTrue; } else { @@ -800,7 +803,7 @@ struct OptimizeInstructions // local, which is bad } } else { - if (!EffectAnalyzer(getPassOptions(), select->ifTrue) + if (!EffectAnalyzer(getPassOptions(), features, select->ifTrue) .hasSideEffects()) { return select->ifFalse; } else { @@ -812,7 +815,7 @@ struct OptimizeInstructions } if (ExpressionAnalyzer::equal(select->ifTrue, select->ifFalse)) { // sides are identical, fold - EffectAnalyzer value(getPassOptions(), select->ifTrue); + EffectAnalyzer value(getPassOptions(), features, select->ifTrue); if (value.hasSideEffects()) { // at best we don't need the condition, but need to execute the value // twice. a block is larger than a select by 2 bytes, and @@ -820,7 +823,8 @@ struct OptimizeInstructions // so it's not clear this is worth it, TODO } else { // value has no side effects - EffectAnalyzer condition(getPassOptions(), select->condition); + EffectAnalyzer condition( + getPassOptions(), features, select->condition); if (!condition.hasSideEffects()) { return select->ifTrue; } else { @@ -887,14 +891,15 @@ private: // write more concise pattern matching code elsewhere. void canonicalize(Binary* binary) { assert(Properties::isSymmetric(binary)); + FeatureSet features = getModule()->features; auto swap = [&]() { assert(EffectAnalyzer::canReorder( - getPassOptions(), binary->left, binary->right)); + getPassOptions(), features, binary->left, binary->right)); std::swap(binary->left, binary->right); }; auto maybeSwap = [&]() { if (EffectAnalyzer::canReorder( - getPassOptions(), binary->left, binary->right)) { + getPassOptions(), features, binary->left, binary->right)) { swap(); } }; @@ -1064,6 +1069,7 @@ private: ZeroRemover(PassOptions& passOptions) : passOptions(passOptions) {} void visitBinary(Binary* curr) { + FeatureSet features = getModule()->features; auto* left = curr->left->dynCast<Const>(); auto* right = curr->right->dynCast<Const>(); if (curr->op == AddInt32) { @@ -1086,7 +1092,8 @@ private: // shift has side effects if (((left && left->value.geti32() == 0) || (right && Bits::getEffectiveShifts(right) == 0)) && - !EffectAnalyzer(passOptions, curr->right).hasSideEffects()) { + !EffectAnalyzer(passOptions, features, curr->right) + .hasSideEffects()) { replaceCurrent(curr->left); return; } @@ -1094,12 +1101,14 @@ private: // multiplying by zero is a zero, unless the other side has side // effects if (left && left->value.geti32() == 0 && - !EffectAnalyzer(passOptions, curr->right).hasSideEffects()) { + !EffectAnalyzer(passOptions, features, curr->right) + .hasSideEffects()) { replaceCurrent(left); return; } if (right && right->value.geti32() == 0 && - !EffectAnalyzer(passOptions, curr->left).hasSideEffects()) { + !EffectAnalyzer(passOptions, features, curr->left) + .hasSideEffects()) { replaceCurrent(right); return; } @@ -1107,7 +1116,9 @@ private: } }; Expression* walked = binary; - ZeroRemover(getPassOptions()).walk(walked); + ZeroRemover remover(getPassOptions()); + remover.setModule(getModule()); + remover.walk(walked); if (constant == 0) { return walked; // nothing more to do } @@ -1142,8 +1153,9 @@ private: if (!Properties::emitsBoolean(left) || !Properties::emitsBoolean(right)) { return nullptr; } - auto leftEffects = EffectAnalyzer(getPassOptions(), left); - auto rightEffects = EffectAnalyzer(getPassOptions(), right); + FeatureSet features = getModule()->features; + auto leftEffects = EffectAnalyzer(getPassOptions(), features, left); + auto rightEffects = EffectAnalyzer(getPassOptions(), features, right); auto leftHasSideEffects = leftEffects.hasSideEffects(); auto rightHasSideEffects = rightEffects.hasSideEffects(); if (leftHasSideEffects && rightHasSideEffects) { @@ -1189,13 +1201,16 @@ private: // (x > y) | (x == y) ==> x >= y Expression* combineOr(Binary* binary) { assert(binary->op == OrInt32); + FeatureSet features = getModule()->features; if (auto* left = binary->left->dynCast<Binary>()) { if (auto* right = binary->right->dynCast<Binary>()) { if (left->op != right->op && ExpressionAnalyzer::equal(left->left, right->left) && ExpressionAnalyzer::equal(left->right, right->right) && - !EffectAnalyzer(getPassOptions(), left->left).hasSideEffects() && - !EffectAnalyzer(getPassOptions(), left->right).hasSideEffects()) { + !EffectAnalyzer(getPassOptions(), features, left->left) + .hasSideEffects() && + !EffectAnalyzer(getPassOptions(), features, left->right) + .hasSideEffects()) { switch (left->op) { // (x > y) | (x == y) ==> x >= y case EqInt32: { @@ -1296,6 +1311,7 @@ private: // is a constant // TODO: templatize on type? Expression* optimizeWithConstantOnRight(Binary* binary) { + FeatureSet features = getModule()->features; auto type = binary->right->type; auto* right = binary->right->cast<Const>(); if (type.isInteger()) { @@ -1309,7 +1325,7 @@ private: return binary->left; } else if ((binary->op == Abstract::getBinary(type, Abstract::Mul) || binary->op == Abstract::getBinary(type, Abstract::And)) && - !EffectAnalyzer(getPassOptions(), binary->left) + !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { return binary->right; } else if (binary->op == EqInt64) { @@ -1323,7 +1339,7 @@ private: if (binary->op == Abstract::getBinary(type, Abstract::And)) { return binary->left; } else if (binary->op == Abstract::getBinary(type, Abstract::Or) && - !EffectAnalyzer(getPassOptions(), binary->left) + !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { return binary->right; } @@ -1381,7 +1397,9 @@ private: if ((binary->op == Abstract::getBinary(type, Abstract::Shl) || binary->op == Abstract::getBinary(type, Abstract::ShrU) || binary->op == Abstract::getBinary(type, Abstract::ShrS)) && - !EffectAnalyzer(getPassOptions(), binary->right).hasSideEffects()) { + !EffectAnalyzer( + getPassOptions(), getModule()->features, binary->right) + .hasSideEffects()) { return binary->left; } } diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 786fcaf67..2d944d212 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -35,7 +35,8 @@ namespace wasm { // not have side effects (as they would run unconditionally) static bool canTurnIfIntoBrIf(Expression* ifCondition, Expression* brValue, - PassOptions& options) { + PassOptions& options, + FeatureSet features) { // if the if isn't even reached, this is all dead code anyhow if (ifCondition->type == Type::unreachable) { return false; @@ -43,11 +44,11 @@ static bool canTurnIfIntoBrIf(Expression* ifCondition, if (!brValue) { return true; } - EffectAnalyzer value(options, brValue); + EffectAnalyzer value(options, features, brValue); if (value.hasSideEffects()) { return false; } - return !EffectAnalyzer(options, ifCondition).invalidates(value); + return !EffectAnalyzer(options, features, ifCondition).invalidates(value); } // Check if it is not worth it to run code unconditionally. This @@ -302,11 +303,13 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } void visitIf(If* curr) { + FeatureSet features = getModule()->features; if (!curr->ifFalse) { // if without an else. try to reduce // if (condition) br => br_if (condition) if (Break* br = curr->ifTrue->dynCast<Break>()) { - if (canTurnIfIntoBrIf(curr->condition, br->value, getPassOptions())) { + if (canTurnIfIntoBrIf( + curr->condition, br->value, getPassOptions(), features)) { if (!br->condition) { br->condition = curr->condition; } else { @@ -327,7 +330,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } // Of course we can't do this if the br's condition has side // effects, as we would then execute those unconditionally. - if (EffectAnalyzer(getPassOptions(), br->condition) + if (EffectAnalyzer(getPassOptions(), features, br->condition) .hasSideEffects()) { return; } @@ -521,7 +524,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { return false; } // if there is control flow, we must stop looking - if (EffectAnalyzer(getPassOptions(), curr).branches) { + if (EffectAnalyzer(getPassOptions(), getModule()->features, curr) + .transfersControlFlow()) { return false; } if (i == 0) { @@ -744,6 +748,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // the if is dead // * note that we do this at the end, because un-conditionalizing can // interfere with optimizeLoop()ing. + FeatureSet features = getModule()->features; auto& list = curr->list; for (Index i = 0; i < list.size(); i++) { auto* iff = list[i]->dynCast<If>(); @@ -755,7 +760,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { auto* ifTrueBreak = iff->ifTrue->dynCast<Break>(); if (ifTrueBreak && !ifTrueBreak->condition && canTurnIfIntoBrIf( - iff->condition, ifTrueBreak->value, passOptions)) { + iff->condition, ifTrueBreak->value, passOptions, features)) { // we are an if-else where the ifTrue is a break without a // condition, so we can do this ifTrueBreak->condition = iff->condition; @@ -768,7 +773,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { auto* ifFalseBreak = iff->ifFalse->dynCast<Break>(); if (ifFalseBreak && !ifFalseBreak->condition && canTurnIfIntoBrIf( - iff->condition, ifFalseBreak->value, passOptions)) { + iff->condition, ifFalseBreak->value, passOptions, features)) { ifFalseBreak->condition = Builder(*getModule()).makeUnary(EqZInt32, iff->condition); ifFalseBreak->finalize(); @@ -797,7 +802,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { if (shrink && br2->type != Type::unreachable) { // Join adjacent br_ifs to the same target, making one br_if // with a "selectified" condition that executes both. - if (!EffectAnalyzer(passOptions, br2->condition) + if (!EffectAnalyzer(passOptions, features, br2->condition) .hasSideEffects()) { // it's ok to execute them both, do it Builder builder(*getModule()); @@ -888,8 +893,10 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // If the items we move around have side effects, we can't do // this. // TODO: we could use a select, in some cases..? - if (!EffectAnalyzer(passOptions, br->value).hasSideEffects() && - !EffectAnalyzer(passOptions, br->condition) + FeatureSet features = getModule()->features; + if (!EffectAnalyzer(passOptions, features, br->value) + .hasSideEffects() && + !EffectAnalyzer(passOptions, features, br->condition) .hasSideEffects()) { ExpressionManipulator::nop(list[0]); Builder builder(*getModule()); @@ -923,11 +930,12 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { return nullptr; } // Check if side effects allow this. - EffectAnalyzer condition(passOptions, iff->condition); + FeatureSet features = getModule()->features; + EffectAnalyzer condition(passOptions, features, iff->condition); if (!condition.hasSideEffects()) { - EffectAnalyzer ifTrue(passOptions, iff->ifTrue); + EffectAnalyzer ifTrue(passOptions, features, iff->ifTrue); if (!ifTrue.hasSideEffects()) { - EffectAnalyzer ifFalse(passOptions, iff->ifFalse); + EffectAnalyzer ifFalse(passOptions, features, iff->ifFalse); if (!ifFalse.hasSideEffects()) { return Builder(*getModule()) .makeSelect(iff->condition, iff->ifTrue, iff->ifFalse); @@ -1184,7 +1192,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } // if the condition has side effects, we can't replace many // appearances of it with a single one - if (EffectAnalyzer(passOptions, conditionValue).hasSideEffects()) { + if (EffectAnalyzer(passOptions, getModule()->features, conditionValue) + .hasSideEffects()) { start++; continue; } diff --git a/src/passes/SimplifyGlobals.cpp b/src/passes/SimplifyGlobals.cpp index b18f726ed..aa211b86b 100644 --- a/src/passes/SimplifyGlobals.cpp +++ b/src/passes/SimplifyGlobals.cpp @@ -133,7 +133,7 @@ struct ConstantGlobalApplier return; } // Otherwise, invalidate if we need to. - EffectAnalyzer effects(getPassOptions()); + EffectAnalyzer effects(getPassOptions(), getModule()->features); effects.visit(curr); assert(effects.globalsWritten.empty()); // handled above if (effects.calls) { diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index f7558aa83..23ab1bc92 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -78,8 +78,10 @@ struct SimplifyLocals Expression** item; EffectAnalyzer effects; - SinkableInfo(Expression** item, PassOptions& passOptions) - : item(item), effects(passOptions, *item) {} + SinkableInfo(Expression** item, + PassOptions& passOptions, + FeatureSet features) + : item(item), effects(passOptions, features, *item) {} }; // a list of sinkables in a linear execution trace @@ -298,7 +300,7 @@ struct SimplifyLocals Expression** currp) { Expression* curr = *currp; - EffectAnalyzer effects(self->getPassOptions()); + EffectAnalyzer effects(self->getPassOptions(), self->getModule()->features); if (effects.checkPre(curr)) { self->checkInvalidations(effects); } @@ -384,7 +386,8 @@ struct SimplifyLocals } } - EffectAnalyzer effects(self->getPassOptions()); + FeatureSet features = self->getModule()->features; + EffectAnalyzer effects(self->getPassOptions(), features); if (effects.checkPost(original)) { self->checkInvalidations(effects); } @@ -392,8 +395,8 @@ struct SimplifyLocals if (set && self->canSink(set)) { Index index = set->index; assert(self->sinkables.count(index) == 0); - self->sinkables.emplace( - std::make_pair(index, SinkableInfo(currp, self->getPassOptions()))); + self->sinkables.emplace(std::make_pair( + index, SinkableInfo(currp, self->getPassOptions(), features))); } if (!allowNesting) { @@ -504,6 +507,7 @@ struct SimplifyLocals // ) // ) // so we must check for that. + FeatureSet features = this->getModule()->features; for (size_t j = 0; j < breaks.size(); j++) { // move break local.set's value to the break auto* breakLocalSetPointer = breaks[j].sinkables.at(sharedIndex).item; @@ -520,8 +524,9 @@ struct SimplifyLocals // itself, there is any risk Nop nop; *breakLocalSetPointer = &nop; - EffectAnalyzer condition(this->getPassOptions(), br->condition); - EffectAnalyzer value(this->getPassOptions(), set); + EffectAnalyzer condition( + this->getPassOptions(), features, br->condition); + EffectAnalyzer value(this->getPassOptions(), features, set); *breakLocalSetPointer = set; if (condition.invalidates(value)) { // indeed, we can't do this, stop @@ -994,7 +999,9 @@ struct SimplifyLocals // We may have already had a local with no uses, or we may have just // gotten there thanks to the EquivalentOptimizer. If there are such // locals, remove all their sets. - UnneededSetRemover setRemover(getCounter, func, this->getPassOptions()); + UnneededSetRemover setRemover( + getCounter, func, this->getPassOptions(), this->getModule()->features); + setRemover.setModule(this->getModule()); return eqOpter.anotherCycle || setRemover.removed; } diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index a222b0159..f0bdd7722 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -54,6 +54,7 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { // * The result may be used or unused. // * The type may or may not matter (a drop can drop anything, for example). Expression* optimize(Expression* curr, bool resultUsed, bool typeMatters) { + FeatureSet features = getModule()->features; auto type = curr->type; // An unreachable node must not be changed. if (type == Type::unreachable) { @@ -97,8 +98,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { // side effects (the load itself may trap, if we are not ignoring such // things) auto* load = curr->cast<Load>(); - if (!resultUsed && - !EffectAnalyzer(getPassOptions(), curr).hasSideEffects()) { + if (!resultUsed && !EffectAnalyzer(getPassOptions(), features, curr) + .hasSideEffects()) { if (!typeMatters || load->ptr->type == type) { return load->ptr; } @@ -124,12 +125,12 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { // side effects, as well as the node itself, as some unaries and // binaries have implicit traps if (auto* unary = curr->dynCast<Unary>()) { - EffectAnalyzer tester(getPassOptions()); + EffectAnalyzer tester(getPassOptions(), features); tester.visitUnary(unary); if (tester.hasSideEffects()) { return curr; } - if (EffectAnalyzer(getPassOptions(), unary->value) + if (EffectAnalyzer(getPassOptions(), features, unary->value) .hasSideEffects()) { curr = unary->value; continue; @@ -137,14 +138,14 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { return nullptr; } } else if (auto* binary = curr->dynCast<Binary>()) { - EffectAnalyzer tester(getPassOptions()); + EffectAnalyzer tester(getPassOptions(), features); tester.visitBinary(binary); if (tester.hasSideEffects()) { return curr; } - if (EffectAnalyzer(getPassOptions(), binary->left) + if (EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { - if (EffectAnalyzer(getPassOptions(), binary->right) + if (EffectAnalyzer(getPassOptions(), features, binary->right) .hasSideEffects()) { return curr; // leave them } else { @@ -152,7 +153,7 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { continue; } } else { - if (EffectAnalyzer(getPassOptions(), binary->right) + if (EffectAnalyzer(getPassOptions(), features, binary->right) .hasSideEffects()) { curr = binary->right; continue; @@ -164,13 +165,14 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { // TODO: if two have side effects, we could replace the select with // say an add? auto* select = curr->cast<Select>(); - if (EffectAnalyzer(getPassOptions(), select->ifTrue) + if (EffectAnalyzer(getPassOptions(), features, select->ifTrue) .hasSideEffects()) { - if (EffectAnalyzer(getPassOptions(), select->ifFalse) + if (EffectAnalyzer(getPassOptions(), features, select->ifFalse) .hasSideEffects()) { return curr; // leave them } else { - if (EffectAnalyzer(getPassOptions(), select->condition) + if (EffectAnalyzer( + getPassOptions(), features, select->condition) .hasSideEffects()) { return curr; // leave them } else { @@ -179,9 +181,10 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } } } else { - if (EffectAnalyzer(getPassOptions(), select->ifFalse) + if (EffectAnalyzer(getPassOptions(), features, select->ifFalse) .hasSideEffects()) { - if (EffectAnalyzer(getPassOptions(), select->condition) + if (EffectAnalyzer( + getPassOptions(), features, select->condition) .hasSideEffects()) { return curr; // leave them } else { @@ -189,7 +192,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { continue; } } else { - if (EffectAnalyzer(getPassOptions(), select->condition) + if (EffectAnalyzer( + getPassOptions(), features, select->condition) .hasSideEffects()) { curr = select->condition; continue; @@ -419,7 +423,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { ExpressionManipulator::nop(curr->body); } if (curr->sig.results == Type::none && - !EffectAnalyzer(getPassOptions(), curr->body).hasSideEffects()) { + !EffectAnalyzer(getPassOptions(), getModule()->features, curr->body) + .hasSideEffects()) { ExpressionManipulator::nop(curr->body); } } diff --git a/src/wasm2js.h b/src/wasm2js.h index 55759c959..802765b1a 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -1128,11 +1128,12 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, // If the target has effects that interact with the operands, we must // reorder it to the start. bool mustReorder = false; - EffectAnalyzer targetEffects(parent->options, curr->target); + EffectAnalyzer targetEffects( + parent->options, module->features, curr->target); if (targetEffects.hasAnything()) { for (auto* operand : curr->operands) { if (targetEffects.invalidates( - EffectAnalyzer(parent->options, operand))) { + EffectAnalyzer(parent->options, module->features, operand))) { mustReorder = true; break; } @@ -1720,9 +1721,12 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, // reorder it to the start. We must also use locals if the values have // side effects, as a JS conditional does not visit both sides. bool useLocals = false; - EffectAnalyzer conditionEffects(parent->options, curr->condition); - EffectAnalyzer ifTrueEffects(parent->options, curr->ifTrue); - EffectAnalyzer ifFalseEffects(parent->options, curr->ifFalse); + EffectAnalyzer conditionEffects( + parent->options, module->features, curr->condition); + EffectAnalyzer ifTrueEffects( + parent->options, module->features, curr->ifTrue); + EffectAnalyzer ifFalseEffects( + parent->options, module->features, curr->ifFalse); if (conditionEffects.invalidates(ifTrueEffects) || conditionEffects.invalidates(ifFalseEffects) || ifTrueEffects.hasSideEffects() || ifFalseEffects.hasSideEffects()) { diff --git a/test/binaryen.js/sideffects.js b/test/binaryen.js/sideffects.js index a486099dc..7db9ab329 100644 --- a/test/binaryen.js/sideffects.js +++ b/test/binaryen.js/sideffects.js @@ -9,6 +9,7 @@ console.log("SideEffects.ReadsMemory=" + binaryen.SideEffects.ReadsMemory); console.log("SideEffects.WritesMemory=" + binaryen.SideEffects.WritesMemory); console.log("SideEffects.ImplicitTrap=" + binaryen.SideEffects.ImplicitTrap); console.log("SideEffects.IsAtomic=" + binaryen.SideEffects.IsAtomic); +console.log("SideEffects.Throws=" + binaryen.SideEffects.Throws); console.log("SideEffects.Any=" + binaryen.SideEffects.Any); var module = new binaryen.Module(); @@ -92,3 +93,14 @@ assert( == binaryen.SideEffects.ImplicitTrap ); + +// If exception handling feature is enabled, calls can throw +var module_all_features = new binaryen.Module(); +module_all_features.setFeatures(binaryen.Features.All); +assert( + binaryen.getSideEffects( + module.call("test", [], binaryen.i32) + ) + == + binaryen.SideEffects.Calls | binaryen.SideEffects.Throws +); diff --git a/test/binaryen.js/sideffects.js.txt b/test/binaryen.js/sideffects.js.txt index 54a1e14bc..4aca0ac46 100644 --- a/test/binaryen.js/sideffects.js.txt +++ b/test/binaryen.js/sideffects.js.txt @@ -9,4 +9,5 @@ SideEffects.ReadsMemory=64 SideEffects.WritesMemory=128 SideEffects.ImplicitTrap=256 SideEffects.IsAtomic=512 -SideEffects.Any=1023 +SideEffects.Throws=1024 +SideEffects.Any=2047 diff --git a/test/passes/code-pushing_all-features.txt b/test/passes/code-pushing_all-features.txt new file mode 100644 index 000000000..230a9006d --- /dev/null +++ b/test/passes/code-pushing_all-features.txt @@ -0,0 +1,97 @@ +(module + (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) + (event $e (attr 0) (param i32)) + (func $cant-push-past-call (; 0 ;) + (local $x i32) + (block $out + (local.set $x + (i32.const 1) + ) + (call $cant-push-past-call) + (drop + (i32.const 1) + ) + (br_if $out + (i32.const 2) + ) + (drop + (local.get $x) + ) + ) + ) + (func $cant-push-past-throw (; 1 ;) + (local $x i32) + (block $out + (local.set $x + (i32.const 1) + ) + (throw $e + (i32.const 0) + ) + (drop + (i32.const 1) + ) + (br_if $out + (i32.const 2) + ) + (drop + (local.get $x) + ) + ) + ) + (func $can-push-past-throw-within-try (; 2 ;) + (local $x i32) + (block $out + (try + (throw $e + (i32.const 0) + ) + (catch + (drop + (exnref.pop) + ) + ) + ) + (drop + (i32.const 1) + ) + (br_if $out + (i32.const 2) + ) + (local.set $x + (i32.const 1) + ) + (drop + (local.get $x) + ) + ) + ) + (func $cant-push-past-rethrow-within-catch (; 3 ;) + (local $x i32) + (block $out + (local.set $x + (i32.const 1) + ) + (try + (throw $e + (i32.const 0) + ) + (catch + (rethrow + (exnref.pop) + ) + ) + ) + (drop + (i32.const 1) + ) + (br_if $out + (i32.const 2) + ) + (drop + (local.get $x) + ) + ) + ) +) diff --git a/test/passes/code-pushing_all-features.wast b/test/passes/code-pushing_all-features.wast new file mode 100644 index 000000000..f24456c6d --- /dev/null +++ b/test/passes/code-pushing_all-features.wast @@ -0,0 +1,63 @@ +(module + (event $e (attr 0) (param i32)) + + (func $cant-push-past-call + (local $x i32) + (block $out + ;; This local.set cannot be pushed down, because the call below can throw + (local.set $x (i32.const 1)) + (call $cant-push-past-call) + (drop (i32.const 1)) + (br_if $out (i32.const 2)) + (drop (local.get $x)) + ) + ) + + (func $cant-push-past-throw + (local $x i32) + (block $out + ;; This local.set cannot be pushed down, because there is 'throw' below + (local.set $x (i32.const 1)) + (throw $e (i32.const 0)) + (drop (i32.const 1)) + (br_if $out (i32.const 2)) + (drop (local.get $x)) + ) + ) + + (func $can-push-past-throw-within-try + (local $x i32) + (block $out + ;; This local.set can be pushed down, because the 'throw' below is going + ;; to be caught by the inner catch + (local.set $x (i32.const 1)) + (try + (throw $e (i32.const 0)) + (catch + (drop (exnref.pop)) + ) + ) + (drop (i32.const 1)) + (br_if $out (i32.const 2)) + (drop (local.get $x)) + ) + ) + + (func $cant-push-past-rethrow-within-catch + (local $x i32) + (block $out + ;; This local.set cannot be pushed down, because there is 'rethrow' within + ;; the inner catch + (local.set $x (i32.const 1)) + (try + (throw $e (i32.const 0)) + (catch + (rethrow (exnref.pop)) + ) + ) + (drop (i32.const 1)) + (br_if $out (i32.const 2)) + (drop (local.get $x)) + ) + ) +) diff --git a/test/passes/simplify-locals_all-features.txt b/test/passes/simplify-locals_all-features.txt index 60459d1a8..54f38abeb 100644 --- a/test/passes/simplify-locals_all-features.txt +++ b/test/passes/simplify-locals_all-features.txt @@ -161,14 +161,18 @@ (drop (i32.const 8) ) - (nop) - (nop) + (local.set $a + (i32.const 9) + ) + (local.set $b + (i32.const 10) + ) (call $waka) (drop - (i32.const 9) + (local.get $a) ) (drop - (i32.const 10) + (local.get $b) ) (drop (i32.const 11) @@ -201,10 +205,12 @@ (i32.const 48) (i32.const 96) ) - (drop + (local.set $a (i32.const 17) ) - (nop) + (local.set $b + (i32.const 18) + ) ) (block $block3 (nop) @@ -470,64 +476,107 @@ (local.set $$rem (local.get $__stackBase__) ) - (nop) - (nop) - (nop) - (nop) + (local.set $$1$0 + (i32.or + (i32.shr_s + (local.get $$a$1) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$1$1 + (i32.or + (i32.shr_s + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$2$0 + (i32.or + (i32.shr_s + (local.get $$b$1) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$2$1 + (i32.or + (i32.shr_s + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) (nop) (nop) (drop (call $___udivmoddi4 (call $_i64Subtract (i32.xor - (local.tee $$1$0 - (i32.or - (i32.shr_s - (local.get $$a$1) - (i32.const 31) - ) - (i32.shl - (if (result i32) - (i32.lt_s - (local.get $$a$1) - (i32.const 0) - ) - (i32.const -1) - (i32.const 0) - ) - (i32.const 1) - ) - ) - ) + (local.get $$1$0) (local.get $$a$0) ) (i32.xor - (local.tee $$1$1 - (i32.or - (i32.shr_s - (if (result i32) - (i32.lt_s - (local.get $$a$1) - (i32.const 0) - ) - (i32.const -1) - (i32.const 0) - ) - (i32.const 31) - ) - (i32.shl - (if (result i32) - (i32.lt_s - (local.get $$a$1) - (i32.const 0) - ) - (i32.const -1) - (i32.const 0) - ) - (i32.const 1) - ) - ) - ) + (local.get $$1$1) (local.get $$a$1) ) (local.get $$1$0) @@ -538,54 +587,11 @@ ) (call $_i64Subtract (i32.xor - (local.tee $$2$0 - (i32.or - (i32.shr_s - (local.get $$b$1) - (i32.const 31) - ) - (i32.shl - (if (result i32) - (i32.lt_s - (local.get $$b$1) - (i32.const 0) - ) - (i32.const -1) - (i32.const 0) - ) - (i32.const 1) - ) - ) - ) + (local.get $$2$0) (local.get $$b$0) ) (i32.xor - (local.tee $$2$1 - (i32.or - (i32.shr_s - (if (result i32) - (i32.lt_s - (local.get $$b$1) - (i32.const 0) - ) - (i32.const -1) - (i32.const 0) - ) - (i32.const 31) - ) - (i32.shl - (if (result i32) - (i32.lt_s - (local.get $$b$1) - (i32.const 0) - ) - (i32.const -1) - (i32.const 0) - ) - (i32.const 1) - ) - ) - ) + (local.get $$2$1) (local.get $$b$1) ) (local.get $$2$0) diff --git a/test/passes/simplify-locals_all-features_disable-exception-handling.txt b/test/passes/simplify-locals_all-features_disable-exception-handling.txt new file mode 100644 index 000000000..60459d1a8 --- /dev/null +++ b/test/passes/simplify-locals_all-features_disable-exception-handling.txt @@ -0,0 +1,1888 @@ +(module + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $none_=>_none (func)) + (type $none_=>_i32 (func (result i32))) + (type $i32_i32_i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32 i32 i32))) + (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) + (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_=>_none (func (param i32 i32))) + (type $i64_=>_none (func (param i64))) + (type $f32_=>_none (func (param f32))) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $i32_i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32 i32) (result i32))) + (type $i32_f64_f64_f32_i32_=>_f64 (func (param i32 f64 f64 f32 i32) (result f64))) + (import "env" "waka" (func $waka)) + (import "env" "waka_int" (func $waka_int (result i32))) + (import "env" "i64sub" (func $_i64Subtract (param i32 i32 i32 i32) (result i32))) + (import "env" "moddi" (func $___udivmoddi4 (param i32 i32 i32 i32 i32) (result i32))) + (import "env" "lp" (func $lp (param i32 i32) (result i32))) + (import "fuzzing-support" "log-f32" (func $fimport$0 (param f32))) + (memory $0 256 256) + (global $global$0 (mut i32) (i32.const 10)) + (func $contrast (; 6 ;) + (local $x i32) + (local $y i32) + (local $z i32) + (local $a i32) + (local $b i32) + (nop) + (if + (local.tee $x + (i32.const 1) + ) + (nop) + ) + (if + (local.get $x) + (nop) + ) + (nop) + (drop + (if (result i32) + (i32.const 2) + (i32.const 3) + (i32.const 4) + ) + ) + (nop) + (drop + (block $block (result i32) + (i32.const 5) + ) + ) + (nop) + (drop + (if (result i32) + (i32.const 6) + (block (result i32) + (nop) + (i32.const 7) + ) + (block (result i32) + (nop) + (i32.const 8) + ) + ) + ) + (nop) + (drop + (block $val (result i32) + (if + (i32.const 10) + (block $block4 + (nop) + (br $val + (i32.const 11) + ) + ) + ) + (nop) + (i32.const 12) + ) + ) + ) + (func $b0-yes (; 7 ;) (param $i1 i32) + (local $x i32) + (local $y i32) + (local $a i32) + (local $b i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (nop) + (drop + (i32.const 5) + ) + (block $block0 + (nop) + (drop + (i32.const 7) + ) + ) + (nop) + (drop + (i32.const 11) + ) + (drop + (i32.const 9) + ) + (drop + (local.get $y) + ) + (block $block1 + (drop + (i32.const 8) + ) + (drop + (local.get $y) + ) + ) + (drop + (i32.const 11) + ) + (drop + (local.get $y) + ) + (nop) + (nop) + (nop) + (nop) + (nop) + (nop) + (nop) + (block $block2 + (nop) + (nop) + (drop + (i32.const 1) + ) + (drop + (i32.const 2) + ) + (drop + (i32.const 3) + ) + (drop + (i32.const 4) + ) + (nop) + (nop) + (drop + (i32.const 6) + ) + (drop + (i32.const 5) + ) + (drop + (i32.const 7) + ) + (drop + (i32.const 8) + ) + (nop) + (nop) + (call $waka) + (drop + (i32.const 9) + ) + (drop + (i32.const 10) + ) + (drop + (i32.const 11) + ) + (drop + (i32.const 12) + ) + (nop) + (nop) + (drop + (i32.load + (i32.const 24) + ) + ) + (drop + (i32.const 13) + ) + (drop + (i32.const 14) + ) + (drop + (i32.const 15) + ) + (drop + (i32.const 16) + ) + (nop) + (nop) + (i32.store + (i32.const 48) + (i32.const 96) + ) + (drop + (i32.const 17) + ) + (nop) + ) + (block $block3 + (nop) + (local.set $a + (call $waka_int) + ) + (call $waka) + (local.set $a + (call $waka_int) + ) + (call $waka) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (call $waka_int) + ) + (drop + (i32.load + (i32.const 1) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (call $waka_int) + ) + (i32.store + (i32.const 1) + (i32.const 2) + ) + (drop + (local.get $a) + ) + (call $waka) + (nop) + (local.set $a + (i32.load + (i32.const 100) + ) + ) + (call $waka) + (nop) + (drop + (i32.load + (i32.const 1) + ) + ) + (local.set $a + (i32.load + (i32.const 101) + ) + ) + (call $waka) + (local.set $a + (i32.load + (i32.const 102) + ) + ) + (call $waka) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (i32.load + (i32.const 103) + ) + ) + (i32.store + (i32.const 1) + (i32.const 2) + ) + (drop + (local.get $a) + ) + (call $waka) + (nop) + (local.set $a + (block $block (result i32) + (block $block5 + (nop) + (i32.store + (i32.const 104) + (local.tee $5 + (i32.const 105) + ) + ) + ) + (local.get $5) + ) + ) + (call $waka) + (local.set $a + (block $block6 (result i32) + (block $block7 + (nop) + (i32.store + (i32.const 106) + (local.tee $6 + (i32.const 107) + ) + ) + ) + (local.get $6) + ) + ) + (call $waka) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (block $block8 (result i32) + (block $block9 + (nop) + (i32.store + (i32.const 108) + (local.tee $7 + (i32.const 109) + ) + ) + ) + (local.get $7) + ) + ) + (drop + (i32.load + (i32.const 1) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (block $block10 (result i32) + (block $block11 + (nop) + (i32.store + (i32.const 110) + (local.tee $8 + (i32.const 111) + ) + ) + ) + (local.get $8) + ) + ) + (i32.store + (i32.const 1) + (i32.const 2) + ) + (drop + (local.get $a) + ) + (call $waka) + ) + (block $out-of-block + (nop) + (nop) + (drop + (block $b (result i32) + (block $c + (br $b + (i32.const 1337) + ) + ) + (nop) + (i32.const 9876) + ) + ) + ) + (block $loopey + (local.set $a + (i32.const 1337) + ) + (drop + (loop $loop-in5 (result i32) + (drop + (local.get $a) + ) + (local.tee $a + (i32.const 9876) + ) + ) + ) + (drop + (local.get $a) + ) + ) + ) + (func $Ia (; 8 ;) (param $a i32) (result i32) + (local $b i32) + (block $switch$0 + (block $switch-default$6 + (nop) + ) + ) + (return + (i32.const 60) + ) + ) + (func $memories (; 9 ;) (param $i2 i32) (param $i3 i32) (param $bi2 i32) (param $bi3 i32) (param $ci3 i32) (param $di3 i32) + (local $set_with_no_get i32) + (nop) + (i32.store8 + (local.get $i2) + (i32.const 1) + ) + (nop) + (i32.store8 + (local.tee $bi3 + (i32.const 1) + ) + (local.get $bi3) + ) + (nop) + (i32.store8 + (local.get $bi3) + (local.get $bi3) + ) + (local.set $di3 + (local.tee $bi3 + (i32.const 123) + ) + ) + (i32.store8 + (local.get $bi3) + (local.get $di3) + ) + (nop) + ) + (func $___remdi3 (; 10 ;) (param $$a$0 i32) (param $$a$1 i32) (param $$b$0 i32) (param $$b$1 i32) (result i32) + (local $$1$1 i32) + (local $$1$0 i32) + (local $$rem i32) + (local $__stackBase__ i32) + (local $$2$1 i32) + (local $$2$0 i32) + (local $$4$1 i32) + (local $$4$0 i32) + (local $$10$1 i32) + (local $$10$0 i32) + (local $$6$0 i32) + (local.set $__stackBase__ + (i32.load + (i32.const 8) + ) + ) + (i32.store + (i32.const 8) + (i32.add + (i32.load + (i32.const 8) + ) + (i32.const 16) + ) + ) + (local.set $$rem + (local.get $__stackBase__) + ) + (nop) + (nop) + (nop) + (nop) + (nop) + (nop) + (drop + (call $___udivmoddi4 + (call $_i64Subtract + (i32.xor + (local.tee $$1$0 + (i32.or + (i32.shr_s + (local.get $$a$1) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.get $$a$0) + ) + (i32.xor + (local.tee $$1$1 + (i32.or + (i32.shr_s + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.get $$a$1) + ) + (local.get $$1$0) + (local.get $$1$1) + ) + (i32.load + (i32.const 168) + ) + (call $_i64Subtract + (i32.xor + (local.tee $$2$0 + (i32.or + (i32.shr_s + (local.get $$b$1) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.get $$b$0) + ) + (i32.xor + (local.tee $$2$1 + (i32.or + (i32.shr_s + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.get $$b$1) + ) + (local.get $$2$0) + (local.get $$2$1) + ) + (i32.load + (i32.const 168) + ) + (local.get $$rem) + ) + ) + (local.set $$10$0 + (call $_i64Subtract + (i32.xor + (i32.load + (local.get $$rem) + ) + (local.get $$1$0) + ) + (i32.xor + (i32.load offset=4 + (local.get $$rem) + ) + (local.get $$1$1) + ) + (local.get $$1$0) + (local.get $$1$1) + ) + ) + (local.set $$10$1 + (i32.load + (i32.const 168) + ) + ) + (i32.store + (i32.const 8) + (local.get $__stackBase__) + ) + (return + (block $block12 (result i32) + (i32.store + (i32.const 168) + (local.get $$10$1) + ) + (local.get $$10$0) + ) + ) + ) + (func $block-returns (; 11 ;) + (local $x i32) + (local.set $x + (block $out (result i32) + (nop) + (drop + (br_if $out + (local.tee $x + (block $waka (result i32) + (nop) + (drop + (br_if $waka + (local.tee $x + (i32.const 12) + ) + (i32.const 1) + ) + ) + (nop) + (i32.const 34) + ) + ) + (i32.const 1) + ) + ) + (drop + (local.get $x) + ) + (block $waka2 + (local.set $x + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 13) + ) + (block (result i32) + (nop) + (i32.const 24) + ) + ) + ) + (nop) + ) + (drop + (br_if $out + (local.tee $x + (if (result i32) + (i32.const 1) + (block (result i32) + (block $block3 + (nop) + ) + (i32.const 14) + ) + (block (result i32) + (block $block5 + (nop) + ) + (i32.const 25) + ) + ) + ) + (i32.const 1) + ) + ) + (block $sink-out-of-me-i-have-but-one-exit + (nop) + ) + (nop) + (i32.const 99) + ) + ) + ) + (func $multiple (; 12 ;) (param $s i32) (param $r i32) (param $f i32) (param $p i32) (param $t i32) (param $m i32) + (nop) + (local.set $r + (i32.add + (local.get $f) + (local.get $p) + ) + ) + (local.set $t + (local.get $p) + ) + (local.set $p + (i32.load + (i32.const 0) + ) + ) + (i32.store + (local.get $r) + (local.get $t) + ) + (drop + (local.get $m) + ) + (drop + (local.get $t) + ) + ) + (func $switch-def (; 13 ;) (param $i3 i32) (result i32) + (local $i1 i32) + (local.set $i1 + (i32.const 10) + ) + (block $switch$def + (block $switch-case$1 + (br_table $switch-case$1 $switch$def + (local.get $i3) + ) + ) + (local.set $i1 + (i32.const 1) + ) + ) + (return + (local.get $i1) + ) + ) + (func $no-out-of-label (; 14 ;) (param $x i32) (param $y i32) + (nop) + (local.set $x + (loop $moar (result i32) + (nop) + (block $block (result i32) + (br_if $moar + (local.get $x) + ) + (i32.const 0) + ) + ) + ) + (block $moar18 + (local.set $y + (block $block19 (result i32) + (br_if $moar18 + (local.get $y) + ) + (i32.const 0) + ) + ) + ) + (drop + (local.get $y) + ) + ) + (func $freetype-cd (; 15 ;) (param $a i32) (result i32) + (local $e i32) + (nop) + (local.tee $a + (loop $while-in$1 (result i32) + (nop) + (block $while-out$0 (result i32) + (local.set $e + (local.get $a) + ) + (nop) + (drop + (br_if $while-out$0 + (local.tee $a + (i32.const 4) + ) + (local.get $e) + ) + ) + (nop) + (i32.add + (local.get $a) + (i32.const 0) + ) + ) + ) + ) + ) + (func $drop-if-value (; 16 ;) (param $x i32) (param $y i32) (param $z i32) (result i32) + (local $temp i32) + (drop + (if (result i32) + (local.get $x) + (block $block53 (result i32) + (nop) + (local.set $temp + (local.get $y) + ) + (local.get $z) + ) + (block $block54 (result i32) + (nop) + (local.set $temp + (local.get $y) + ) + (local.get $z) + ) + ) + ) + (drop + (local.get $temp) + ) + (return + (i32.const 0) + ) + ) + (func $drop-br_if (; 17 ;) (param $label i32) (param $$cond2 i32) (param $$$0151 i32) (result i32) + (nop) + (local.tee $label + (block $label$break$L4 (result i32) + (if + (i32.eq + (local.get $label) + (i32.const 15) + ) + (block $block + (nop) + (nop) + (drop + (br_if $label$break$L4 + (local.tee $label + (i32.const 0) + ) + (i32.eqz + (i32.eq + (local.get $$$0151) + (i32.const 0) + ) + ) + ) + ) + ) + ) + (nop) + (i32.const 1) + ) + ) + ) + (func $drop-tee-unreachable (; 18 ;) + (local $x i32) + (local.tee $x + (unreachable) + ) + (drop + (local.get $x) + ) + ) + (func $if-return-but-unreachable (; 19 ;) (param $var$0 i64) + (if + (unreachable) + (drop + (local.get $var$0) + ) + (local.set $var$0 + (i64.const 1) + ) + ) + ) + (func $if-one-side (; 20 ;) (result i32) + (local $x i32) + (nop) + (local.tee $x + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 2) + ) + (local.get $x) + ) + ) + ) + (func $if-one-side-undo (; 21 ;) (result i32) + (local $x i32) + (local $y i32) + (local.set $y + (i32.const 0) + ) + (local.set $x + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 2) + ) + (local.get $x) + ) + ) + (local.get $y) + ) + (func $if-one-side-multi (; 22 ;) (param $0 i32) (result i32) + (nop) + (local.tee $0 + (if (result i32) + (i32.lt_s + (local.get $0) + (i32.const -1073741824) + ) + (block (result i32) + (nop) + (i32.const -1073741824) + ) + (block (result i32) + (nop) + (if (result i32) + (i32.gt_s + (local.get $0) + (i32.const 1073741823) + ) + (block (result i32) + (nop) + (i32.const 1073741823) + ) + (local.get $0) + ) + ) + ) + ) + ) + (func $if-one-side-undo-but-its-a-tee (; 23 ;) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $x i32) + (local $y i32) + (local $z i32) + (local.set $x + (if (result i32) + (i32.const -1) + (i32.const -2) + (local.get $x) + ) + ) + (drop + (call $if-one-side-undo-but-its-a-tee + (local.tee $x + (if (result i32) + (i32.const -3) + (i32.const -4) + (local.get $x) + ) + ) + ) + ) + (nop) + (drop + (i32.eqz + (local.tee $y + (if (result i32) + (i32.const -5) + (i32.const -6) + (local.get $y) + ) + ) + ) + ) + (nop) + (drop + (i32.add + (local.tee $z + (if (result i32) + (i32.const -7) + (i32.const -8) + (local.get $z) + ) + ) + (local.get $z) + ) + ) + (if + (block $label$1 (result i32) + (nop) + (nop) + (local.tee $4 + (if (result i32) + (local.tee $4 + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 2) + ) + (local.get $4) + ) + ) + (block (result i32) + (nop) + (i32.const 0) + ) + (local.get $4) + ) + ) + ) + (unreachable) + ) + (i32.const 0) + ) + (func $splittable-ifs-multicycle (; 24 ;) (param $20 i32) (result i32) + (nop) + (local.tee $20 + (if (result i32) + (i32.const 1) + (if (result i32) + (i32.const 2) + (if (result i32) + (i32.const 3) + (i32.const 4) + (local.get $20) + ) + (local.get $20) + ) + (local.get $20) + ) + ) + ) + (func $update-getCounter (; 25 ;) (param $0 i32) (param $1 f64) (param $2 f64) (param $3 f32) (param $4 i32) (result f64) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (loop $label$1 (result f64) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (call $fimport$0 + (local.tee $3 + (if (result f32) + (i32.eqz + (local.get $0) + ) + (f32.const 4623408228068004207103214e13) + (local.get $3) + ) + ) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (if (result f64) + (global.get $global$0) + (block $block + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (local.set $0 + (i32.const -65) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (br $label$1) + ) + (f64.const -70) + ) + ) + ) +) +(module + (type $none_=>_none (func)) + (type $none_=>_i32 (func (result i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32 i32) (result i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_=>_none (func (param i32 i32))) + (type $f32_=>_none (func (param f32))) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $none_=>_f64 (func (result f64))) + (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) + (import "fuzzing-support" "log1" (func $fimport$0 (result i32))) + (import "fuzzing-support" "log2" (func $fimport$1 (param i32))) + (import "fuzzing-support" "log3" (func $fimport$2 (param f32))) + (memory $0 (shared 256 256)) + (global $global$0 (mut i32) (i32.const 10)) + (func $nonatomics (; 3 ;) (result i32) + (local $x i32) + (nop) + (drop + (i32.load + (i32.const 1028) + ) + ) + (i32.load + (i32.const 1024) + ) + ) + (func $nonatomic-growmem (; 4 ;) (result i32) + (local $x i32) + (local.set $x + (i32.load + (memory.grow + (i32.const 1) + ) + ) + ) + (drop + (i32.load + (i32.const 1028) + ) + ) + (local.get $x) + ) + (func $atomics (; 5 ;) + (local $x i32) + (local.set $x + (i32.atomic.load + (i32.const 1024) + ) + ) + (drop + (i32.atomic.load + (i32.const 1028) + ) + ) + (drop + (local.get $x) + ) + ) + (func $one-atomic (; 6 ;) + (local $x i32) + (local.set $x + (i32.load + (i32.const 1024) + ) + ) + (drop + (i32.atomic.load + (i32.const 1028) + ) + ) + (drop + (local.get $x) + ) + ) + (func $other-atomic (; 7 ;) + (local $x i32) + (local.set $x + (i32.atomic.load + (i32.const 1024) + ) + ) + (drop + (i32.load + (i32.const 1028) + ) + ) + (drop + (local.get $x) + ) + ) + (func $atomic-growmem (; 8 ;) (result i32) + (local $x i32) + (local.set $x + (i32.load + (memory.grow + (i32.const 1) + ) + ) + ) + (drop + (i32.atomic.load + (i32.const 1028) + ) + ) + (local.get $x) + ) + (func $atomicrmw (; 9 ;) + (local $x i32) + (local.set $x + (i32.atomic.rmw.add + (i32.const 1024) + (i32.const 1) + ) + ) + (drop + (i32.atomic.load + (i32.const 1028) + ) + ) + (drop + (local.get $x) + ) + ) + (func $atomic-cmpxchg (; 10 ;) + (local $x i32) + (local.set $x + (i32.atomic.rmw.cmpxchg + (i32.const 1024) + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (i32.atomic.load + (i32.const 1028) + ) + ) + (drop + (local.get $x) + ) + ) + (func $br-value-reordering (; 11 ;) (result i32) + (local $temp i32) + (block $outside + (loop $loop + (br_if $outside + (block $block (result i32) + (br_if $loop + (local.get $temp) + ) + (unreachable) + (local.set $temp + (i32.const -1) + ) + (i32.const 0) + ) + ) + ) + (local.set $temp + (i32.const -1) + ) + ) + (unreachable) + ) + (func $br-value-reordering-safe (; 12 ;) (result i32) + (local $temp i32) + (local.set $temp + (block $outside (result i32) + (loop $loop + (drop + (local.get $temp) + ) + (drop + (br_if $outside + (local.tee $temp + (i32.const -1) + ) + (block $block (result i32) + (nop) + (i32.const 0) + ) + ) + ) + ) + (nop) + (i32.const -1) + ) + ) + (unreachable) + ) + (func $if-one-side-unreachable (; 13 ;) + (local $x i32) + (block $out + (drop + (if (result i32) + (i32.const 1) + (block + (br $out) + (nop) + ) + (block (result i32) + (nop) + (i32.const 2) + ) + ) + ) + (drop + (if (result i32) + (i32.const 3) + (block (result i32) + (nop) + (i32.const 4) + ) + (block + (br $out) + (nop) + ) + ) + ) + (if + (i32.const 5) + (br $out) + (br $out) + ) + ) + ) + (func $if-one-side-unreachable-blocks (; 14 ;) + (local $x i32) + (local $y i32) + (block $out + (drop + (if (result i32) + (i32.const 1) + (block + (block $block + (nop) + (nop) + (br $out) + ) + (nop) + ) + (block (result i32) + (block $block2 + (nop) + (nop) + ) + (i32.const 4) + ) + ) + ) + (drop + (if (result i32) + (i32.const 6) + (block (result i32) + (block $block4 + (nop) + (nop) + ) + (i32.const 7) + ) + (block + (block $block5 + (nop) + (nop) + (br $out) + ) + (nop) + ) + ) + ) + (if + (i32.const 11) + (block $block7 + (nop) + (nop) + (br $out) + ) + (block $block8 + (nop) + (nop) + (br $out) + ) + ) + ) + ) + (func $loop-value (; 15 ;) (param $x i32) (result i32) + (loop $loopy + (unreachable) + ) + (nop) + (loop $loopy9 (result i32) + (nop) + (i32.const 1) + ) + ) + (func $loop-loop-loopy-value (; 16 ;) (param $x i32) (result i32) + (nop) + (loop $loopy1 (result i32) + (nop) + (loop $loopy2 (result i32) + (nop) + (loop $loopy3 (result i32) + (nop) + (i32.const 1) + ) + ) + ) + ) + (func $loop-modified-during-main-pass-be-careful-fuzz (; 17 ;) (result i32) + (local $0 i32) + (nop) + (if (result i32) + (i32.const 0) + (block (result i32) + (nop) + (i32.const 0) + ) + (block + (loop $label$4 + (br $label$4) + ) + (nop) + ) + ) + ) + (func $loop-later (; 18 ;) (param $var$0 i32) (param $var$1 i32) (param $var$2 i32) (param $var$3 i32) (param $var$4 i32) (result i32) + (nop) + (i32.const 0) + ) + (func $pick (; 19 ;) + (local $x i32) + (local $y i32) + (local.set $x + (local.get $y) + ) + (drop + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 1) + ) + (local.get $x) + ) + ) + (drop + (local.get $y) + ) + (local.set $x + (local.get $y) + ) + ) + (func $pick-2 (; 20 ;) + (local $x i32) + (local $y i32) + (local.set $y + (local.get $x) + ) + (drop + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 1) + ) + (local.get $y) + ) + ) + (drop + (local.get $x) + ) + (local.set $y + (local.get $x) + ) + ) + (func $many (; 21 ;) + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (nop) + (nop) + (local.set $z + (local.tee $y + (local.get $x) + ) + ) + (drop + (local.get $x) + ) + (local.set $y + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 1) + ) + (local.get $y) + ) + ) + (local.set $x + (local.get $z) + ) + (drop + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 1) + ) + (local.get $y) + ) + ) + (nop) + (nop) + (local.set $y + (local.get $x) + ) + (nop) + (local.set $x + (local.tee $z + (i32.const 2) + ) + ) + (drop + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 1) + ) + (local.get $y) + ) + ) + (nop) + (nop) + (local.set $y + (local.get $x) + ) + (local.set $z + (i32.const 2) + ) + (drop + (local.get $x) + ) + ) + (func $loop-copies (; 22 ;) (param $x i32) (param $y i32) + (loop $loop + (nop) + (drop + (local.get $y) + ) + (br_if $loop + (local.get $y) + ) + ) + ) + (func $proper-type (; 23 ;) (result f64) + (local $var$0 i32) + (local $var$2 f64) + (local.set $var$0 + (select + (i32.const 0) + (i32.const 1) + (local.get $var$0) + ) + ) + (local.get $var$2) + ) + (func $multi-pass-get-equivs-right (; 24 ;) (param $var$0 i32) (param $var$1 i32) (result f64) + (local $var$2 i32) + (nop) + (i32.store + (local.get $var$0) + (i32.const 1) + ) + (f64.promote_f32 + (f32.load + (local.get $var$0) + ) + ) + ) + (func $if-value-structure-equivalent (; 25 ;) (param $x i32) (result i32) + (local $y i32) + (nop) + (local.tee $x + (if (result i32) + (i32.const 1) + (block (result i32) + (nop) + (i32.const 2) + ) + (block (result i32) + (block $block + (nop) + (nop) + ) + (local.get $x) + ) + ) + ) + ) + (func $set-tee-need-one-of-them (; 26 ;) (param $var$0 i32) (param $var$1 i32) (result i32) + (local $var$2 i32) + (local $var$3 i32) + (local.set $var$2 + (local.get $var$0) + ) + (loop $loop + (br_if $loop + (local.get $var$1) + ) + ) + (local.get $var$2) + ) + (func $loop-value-harder (; 27 ;) (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 f32) + (local $4 f32) + (local $5 f32) + (local $6 f32) + (local $7 f32) + (local $8 f32) + (local $9 f32) + (local $10 f32) + (block $label$1 + (nop) + (nop) + (call $fimport$2 + (loop $label$2 (result f32) + (block $label$3 + (global.set $global$0 + (i32.const -1) + ) + (block $label$4 + (nop) + (nop) + ) + (nop) + (nop) + ) + (nop) + (nop) + (if (result f32) + (call $fimport$0) + (block (result f32) + (nop) + (f32.const -2048) + ) + (block + (block $block + (call $fimport$1 + (i32.const -25732) + ) + (br $label$2) + ) + (nop) + ) + ) + ) + ) + (nop) + ) + (nop) + (return + (i32.const -5417091) + ) + ) + (func $tee-chain (; 28 ;) (param $x i32) (param $z i32) (param $t1 i32) (param $t2 i32) (param $t3 i32) (result i32) + (nop) + (drop + (i32.const 10) + ) + (nop) + (local.set $t2 + (local.tee $t3 + (local.tee $t1 + (call $tee-chain + (local.get $x) + (local.tee $z + (i32.const 10) + ) + (local.get $t1) + (local.get $t2) + (local.get $t3) + ) + ) + ) + ) + (call $tee-chain + (local.get $x) + (local.get $z) + (local.get $t1) + (local.get $t2) + (local.get $t3) + ) + ) +) +(module + (type $none_=>_none (func)) + (memory $0 256 256) + (data passive "hello, there!") + (func $memory-init-load (; 0 ;) + (local $x i32) + (local.set $x + (i32.load + (i32.const 0) + ) + ) + (memory.init 0 + (i32.const 0) + (i32.const 0) + (i32.const 5) + ) + (drop + (local.get $x) + ) + ) + (func $memory-init-store (; 1 ;) + (local $x i32) + (local.set $x + (block $block (result i32) + (i32.store + (i32.const 0) + (i32.const 42) + ) + (i32.const 0) + ) + ) + (memory.init 0 + (i32.const 0) + (i32.const 0) + (i32.const 5) + ) + (drop + (local.get $x) + ) + ) + (func $memory-copy-load (; 2 ;) + (local $x i32) + (local.set $x + (i32.load + (i32.const 0) + ) + ) + (memory.copy + (i32.const 0) + (i32.const 8) + (i32.const 8) + ) + (drop + (local.get $x) + ) + ) + (func $memory-copy-store (; 3 ;) + (local $x i32) + (local.set $x + (block $block (result i32) + (i32.store + (i32.const 0) + (i32.const 42) + ) + (i32.const 0) + ) + ) + (memory.copy + (i32.const 0) + (i32.const 8) + (i32.const 8) + ) + (drop + (local.get $x) + ) + ) + (func $memory-fill-load (; 4 ;) + (local $x i32) + (local.set $x + (i32.load + (i32.const 0) + ) + ) + (memory.fill + (i32.const 0) + (i32.const 42) + (i32.const 8) + ) + (drop + (local.get $x) + ) + ) + (func $memory-fill-store (; 5 ;) + (local $x i32) + (local.set $x + (block $block (result i32) + (i32.store + (i32.const 0) + (i32.const 42) + ) + (i32.const 0) + ) + ) + (memory.fill + (i32.const 0) + (i32.const 8) + (i32.const 8) + ) + (drop + (local.get $x) + ) + ) + (func $data-drop-load (; 6 ;) + (local $x i32) + (nop) + (data.drop 0) + (drop + (i32.load + (i32.const 0) + ) + ) + ) + (func $data-drop-store (; 7 ;) + (local $x i32) + (local.set $x + (block $block (result i32) + (i32.store + (i32.const 0) + (i32.const 42) + ) + (i32.const 0) + ) + ) + (data.drop 0) + (drop + (local.get $x) + ) + ) + (func $data-drop-memory-init (; 8 ;) + (local $x i32) + (local.set $x + (block $block (result i32) + (memory.init 0 + (i32.const 0) + (i32.const 0) + (i32.const 5) + ) + (i32.const 0) + ) + ) + (data.drop 0) + (drop + (local.get $x) + ) + ) +) +(module + (type $none_=>_funcref (func (result funcref))) + (func $subtype-test (; 0 ;) (result funcref) + (local $0 nullref) + (local $1 funcref) + (local $2 funcref) + (block $block + (nop) + ) + (nop) + (local.get $0) + ) +) diff --git a/test/passes/simplify-locals_all-features_disable-exception-handling.wast b/test/passes/simplify-locals_all-features_disable-exception-handling.wast new file mode 100644 index 000000000..a819dda15 --- /dev/null +++ b/test/passes/simplify-locals_all-features_disable-exception-handling.wast @@ -0,0 +1,1673 @@ +(module + (memory 256 256) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$i (func (result i32))) + (type $FUNCSIG$iiiii (func (param i32 i32 i32 i32) (result i32))) + (type $FUNCSIG$iiiiii (func (param i32 i32 i32 i32 i32) (result i32))) + (type $4 (func (param i32))) + (type $5 (func (param i32) (result i32))) + (type $6 (func (param i32 i32 i32 i32 i32 i32))) + (import $waka "env" "waka") + (import $waka_int "env" "waka_int" (result i32)) + (import $_i64Subtract "env" "i64sub" (param i32 i32 i32 i32) (result i32)) + (import $___udivmoddi4 "env" "moddi" (param i32 i32 i32 i32 i32) (result i32)) + (import $lp "env" "lp" (param i32 i32) (result i32)) + (import "fuzzing-support" "log-f32" (func $fimport$0 (param f32))) + (global $global$0 (mut i32) (i32.const 10)) + (func $contrast ;; check for tee and structure sinking + (local $x i32) + (local $y i32) + (local $z i32) + (local $a i32) + (local $b i32) + (local.set $x (i32.const 1)) + (if (local.get $x) (nop)) + (if (local.get $x) (nop)) + (local.set $y (if (result i32) (i32.const 2) (i32.const 3) (i32.const 4))) + (drop (local.get $y)) + (local.set $z (block (result i32) (i32.const 5))) + (drop (local.get $z)) + (if (i32.const 6) + (local.set $a (i32.const 7)) + (local.set $a (i32.const 8)) + ) + (drop (local.get $a)) + (block $val + (if (i32.const 10) + (block + (local.set $b (i32.const 11)) + (br $val) + ) + ) + (local.set $b (i32.const 12)) + ) + (drop (local.get $b)) + ) + (func $b0-yes (type $4) (param $i1 i32) + (local $x i32) + (local $y i32) + (local $a i32) + (local $b i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local.set $x + (i32.const 5) + ) + (drop + (local.get $x) + ) + (block $block0 + (local.set $x + (i32.const 7) + ) + (drop + (local.get $x) + ) + ) + (local.set $x + (i32.const 11) + ) + (drop + (local.get $x) + ) + (local.set $x + (i32.const 9) + ) + (drop + (local.get $y) + ) + (block $block1 + (local.set $x + (i32.const 8) + ) + (drop + (local.get $y) + ) + ) + (local.set $x + (i32.const 11) + ) + (drop + (local.get $y) + ) + (local.set $x + (i32.const 17) + ) + (drop + (local.get $x) + ) + (drop + (local.get $x) + ) + (drop + (local.get $x) + ) + (drop + (local.get $x) + ) + (drop + (local.get $x) + ) + (drop + (local.get $x) + ) + (block $block2 + (local.set $a + (i32.const 1) + ) + (local.set $b + (i32.const 2) + ) + (drop + (local.get $a) + ) + (drop + (local.get $b) + ) + (local.set $a + (i32.const 3) + ) + (local.set $b + (i32.const 4) + ) + (local.set $a + (i32.const 5) + ) + (local.set $b + (i32.const 6) + ) + (drop + (local.get $b) + ) + (drop + (local.get $a) + ) + (local.set $a + (i32.const 7) + ) + (local.set $b + (i32.const 8) + ) + (local.set $a + (i32.const 9) + ) + (local.set $b + (i32.const 10) + ) + (call $waka) + (drop + (local.get $a) + ) + (drop + (local.get $b) + ) + (local.set $a + (i32.const 11) + ) + (local.set $b + (i32.const 12) + ) + (local.set $a + (i32.const 13) + ) + (local.set $b + (i32.const 14) + ) + (drop + (i32.load + (i32.const 24) + ) + ) + (drop + (local.get $a) + ) + (drop + (local.get $b) + ) + (local.set $a + (i32.const 15) + ) + (local.set $b + (i32.const 16) + ) + (local.set $a + (i32.const 17) + ) + (local.set $b + (i32.const 18) + ) + (i32.store + (i32.const 48) + (i32.const 96) + ) + (drop + (local.get $a) + ) + (drop + (local.get $b) + ) + ) + (block $block3 + (local.set $a + (call $waka_int) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (call $waka_int) + ) + (call $waka) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (call $waka_int) + ) + (drop + (i32.load + (i32.const 1) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (call $waka_int) + ) + (i32.store + (i32.const 1) + (i32.const 2) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (i32.load + (i32.const 100) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (i32.load + (i32.const 101) + ) + ) + (drop + (i32.load + (i32.const 1) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (i32.load + (i32.const 102) + ) + ) + (call $waka) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (i32.load + (i32.const 103) + ) + ) + (i32.store + (i32.const 1) + (i32.const 2) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (block (result i32) + (block + (local.set $5 + (i32.const 105) + ) + (i32.store + (i32.const 104) + (local.get $5) + ) + ) + (local.get $5) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (block (result i32) + (block + (local.set $6 + (i32.const 107) + ) + (i32.store + (i32.const 106) + (local.get $6) + ) + ) + (local.get $6) + ) + ) + (call $waka) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (block (result i32) + (block + (local.set $7 + (i32.const 109) + ) + (i32.store + (i32.const 108) + (local.get $7) + ) + ) + (local.get $7) + ) + ) + (drop + (i32.load + (i32.const 1) + ) + ) + (drop + (local.get $a) + ) + (call $waka) + (local.set $a + (block (result i32) + (block + (local.set $8 + (i32.const 111) + ) + (i32.store + (i32.const 110) + (local.get $8) + ) + ) + (local.get $8) + ) + ) + (i32.store + (i32.const 1) + (i32.const 2) + ) + (drop + (local.get $a) + ) + (call $waka) + ) + (block $out-of-block + (local.set $a + (i32.const 1337) + ) + (block $b + (block $c + (br $b) + ) + (local.set $a + (i32.const 9876) + ) + ) + (drop + (local.get $a) + ) + ) + (block $loopey + (local.set $a + (i32.const 1337) + ) + (drop + (loop $loop-in5 (result i32) + (drop + (local.get $a) + ) + (local.tee $a + (i32.const 9876) + ) + ) + ) + (drop + (local.get $a) + ) + ) + ) + (func $Ia (type $5) (param $a i32) (result i32) + (local $b i32) + (block $switch$0 + (block $switch-default$6 + (local.set $b + (i32.const 60) + ) + ) + ) + (return + (local.get $b) + ) + ) + (func $memories (type $6) (param $i2 i32) (param $i3 i32) (param $bi2 i32) (param $bi3 i32) (param $ci3 i32) (param $di3 i32) + (local $set_with_no_get i32) + (local.set $i3 + (i32.const 1) + ) + (i32.store8 + (local.get $i2) + (local.get $i3) + ) + (local.set $bi3 + (i32.const 1) + ) + (i32.store8 + (local.get $bi3) + (local.get $bi3) + ) + (local.set $ci3 + (local.get $bi3) + ) + (i32.store8 + (local.get $bi3) + (local.get $ci3) + ) + (local.set $di3 + (local.tee $bi3 + (i32.const 123) + ) + ) + (i32.store8 + (local.get $bi3) + (local.get $di3) + ) + (local.set $set_with_no_get + (i32.const 456) + ) + ) + (func $___remdi3 (type $FUNCSIG$iiiii) (param $$a$0 i32) (param $$a$1 i32) (param $$b$0 i32) (param $$b$1 i32) (result i32) + (local $$1$1 i32) + (local $$1$0 i32) + (local $$rem i32) + (local $__stackBase__ i32) + (local $$2$1 i32) + (local $$2$0 i32) + (local $$4$1 i32) + (local $$4$0 i32) + (local $$10$1 i32) + (local $$10$0 i32) + (local $$6$0 i32) + (local.set $__stackBase__ + (i32.load + (i32.const 8) + ) + ) + (i32.store + (i32.const 8) + (i32.add + (i32.load + (i32.const 8) + ) + (i32.const 16) + ) + ) + (local.set $$rem + (local.get $__stackBase__) + ) + (local.set $$1$0 + (i32.or + (i32.shr_s + (local.get $$a$1) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$1$1 + (i32.or + (i32.shr_s + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$a$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$2$0 + (i32.or + (i32.shr_s + (local.get $$b$1) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$2$1 + (i32.or + (i32.shr_s + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 31) + ) + (i32.shl + (if (result i32) + (i32.lt_s + (local.get $$b$1) + (i32.const 0) + ) + (i32.const -1) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + (local.set $$4$0 + (call $_i64Subtract + (i32.xor + (local.get $$1$0) + (local.get $$a$0) + ) + (i32.xor + (local.get $$1$1) + (local.get $$a$1) + ) + (local.get $$1$0) + (local.get $$1$1) + ) + ) + (local.set $$4$1 + (i32.load + (i32.const 168) + ) + ) + (drop + (call $___udivmoddi4 + (local.get $$4$0) + (local.get $$4$1) + (call $_i64Subtract + (i32.xor + (local.get $$2$0) + (local.get $$b$0) + ) + (i32.xor + (local.get $$2$1) + (local.get $$b$1) + ) + (local.get $$2$0) + (local.get $$2$1) + ) + (i32.load + (i32.const 168) + ) + (local.get $$rem) + ) + ) + (local.set $$10$0 + (call $_i64Subtract + (i32.xor + (i32.load + (local.get $$rem) + ) + (local.get $$1$0) + ) + (i32.xor + (i32.load offset=4 + (local.get $$rem) + ) + (local.get $$1$1) + ) + (local.get $$1$0) + (local.get $$1$1) + ) + ) + (local.set $$10$1 + (i32.load + (i32.const 168) + ) + ) + (i32.store + (i32.const 8) + (local.get $__stackBase__) + ) + (return + (block $block12 (result i32) + (i32.store + (i32.const 168) + (local.get $$10$1) + ) + (local.get $$10$0) + ) + ) + ) + (func $block-returns (type $FUNCSIG$v) + (local $x i32) + (block $out + (block $waka + (local.set $x + (i32.const 12) + ) + (br_if $waka + (i32.const 1) + ) + (local.set $x + (i32.const 34) + ) + ) + (br_if $out + (i32.const 1) + ) + (drop + (local.get $x) + ) + (block $waka2 + (if + (i32.const 1) + (local.set $x + (i32.const 13) + ) + (local.set $x + (i32.const 24) + ) + ) + (if + (i32.const 1) + (block $block3 + (local.set $x + (i32.const 14) + ) + ) + (block $block5 + (local.set $x + (i32.const 25) + ) + ) + ) + ) + (br_if $out + (i32.const 1) + ) + (block $sink-out-of-me-i-have-but-one-exit + (local.set $x + (i32.const 99) + ) + ) + (drop + (local.get $x) + ) + ) + ) + (func $multiple (type $6) (param $s i32) (param $r i32) (param $f i32) (param $p i32) (param $t i32) (param $m i32) + (local.set $s + (local.get $m) + ) + (local.set $r + (i32.add + (local.get $f) + (local.get $p) + ) + ) + (local.set $t + (local.get $p) + ) + (local.set $p + (i32.load + (i32.const 0) + ) + ) + (i32.store + (local.get $r) + (local.get $t) + ) + (drop + (local.get $s) + ) + (drop + (local.get $t) + ) + ) + (func $switch-def (type $5) (param $i3 i32) (result i32) + (local $i1 i32) + (local.set $i1 + (i32.const 10) + ) + (block $switch$def + (block $switch-case$1 + (br_table $switch-case$1 $switch$def + (local.get $i3) + ) + ) + (local.set $i1 + (i32.const 1) + ) + ) + (return + (local.get $i1) + ) + ) + (func $no-out-of-label (param $x i32) (param $y i32) + (loop $moar + (local.set $x + (block (result i32) + (br_if $moar (local.get $x)) + (i32.const 0) + ) + ) + ) + (drop (local.get $x)) + (block $moar + (local.set $y + (block (result i32) + (br_if $moar (local.get $y)) + (i32.const 0) + ) + ) + ) + (drop (local.get $y)) + ) + (func $freetype-cd (param $a i32) (result i32) + (local $e i32) + (loop $while-in$1 + (block $while-out$0 + (local.set $e + (local.get $a) + ) + (local.set $a ;; this set must happen, so that if the br_if does not break, we have the right $a later down - once we use a block return value, the $a set's outside the block + (i32.const 4) + ) + (br_if $while-out$0 + (local.get $e) + ) + (local.set $a + (i32.add + (local.get $a) + (i32.const 0) + ) + ) + ) + ) + (local.get $a) + ) + (func $drop-if-value (param $x i32) (param $y i32) (param $z i32) (result i32) + (local $temp i32) + (drop + (if (result i32) + (local.get $x) + (block $block53 (result i32) + (nop) + (local.set $temp + (local.get $y) + ) + (local.get $z) + ) + (block $block54 (result i32) + (nop) + (local.set $temp + (local.get $y) + ) + (local.get $z) + ) + ) + ) + (drop (local.get $temp)) + (return + (i32.const 0) + ) + ) + (func $drop-br_if (param $label i32) (param $$cond2 i32) (param $$$0151 i32) (result i32) + (block $label$break$L4 + (if + (i32.eq + (local.get $label) + (i32.const 15) + ) + (block $block + (local.set $label + (i32.const 0) + ) + (local.set $$cond2 + (i32.eq + (local.get $$$0151) + (i32.const 0) + ) + ) + (br_if $label$break$L4 ;; when we add a value to this, its type changes as it returns the value too, so must be dropped + (i32.eqz + (local.get $$cond2) + ) + ) + ) + ) + (local.set $label + (i32.const 1) + ) + ) + (local.get $label) + ) + (func $drop-tee-unreachable + (local $x i32) + (drop + (local.tee $x + (unreachable) + ) + ) + (drop + (local.get $x) + ) + ) + (func $if-return-but-unreachable (param $var$0 i64) + (if + (unreachable) + (local.set $var$0 + (local.get $var$0) + ) + (local.set $var$0 + (i64.const 1) + ) + ) + ) + (func $if-one-side (result i32) + (local $x i32) + (if + (i32.const 1) + (local.set $x + (i32.const 2) + ) + ) + (local.get $x) + ) + (func $if-one-side-undo (result i32) + (local $x i32) + (local $y i32) + (local.set $y + (i32.const 0) + ) + (if + (i32.const 1) + (local.set $x + (i32.const 2) + ) + ) + (local.get $y) + ) + (func $if-one-side-multi (param $0 i32) (result i32) + (if + (i32.lt_s + (local.get $0) + (i32.const -1073741824) + ) + (local.set $0 + (i32.const -1073741824) + ) + (if + (i32.gt_s + (local.get $0) + (i32.const 1073741823) + ) + (local.set $0 + (i32.const 1073741823) + ) + ) + ) + (local.get $0) + ) + (func $if-one-side-undo-but-its-a-tee (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $x i32) + (local $y i32) + (local $z i32) + ;; break these splittable ifs up + (local.set $x + (if (result i32) + (i32.const -1) + (i32.const -2) + (local.get $x) + ) + ) + ;; oops, this one is a tee + (drop + (call $if-one-side-undo-but-its-a-tee + (local.tee $x + (if (result i32) + (i32.const -3) + (i32.const -4) + (local.get $x) + ) + ) + ) + ) + ;; sinkable + (local.set $y + (if (result i32) + (i32.const -5) + (i32.const -6) + (local.get $y) + ) + ) + (drop (i32.eqz (local.get $y))) + ;; tee-able at best + (local.set $z + (if (result i32) + (i32.const -7) + (i32.const -8) + (local.get $z) + ) + ) + (drop + (i32.add + (local.get $z) + (local.get $z) + ) + ) + (if + (block $label$1 (result i32) + (if + (i32.const 1) + (local.set $4 + (i32.const 2) + ) + ) + (if + (local.get $4) + (local.set $4 + (i32.const 0) + ) + ) + (local.get $4) + ) + (unreachable) + ) + (i32.const 0) + ) + (func $splittable-ifs-multicycle (param $20 i32) (result i32) + (local.set $20 + (if (result i32) + (i32.const 1) + (if (result i32) + (i32.const 2) + (if (result i32) + (i32.const 3) + (i32.const 4) + (local.get $20) + ) + (local.get $20) + ) + (local.get $20) + ) + ) + (local.get $20) + ) + (func $update-getCounter (param $0 i32) (param $1 f64) (param $2 f64) (param $3 f32) (param $4 i32) (result f64) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (loop $label$1 (result f64) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (call $fimport$0 + (local.tee $3 + (if (result f32) + (i32.eqz + (local.get $0) + ) + (f32.const 4623408228068004207103214e13) + (local.get $3) + ) + ) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (if (result f64) + (global.get $global$0) + (block + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (local.set $0 + (i32.const -65) + ) + (global.set $global$0 + (i32.sub + (global.get $global$0) + (i32.const 1) + ) + ) + (br $label$1) + ) + (f64.const -70) + ) + ) + ) +) +(module + (memory (shared 256 256)) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$i (func (result i32))) + (type $FUNCSIG$iiiii (func (param i32 i32 i32 i32) (result i32))) + (type $FUNCSIG$iiiiii (func (param i32 i32 i32 i32 i32) (result i32))) + (type $4 (func (param i32))) + (type $5 (func (param i32) (result i32))) + (type $6 (func (param i32 i32 i32 i32 i32 i32))) + (import "fuzzing-support" "log1" (func $fimport$0 (result i32))) + (import "fuzzing-support" "log2" (func $fimport$1 (param i32))) + (import "fuzzing-support" "log3" (func $fimport$2 (param f32))) + (global $global$0 (mut i32) (i32.const 10)) + (func $nonatomics (result i32) ;; loads are reordered + (local $x i32) + (local.set $x (i32.load (i32.const 1024))) + (drop (i32.load (i32.const 1028))) + (local.get $x) + ) + (func $nonatomic-growmem (result i32) ;; memory.grow is modeled as modifying memory + (local $x i32) + (local.set $x (i32.load (memory.grow (i32.const 1)))) + (drop (i32.load (i32.const 1028))) + (local.get $x) + ) + (func $atomics ;; atomic loads don't pass each other + (local $x i32) + (local.set $x (i32.atomic.load (i32.const 1024))) + (drop (i32.atomic.load (i32.const 1028))) + (drop (local.get $x)) + ) + (func $one-atomic ;; atomic loads don't pass other loads + (local $x i32) + (local.set $x (i32.load (i32.const 1024))) + (drop (i32.atomic.load (i32.const 1028))) + (drop (local.get $x)) + ) + (func $other-atomic ;; atomic loads don't pass other loads + (local $x i32) + (local.set $x (i32.atomic.load (i32.const 1024))) + (drop (i32.load (i32.const 1028))) + (drop (local.get $x)) + ) + (func $atomic-growmem (result i32) ;; memory.grow is modeled as modifying memory + (local $x i32) + (local.set $x (i32.load (memory.grow (i32.const 1)))) + (drop (i32.atomic.load (i32.const 1028))) + (local.get $x) + ) + (func $atomicrmw ;; atomic rmw don't pass loads + (local $x i32) + (local.set $x (i32.atomic.rmw.add (i32.const 1024) (i32.const 1))) + (drop (i32.atomic.load (i32.const 1028))) + (drop (local.get $x)) + ) + (func $atomic-cmpxchg ;; cmpxchg don't pass loads + (local $x i32) + (local.set $x (i32.atomic.rmw.cmpxchg (i32.const 1024) (i32.const 1) (i32.const 2))) + (drop (i32.atomic.load (i32.const 1028))) + (drop (local.get $x)) + ) + (func $br-value-reordering (result i32) + (local $temp i32) + (block $outside + (loop $loop ;; we should exit this loop, hit the unreachable outside + ;; loop logic + (br_if $outside ;; we should not create a block value that adds a value to a br, if the value&condition of the br cannot be reordered, + ;; as the value comes first + (block (result i32) + (br_if $loop + (local.get $temp) ;; false, don't loop + ) + (unreachable) ;; the end + (local.set $temp + (i32.const -1) + ) + (i32.const 0) + ) + ) + ) + (local.set $temp + (i32.const -1) + ) + ) + (unreachable) + ) + (func $br-value-reordering-safe (result i32) + (local $temp i32) + (block $outside + (loop $loop ;; we should exit this loop, hit the unreachable outside + ;; loop logic + (drop (local.get $temp)) ;; different from above - add a use here + (br_if $outside ;; we should not create a block value that adds a value to a br, if the value&condition of the br cannot be reordered, + ;; as the value comes first + (block (result i32) + (local.set $temp ;; the use *is* in the condition, but it's ok, no conflicts + (i32.const -1) + ) + (i32.const 0) + ) + ) + ) + (local.set $temp + (i32.const -1) + ) + ) + (unreachable) + ) + (func $if-one-side-unreachable + (local $x i32) + (block $out + (if + (i32.const 1) + (br $out) + (local.set $x + (i32.const 2) + ) + ) + (if + (i32.const 3) + (local.set $x + (i32.const 4) + ) + (br $out) + ) + (if + (i32.const 5) + (br $out) + (br $out) + ) + ) + ) + (func $if-one-side-unreachable-blocks + (local $x i32) + (local $y i32) + (block $out + (if + (i32.const 1) + (block + (local.set $x + (i32.const 2) + ) + (local.set $y + (i32.const 3) + ) + (br $out) + ) + (block + (local.set $x + (i32.const 4) + ) + (local.set $y + (i32.const 5) + ) + ) + ) + (if + (i32.const 6) + (block + (local.set $x + (i32.const 7) + ) + (local.set $y + (i32.const 8) + ) + ) + (block + (local.set $x + (i32.const 9) + ) + (local.set $y + (i32.const 10) + ) + (br $out) + ) + ) + (if + (i32.const 11) + (block + (local.set $x + (i32.const 12) + ) + (local.set $y + (i32.const 13) + ) + (br $out) + ) + (block + (local.set $x + (i32.const 14) + ) + (local.set $y + (i32.const 15) + ) + (br $out) + ) + ) + ) + ) + (func $loop-value (param $x i32) (result i32) + (loop $loopy + (local.set $x (unreachable)) + ) + (loop $loopy + (local.set $x (i32.const 1)) + ) + (local.get $x) + ) + (func $loop-loop-loopy-value (param $x i32) (result i32) + (loop $loopy1 + (loop $loopy2 + (loop $loopy3 + (local.set $x (i32.const 1)) + ) + ) + ) + (local.get $x) + ) + (func $loop-modified-during-main-pass-be-careful-fuzz (result i32) + (local $0 i32) + (if + (i32.const 0) + (local.set $0 + (i32.const 0) + ) + (loop $label$4 + (br $label$4) + ) + ) + (local.get $0) + ) + (func $loop-later (param $var$0 i32) (param $var$1 i32) (param $var$2 i32) (param $var$3 i32) (param $var$4 i32) (result i32) + (loop $label$1 + (block $label$2 + (if + (i32.const 0) + (block + (local.set $var$0 + (i32.const -1) + ) + (br $label$2) + ) + ) + (local.set $var$0 + (i32.const -1) + ) + ) + ) + (i32.const 0) + ) + (func $pick + (local $x i32) + (local $y i32) + (local.set $x (local.get $y)) + (if (i32.const 1) + (local.set $x (i32.const 1)) + ) + (local.set $x (local.get $y)) + (local.set $x (local.get $y)) + ) + (func $pick-2 + (local $x i32) + (local $y i32) + (local.set $y (local.get $x)) + (if (i32.const 1) + (local.set $y (i32.const 1)) + ) + (local.set $y (local.get $x)) + (local.set $y (local.get $x)) + ) + (func $many + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local.set $y (local.get $x)) + (local.set $z (local.get $y)) + (local.set $w (local.get $z)) + (local.set $x (local.get $z)) + (if (i32.const 1) + (local.set $y (i32.const 1)) + ) + (local.set $x (local.get $z)) + (if (i32.const 1) + (local.set $y (i32.const 1)) + ) + (local.set $y (local.get $x)) + (local.set $z (local.get $y)) + (local.set $w (local.get $z)) + (local.set $z (i32.const 2)) + (local.set $x (local.get $z)) + (if (i32.const 1) + (local.set $y (i32.const 1)) + ) + (local.set $y (local.get $x)) + (local.set $z (local.get $y)) + (local.set $w (local.get $z)) + (local.set $z (i32.const 2)) + (local.set $x (local.get $w)) + ) + (func $loop-copies (param $x i32) (param $y i32) + (loop $loop + (local.set $x (local.get $y)) + (local.set $y (local.get $x)) + (br_if $loop (local.get $x)) + ) + ) + (func $proper-type (result f64) + (local $var$0 i32) + (local $var$2 f64) + (local.set $var$0 + (select + (i32.const 0) + (i32.const 1) + (local.get $var$0) + ) + ) + (local.tee $var$2 + (local.get $var$2) + ) + ) + (func $multi-pass-get-equivs-right (param $var$0 i32) (param $var$1 i32) (result f64) + (local $var$2 i32) + (local.set $var$2 + (local.get $var$0) + ) + (i32.store + (local.get $var$2) + (i32.const 1) + ) + (f64.promote_f32 + (f32.load + (local.get $var$2) + ) + ) + ) + (func $if-value-structure-equivalent (param $x i32) (result i32) + (local $y i32) + (if (i32.const 1) + (local.set $x (i32.const 2)) + (block + (local.set $y (local.get $x)) + (local.set $x (local.get $y)) + ) + ) + (local.get $x) + ) + (func $set-tee-need-one-of-them (param $var$0 i32) (param $var$1 i32) (result i32) + (local $var$2 i32) + (local $var$3 i32) + (local.set $var$0 ;; this is redundant + (local.tee $var$2 ;; but this is not - we need this set, we read it at the end + (local.get $var$0) + ) + ) + (loop $loop + (br_if $loop + (local.get $var$1) + ) + ) + (local.get $var$2) + ) + (func $loop-value-harder (result i32) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 f32) + (local $4 f32) + (local $5 f32) + (local $6 f32) + (local $7 f32) + (local $8 f32) + (local $9 f32) + (local $10 f32) + (block $label$1 + (loop $label$2 + (block $label$3 + (global.set $global$0 + (i32.const -1) + ) + (block $label$4 + (local.set $0 + (call $fimport$0) + ) + (if + (local.get $0) + (local.set $5 + (f32.const -2048) + ) + (block + (call $fimport$1 + (i32.const -25732) + ) + (br $label$2) + ) + ) + ) + (local.set $6 + (local.get $5) + ) + (local.set $7 + (local.get $6) + ) + ) + (local.set $8 + (local.get $7) + ) + (local.set $9 + (local.get $8) + ) + ) + (local.set $10 + (local.get $9) + ) + (call $fimport$2 + (local.get $10) + ) + (local.set $1 + (i32.const -5417091) + ) + ) + (local.set $2 + (local.get $1) + ) + (return + (local.get $2) + ) + ) + (func $tee-chain + (param $x i32) + (param $z i32) + (param $t1 i32) + (param $t2 i32) + (param $t3 i32) + (result i32) + (local.set $x + (local.get $x) + ) + (local.set $z + (local.tee $z + (i32.const 10) + ) + ) + (local.set $z + (local.tee $z + (i32.const 10) + ) + ) + (local.set $t1 + (local.tee $t2 + (local.tee $t3 + (local.tee $t1 + (call $tee-chain (local.get $x) (local.get $z) (local.get $t1) (local.get $t2) (local.get $t3)) + ) + ) + ) + ) + (call $tee-chain (local.get $x) (local.get $z) (local.get $t1) (local.get $t2) (local.get $t3)) + ) +) +(module + (memory 256 256) + (data passive "hello, there!") + (func $memory-init-load + (local $x i32) + (local.set $x + (i32.load (i32.const 0)) + ) + (memory.init 0 (i32.const 0) (i32.const 0) (i32.const 5)) + (drop + (local.get $x) + ) + ) + (func $memory-init-store + (local $x i32) + (local.set $x + (block i32 + (i32.store (i32.const 0) (i32.const 42)) + (i32.const 0) + ) + ) + (memory.init 0 (i32.const 0) (i32.const 0) (i32.const 5)) + (drop + (local.get $x) + ) + ) + (func $memory-copy-load + (local $x i32) + (local.set $x + (i32.load (i32.const 0)) + ) + (memory.copy (i32.const 0) (i32.const 8) (i32.const 8)) + (drop + (local.get $x) + ) + ) + (func $memory-copy-store + (local $x i32) + (local.set $x + (block i32 + (i32.store (i32.const 0) (i32.const 42)) + (i32.const 0) + ) + ) + (memory.copy (i32.const 0) (i32.const 8) (i32.const 8)) + (drop + (local.get $x) + ) + ) + (func $memory-fill-load + (local $x i32) + (local.set $x + (i32.load (i32.const 0)) + ) + (memory.fill (i32.const 0) (i32.const 42) (i32.const 8)) + (drop + (local.get $x) + ) + ) + (func $memory-fill-store + (local $x i32) + (local.set $x + (block i32 + (i32.store (i32.const 0) (i32.const 42)) + (i32.const 0) + ) + ) + (memory.fill (i32.const 0) (i32.const 8) (i32.const 8)) + (drop + (local.get $x) + ) + ) + (func $data-drop-load + (local $x i32) + (local.set $x + (i32.load (i32.const 0)) + ) + (data.drop 0) + (drop + (local.get $x) + ) + ) + (func $data-drop-store + (local $x i32) + (local.set $x + (block i32 + (i32.store (i32.const 0) (i32.const 42)) + (i32.const 0) + ) + ) + (data.drop 0) + (drop + (local.get $x) + ) + ) + (func $data-drop-memory-init + (local $x i32) + (local.set $x + (block i32 + (memory.init 0 (i32.const 0) (i32.const 0) (i32.const 5)) + (i32.const 0) + ) + ) + (data.drop 0) + (drop + (local.get $x) + ) + ) +) +(module + (func $subtype-test (result funcref) + (local $0 nullref) + (local $1 funcref) + (local $2 funcref) + (block + (local.set $1 + (local.get $0) + ) + ) + (local.set $2 + (local.get $1) + ) + (local.get $1) + ) +) |