1# RUN: llc -mtriple=wasm32-unknown-unknown -exception-model=wasm -run-pass wasm-cfg-stackify %s -o - | FileCheck %s
2
3--- |
4  target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
5  target triple = "wasm32-unknown-unknown"
6
7  @__wasm_lpad_context = external global { i32, i8*, i32 }
8
9  declare void @may_throw()
10  ; Function Attrs: nounwind
11  declare void @dont_throw() #0
12  declare i8* @__cxa_begin_catch(i8*)
13  declare void @__cxa_end_catch()
14  declare void @__cxa_rethrow()
15  ; Function Attrs: nounwind
16  declare i32 @__gxx_wasm_personality_v0(...)
17  declare i32 @_Unwind_CallPersonality(i8*) #0
18
19  define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
20    unreachable
21  }
22  define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
23    unreachable
24  }
25  define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
26    unreachable
27  }
28  define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
29    unreachable
30  }
31
32  attributes #0 = { nounwind }
33
34---
35# Simplest try-catch
36# try {
37#   may_throw();
38# } catch (...) {
39# }
40name: test0
41# CHECK-LABEL: name: test0
42liveins:
43  - { reg: '$arguments', reg: '$value_stack' }
44body: |
45  bb.0:
46    successors: %bb.2, %bb.1
47
48    CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
49    BR %bb.2, implicit-def $arguments
50  ; CHECK-LABEL: bb.0:
51    ; CHECK: TRY
52    ; CHECK-NEXT: CALL_VOID @may_throw
53
54  bb.1 (landing-pad):
55  ; predecessors: %bb.0
56    successors: %bb.2
57
58    %2:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
59    %3:i32 = CALL_I32 @__cxa_begin_catch, %2:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
60    DROP_I32 killed %3:i32, implicit-def $arguments
61    CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
62
63  bb.2:
64  ; predecessors: %bb.0, %bb.1
65
66    RETURN_VOID implicit-def dead $arguments
67  ; CHECK-LABEL: bb.2:
68    ; CHECK-NEXT: END_TRY
69    ; CHECK: RETURN_VOID
70...
71---
72
73# Nested try-catch inside another catch
74#  try {
75#    may_throw();
76#  } catch (int n) {
77#    try {
78#      may_throw();
79#    } catch (int n) {
80#    }
81#  }
82name: test1
83# CHECK-LABEL: name: test1
84liveins:
85  - { reg: '$arguments', reg: '$value_stack' }
86body: |
87  bb.0:
88    successors: %bb.9, %bb.1
89
90    CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
91    BR %bb.9, implicit-def $arguments
92  ; CHECK-LABEL: bb.0:
93    ; CHECK: TRY
94    ; CHECK-NEXT: CALL_VOID @may_throw
95
96  bb.1 (landing-pad):
97  ; predecessors: %bb.0
98    successors: %bb.2, %bb.7
99
100    %30:i32 = CATCH_I32 0, implicit-def dead $arguments
101    SET_LOCAL_I32 0, %30:i32, implicit-def $arguments
102    %16:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
103    %27:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
104    STORE_I32 2, @__wasm_lpad_context + 4, %16:i32, %27:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i8** getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 1)`)
105    %26:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
106    %25:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
107    STORE_I32 2, @__wasm_lpad_context, %26:i32, %25:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
108    %32:i32 = GET_LOCAL_I32 0, implicit-def $arguments
109    %31:i32 = CALL_I32 @_Unwind_CallPersonality, %32:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
110    DROP_I32 killed %31:i32, implicit-def $arguments
111    %24:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
112    %17:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %24:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
113    %18:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
114    %19:i32 = NE_I32 %17:i32, %18:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
115    BR_IF %bb.7, %19:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
116
117  bb.2:
118  ; predecessors: %bb.1
119    successors: %bb.8, %bb.3, %bb.6
120
121    %34:i32 = GET_LOCAL_I32 0, implicit-def $arguments
122    %33:i32 = CALL_I32 @__cxa_begin_catch, %34:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
123    DROP_I32 killed %33:i32, implicit-def $arguments
124    CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
125    BR %bb.8, implicit-def $arguments
126  ; CHECK-LABEL: bb.2:
127    ; CHECK: DROP_I32
128    ; CHECK-NEXT: TRY
129    ; CHECK-NEXT: TRY
130    ; CHECK-NEXT: CALL_VOID @may_throw
131
132  bb.3 (landing-pad):
133  ; predecessors: %bb.2
134    successors: %bb.4, %bb.5
135
136    %35:i32 = CATCH_I32 0, implicit-def dead $arguments
137    SET_LOCAL_I32 0, %35:i32, implicit-def $arguments
138    %21:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
139    %20:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
140    STORE_I32 2, @__wasm_lpad_context, %21:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store 4 into `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 0)`)
141    %37:i32 = GET_LOCAL_I32 0, implicit-def $arguments
142    %36:i32 = CALL_I32 @_Unwind_CallPersonality, %37:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
143    DROP_I32 killed %36:i32, implicit-def $arguments
144    %29:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
145    %22:i32 = LOAD_I32 2, @__wasm_lpad_context + 8, %29:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (dereferenceable load 4 from `i32* getelementptr inbounds ({ i32, i8*, i32 }, { i32, i8*, i32 }* @__wasm_lpad_context, i32 0, i32 2)`)
146    %28:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
147    %23:i32 = NE_I32 %22:i32, %28:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
148    BR_IF %bb.5, %23:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
149
150  bb.4:
151  ; predecessors: %bb.3
152    successors: %bb.8
153
154    %39:i32 = GET_LOCAL_I32 0, implicit-def $arguments
155    %38:i32 = CALL_I32 @__cxa_begin_catch, %39:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64
156    DROP_I32 killed %38:i32, implicit-def $arguments
157    CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
158    BR %bb.8, implicit-def $arguments
159
160  bb.5:
161  ; predecessors: %bb.3
162    successors: %bb.6
163
164    CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
165    RETHROW %bb.6, implicit-def $arguments
166
167  bb.6 (landing-pad):
168  ; predecessors: %bb.2, %bb.5
169
170    CATCH_ALL implicit-def $arguments
171    CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
172    RETHROW_TO_CALLER implicit-def $arguments
173  ; CHECK-LABEL: bb.6 (landing-pad):
174    ; CHECK-NEXT: END_TRY
175
176  bb.7:
177  ; predecessors: %bb.1
178
179    CALL_VOID @__cxa_rethrow, implicit-def dead $arguments, implicit $sp32, implicit $sp64
180    RETHROW_TO_CALLER implicit-def $arguments
181  ; CHECK-LABEL: bb.7:
182    ; CHECK-NEXT: END_TRY
183    ; CHECK: RETHROW 0
184
185  bb.8:
186  ; predecessors: %bb.2, %bb.4
187    successors: %bb.9
188
189    CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
190
191  bb.9:
192  ; predecessors: %bb.0, %bb.8
193
194    RETURN_VOID implicit-def dead $arguments
195  ; CHECK-LABEL: bb.9:
196    ; CHECK-NEXT: END_TRY
197...
198---
199
200# A loop within a try.
201#  try {
202#    for (int i = 0; i < n; ++i)
203#      may_throw();
204#  } catch (...) {
205#  }
206name: test2
207# CHECK-LABEL: name: test2
208liveins:
209  - { reg: '$arguments', reg: '$value_stack' }
210body: |
211  bb.0:
212    successors: %bb.1, %bb.4
213
214    %18:i32 = CONST_I32 0, implicit-def dead $arguments
215    SET_LOCAL_I32 1, %18:i32, implicit-def $arguments
216    %14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
217    %19:i32 = GET_LOCAL_I32 0, implicit-def $arguments
218    %9:i32 = GE_S_I32 %14:i32, %19:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
219    BR_IF %bb.4, %9:i32, implicit-def $arguments
220
221  bb.1:
222  ; predecessors: %bb.0, %bb.3
223    successors: %bb.3, %bb.2
224
225    CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
226    BR %bb.3, implicit-def $arguments
227  ; CHECK-LABEL: bb.1:
228    ; CHECK: LOOP
229    ; CHECK: TRY
230    ; CHECK-NEXT: CALL_VOID @may_throw
231
232  bb.2 (landing-pad):
233  ; predecessors: %bb.1
234    successors: %bb.4
235
236    %11:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
237    %22:i32 = CALL_I32 @__cxa_begin_catch, %11:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
238    DROP_I32 killed %22:i32, implicit-def $arguments
239    CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
240    BR %bb.4, implicit-def $arguments
241
242  bb.3:
243  ; predecessors: %bb.1
244    successors: %bb.1, %bb.4
245
246    %20:i32 = GET_LOCAL_I32 1, implicit-def $arguments
247    %17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
248    %16:i32 = ADD_I32 %20:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
249    %15:i32 = TEE_LOCAL_I32 1, %16:i32, implicit-def $arguments
250    %21:i32 = GET_LOCAL_I32 0, implicit-def $arguments
251    %10:i32 = GE_S_I32 %15:i32, %21:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
252    BR_UNLESS %bb.1, %10:i32, implicit-def $arguments
253  ; CHECK-LABEL: bb.3:
254    ; CHECK: END_TRY
255
256  bb.4:
257  ; predecessors: %bb.2, %bb.0, %bb.3
258
259    RETURN_VOID implicit-def dead $arguments
260...
261---
262
263# A loop within a catch
264#  try {
265#    may_throw();
266#  } catch (...) {
267#    for (int i = 0; i < n; ++i)
268#      dont_throw();
269#  }
270name: test3
271# CHECK-LABEL: name: test3
272liveins:
273  - { reg: '$arguments', reg: '$value_stack' }
274body: |
275  bb.0:
276    successors: %bb.4, %bb.1
277
278    CALL_VOID @may_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
279    BR %bb.4, implicit-def $arguments
280  ; CHECK-LABEL: bb.0:
281    ; CHECK: TRY
282    ; CHECK-NEXT: CALL_VOID @may_throw
283
284  bb.1 (landing-pad):
285  ; predecessors: %bb.0
286    successors: %bb.2, %bb.3
287
288    %9:i32 = CATCH_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
289    %18:i32 = CALL_I32 @__cxa_begin_catch, %9:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def $value_stack, implicit $value_stack
290    DROP_I32 killed %18:i32, implicit-def $arguments
291    %19:i32 = CONST_I32 0, implicit-def dead $arguments
292    SET_LOCAL_I32 1, %19:i32, implicit-def $arguments
293    %14:i32 = CONST_I32 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
294    %20:i32 = GET_LOCAL_I32 0, implicit-def $arguments
295    %10:i32 = GE_S_I32 %14:i32, %20:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
296    BR_IF %bb.3, %10:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
297
298  bb.2:
299  ; predecessors: %bb.1, %bb.2
300    successors: %bb.2, %bb.3
301
302    CALL_VOID @dont_throw, implicit-def dead $arguments, implicit $sp32, implicit $sp64
303    %21:i32 = GET_LOCAL_I32 1, implicit-def $arguments
304    %17:i32 = CONST_I32 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
305    %16:i32 = ADD_I32 %21:i32, %17:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
306    %15:i32 = TEE_LOCAL_I32 1, %16:i32, implicit-def $arguments
307    %22:i32 = GET_LOCAL_I32 0, implicit-def $arguments
308    %11:i32 = GE_S_I32 %15:i32, %22:i32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack
309    BR_UNLESS %bb.2, %11:i32, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack
310
311  bb.3:
312  ; predecessors: %bb.1, %bb.2
313    successors: %bb.4
314
315    CALL_VOID @__cxa_end_catch, implicit-def dead $arguments, implicit $sp32, implicit $sp64
316
317  bb.4:
318  ; predecessors: %bb.0, %bb.3
319
320    RETURN_VOID implicit-def dead $arguments
321  ; CHECK-LABEL: bb.4:
322    ; CHECK: END_TRY
323