diff options
author | Heejin Ahn <aheejin@gmail.com> | 2020-02-03 10:44:49 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-03 10:44:49 -0800 |
commit | c9f2e9b7b24182e830f39c176170d5ca64d3d05e (patch) | |
tree | b9aeb5648997393c474d1e3ebf4513d640395c70 | |
parent | cd8d82910d229aa8357eb18882745397f6ed87eb (diff) | |
download | binaryen-c9f2e9b7b24182e830f39c176170d5ca64d3d05e.tar.gz binaryen-c9f2e9b7b24182e830f39c176170d5ca64d3d05e.tar.bz2 binaryen-c9f2e9b7b24182e830f39c176170d5ca64d3d05e.zip |
Add EH support for EffectAnalyzer (#2631)
This adds EH support to `EffectAnalyzer`. Before `throw` and `rethrow`
conservatively set property. Now `EffectAnalyzer` has a new property
`throws` to represent an expression that can throw, and expression that
can throw sets `throws` correctly.
When EH is enabled, any calls can throw too, so we cannot reorder them
with another expression with any side effects, meaning all calls should
be treated in the same way as branches when evaluating `invalidate`.
This prevents many reorderings, so this patch sets `throws` for calls
only when the exception handling features is enabled. This is also why I
passed `--disable-exception-handling` to `wasm2js` tests. Most of code
changes outside of `EffectAnalyzer` class was made in order to pass
`FeatureSet` to it.
`throws` isn't always set whenever an expression contains a throwable
instruction. When an throwable instruction is within an inner try, it
will be caught by the corresponding inner catch, so it does not set
`throws`.
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) + ) +) |