summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/SimplifyLocals.cpp153
-rw-r--r--src/passes/pass.cpp3
-rw-r--r--src/passes/passes.h1
-rw-r--r--test/passes/converge_O3_metrics.bin.txt44
-rw-r--r--test/passes/simplify-locals-nonesting.txt202
-rw-r--r--test/passes/simplify-locals-nonesting.wast262
6 files changed, 578 insertions, 87 deletions
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp
index f6997d14e..4db85eb3e 100644
--- a/src/passes/SimplifyLocals.cpp
+++ b/src/passes/SimplifyLocals.cpp
@@ -32,7 +32,7 @@
// can get rid of those (the operation is trivial there after it sorts by use
// frequency).
//
-// This pass has two main options:
+// This pass has options:
//
// * Tee: allow teeing, i.e., sinking a local with more than one use,
// and so after sinking we have a tee for the first use.
@@ -40,6 +40,11 @@
// internal set_locals into one on the outside,
// that can itself then be sunk further.
//
+// There is also an option to disallow nesting entirely, which disallows
+// Tee and Structure from those 2 options, and also disallows any sinking
+// operation that would create nesting. This keeps the IR flat while
+// removing redundant locals.
+//
#include <wasm.h>
#include <wasm-builder.h>
@@ -73,14 +78,11 @@ struct SetLocalRemover : public PostWalker<SetLocalRemover> {
// Main class
-struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>> {
+template<bool allowTee = true, bool allowStructure = true, bool allowNesting = true>
+struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<allowTee, allowStructure, allowNesting>>> {
bool isFunctionParallel() override { return true; }
- Pass* create() override { return new SimplifyLocals(allowTee, allowStructure); }
-
- bool allowTee, allowStructure;
-
- SimplifyLocals(bool allowTee, bool allowStructure) : allowTee(allowTee), allowStructure(allowStructure) {}
+ Pass* create() override { return new SimplifyLocals<allowTee, allowStructure, allowNesting>(); }
// information for a set_local we can sink
struct SinkableInfo {
@@ -127,7 +129,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
// local => # of get_locals for it
GetLocalCounter getCounter;
- static void doNoteNonLinear(SimplifyLocals* self, Expression** currp) {
+ static void doNoteNonLinear(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
auto* curr = *currp;
if (curr->is<Break>()) {
auto* br = curr->cast<Break>();
@@ -152,25 +154,25 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
self->sinkables.clear();
}
- static void doNoteIfElseCondition(SimplifyLocals* self, Expression** currp) {
+ static void doNoteIfElseCondition(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
// we processed the condition of this if-else, and now control flow branches
// into either the true or the false sides
assert((*currp)->cast<If>()->ifFalse);
self->sinkables.clear();
}
- static void doNoteIfElseTrue(SimplifyLocals* self, Expression** currp) {
+ static void doNoteIfElseTrue(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
// we processed the ifTrue side of this if-else, save it on the stack
assert((*currp)->cast<If>()->ifFalse);
self->ifStack.push_back(std::move(self->sinkables));
}
- static void doNoteIfElseFalse(SimplifyLocals* self, Expression** currp) {
+ static void doNoteIfElseFalse(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
// we processed the ifFalse side of this if-else, we can now try to
// mere with the ifTrue side and optimize a return value, if possible
auto* iff = (*currp)->cast<If>();
assert(iff->ifFalse);
- if (self->allowStructure) {
+ if (allowStructure) {
self->optimizeIfReturn(iff, currp, self->ifStack.back());
}
self->ifStack.pop_back();
@@ -202,12 +204,39 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
void visitGetLocal(GetLocal *curr) {
auto found = sinkables.find(curr->index);
if (found != sinkables.end()) {
+ auto* set = (*found->second.item)->template cast<SetLocal>(); // the set we may be sinking
+ bool oneUse = firstCycle || getCounter.num[curr->index] == 1;
+ auto* get = set->value->template dynCast<GetLocal>(); // the set's value may be a get (i.e., the set is a copy)
+ // if nesting is not allowed, and this might cause nesting, check if the sink would cause such a thing
+ if (!allowNesting) {
+ // a get is always ok to sink
+ if (!get) {
+ assert(expressionStack.size() >= 2);
+ assert(expressionStack[expressionStack.size() - 1] == curr);
+ auto* parent = expressionStack[expressionStack.size() - 2];
+ bool parentIsSet = parent->template is<SetLocal>();
+ // if the parent of this get is a set, we can sink into the set's value,
+ // it would not be nested.
+ if (!parentIsSet) {
+ return;
+ }
+ }
+ }
+ // we can optimize here
+ if (!allowNesting && get && !oneUse) {
+ // if we can't nest 's a copy with multiple uses, then we can't create
+ // a tee, and we can't nop the origin, but we can at least switch to
+ // the copied index, which may make the origin unneeded eventually.
+ curr->index = get->index;
+ anotherCycle = true;
+ return;
+ }
// sink it, and nop the origin
- auto* set = (*found->second.item)->cast<SetLocal>();
- if (firstCycle || getCounter.num[curr->index] == 1) {
- replaceCurrent(set->value);
+ if (oneUse) {
+ // with just one use, we can sink just the value
+ this->replaceCurrent(set->value);
} else {
- replaceCurrent(set);
+ this->replaceCurrent(set);
assert(!set->isTee());
set->setTee(true);
}
@@ -225,7 +254,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
if (set) {
assert(set->isTee());
set->setTee(false);
- replaceCurrent(set);
+ this->replaceCurrent(set);
}
}
@@ -242,9 +271,11 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
}
}
+ // a full expression stack is used when !allowNesting, so that we can check if
+ // a sink would cause nesting
std::vector<Expression*> expressionStack;
- static void visitPre(SimplifyLocals* self, Expression** currp) {
+ static void visitPre(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
Expression* curr = *currp;
EffectAnalyzer effects(self->getPassOptions());
@@ -252,10 +283,12 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
self->checkInvalidations(effects);
}
- self->expressionStack.push_back(curr);
+ if (!allowNesting) {
+ self->expressionStack.push_back(curr);
+ }
}
- static void visitPost(SimplifyLocals* self, Expression** currp) {
+ static void visitPost(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
// perform main SetLocal processing here, since we may be the result of
// replaceCurrent, i.e., the visitor was not called.
auto* set = (*currp)->dynCast<SetLocal>();
@@ -265,7 +298,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
// store is dead, leave just the value
auto found = self->sinkables.find(set->index);
if (found != self->sinkables.end()) {
- auto* previous = (*found->second.item)->cast<SetLocal>();
+ auto* previous = (*found->second.item)->template cast<SetLocal>();
assert(!previous->isTee());
auto* previousValue = previous->value;
Drop* drop = ExpressionManipulator::convert<SetLocal, Drop>(previous);
@@ -287,7 +320,9 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
self->sinkables.emplace(std::make_pair(index, SinkableInfo(currp, self->getPassOptions())));
}
- self->expressionStack.pop_back();
+ if (!allowNesting) {
+ self->expressionStack.pop_back();
+ }
}
bool canSink(SetLocal* set) {
@@ -308,7 +343,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
auto breaks = std::move(blockBreaks[block->name]);
blockBreaks.erase(block->name);
if (breaks.size() == 0) return; // block has no branches TODO we might optimize trivial stuff here too
- assert(!(*breaks[0].brp)->cast<Break>()->value); // block does not already have a return value (if one break has one, they all do)
+ assert(!(*breaks[0].brp)->template cast<Break>()->value); // block does not already have a return value (if one break has one, they all do)
// look for a set_local that is present in them all
bool found = false;
Index sharedIndex = -1;
@@ -349,8 +384,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
// move break set_local's value to the break
auto* breakSetLocalPointer = breaks[j].sinkables.at(sharedIndex).item;
auto* brp = breaks[j].brp;
- auto* br = (*brp)->cast<Break>();
- auto* set = (*breakSetLocalPointer)->cast<SetLocal>();
+ auto* br = (*brp)->template cast<Break>();
+ auto* set = (*breakSetLocalPointer)->template cast<SetLocal>();
if (br->condition) {
// TODO: optimize
FindAll<SetLocal> findAll(br->condition);
@@ -361,8 +396,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
// itself, there is any risk
Nop nop;
*breakSetLocalPointer = &nop;
- EffectAnalyzer condition(getPassOptions(), br->condition);
- EffectAnalyzer value(getPassOptions(), set);
+ EffectAnalyzer condition(this->getPassOptions(), br->condition);
+ EffectAnalyzer value(this->getPassOptions(), set);
*breakSetLocalPointer = set;
if (condition.invalidates(value)) {
// indeed, we can't do this, stop
@@ -384,7 +419,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
}
// move block set_local's value to the end, in return position, and nop the set
auto* blockSetLocalPointer = sinkables.at(sharedIndex).item;
- auto* value = (*blockSetLocalPointer)->cast<SetLocal>()->value;
+ auto* value = (*blockSetLocalPointer)->template cast<SetLocal>()->value;
block->list[block->list.size() - 1] = value;
block->type = value->type;
ExpressionManipulator::nop(*blockSetLocalPointer);
@@ -392,25 +427,25 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
// move break set_local's value to the break
auto* breakSetLocalPointer = breaks[j].sinkables.at(sharedIndex).item;
auto* brp = breaks[j].brp;
- auto* br = (*brp)->cast<Break>();
+ auto* br = (*brp)->template cast<Break>();
assert(!br->value);
// if the break is conditional, then we must set the value here - if the break is not reached, we must still have the new value in the local
- auto* set = (*breakSetLocalPointer)->cast<SetLocal>();
+ auto* set = (*breakSetLocalPointer)->template cast<SetLocal>();
if (br->condition) {
br->value = set;
set->setTee(true);
- *breakSetLocalPointer = getModule()->allocator.alloc<Nop>();
+ *breakSetLocalPointer = this->getModule()->allocator.template alloc<Nop>();
// in addition, as this is a conditional br that now has a value, it now returns a value, so it must be dropped
br->finalize();
- *brp = Builder(*getModule()).makeDrop(br);
+ *brp = Builder(*this->getModule()).makeDrop(br);
} else {
br->value = set->value;
ExpressionManipulator::nop(set);
}
}
// finally, create a set_local on the block itself
- auto* newSetLocal = Builder(*getModule()).makeSetLocal(sharedIndex, block);
- replaceCurrent(newSetLocal);
+ auto* newSetLocal = Builder(*this->getModule()).makeSetLocal(sharedIndex, block);
+ this->replaceCurrent(newSetLocal);
sinkables.clear();
anotherCycle = true;
}
@@ -446,39 +481,39 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
}
// all set, go
auto *ifTrueItem = ifTrue.at(sharedIndex).item;
- ifTrueBlock->list[ifTrueBlock->list.size() - 1] = (*ifTrueItem)->cast<SetLocal>()->value;
+ ifTrueBlock->list[ifTrueBlock->list.size() - 1] = (*ifTrueItem)->template cast<SetLocal>()->value;
ExpressionManipulator::nop(*ifTrueItem);
ifTrueBlock->finalize();
assert(ifTrueBlock->type != none);
auto *ifFalseItem = ifFalse.at(sharedIndex).item;
- ifFalseBlock->list[ifFalseBlock->list.size() - 1] = (*ifFalseItem)->cast<SetLocal>()->value;
+ ifFalseBlock->list[ifFalseBlock->list.size() - 1] = (*ifFalseItem)->template cast<SetLocal>()->value;
ExpressionManipulator::nop(*ifFalseItem);
ifFalseBlock->finalize();
assert(ifTrueBlock->type != none);
iff->finalize(); // update type
assert(iff->type != none);
// finally, create a set_local on the iff itself
- auto* newSetLocal = Builder(*getModule()).makeSetLocal(sharedIndex, iff);
+ auto* newSetLocal = Builder(*this->getModule()).makeSetLocal(sharedIndex, iff);
*currp = newSetLocal;
anotherCycle = true;
}
// override scan to add a pre and a post check task to all nodes
- static void scan(SimplifyLocals* self, Expression** currp) {
+ static void scan(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) {
self->pushTask(visitPost, currp);
auto* curr = *currp;
if (curr->is<If>() && curr->cast<If>()->ifFalse) {
// handle if-elses in a special manner, using the ifStack
- self->pushTask(SimplifyLocals::doNoteIfElseFalse, currp);
- self->pushTask(SimplifyLocals::scan, &curr->cast<If>()->ifFalse);
- self->pushTask(SimplifyLocals::doNoteIfElseTrue, currp);
- self->pushTask(SimplifyLocals::scan, &curr->cast<If>()->ifTrue);
- self->pushTask(SimplifyLocals::doNoteIfElseCondition, currp);
- self->pushTask(SimplifyLocals::scan, &curr->cast<If>()->condition);
+ self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfElseFalse, currp);
+ self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, &curr->cast<If>()->ifFalse);
+ self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfElseTrue, currp);
+ self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, &curr->cast<If>()->ifTrue);
+ self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfElseCondition, currp);
+ self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, &curr->cast<If>()->condition);
} else {
- super::scan(self, currp);
+ WalkerPass<LinearExecutionWalker<SimplifyLocals<allowTee, allowStructure, allowNesting>>>::scan(self, currp);
}
self->pushTask(visitPre, currp);
@@ -500,11 +535,11 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
do {
anotherCycle = false;
// main operation
- super::doWalkFunction(func);
+ WalkerPass<LinearExecutionWalker<SimplifyLocals<allowTee, allowStructure, allowNesting>>>::doWalkFunction(func);
// enlarge blocks that were marked, for the next round
if (blocksToEnlarge.size() > 0) {
for (auto* block : blocksToEnlarge) {
- block->list.push_back(getModule()->allocator.alloc<Nop>());
+ block->list.push_back(this->getModule()->allocator.template alloc<Nop>());
}
blocksToEnlarge.clear();
anotherCycle = true;
@@ -512,15 +547,15 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
// enlarge ifs that were marked, for the next round
if (ifsToEnlarge.size() > 0) {
for (auto* iff : ifsToEnlarge) {
- auto ifTrue = Builder(*getModule()).blockify(iff->ifTrue);
+ auto ifTrue = Builder(*this->getModule()).blockify(iff->ifTrue);
iff->ifTrue = ifTrue;
- if (ifTrue->list.size() == 0 || !ifTrue->list.back()->is<Nop>()) {
- ifTrue->list.push_back(getModule()->allocator.alloc<Nop>());
+ if (ifTrue->list.size() == 0 || !ifTrue->list.back()->template is<Nop>()) {
+ ifTrue->list.push_back(this->getModule()->allocator.template alloc<Nop>());
}
- auto ifFalse = Builder(*getModule()).blockify(iff->ifFalse);
+ auto ifFalse = Builder(*this->getModule()).blockify(iff->ifFalse);
iff->ifFalse = ifFalse;
- if (ifFalse->list.size() == 0 || !ifFalse->list.back()->is<Nop>()) {
- ifFalse->list.push_back(getModule()->allocator.alloc<Nop>());
+ if (ifFalse->list.size() == 0 || !ifFalse->list.back()->template is<Nop>()) {
+ ifFalse->list.push_back(this->getModule()->allocator.template alloc<Nop>());
}
}
ifsToEnlarge.clear();
@@ -548,19 +583,23 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
};
Pass *createSimplifyLocalsPass() {
- return new SimplifyLocals(true, true);
+ return new SimplifyLocals<true, true>();
}
Pass *createSimplifyLocalsNoTeePass() {
- return new SimplifyLocals(false, true);
+ return new SimplifyLocals<false, true>();
}
Pass *createSimplifyLocalsNoStructurePass() {
- return new SimplifyLocals(true, false);
+ return new SimplifyLocals<true, false>();
}
Pass *createSimplifyLocalsNoTeeNoStructurePass() {
- return new SimplifyLocals(false, false);
+ return new SimplifyLocals<false, false>();
+}
+
+Pass *createSimplifyLocalsNoNestingPass() {
+ return new SimplifyLocals<false, false, false>();
}
} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 0ec5864e0..ae9484d29 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -108,8 +108,9 @@ void PassRegistry::registerPasses() {
registerPass("reorder-locals", "sorts locals by access frequency", createReorderLocalsPass);
registerPass("rereloop", "re-optimize control flow using the relooper algorithm", createReReloopPass);
registerPass("rse", "remove redundant set_locals", createRedundantSetEliminationPass);
- registerPass("simplify-locals", "miscellaneous locals-related optimizations", createSimplifyLocalsPass);
registerPass("safe-heap", "instrument loads and stores to check for invalid behavior", createSafeHeapPass);
+ registerPass("simplify-locals", "miscellaneous locals-related optimizations", createSimplifyLocalsPass);
+ registerPass("simplify-locals-nonesting", "miscellaneous locals-related optimizations (no nesting at all; preserves flatness)", createSimplifyLocalsNoNestingPass);
registerPass("simplify-locals-notee", "miscellaneous locals-related optimizations", createSimplifyLocalsNoTeePass);
registerPass("simplify-locals-nostructure", "miscellaneous locals-related optimizations", createSimplifyLocalsNoStructurePass);
registerPass("simplify-locals-notee-nostructure", "miscellaneous locals-related optimizations", createSimplifyLocalsNoTeeNoStructurePass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 2eaf1049e..5ab03a439 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -67,6 +67,7 @@ Pass* createReReloopPass();
Pass* createRedundantSetEliminationPass();
Pass* createSafeHeapPass();
Pass* createSimplifyLocalsPass();
+Pass* createSimplifyLocalsNoNestingPass();
Pass* createSimplifyLocalsNoTeePass();
Pass* createSimplifyLocalsNoStructurePass();
Pass* createSimplifyLocalsNoTeeNoStructurePass();
diff --git a/test/passes/converge_O3_metrics.bin.txt b/test/passes/converge_O3_metrics.bin.txt
index ab88c57d0..452c2378d 100644
--- a/test/passes/converge_O3_metrics.bin.txt
+++ b/test/passes/converge_O3_metrics.bin.txt
@@ -2,15 +2,15 @@ total
[funcs] : 9
[memory-data] : 28
[table-data] : 429
- [total] : 145
+ [total] : 137
[vars] : 5
- binary : 14
+ binary : 12
block : 9
break : 3
call : 3
call_import : 1
call_indirect : 4
- const : 50
+ const : 48
drop : 3
get_global : 1
get_local : 20
@@ -18,7 +18,7 @@ total
load : 16
loop : 1
set_global : 1
- set_local : 11
+ set_local : 7
store : 5
(module
(type $0 (func (param i32 i32) (result i32)))
@@ -141,17 +141,7 @@ total
)
(block $label$2
(if
- (tee_local $0
- (i32.sub
- (tee_local $0
- (i32.add
- (get_local $0)
- (i32.const 10888)
- )
- )
- (i32.const 10888)
- )
- )
+ (get_local $0)
(br_if $label$2
(call_indirect (type $1)
(get_local $1)
@@ -227,12 +217,10 @@ total
(i32.const 1)
(i32.add
(i32.load offset=36
- (tee_local $0
- (i32.load
- (i32.add
- (get_local $0)
- (i32.const 32)
- )
+ (i32.load
+ (i32.add
+ (get_local $0)
+ (i32.const 32)
)
)
)
@@ -250,10 +238,8 @@ total
(get_local $2)
(i32.add
(i32.load offset=36
- (tee_local $0
- (i32.load offset=32
- (get_local $0)
- )
+ (i32.load offset=32
+ (get_local $0)
)
)
(i32.const 8)
@@ -267,15 +253,15 @@ total
[funcs] : 9
[memory-data] : 28
[table-data] : 429
- [total] : 136 -9
+ [total] : 136 -1
[vars] : 4 -1
- binary : 12 -2
+ binary : 12
block : 9
break : 3
call : 3
call_import : 1
call_indirect : 4
- const : 48 -2
+ const : 48
drop : 3
get_global : 1
get_local : 20
@@ -283,7 +269,7 @@ total
load : 16
loop : 1
set_global : 1
- set_local : 6 -5
+ set_local : 6 -1
store : 5
(module
(type $0 (func (param i32 i32) (result i32)))
diff --git a/test/passes/simplify-locals-nonesting.txt b/test/passes/simplify-locals-nonesting.txt
new file mode 100644
index 000000000..903c97df5
--- /dev/null
+++ b/test/passes/simplify-locals-nonesting.txt
@@ -0,0 +1,202 @@
+(module
+ (type $0 (func (param i64 i64 i64) (result i32)))
+ (type $1 (func (param i32) (result i32)))
+ (func $figure-1a (; 0 ;) (type $0) (param $a i64) (param $x i64) (param $y i64) (result i32)
+ (local $i i32)
+ (local $j i32)
+ (local $r i32)
+ (local $6 i64)
+ (local $7 i64)
+ (local $8 i32)
+ (local $9 i64)
+ (local $10 i64)
+ (local $11 i32)
+ (local $12 i32)
+ (local $13 i32)
+ (local $14 i32)
+ (local $15 i32)
+ (local $16 i32)
+ (local $17 i32)
+ (block $block
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (set_local $12
+ (i64.eq
+ (get_local $a)
+ (get_local $x)
+ )
+ )
+ (set_local $13
+ (i64.ne
+ (get_local $a)
+ (get_local $y)
+ )
+ )
+ (nop)
+ (nop)
+ (nop)
+ (set_local $15
+ (i32.and
+ (get_local $12)
+ (get_local $13)
+ )
+ )
+ (return
+ (get_local $15)
+ )
+ (unreachable)
+ )
+ (nop)
+ (return
+ (get_local $16)
+ )
+ )
+ (func $figure-1b (; 1 ;) (type $0) (param $a i64) (param $x i64) (param $y i64) (result i32)
+ (local $i i32)
+ (local $j i32)
+ (local $r i32)
+ (local $6 i64)
+ (local $7 i64)
+ (local $8 i32)
+ (local $9 i64)
+ (local $10 i64)
+ (local $11 i32)
+ (local $12 i64)
+ (local $13 i64)
+ (local $14 i32)
+ (local $15 i32)
+ (local $16 i32)
+ (local $17 i32)
+ (local $18 i32)
+ (block $block
+ (nop)
+ (nop)
+ (set_local $8
+ (i64.lt_s
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ (if
+ (get_local $8)
+ (block $block0
+ (block $block1
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (nop)
+ (set_local $15
+ (i64.eq
+ (get_local $a)
+ (get_local $x)
+ )
+ )
+ (set_local $16
+ (i64.ne
+ (get_local $a)
+ (get_local $y)
+ )
+ )
+ (nop)
+ (nop)
+ (nop)
+ (set_local $18
+ (i32.and
+ (get_local $15)
+ (get_local $16)
+ )
+ )
+ (return
+ (get_local $18)
+ )
+ (unreachable)
+ )
+ (unreachable)
+ )
+ (block $block2
+ (unreachable)
+ (unreachable)
+ )
+ )
+ )
+ (unreachable)
+ )
+ (func $figure-3-if (; 2 ;) (type $1) (param $x i32) (result i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (local $6 i32)
+ (local $7 i32)
+ (local $8 i32)
+ (local $9 i32)
+ (local $10 i32)
+ (block $block
+ (block $block3
+ (nop)
+ (set_local $2
+ (i32.and
+ (get_local $x)
+ (i32.const 1)
+ )
+ )
+ (if
+ (get_local $2)
+ (block $block4
+ (nop)
+ (nop)
+ (set_local $x
+ (i32.add
+ (get_local $x)
+ (i32.const 1)
+ )
+ )
+ (nop)
+ )
+ (block $block5
+ (nop)
+ (nop)
+ (set_local $x
+ (i32.add
+ (get_local $x)
+ (i32.const 2)
+ )
+ )
+ (nop)
+ )
+ )
+ )
+ (nop)
+ (nop)
+ (set_local $8
+ (i32.and
+ (get_local $x)
+ (i32.const 1)
+ )
+ )
+ (return
+ (get_local $8)
+ )
+ (unreachable)
+ )
+ (nop)
+ (return
+ (get_local $9)
+ )
+ )
+)
diff --git a/test/passes/simplify-locals-nonesting.wast b/test/passes/simplify-locals-nonesting.wast
new file mode 100644
index 000000000..99fcbf4c6
--- /dev/null
+++ b/test/passes/simplify-locals-nonesting.wast
@@ -0,0 +1,262 @@
+(module
+ (type $0 (func (param i64 i64 i64) (result i32)))
+ (type $1 (func (param i32) (result i32)))
+ (func $figure-1a (; 0 ;) (type $0) (param $a i64) (param $x i64) (param $y i64) (result i32)
+ (local $i i32)
+ (local $j i32)
+ (local $r i32)
+ (local $6 i64)
+ (local $7 i64)
+ (local $8 i32)
+ (local $9 i64)
+ (local $10 i64)
+ (local $11 i32)
+ (local $12 i32)
+ (local $13 i32)
+ (local $14 i32)
+ (local $15 i32)
+ (local $16 i32)
+ (local $17 i32)
+ (block
+ (set_local $6
+ (get_local $a)
+ )
+ (set_local $7
+ (get_local $x)
+ )
+ (set_local $8
+ (i64.eq
+ (get_local $6)
+ (get_local $7)
+ )
+ )
+ (set_local $i
+ (get_local $8)
+ )
+ (nop)
+ (set_local $9
+ (get_local $a)
+ )
+ (set_local $10
+ (get_local $y)
+ )
+ (set_local $11
+ (i64.ne
+ (get_local $9)
+ (get_local $10)
+ )
+ )
+ (set_local $j
+ (get_local $11)
+ )
+ (nop)
+ (set_local $12
+ (get_local $i)
+ )
+ (set_local $13
+ (get_local $j)
+ )
+ (set_local $14
+ (i32.and
+ (get_local $12)
+ (get_local $13)
+ )
+ )
+ (set_local $r
+ (get_local $14)
+ )
+ (nop)
+ (set_local $15
+ (get_local $r)
+ )
+ (return
+ (get_local $15)
+ )
+ (unreachable)
+ )
+ (set_local $17
+ (get_local $16)
+ )
+ (return
+ (get_local $17)
+ )
+ )
+ (func $figure-1b (; 1 ;) (type $0) (param $a i64) (param $x i64) (param $y i64) (result i32)
+ (local $i i32)
+ (local $j i32)
+ (local $r i32)
+ (local $6 i64)
+ (local $7 i64)
+ (local $8 i32)
+ (local $9 i64)
+ (local $10 i64)
+ (local $11 i32)
+ (local $12 i64)
+ (local $13 i64)
+ (local $14 i32)
+ (local $15 i32)
+ (local $16 i32)
+ (local $17 i32)
+ (local $18 i32)
+ (block
+ (set_local $6
+ (get_local $x)
+ )
+ (set_local $7
+ (get_local $y)
+ )
+ (set_local $8
+ (i64.lt_s
+ (get_local $6)
+ (get_local $7)
+ )
+ )
+ (if
+ (get_local $8)
+ (block
+ (block $block
+ (set_local $9
+ (get_local $a)
+ )
+ (set_local $10
+ (get_local $x)
+ )
+ (set_local $11
+ (i64.eq
+ (get_local $9)
+ (get_local $10)
+ )
+ )
+ (set_local $i
+ (get_local $11)
+ )
+ (nop)
+ (set_local $12
+ (get_local $a)
+ )
+ (set_local $13
+ (get_local $y)
+ )
+ (set_local $14
+ (i64.ne
+ (get_local $12)
+ (get_local $13)
+ )
+ )
+ (set_local $j
+ (get_local $14)
+ )
+ (nop)
+ (set_local $15
+ (get_local $i)
+ )
+ (set_local $16
+ (get_local $j)
+ )
+ (set_local $17
+ (i32.and
+ (get_local $15)
+ (get_local $16)
+ )
+ )
+ (set_local $r
+ (get_local $17)
+ )
+ (nop)
+ (set_local $18
+ (get_local $r)
+ )
+ (return
+ (get_local $18)
+ )
+ (unreachable)
+ )
+ (unreachable)
+ )
+ (block
+ (unreachable)
+ (unreachable)
+ )
+ )
+ )
+ (unreachable)
+ )
+ (func $figure-3-if (; 2 ;) (type $1) (param $x i32) (result i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (local $6 i32)
+ (local $7 i32)
+ (local $8 i32)
+ (local $9 i32)
+ (local $10 i32)
+ (block
+ (block
+ (set_local $1
+ (get_local $x)
+ )
+ (set_local $2
+ (i32.and
+ (get_local $1)
+ (i32.const 1)
+ )
+ )
+ (if
+ (get_local $2)
+ (block
+ (set_local $3
+ (get_local $x)
+ )
+ (set_local $4
+ (i32.add
+ (get_local $3)
+ (i32.const 1)
+ )
+ )
+ (set_local $x
+ (get_local $4)
+ )
+ (nop)
+ )
+ (block
+ (set_local $5
+ (get_local $x)
+ )
+ (set_local $6
+ (i32.add
+ (get_local $5)
+ (i32.const 2)
+ )
+ )
+ (set_local $x
+ (get_local $6)
+ )
+ (nop)
+ )
+ )
+ )
+ (nop)
+ (set_local $7
+ (get_local $x)
+ )
+ (set_local $8
+ (i32.and
+ (get_local $7)
+ (i32.const 1)
+ )
+ )
+ (return
+ (get_local $8)
+ )
+ (unreachable)
+ )
+ (set_local $10
+ (get_local $9)
+ )
+ (return
+ (get_local $10)
+ )
+ )
+)