summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rwxr-xr-xscripts/test/wasm2js.py15
-rw-r--r--src/binaryen-c.cpp11
-rw-r--r--src/binaryen-c.h5
-rw-r--r--src/ir/block-utils.h6
-rw-r--r--src/ir/effects.h93
-rw-r--r--src/ir/local-utils.h18
-rw-r--r--src/js/binaryen.js-post.js5
-rw-r--r--src/passes/CodeFolding.cpp4
-rw-r--r--src/passes/CodePushing.cpp24
-rw-r--r--src/passes/DeadArgumentElimination.cpp3
-rw-r--r--src/passes/LocalCSE.cpp18
-rw-r--r--src/passes/LoopInvariantCodeMotion.cpp9
-rw-r--r--src/passes/MergeBlocks.cpp27
-rw-r--r--src/passes/OptimizeAddedConstants.cpp3
-rw-r--r--src/passes/OptimizeInstructions.cpp62
-rw-r--r--src/passes/RemoveUnusedBrs.cpp39
-rw-r--r--src/passes/SimplifyGlobals.cpp2
-rw-r--r--src/passes/SimplifyLocals.cpp25
-rw-r--r--src/passes/Vacuum.cpp35
-rw-r--r--src/wasm2js.h14
-rw-r--r--test/binaryen.js/sideffects.js12
-rw-r--r--test/binaryen.js/sideffects.js.txt3
-rw-r--r--test/passes/code-pushing_all-features.txt97
-rw-r--r--test/passes/code-pushing_all-features.wast63
-rw-r--r--test/passes/simplify-locals_all-features.txt206
-rw-r--r--test/passes/simplify-locals_all-features_disable-exception-handling.txt1888
-rw-r--r--test/passes/simplify-locals_all-features_disable-exception-handling.wast1673
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)
+ )
+)