Skip to content

Commit 5f6911f

Browse files
authored
Handle try in Flatten pass (#2567)
This adds handling of try in the Flatten pass.
1 parent 057df43 commit 5f6911f

File tree

3 files changed

+274
-0
lines changed

3 files changed

+274
-0
lines changed

src/ir/eh-utils.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ bool isPopValid(Expression* catchBody) {
108108
}
109109

110110
void handleBlockNestedPops(Function* func, Module& wasm) {
111+
if (!wasm.features.hasExceptionHandling()) {
112+
return;
113+
}
114+
111115
Builder builder(wasm);
112116
FindAll<Try> trys(func->body);
113117
for (auto* try_ : trys.list) {

src/passes/Flatten.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
#include <ir/branch-utils.h>
4343
#include <ir/effects.h>
44+
#include <ir/eh-utils.h>
4445
#include <ir/flat.h>
4546
#include <ir/properties.h>
4647
#include <ir/type-updating.h>
@@ -192,6 +193,37 @@ struct Flatten
192193
loop->finalize();
193194
replaceCurrent(rep);
194195

196+
} else if (auto* tryy = curr->dynCast<Try>()) {
197+
// remove a try value
198+
Expression* rep = tryy;
199+
auto* originalBody = tryy->body;
200+
std::vector<Expression*> originalCatchBodies(tryy->catchBodies.begin(),
201+
tryy->catchBodies.end());
202+
auto type = tryy->type;
203+
if (type.isConcrete()) {
204+
Index temp = builder.addVar(getFunction(), type);
205+
if (tryy->body->type.isConcrete()) {
206+
tryy->body = builder.makeLocalSet(temp, tryy->body);
207+
}
208+
for (Index i = 0; i < tryy->catchBodies.size(); i++) {
209+
if (tryy->catchBodies[i]->type.isConcrete()) {
210+
tryy->catchBodies[i] =
211+
builder.makeLocalSet(temp, tryy->catchBodies[i]);
212+
}
213+
}
214+
// and we leave just a get of the value
215+
rep = builder.makeLocalGet(temp, type);
216+
// the whole try is now a prelude
217+
ourPreludes.push_back(tryy);
218+
}
219+
tryy->body = getPreludesWithExpression(originalBody, tryy->body);
220+
for (Index i = 0; i < tryy->catchBodies.size(); i++) {
221+
tryy->catchBodies[i] = getPreludesWithExpression(
222+
originalCatchBodies[i], tryy->catchBodies[i]);
223+
}
224+
tryy->finalize();
225+
replaceCurrent(rep);
226+
195227
} else {
196228
WASM_UNREACHABLE("unexpected expr type");
197229
}
@@ -347,6 +379,10 @@ struct Flatten
347379
<< type;
348380
}
349381
}
382+
383+
// Flatten can generate blocks within 'catch', making pops invalid. Fix them
384+
// up.
385+
EHUtils::handleBlockNestedPops(curr, *getModule());
350386
}
351387

352388
private:

test/lit/passes/flatten-eh.wast

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
;; RUN: wasm-opt %s -all --flatten -S -o - | filecheck %s
3+
4+
(module
5+
;; CHECK: (tag $e-i32 (param i32))
6+
(tag $e-i32 (param i32))
7+
;; CHECK: (tag $e-f32 (param f32))
8+
(tag $e-f32 (param f32))
9+
10+
;; CHECK: (func $try_catch
11+
;; CHECK-NEXT: (local $x i32)
12+
;; CHECK-NEXT: (local $1 i32)
13+
;; CHECK-NEXT: (local $2 f32)
14+
;; CHECK-NEXT: (try $try
15+
;; CHECK-NEXT: (do
16+
;; CHECK-NEXT: (throw $e-i32
17+
;; CHECK-NEXT: (i32.const 0)
18+
;; CHECK-NEXT: )
19+
;; CHECK-NEXT: (unreachable)
20+
;; CHECK-NEXT: )
21+
;; CHECK-NEXT: (catch $e-i32
22+
;; CHECK-NEXT: (local.set $1
23+
;; CHECK-NEXT: (pop i32)
24+
;; CHECK-NEXT: )
25+
;; CHECK-NEXT: (drop
26+
;; CHECK-NEXT: (local.get $1)
27+
;; CHECK-NEXT: )
28+
;; CHECK-NEXT: )
29+
;; CHECK-NEXT: (catch $e-f32
30+
;; CHECK-NEXT: (local.set $2
31+
;; CHECK-NEXT: (pop f32)
32+
;; CHECK-NEXT: )
33+
;; CHECK-NEXT: (drop
34+
;; CHECK-NEXT: (local.get $2)
35+
;; CHECK-NEXT: )
36+
;; CHECK-NEXT: )
37+
;; CHECK-NEXT: )
38+
;; CHECK-NEXT: )
39+
(func $try_catch (local $x i32)
40+
;; Basic try-catch test
41+
(try
42+
(do
43+
(throw $e-i32 (i32.const 0))
44+
)
45+
(catch $e-i32
46+
(drop
47+
(pop i32)
48+
)
49+
)
50+
(catch $e-f32
51+
(drop
52+
(pop f32)
53+
)
54+
)
55+
)
56+
)
57+
58+
;; CHECK: (func $try_catch_pop_fixup
59+
;; CHECK-NEXT: (local $0 i32)
60+
;; CHECK-NEXT: (local $1 i32)
61+
;; CHECK-NEXT: (block $l0
62+
;; CHECK-NEXT: (try $try
63+
;; CHECK-NEXT: (do
64+
;; CHECK-NEXT: (nop)
65+
;; CHECK-NEXT: )
66+
;; CHECK-NEXT: (catch $e-i32
67+
;; CHECK-NEXT: (local.set $1
68+
;; CHECK-NEXT: (pop i32)
69+
;; CHECK-NEXT: )
70+
;; CHECK-NEXT: (block
71+
;; CHECK-NEXT: (block
72+
;; CHECK-NEXT: (local.set $0
73+
;; CHECK-NEXT: (local.get $1)
74+
;; CHECK-NEXT: )
75+
;; CHECK-NEXT: (drop
76+
;; CHECK-NEXT: (local.get $0)
77+
;; CHECK-NEXT: )
78+
;; CHECK-NEXT: (br $l0)
79+
;; CHECK-NEXT: (unreachable)
80+
;; CHECK-NEXT: )
81+
;; CHECK-NEXT: (unreachable)
82+
;; CHECK-NEXT: )
83+
;; CHECK-NEXT: )
84+
;; CHECK-NEXT: )
85+
;; CHECK-NEXT: )
86+
;; CHECK-NEXT: )
87+
(func $try_catch_pop_fixup
88+
;; After --flatten, a block is created within the 'catch', which makes the
89+
;; pop's location invalid. This tests whether it is fixed up correctly after
90+
;; --flatten.
91+
(block $l0
92+
(try
93+
(do)
94+
(catch $e-i32
95+
(drop
96+
(pop i32)
97+
)
98+
(br $l0)
99+
)
100+
)
101+
)
102+
)
103+
104+
;; CHECK: (func $try_catch_return_value (result i32)
105+
;; CHECK-NEXT: (local $0 i32)
106+
;; CHECK-NEXT: (local $1 i32)
107+
;; CHECK-NEXT: (local $2 i32)
108+
;; CHECK-NEXT: (try $try
109+
;; CHECK-NEXT: (do
110+
;; CHECK-NEXT: (local.set $1
111+
;; CHECK-NEXT: (i32.const 0)
112+
;; CHECK-NEXT: )
113+
;; CHECK-NEXT: )
114+
;; CHECK-NEXT: (catch $e-i32
115+
;; CHECK-NEXT: (local.set $0
116+
;; CHECK-NEXT: (pop i32)
117+
;; CHECK-NEXT: )
118+
;; CHECK-NEXT: (local.set $1
119+
;; CHECK-NEXT: (local.get $0)
120+
;; CHECK-NEXT: )
121+
;; CHECK-NEXT: )
122+
;; CHECK-NEXT: )
123+
;; CHECK-NEXT: (local.set $2
124+
;; CHECK-NEXT: (local.get $1)
125+
;; CHECK-NEXT: )
126+
;; CHECK-NEXT: (return
127+
;; CHECK-NEXT: (local.get $2)
128+
;; CHECK-NEXT: )
129+
;; CHECK-NEXT: )
130+
(func $try_catch_return_value (result i32)
131+
(try (result i32)
132+
(do
133+
(i32.const 0)
134+
)
135+
(catch $e-i32
136+
(pop i32)
137+
)
138+
)
139+
)
140+
141+
;; CHECK: (func $try_unreachable (result i32)
142+
;; CHECK-NEXT: (local $0 i32)
143+
;; CHECK-NEXT: (local $1 i32)
144+
;; CHECK-NEXT: (local $2 i32)
145+
;; CHECK-NEXT: (try $try
146+
;; CHECK-NEXT: (do
147+
;; CHECK-NEXT: (unreachable)
148+
;; CHECK-NEXT: (unreachable)
149+
;; CHECK-NEXT: )
150+
;; CHECK-NEXT: (catch $e-i32
151+
;; CHECK-NEXT: (local.set $0
152+
;; CHECK-NEXT: (pop i32)
153+
;; CHECK-NEXT: )
154+
;; CHECK-NEXT: (local.set $1
155+
;; CHECK-NEXT: (local.get $0)
156+
;; CHECK-NEXT: )
157+
;; CHECK-NEXT: )
158+
;; CHECK-NEXT: )
159+
;; CHECK-NEXT: (local.set $2
160+
;; CHECK-NEXT: (local.get $1)
161+
;; CHECK-NEXT: )
162+
;; CHECK-NEXT: (return
163+
;; CHECK-NEXT: (local.get $2)
164+
;; CHECK-NEXT: )
165+
;; CHECK-NEXT: )
166+
(func $try_unreachable (result i32)
167+
(try (result i32)
168+
(do
169+
(unreachable)
170+
)
171+
(catch $e-i32
172+
(pop i32)
173+
)
174+
)
175+
)
176+
177+
;; CHECK: (func $catch_unreachable (result i32)
178+
;; CHECK-NEXT: (local $0 i32)
179+
;; CHECK-NEXT: (local $1 i32)
180+
;; CHECK-NEXT: (local $2 i32)
181+
;; CHECK-NEXT: (local $3 i32)
182+
;; CHECK-NEXT: (local $4 i32)
183+
;; CHECK-NEXT: (local $5 i32)
184+
;; CHECK-NEXT: (try $try
185+
;; CHECK-NEXT: (do
186+
;; CHECK-NEXT: (local.set $3
187+
;; CHECK-NEXT: (i32.const 3)
188+
;; CHECK-NEXT: )
189+
;; CHECK-NEXT: )
190+
;; CHECK-NEXT: (catch $e-i32
191+
;; CHECK-NEXT: (local.set $5
192+
;; CHECK-NEXT: (pop i32)
193+
;; CHECK-NEXT: )
194+
;; CHECK-NEXT: (block
195+
;; CHECK-NEXT: (block
196+
;; CHECK-NEXT: (local.set $0
197+
;; CHECK-NEXT: (local.get $5)
198+
;; CHECK-NEXT: )
199+
;; CHECK-NEXT: (drop
200+
;; CHECK-NEXT: (local.get $0)
201+
;; CHECK-NEXT: )
202+
;; CHECK-NEXT: (unreachable)
203+
;; CHECK-NEXT: (unreachable)
204+
;; CHECK-NEXT: )
205+
;; CHECK-NEXT: (local.set $2
206+
;; CHECK-NEXT: (local.get $1)
207+
;; CHECK-NEXT: )
208+
;; CHECK-NEXT: (local.set $3
209+
;; CHECK-NEXT: (local.get $2)
210+
;; CHECK-NEXT: )
211+
;; CHECK-NEXT: )
212+
;; CHECK-NEXT: )
213+
;; CHECK-NEXT: )
214+
;; CHECK-NEXT: (local.set $4
215+
;; CHECK-NEXT: (local.get $3)
216+
;; CHECK-NEXT: )
217+
;; CHECK-NEXT: (return
218+
;; CHECK-NEXT: (local.get $4)
219+
;; CHECK-NEXT: )
220+
;; CHECK-NEXT: )
221+
(func $catch_unreachable (result i32)
222+
(try (result i32)
223+
(do
224+
(i32.const 3)
225+
)
226+
(catch $e-i32
227+
(drop
228+
(pop i32)
229+
)
230+
(unreachable)
231+
)
232+
)
233+
)
234+
)

0 commit comments

Comments
 (0)