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