Skip to content

Commit 432ed1c

Browse files
authored
[EH] Fix pop enclosed within a block in DCE (#6922)
#6400 fixed this case but that handled only when a `pop` is an immediate child of the current expression, but a `pop` can be nested deeper down. We conservatively run the EH fixup whenever we have a `pop` and create `block`s in the optimization. We considered using `FindAll<Pop>` to make it precise, but we decided the quadratic time plexity was not worth it. Fixes #6918.
1 parent 1a2d26f commit 432ed1c

File tree

2 files changed

+82
-15
lines changed

2 files changed

+82
-15
lines changed

src/passes/DeadCodeElimination.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ struct DeadCodeElimination
5656
// as we remove code, we must keep the types of other nodes valid
5757
TypeUpdater typeUpdater;
5858

59+
// Information used to decide whether we need EH fixups at the end
60+
bool hasPop = false; // Do we have a 'pop' in this function?
61+
bool addedBlock = false; // Have we added blocks in this function?
62+
5963
Expression* replaceCurrent(Expression* expression) {
6064
auto* old = getCurrent();
6165
if (old == expression) {
@@ -73,6 +77,10 @@ struct DeadCodeElimination
7377
}
7478

7579
void visitExpression(Expression* curr) {
80+
if (curr->is<Pop>()) {
81+
hasPop = true;
82+
}
83+
7684
if (!Properties::isControlFlowStructure(curr)) {
7785
// Control flow structures require special handling, but others are
7886
// simple.
@@ -90,11 +98,7 @@ struct DeadCodeElimination
9098
Builder builder(*getModule());
9199
std::vector<Expression*> remainingChildren;
92100
bool afterUnreachable = false;
93-
bool hasPop = false;
94101
for (auto* child : ChildIterator(curr)) {
95-
if (child->is<Pop>()) {
96-
hasPop = true;
97-
}
98102
if (afterUnreachable) {
99103
typeUpdater.noteRecursiveRemoval(child);
100104
continue;
@@ -109,12 +113,8 @@ struct DeadCodeElimination
109113
if (remainingChildren.size() == 1) {
110114
replaceCurrent(remainingChildren[0]);
111115
} else {
116+
addedBlock = true;
112117
replaceCurrent(builder.makeBlock(remainingChildren));
113-
if (hasPop) {
114-
// We are moving a pop into a new block we just created, which
115-
// means we may need to fix things up here.
116-
needEHFixups = true;
117-
}
118118
}
119119
}
120120
}
@@ -196,10 +196,11 @@ struct DeadCodeElimination
196196
}
197197
}
198198

199-
bool needEHFixups = false;
200-
201199
void visitFunction(Function* curr) {
202-
if (needEHFixups) {
200+
// We conservatively run the EH pop fixup if this function has a 'pop' and
201+
// if we have ever added blocks in the optimization, which may have moved
202+
// pops into the blocks.
203+
if (hasPop && addedBlock) {
203204
EHUtils::handleBlockNestedPops(curr, *getModule());
204205
}
205206
}

test/lit/passes/dce-eh-legacy.wast

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
1-
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
1+
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
22
;; RUN: wasm-opt %s --dce -all -S -o - | filecheck %s
33

44
;; If either try body or catch body is reachable, the whole try construct is
55
;; reachable
66
(module
7+
;; CHECK: (type $0 (func))
8+
9+
;; CHECK: (type $1 (func (param i32)))
10+
11+
;; CHECK: (type $2 (func (param eqref)))
12+
13+
;; CHECK: (type $3 (func (param i32 i32)))
14+
15+
;; CHECK: (type $4 (func (result i32)))
16+
17+
;; CHECK: (type $struct (struct (field (mut eqref))))
18+
(type $struct (struct (field (mut (ref null eq)))))
19+
720
;; CHECK: (tag $e)
821
(tag $e)
9-
1022
;; CHECK: (tag $e-i32 (param i32))
1123
(tag $e-i32 (param i32))
24+
;; CHECK: (tag $e-eqref (param eqref))
25+
(tag $e-eqref (param (ref null eq)))
1226

1327
;; CHECK: (func $foo (type $0)
1428
;; CHECK-NEXT: (nop)
@@ -149,11 +163,63 @@
149163
)
150164
)
151165

152-
;; CHECK: (func $helper-i32-i32 (type $2) (param $0 i32) (param $1 i32)
166+
;; CHECK: (func $helper-i32-i32 (type $3) (param $0 i32) (param $1 i32)
153167
;; CHECK-NEXT: (nop)
154168
;; CHECK-NEXT: )
155169
(func $helper-i32-i32 (param $0 i32) (param $1 i32)
156170
(nop)
157171
)
172+
173+
;; CHECK: (func $pop-within-block (type $4) (result i32)
174+
;; CHECK-NEXT: (local $0 eqref)
175+
;; CHECK-NEXT: (try (result i32)
176+
;; CHECK-NEXT: (do
177+
;; CHECK-NEXT: (i32.const 0)
178+
;; CHECK-NEXT: )
179+
;; CHECK-NEXT: (catch $e-eqref
180+
;; CHECK-NEXT: (local.set $0
181+
;; CHECK-NEXT: (pop eqref)
182+
;; CHECK-NEXT: )
183+
;; CHECK-NEXT: (block
184+
;; CHECK-NEXT: (block
185+
;; CHECK-NEXT: (drop
186+
;; CHECK-NEXT: (struct.new $struct
187+
;; CHECK-NEXT: (local.get $0)
188+
;; CHECK-NEXT: )
189+
;; CHECK-NEXT: )
190+
;; CHECK-NEXT: (unreachable)
191+
;; CHECK-NEXT: )
192+
;; CHECK-NEXT: )
193+
;; CHECK-NEXT: )
194+
;; CHECK-NEXT: )
195+
;; CHECK-NEXT: )
196+
(func $pop-within-block (result i32)
197+
(try (result i32)
198+
(do
199+
(i32.const 0)
200+
)
201+
(catch $e-eqref
202+
(drop
203+
;; Optimization moves the 'pop' inside a block, which needs to be
204+
;; extracted out of the block at the end.
205+
;; (block
206+
;; (drop
207+
;; (struct.new $struct.0
208+
;; (pop eqref)
209+
;; )
210+
;; )
211+
;; (unreachable)
212+
;; )
213+
(ref.eq
214+
(struct.new $struct
215+
(pop (ref null eq))
216+
)
217+
(unreachable)
218+
)
219+
)
220+
(i32.const 0)
221+
)
222+
)
223+
)
158224
)
159225

0 commit comments

Comments
 (0)