1; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm
2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs | FileCheck -allow-deprecated-dag-overlap %s
3; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling
4
5target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6target triple = "wasm32-unknown-unknown"
7
8%struct.Temp = type { i8 }
9
10@_ZTIi = external constant i8*
11
12; CHECK-LABEL: test_throw:
13; CHECK:     throw __cpp_exception, $0
14; CHECK-NOT: unreachable
15define void @test_throw(i8* %p) {
16  call void @llvm.wasm.throw(i32 0, i8* %p)
17  ret void
18}
19
20; CHECK-LABEL: test_rethrow:
21; CHECK:     rethrow
22; CHECK-NOT: unreachable
23define void @test_rethrow(i8* %p) {
24  call void @llvm.wasm.rethrow()
25  ret void
26}
27
28; Simple test with a try-catch
29;
30; void foo();
31; void test_catch() {
32;   try {
33;     foo();
34;   } catch (int) {
35;   }
36; }
37
38; CHECK-LABEL: test_catch:
39; CHECK:     global.get  ${{.+}}=, __stack_pointer
40; CHECK:     try
41; CHECK:       call      foo
42; CHECK:     catch     $[[EXCEPT_REF:[0-9]+]]=
43; CHECK:       global.set  __stack_pointer
44; CHECK:       block i32
45; CHECK:         br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
46; CHECK:         rethrow
47; CHECK:       end_block
48; CHECK:       extract_exception $[[EXN:[0-9]+]]=
49; CHECK-DAG:   i32.store  __wasm_lpad_context
50; CHECK-DAG:   i32.store  __wasm_lpad_context+4
51; CHECK:       i32.call  $drop=, _Unwind_CallPersonality, $[[EXN]]
52; CHECK:       block
53; CHECK:         br_if     0
54; CHECK:         i32.call  $drop=, __cxa_begin_catch
55; CHECK:         call      __cxa_end_catch
56; CHECK:         br        1
57; CHECK:       end_block
58; CHECK:       call      __cxa_rethrow
59; CHECK:     end_try
60define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
61entry:
62  invoke void @foo()
63          to label %try.cont unwind label %catch.dispatch
64
65catch.dispatch:                                   ; preds = %entry
66  %0 = catchswitch within none [label %catch.start] unwind to caller
67
68catch.start:                                      ; preds = %catch.dispatch
69  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
70  %2 = call i8* @llvm.wasm.get.exception(token %1)
71  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
72  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
73  %matches = icmp eq i32 %3, %4
74  br i1 %matches, label %catch, label %rethrow
75
76catch:                                            ; preds = %catch.start
77  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
78  call void @__cxa_end_catch() [ "funclet"(token %1) ]
79  catchret from %1 to label %try.cont
80
81rethrow:                                          ; preds = %catch.start
82  call void @__cxa_rethrow() [ "funclet"(token %1) ]
83  unreachable
84
85try.cont:                                         ; preds = %entry, %catch
86  ret void
87}
88
89; Destructor (cleanup) test
90;
91; void foo();
92; struct Temp {
93;   ~Temp() {}
94; };
95; void test_cleanup() {
96;   Temp t;
97;   foo();
98; }
99
100; CHECK-LABEL: test_cleanup:
101; CHECK: try
102; CHECK:   call      foo
103; CHECK: catch
104; CHECK:   global.set  __stack_pointer
105; CHECK:   i32.call  $drop=, _ZN4TempD2Ev
106; CHECK:   rethrow
107; CHECK: end_try
108define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
109entry:
110  %t = alloca %struct.Temp, align 1
111  invoke void @foo()
112          to label %invoke.cont unwind label %ehcleanup
113
114invoke.cont:                                      ; preds = %entry
115  %call = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t)
116  ret void
117
118ehcleanup:                                        ; preds = %entry
119  %0 = cleanuppad within none []
120  %call1 = call %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* %t) [ "funclet"(token %0) ]
121  cleanupret from %0 unwind to caller
122}
123
124; Calling a function that may throw within a 'catch (...)' generates a
125; temrinatepad, because __cxa_end_catch() also can throw within 'catch (...)'.
126;
127; void foo();
128; void test_terminatepad() {
129;   try {
130;     foo();
131;   } catch (...) {
132;     foo();
133;   }
134; }
135
136; CHECK-LABEL: test_terminatepad
137; CHECK: try
138; CHECK:   call      foo
139; CHECK: catch
140; CHECK:   i32.call  $drop=, __cxa_begin_catch
141; CHECK:   try
142; CHECK:     call      foo
143; CHECK:   catch
144; CHECK:     try
145; CHECK:       call      __cxa_end_catch
146; CHECK:     catch
147; CHECK:       block     i32
148; CHECK:         br_on_exn   0, __cpp_exception
149; CHECK:         call      __clang_call_terminate, 0
150; CHECK:         unreachable
151; CHECK:       end_block
152; CHECK:       call      __clang_call_terminate
153; CHECK:       unreachable
154; CHECK:     end_try
155; CHECK:     rethrow
156; CHECK:   end_try
157; CHECK:   call      __cxa_end_catch
158; CHECK: end_try
159define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
160entry:
161  invoke void @foo()
162          to label %try.cont unwind label %catch.dispatch
163
164catch.dispatch:                                   ; preds = %entry
165  %0 = catchswitch within none [label %catch.start] unwind to caller
166
167catch.start:                                      ; preds = %catch.dispatch
168  %1 = catchpad within %0 [i8* null]
169  %2 = call i8* @llvm.wasm.get.exception(token %1)
170  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
171  %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
172  invoke void @foo() [ "funclet"(token %1) ]
173          to label %invoke.cont1 unwind label %ehcleanup
174
175invoke.cont1:                                     ; preds = %catch.start
176  call void @__cxa_end_catch() [ "funclet"(token %1) ]
177  catchret from %1 to label %try.cont
178
179try.cont:                                         ; preds = %entry, %invoke.cont1
180  ret void
181
182ehcleanup:                                        ; preds = %catch.start
183  %5 = cleanuppad within %1 []
184  invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
185          to label %invoke.cont2 unwind label %terminate
186
187invoke.cont2:                                     ; preds = %ehcleanup
188  cleanupret from %5 unwind to caller
189
190terminate:                                        ; preds = %ehcleanup
191  %6 = cleanuppad within %5 []
192  %7 = call i8* @llvm.wasm.get.exception(token %6)
193  call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ]
194  unreachable
195}
196
197; Tests prologues and epilogues are not generated within EH scopes.
198; They should not be treated as funclets; BBs starting with a catch instruction
199; should not have a prologue, and BBs ending with a catchret/cleanupret should
200; not have an epilogue. This is separate from __stack_pointer restoring
201; instructions after a catch instruction.
202;
203; void bar(int) noexcept;
204; void test_no_prolog_epilog_in_ehpad() {
205;   int stack_var = 0;
206;   bar(stack_var);
207;   try {
208;     foo();
209;   } catch (int) {
210;     foo();
211;   }
212; }
213
214; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
215; CHECK:     try
216; CHECK:       call      foo
217; CHECK:     catch
218; CHECK-NOT:   global.get  $push{{.+}}=, __stack_pointer
219; CHECK:       global.set  __stack_pointer
220; CHECK:       block
221; CHECK:         block
222; CHECK:           br_if     0
223; CHECK:           i32.call  $drop=, __cxa_begin_catch
224; CHECK:           try
225; CHECK:             call      foo
226; CHECK:           catch
227; CHECK-NOT:         global.get  $push{{.+}}=, __stack_pointer
228; CHECK:             global.set  __stack_pointer
229; CHECK:             call      __cxa_end_catch
230; CHECK:             rethrow
231; CHECK-NOT:         global.set  __stack_pointer, $pop{{.+}}
232; CHECK:           end_try
233; CHECK:         end_block
234; CHECK:         call      __cxa_rethrow
235; CHECK:       end_block
236; CHECK-NOT:   global.set  __stack_pointer, $pop{{.+}}
237; CHECK:       call      __cxa_end_catch
238; CHECK:     end_try
239define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
240entry:
241  %stack_var = alloca i32, align 4
242  call void @bar(i32* %stack_var)
243  invoke void @foo()
244          to label %try.cont unwind label %catch.dispatch
245
246catch.dispatch:                                   ; preds = %entry
247  %0 = catchswitch within none [label %catch.start] unwind to caller
248
249catch.start:                                      ; preds = %catch.dispatch
250  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
251  %2 = call i8* @llvm.wasm.get.exception(token %1)
252  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
253  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
254  %matches = icmp eq i32 %3, %4
255  br i1 %matches, label %catch, label %rethrow
256
257catch:                                            ; preds = %catch.start
258  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
259  %6 = bitcast i8* %5 to float*
260  %7 = load float, float* %6, align 4
261  invoke void @foo() [ "funclet"(token %1) ]
262          to label %invoke.cont1 unwind label %ehcleanup
263
264invoke.cont1:                                     ; preds = %catch
265  call void @__cxa_end_catch() [ "funclet"(token %1) ]
266  catchret from %1 to label %try.cont
267
268rethrow:                                          ; preds = %catch.start
269  call void @__cxa_rethrow() [ "funclet"(token %1) ]
270  unreachable
271
272try.cont:                                         ; preds = %entry, %invoke.cont1
273  ret void
274
275ehcleanup:                                        ; preds = %catch
276  %8 = cleanuppad within %1 []
277  call void @__cxa_end_catch() [ "funclet"(token %8) ]
278  cleanupret from %8 unwind to caller
279}
280
281; When a function does not have stack-allocated objects, it does not need to
282; store SP back to __stack_pointer global at the epilog.
283;
284; void foo();
285; void test_no_sp_writeback() {
286;   try {
287;     foo();
288;   } catch (...) {
289;   }
290; }
291
292; CHECK-LABEL: test_no_sp_writeback
293; CHECK:     try
294; CHECK:       call      foo
295; CHECK:     catch
296; CHECK:       i32.call  $drop=, __cxa_begin_catch
297; CHECK:       call      __cxa_end_catch
298; CHECK:     end_try
299; CHECK-NOT: global.set  __stack_pointer
300; CHECK:     return
301define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
302entry:
303  invoke void @foo()
304          to label %try.cont unwind label %catch.dispatch
305
306catch.dispatch:                                   ; preds = %entry
307  %0 = catchswitch within none [label %catch.start] unwind to caller
308
309catch.start:                                      ; preds = %catch.dispatch
310  %1 = catchpad within %0 [i8* null]
311  %2 = call i8* @llvm.wasm.get.exception(token %1)
312  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
313  %4 = call i8* @__cxa_begin_catch(i8* %2) #2 [ "funclet"(token %1) ]
314  call void @__cxa_end_catch() [ "funclet"(token %1) ]
315  catchret from %1 to label %try.cont
316
317try.cont:                                         ; preds = %entry, %catch.start
318  ret void
319}
320
321; When the result of @llvm.wasm.get.exception is not used. This is created to
322; fix a bug in LateEHPrepare and this should not crash.
323define void @test_get_exception_wo_use() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
324entry:
325  invoke void @foo()
326          to label %try.cont unwind label %catch.dispatch
327
328catch.dispatch:                                   ; preds = %entry
329  %0 = catchswitch within none [label %catch.start] unwind to caller
330
331catch.start:                                      ; preds = %catch.dispatch
332  %1 = catchpad within %0 [i8* null]
333  %2 = call i8* @llvm.wasm.get.exception(token %1)
334  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
335  catchret from %1 to label %try.cont
336
337try.cont:                                         ; preds = %entry, %catch.start
338  ret void
339}
340
341declare void @foo()
342declare void @bar(i32*)
343declare i32 @__gxx_wasm_personality_v0(...)
344declare void @llvm.wasm.throw(i32, i8*)
345declare void @llvm.wasm.rethrow()
346declare i8* @llvm.wasm.get.exception(token)
347declare i32 @llvm.wasm.get.ehselector(token)
348declare i32 @llvm.eh.typeid.for(i8*)
349declare i8* @__cxa_begin_catch(i8*)
350declare void @__cxa_end_catch()
351declare void @__cxa_rethrow()
352declare void @__clang_call_terminate(i8*)
353declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
354
355; CHECK: __cpp_exception:
356; CHECK: .eventtype  __cpp_exception i32
357