Skip to content

Commit e93babc

Browse files
authored
StackIR: Optimize away a drop before an unreachable (#6719)
Anything else right before an unreachable is removed by the main DCE pass anyhow, but because of the structured form of BinaryenIR we can't remove a drop. That is, this is the difference between (i32.eqz (i32.const 42) (unreachable) ) and (drop (call $foo) ) (unreachable) In both cases the unreachable is preceded by something we don't need, but in the latter case it must remain in BinaryenIR for validation. To optimize this, add a rule in StackIR. Fixes #6715
1 parent 4179603 commit e93babc

File tree

2 files changed

+156
-1
lines changed

2 files changed

+156
-1
lines changed

src/wasm/wasm-stack-opts.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ void StackIROptimizer::run() {
4444
vacuum();
4545
}
4646

47-
// Remove unreachable code.
4847
void StackIROptimizer::dce() {
48+
// Remove code after an unreachable instruction: anything after it, up to the
49+
// next control flow barrier, can simply be removed.
4950
bool inUnreachableCode = false;
5051
for (Index i = 0; i < insts.size(); i++) {
5152
auto* inst = insts[i];
@@ -64,6 +65,42 @@ void StackIROptimizer::dce() {
6465
inUnreachableCode = true;
6566
}
6667
}
68+
69+
// Remove code before an Unreachable. Consider this:
70+
//
71+
// (drop
72+
// ..
73+
// )
74+
// (unreachable)
75+
//
76+
// The drop is not needed, as the unreachable puts the stack in the
77+
// polymorphic state anyhow. Note that we don't need to optimize anything
78+
// other than a drop here, as in general the Binaryen IR DCE pass will handle
79+
// everything else. A drop followed by an unreachable is the only thing that
80+
// pass cannot handle, as the structured form of Binaryen IR does not allow
81+
// removing such a drop, and so we can only do it here in StackIR.
82+
//
83+
// TODO: We can look even further back, say if there is another drop of
84+
// something before, then we can remove that drop as well. To do that
85+
// we'd need to inspect the stack going backwards.
86+
for (Index i = 1; i < insts.size(); i++) {
87+
auto* inst = insts[i];
88+
if (!inst || inst->op != StackInst::Basic ||
89+
!inst->origin->is<Unreachable>()) {
90+
continue;
91+
}
92+
93+
// Look back past nulls.
94+
Index j = i - 1;
95+
while (j > 0 && !insts[j]) {
96+
j--;
97+
}
98+
99+
auto*& prev = insts[j];
100+
if (prev && prev->op == StackInst::Basic && prev->origin->is<Drop>()) {
101+
prev = nullptr;
102+
}
103+
}
67104
}
68105

69106
// Remove obviously-unneeded code.

test/lit/passes/stack-ir-dce.wast

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
3+
;; RUN: wasm-opt %s --generate-stack-ir --optimize-stack-ir -all --print-stack-ir | filecheck %s
4+
;; Also verify we roundtrip the output here properly.
5+
;; RUN: wasm-opt %s --generate-stack-ir --optimize-stack-ir -all --roundtrip --print | filecheck %s --check-prefix=ROUNDTRIP
6+
7+
(module
8+
;; CHECK: (func $drop-unreachable (type $0) (result i32)
9+
;; CHECK-NEXT: call $drop-unreachable
10+
;; CHECK-NEXT: unreachable
11+
;; CHECK-NEXT: )
12+
;; ROUNDTRIP: (func $drop-unreachable (type $0) (result i32)
13+
;; ROUNDTRIP-NEXT: (drop
14+
;; ROUNDTRIP-NEXT: (call $drop-unreachable)
15+
;; ROUNDTRIP-NEXT: )
16+
;; ROUNDTRIP-NEXT: (unreachable)
17+
;; ROUNDTRIP-NEXT: )
18+
(func $drop-unreachable (result i32)
19+
;; This drop can be removed.
20+
(drop
21+
(call $drop-unreachable)
22+
)
23+
(unreachable)
24+
)
25+
26+
;; CHECK: (func $unreachable (type $0) (result i32)
27+
;; CHECK-NEXT: unreachable
28+
;; CHECK-NEXT: )
29+
;; ROUNDTRIP: (func $unreachable (type $0) (result i32)
30+
;; ROUNDTRIP-NEXT: (unreachable)
31+
;; ROUNDTRIP-NEXT: )
32+
(func $unreachable (result i32)
33+
;; An unreachable with nothing before it. Check we do not error here.
34+
(unreachable)
35+
)
36+
37+
;; CHECK: (func $unreachable-non-drop (type $1)
38+
;; CHECK-NEXT: call $unreachable-non-drop
39+
;; CHECK-NEXT: unreachable
40+
;; CHECK-NEXT: )
41+
;; ROUNDTRIP: (func $unreachable-non-drop (type $1)
42+
;; ROUNDTRIP-NEXT: (call $unreachable-non-drop)
43+
;; ROUNDTRIP-NEXT: (unreachable)
44+
;; ROUNDTRIP-NEXT: )
45+
(func $unreachable-non-drop
46+
;; An unreachable with something other than a drop before it. Check we do
47+
;; not error here.
48+
(call $unreachable-non-drop)
49+
(unreachable)
50+
)
51+
52+
;; CHECK: (func $many-drop-unreachable (type $0) (result i32)
53+
;; CHECK-NEXT: i32.const 1
54+
;; CHECK-NEXT: if (result i32)
55+
;; CHECK-NEXT: call $drop-unreachable
56+
;; CHECK-NEXT: unreachable
57+
;; CHECK-NEXT: else
58+
;; CHECK-NEXT: call $drop-unreachable
59+
;; CHECK-NEXT: unreachable
60+
;; CHECK-NEXT: end
61+
;; CHECK-NEXT: drop
62+
;; CHECK-NEXT: call $drop-unreachable
63+
;; CHECK-NEXT: unreachable
64+
;; CHECK-NEXT: )
65+
;; ROUNDTRIP: (func $many-drop-unreachable (type $0) (result i32)
66+
;; ROUNDTRIP-NEXT: (drop
67+
;; ROUNDTRIP-NEXT: (if (result i32)
68+
;; ROUNDTRIP-NEXT: (i32.const 1)
69+
;; ROUNDTRIP-NEXT: (then
70+
;; ROUNDTRIP-NEXT: (drop
71+
;; ROUNDTRIP-NEXT: (call $drop-unreachable)
72+
;; ROUNDTRIP-NEXT: )
73+
;; ROUNDTRIP-NEXT: (unreachable)
74+
;; ROUNDTRIP-NEXT: )
75+
;; ROUNDTRIP-NEXT: (else
76+
;; ROUNDTRIP-NEXT: (drop
77+
;; ROUNDTRIP-NEXT: (call $drop-unreachable)
78+
;; ROUNDTRIP-NEXT: )
79+
;; ROUNDTRIP-NEXT: (unreachable)
80+
;; ROUNDTRIP-NEXT: )
81+
;; ROUNDTRIP-NEXT: )
82+
;; ROUNDTRIP-NEXT: )
83+
;; ROUNDTRIP-NEXT: (drop
84+
;; ROUNDTRIP-NEXT: (call $drop-unreachable)
85+
;; ROUNDTRIP-NEXT: )
86+
;; ROUNDTRIP-NEXT: (unreachable)
87+
;; ROUNDTRIP-NEXT: )
88+
(func $many-drop-unreachable (result i32)
89+
;; Two drop-unreachables in an if. The drop on the if can remain, but all
90+
;; others are removable.
91+
(drop
92+
(if (result i32)
93+
(i32.const 1)
94+
(then
95+
(drop
96+
(call $drop-unreachable)
97+
)
98+
(unreachable)
99+
)
100+
(else
101+
(drop
102+
(call $drop-unreachable)
103+
)
104+
(unreachable)
105+
)
106+
)
107+
)
108+
;; Two more outside the if.
109+
(drop
110+
(call $drop-unreachable)
111+
)
112+
(unreachable)
113+
(drop
114+
(call $drop-unreachable)
115+
)
116+
(unreachable)
117+
)
118+
)

0 commit comments

Comments
 (0)