summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/iteration.h23
-rw-r--r--src/passes/MergeBlocks.cpp189
-rw-r--r--src/passes/ReorderFunctions.cpp2
-rw-r--r--test/lit/passes/inlining-optimizing_optimize-level=3.wast46
-rw-r--r--test/lit/passes/merge-blocks.wast207
-rw-r--r--test/passes/converge_O3_metrics.bin.txt90
-rw-r--r--test/passes/remove-unused-names_merge-blocks_all-features.txt106
7 files changed, 459 insertions, 204 deletions
diff --git a/src/ir/iteration.h b/src/ir/iteration.h
index 29d183678..6574e5dff 100644
--- a/src/ir/iteration.h
+++ b/src/ir/iteration.h
@@ -58,15 +58,21 @@ template<class Specific> class AbstractChildIterator {
void operator++() { index++; }
Expression*& operator*() {
- assert(index < parent.children.size());
-
- // The vector of children is in reverse order, as that is how
- // wasm-delegations-fields works. To get the order of execution, reverse
- // things.
- return *parent.children[parent.children.size() - 1 - index];
+ return *parent.children[parent.mapIndex(index)];
}
};
+ friend struct Iterator;
+
+ Index mapIndex(Index index) const {
+ assert(index < children.size());
+
+ // The vector of children is in reverse order, as that is how
+ // wasm-delegations-fields works. To get the order of execution, reverse
+ // things.
+ return children.size() - 1 - index;
+ }
+
public:
// The vector of children in the order emitted by wasm-delegations-fields
// (which is in reverse execution order).
@@ -112,6 +118,11 @@ public:
void addChild(Expression* parent, Expression** child) {
children.push_back(child);
}
+
+ // API for accessing children in random order.
+ Expression*& getChild(Index index) { return *children[mapIndex(index)]; }
+
+ Index getNumChildren() { return children.size(); }
};
class ChildIterator : public AbstractChildIterator<ChildIterator> {
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index 6ba486717..225f4f05d 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -509,74 +509,155 @@ struct MergeBlocks
return;
}
+ // As we go through the children, to move things to the outside means
+ // moving them past the children before them:
+ //
+ // (parent
+ // (child1
+ // (A)
+ // (B)
+ // )
+ // (child2
+ //
+ // If we move (A) out of parent, then that is fine (further things moved
+ // out would appear after it). But if we leave (B) in its current position
+ // then if we try to move anything from child2 out of parent then we must
+ // move those things past (B). We use a vector to track the effects of the
+ // children, where it contains the effects of what was left in the child
+ // after optimization.
+ std::vector<EffectAnalyzer> childEffects;
+
ChildIterator iterator(curr);
- auto& children = iterator.children;
- if (children.size() == 1) {
- optimize(curr, *children[0]);
- } else if (children.size() == 2) {
- optimize(curr, *children[0], optimize(curr, *children[1]), children[1]);
- } else if (children.size() == 3) {
- optimizeTernary(curr, *children[2], *children[1], *children[0]);
+ auto numChildren = iterator.getNumChildren();
+
+ // Find the last block among the children, as all we are trying to do here
+ // is move the contents of blocks outwards.
+ Index lastBlock = -1;
+ for (Index i = 0; i < numChildren; i++) {
+ if (iterator.getChild(i)->is<Block>()) {
+ lastBlock = i;
+ }
}
- }
-
- void visitIf(If* curr) {
- // We can move code out of the condition, but not any of the other children.
- optimize(curr, curr->condition);
- }
-
- void optimizeTernary(Expression* curr,
- Expression*& first,
- Expression*& second,
- Expression*& third) {
- Block* outer = nullptr;
- outer = optimize(curr, first, outer);
- // TODO: for now, just stop when we see any side effect after the first
- // item, but we could handle them carefully like we do for binaries.
- if (EffectAnalyzer(getPassOptions(), *getModule(), second)
- .hasSideEffects()) {
+ if (lastBlock == Index(-1)) {
+ // There are no blocks at all, so there is nothing to optimize.
return;
}
- outer = optimize(curr, second, outer);
- if (EffectAnalyzer(getPassOptions(), *getModule(), third)
- .hasSideEffects()) {
- return;
+
+ // We'll only compute effects up to the child before the last block, since
+ // we have nothing to optimize afterwards, which sets a maximum size on the
+ // vector.
+ if (lastBlock > 0) {
+ childEffects.reserve(lastBlock);
}
- optimize(curr, third, outer);
- }
- template<typename T> void handleCall(T* curr) {
- Block* outer = nullptr;
- for (Index i = 0; i < curr->operands.size(); i++) {
- if (EffectAnalyzer(getPassOptions(), *getModule(), curr->operands[i])
- .hasSideEffects()) {
- return;
+ // The outer block that will replace us, containing the contents moved out
+ // and then ourselves, assuming we manage to optimize.
+ Block* outerBlock = nullptr;
+
+ for (Index i = 0; i <= lastBlock; i++) {
+ auto* child = iterator.getChild(i);
+ auto* block = child->dynCast<Block>();
+
+ auto continueEarly = [&]() {
+ // When we continue early, after failing to find anything to optimize,
+ // the effects we need to note for the child are simply those of the
+ // child in its original form.
+ childEffects.emplace_back(getPassOptions(), *getModule(), child);
+ };
+
+ // If there is no block, or it is one that might have branches, or it is
+ // too small for us to remove anything from (we cannot remove the last
+ // element), or if it has unreachable code (leave that for dce), then give
+ // up.
+ if (!block || block->name.is() || block->list.size() <= 1 ||
+ hasUnreachableChild(block)) {
+ continueEarly();
+ continue;
}
- outer = optimize(curr, curr->operands[i], outer);
- }
- }
- void visitCall(Call* curr) { handleCall(curr); }
+ // Also give up if the block's last element has a different type than the
+ // block, as that would mean we would change the type received by the
+ // parent (which might cause its type to need to be updated, for example).
+ // Leave this alone, as other passes will simplify this anyhow (using
+ // refinalize).
+ auto* back = block->list.back();
+ if (block->type != back->type) {
+ continueEarly();
+ continue;
+ }
- template<typename T> void handleNonDirectCall(T* curr) {
- Block* outer = nullptr;
- for (Index i = 0; i < curr->operands.size(); i++) {
- if (EffectAnalyzer(getPassOptions(), *getModule(), curr->operands[i])
- .hasSideEffects()) {
- return;
+ // The block seems to have the shape we want. Check for effects: we want
+ // to move all the items out but the last one, so they must all cross over
+ // anything we need to move past.
+ //
+ // In principle we could also handle the case where we can move out only
+ // some of the block items. However, that would be more complex (we'd need
+ // to allocate a new block sometimes), it is rare, and it may not always
+ // be helpful (we wouldn't actually be getting rid of the child block -
+ // although, in the binary format such blocks tend to vanish anyhow).
+ bool fail = false;
+ for (auto* blockChild : block->list) {
+ if (blockChild == back) {
+ break;
+ }
+ EffectAnalyzer blockChildEffects(
+ getPassOptions(), *getModule(), blockChild);
+ for (auto& effects : childEffects) {
+ if (blockChildEffects.invalidates(effects)) {
+ fail = true;
+ break;
+ }
+ }
+ if (fail) {
+ break;
+ }
+ }
+ if (fail) {
+ continueEarly();
+ continue;
+ }
+
+ // Wonderful, we can do this! Move our items to an outer block, reusing
+ // this one if there isn't one already.
+ if (!outerBlock) {
+ // Leave all the items there, just remove the last one which will remain
+ // where it was.
+ block->list.pop_back();
+ outerBlock = block;
+ } else {
+ // Move the items to the existing outer block.
+ for (auto* blockChild : block->list) {
+ if (blockChild == back) {
+ break;
+ }
+ outerBlock->list.push_back(blockChild);
+ }
+ }
+
+ // Set the back element as the new child, replacing the block that was
+ // there.
+ iterator.getChild(i) = back;
+
+ // If there are further elements, we need to know what effects the
+ // remaining code has, as if they move they'll move past it.
+ if (i < lastBlock) {
+ childEffects.emplace_back(getPassOptions(), *getModule(), back);
}
- outer = optimize(curr, curr->operands[i], outer);
}
- if (EffectAnalyzer(getPassOptions(), *getModule(), curr->target)
- .hasSideEffects()) {
- return;
+
+ if (outerBlock) {
+ // We moved items outside, which means we must replace ourselves with the
+ // block.
+ outerBlock->list.push_back(curr);
+ outerBlock->finalize(curr->type);
+ replaceCurrent(outerBlock);
}
- optimize(curr, curr->target, outer);
}
- void visitCallIndirect(CallIndirect* curr) { handleNonDirectCall(curr); }
-
- void visitCallRef(CallRef* curr) { handleNonDirectCall(curr); }
+ void visitIf(If* curr) {
+ // We can move code out of the condition, but not any of the other children.
+ optimize(curr, curr->condition);
+ }
void visitThrow(Throw* curr) {
Block* outer = nullptr;
diff --git a/src/passes/ReorderFunctions.cpp b/src/passes/ReorderFunctions.cpp
index 66b8275ef..326893951 100644
--- a/src/passes/ReorderFunctions.cpp
+++ b/src/passes/ReorderFunctions.cpp
@@ -24,7 +24,7 @@
// a less beneficial position for compression, that is, mutually-compressible
// functions are no longer together (when they were before, in the original
// order, the has some natural tendency one way or the other). TODO: investigate
-// similarity ordering here.
+// similarity ordering here (see #4322)
//
#include <memory>
diff --git a/test/lit/passes/inlining-optimizing_optimize-level=3.wast b/test/lit/passes/inlining-optimizing_optimize-level=3.wast
index 11eea40ff..980075d14 100644
--- a/test/lit/passes/inlining-optimizing_optimize-level=3.wast
+++ b/test/lit/passes/inlining-optimizing_optimize-level=3.wast
@@ -6478,34 +6478,32 @@
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (loop $while-in66
- ;; CHECK-NEXT: (i32.store
- ;; CHECK-NEXT: (local.get $11)
- ;; CHECK-NEXT: (call $___uremdi3
- ;; CHECK-NEXT: (block (result i32)
- ;; CHECK-NEXT: (global.set $tempRet0
- ;; CHECK-NEXT: (i32.add
- ;; CHECK-NEXT: (i32.gt_u
- ;; CHECK-NEXT: (local.tee $20
- ;; CHECK-NEXT: (call $_bitshift64Shl
- ;; CHECK-NEXT: (i32.load
- ;; CHECK-NEXT: (local.get $11)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: (local.get $17)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (local.tee $12
- ;; CHECK-NEXT: (i32.add
- ;; CHECK-NEXT: (local.get $12)
- ;; CHECK-NEXT: (local.get $20)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.set $tempRet0
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (i32.gt_u
+ ;; CHECK-NEXT: (local.tee $20
+ ;; CHECK-NEXT: (call $_bitshift64Shl
+ ;; CHECK-NEXT: (i32.load
+ ;; CHECK-NEXT: (local.get $11)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (global.get $tempRet0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (local.get $17)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.tee $12
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (local.get $12)
+ ;; CHECK-NEXT: (local.get $20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (local.get $12)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $tempRet0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.store
+ ;; CHECK-NEXT: (local.get $11)
+ ;; CHECK-NEXT: (call $___uremdi3
+ ;; CHECK-NEXT: (local.get $12)
;; CHECK-NEXT: (local.tee $20
;; CHECK-NEXT: (global.get $tempRet0)
;; CHECK-NEXT: )
diff --git a/test/lit/passes/merge-blocks.wast b/test/lit/passes/merge-blocks.wast
index 3d931690b..d6ff8b3d8 100644
--- a/test/lit/passes/merge-blocks.wast
+++ b/test/lit/passes/merge-blocks.wast
@@ -127,15 +127,13 @@
;; CHECK: (func $array.set-no-1 (param $foo (ref $array))
;; CHECK-NEXT: (local $bar i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (array.set $array
;; CHECK-NEXT: (local.get $foo)
- ;; CHECK-NEXT: (block (result i32)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (local.tee $bar
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.tee $bar
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 37)
;; CHECK-NEXT: )
@@ -159,16 +157,14 @@
;; CHECK: (func $array.set-no-2 (param $foo (ref $array))
;; CHECK-NEXT: (local $bar i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (array.set $array
;; CHECK-NEXT: (local.get $foo)
;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: (block (result i32)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (local.tee $bar
- ;; CHECK-NEXT: (i32.const 37)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.tee $bar
+ ;; CHECK-NEXT: (i32.const 37)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -226,4 +222,187 @@
)
)
)
+
+ ;; CHECK: (func $subsequent-children (param $x i32) (param $y i32) (param $z i32) (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $subsequent-children
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $subsequent-children (param $x i32) (param $y i32) (param $z i32) (result i32)
+ ;; Both of the calls to helper can be moved outside. Those calls remain in
+ ;; order after doing so, so there is no problem, and none of them are moved
+ ;; across anything with side effects. This leaves only consts in the call to
+ ;; $subsequent-children.
+ (call $subsequent-children
+ (block (result i32)
+ (drop (call $helper (i32.const 0)))
+ (i32.const 1)
+ )
+ (i32.const 2)
+ (block (result i32)
+ (drop (call $helper (i32.const 3)))
+ (i32.const 4)
+ )
+ )
+ )
+
+ ;; CHECK: (func $subsequent-children-1 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $subsequent-children-1
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $subsequent-children-1 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ (call $subsequent-children-1
+ (block (result i32)
+ (drop (call $helper (i32.const 0)))
+ (call $helper (i32.const 1)) ;; Compared to before, this is now a call, so
+ ;; it has side effects, and the call with arg
+ ;; 3 cannot be moved past it.
+ )
+ (i32.const 2)
+ (block (result i32)
+ (drop (call $helper (i32.const 3)))
+ (i32.const 4)
+ )
+ )
+ )
+
+ ;; CHECK: (func $subsequent-children-2 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $subsequent-children-2
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $subsequent-children-2 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ (call $subsequent-children-2
+ (block (result i32)
+ (drop (call $helper (i32.const 0)))
+ (call $helper (i32.const 1))
+ )
+ ;; Similar to the above, but with the main call's last two arguments flipped.
+ ;; This should not have an effect on the output: we still can't pull out the
+ ;; call with arg 3.
+ (block (result i32)
+ (drop (call $helper (i32.const 3)))
+ (i32.const 4)
+ )
+ (i32.const 2)
+ )
+ )
+
+ ;; CHECK: (func $subsequent-children-3 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $subsequent-children-3
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $subsequent-children-3 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ (call $subsequent-children-3
+ (block (result i32)
+ (drop (i32.const 0)) ;; Similar to the above, but this is just a const now
+ ;; and not a call. We still can't pull out the call
+ ;; with arg 3, due to the call with arg 1.
+ (call $helper (i32.const 1))
+ )
+ (block (result i32)
+ (drop (call $helper (i32.const 3)))
+ (i32.const 4)
+ )
+ (i32.const 2)
+ )
+ )
+
+ ;; CHECK: (func $subsequent-children-4 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (call $helper
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $subsequent-children-4
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $subsequent-children-4 (param $x i32) (param $y i32) (param $z i32) (result i32)
+ (call $subsequent-children-4
+ (block (result i32)
+ (drop (i32.const 0))
+ ;; Similar to the above, but remove the call on arg 1 as well. Now we *can*
+ ;; pull out the call with arg 3.
+ (i32.const 1)
+ )
+ (block (result i32)
+ (drop (call $helper (i32.const 3)))
+ (i32.const 4)
+ )
+ (i32.const 2)
+ )
+ )
+
+ ;; CHECK: (func $helper (param $x i32) (result i32)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $helper (param $x i32) (result i32)
+ (unreachable)
+ )
)
diff --git a/test/passes/converge_O3_metrics.bin.txt b/test/passes/converge_O3_metrics.bin.txt
index 48cc55541..8b17e64f5 100644
--- a/test/passes/converge_O3_metrics.bin.txt
+++ b/test/passes/converge_O3_metrics.bin.txt
@@ -54,21 +54,6 @@ total
(func $_main (; has Stack IR ;) (result i32)
(local $0 i32)
(local $1 i32)
- (local.set $1
- (i32.load offset=24
- (i32.add
- (i32.load
- (i32.sub
- (i32.load
- (i32.const 18100)
- )
- (i32.const 12)
- )
- )
- (i32.const 18100)
- )
- )
- )
(local.set $0
(i32.const 10888)
)
@@ -84,6 +69,21 @@ total
)
)
)
+ (local.set $1
+ (i32.load offset=24
+ (i32.add
+ (i32.load
+ (i32.sub
+ (i32.load
+ (i32.const 18100)
+ )
+ (i32.const 12)
+ )
+ )
+ (i32.const 18100)
+ )
+ )
+ )
(if
(local.tee $0
(i32.sub
@@ -278,21 +278,6 @@ total
(func $_main (; has Stack IR ;) (result i32)
(local $0 i32)
(local $1 i32)
- (local.set $1
- (i32.load offset=24
- (i32.add
- (i32.load
- (i32.sub
- (i32.load
- (i32.const 18100)
- )
- (i32.const 12)
- )
- )
- (i32.const 18100)
- )
- )
- )
(local.set $0
(i32.const 10888)
)
@@ -308,6 +293,21 @@ total
)
)
)
+ (local.set $1
+ (i32.load offset=24
+ (i32.add
+ (i32.load
+ (i32.sub
+ (i32.load
+ (i32.const 18100)
+ )
+ (i32.const 12)
+ )
+ )
+ (i32.const 18100)
+ )
+ )
+ )
(if
(local.tee $0
(i32.sub
@@ -497,21 +497,6 @@ total
(func $_main (; has Stack IR ;) (result i32)
(local $0 i32)
(local $1 i32)
- (local.set $1
- (i32.load offset=24
- (i32.add
- (i32.load
- (i32.sub
- (i32.load
- (i32.const 18100)
- )
- (i32.const 12)
- )
- )
- (i32.const 18100)
- )
- )
- )
(local.set $0
(i32.const 10888)
)
@@ -527,6 +512,21 @@ total
)
)
)
+ (local.set $1
+ (i32.load offset=24
+ (i32.add
+ (i32.load
+ (i32.sub
+ (i32.load
+ (i32.const 18100)
+ )
+ (i32.const 12)
+ )
+ )
+ (i32.const 18100)
+ )
+ )
+ )
(if
(local.tee $0
(i32.sub
diff --git a/test/passes/remove-unused-names_merge-blocks_all-features.txt b/test/passes/remove-unused-names_merge-blocks_all-features.txt
index ab1837ef7..e92d2ab9b 100644
--- a/test/passes/remove-unused-names_merge-blocks_all-features.txt
+++ b/test/passes/remove-unused-names_merge-blocks_all-features.txt
@@ -300,15 +300,15 @@
)
)
(drop
- (block (result i32)
- (unreachable)
- (drop
- (i32.const 20)
- )
- (i32.add
+ (i32.const 20)
+ )
+ (drop
+ (i32.add
+ (block (result i32)
+ (unreachable)
(i32.const 10)
- (i32.const 30)
)
+ (i32.const 30)
)
)
)
@@ -417,19 +417,19 @@
)
)
(drop
- (block (result i32)
- (unreachable)
- (drop
- (i32.const 30)
- )
- (drop
- (i32.const 50)
- )
- (select
+ (i32.const 30)
+ )
+ (drop
+ (i32.const 50)
+ )
+ (drop
+ (select
+ (block (result i32)
+ (unreachable)
(i32.const 20)
- (i32.const 40)
- (i32.const 60)
)
+ (i32.const 40)
+ (i32.const 60)
)
)
(drop
@@ -454,24 +454,25 @@
(i32.const 10)
)
(drop
+ (i32.const 50)
+ )
+ (drop
(select
(i32.const 20)
(block (result i32)
(unreachable)
(i32.const 40)
)
- (block (result i32)
- (drop
- (i32.const 50)
- )
- (i32.const 60)
- )
+ (i32.const 60)
)
)
(drop
(i32.const 10)
)
(drop
+ (i32.const 50)
+ )
+ (drop
(select
(i32.const 20)
(block (result i32)
@@ -480,12 +481,7 @@
)
(unreachable)
)
- (block (result i32)
- (drop
- (i32.const 50)
- )
- (i32.const 60)
- )
+ (i32.const 60)
)
)
(drop
@@ -604,17 +600,18 @@
(i32.const 20)
(i32.const 40)
)
+ (drop
+ (i32.const 20)
+ )
(call $call-ii
(block (result i32)
(unreachable)
(i32.const 10)
)
- (block (result i32)
- (drop
- (i32.const 20)
- )
- (i32.const 30)
- )
+ (i32.const 30)
+ )
+ (drop
+ (i32.const 20)
)
(call $call-ii
(block (result i32)
@@ -623,12 +620,7 @@
)
(unreachable)
)
- (block (result i32)
- (drop
- (i32.const 20)
- )
- (i32.const 30)
- )
+ (i32.const 30)
)
(drop
(i32.const 10)
@@ -691,33 +683,27 @@
(i32.const 40)
(i32.const 60)
)
+ (drop
+ (i32.const 30)
+ )
+ (drop
+ (i32.const 50)
+ )
(call_indirect $0 (type $ii)
(unreachable)
- (block (result i32)
- (drop
- (i32.const 30)
- )
- (i32.const 40)
- )
- (block (result i32)
- (drop
- (i32.const 50)
- )
- (i32.const 60)
- )
+ (i32.const 40)
+ (i32.const 60)
)
(drop
(i32.const 31)
)
+ (drop
+ (i32.const 51)
+ )
(call_indirect $0 (type $ii)
(i32.const 41)
(unreachable)
- (block (result i32)
- (drop
- (i32.const 51)
- )
- (i32.const 61)
- )
+ (i32.const 61)
)
(drop
(i32.const 32)