1; RUN: not --crash 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     $[[EXNREF:[0-9]+]]=
35; CHECK:       global.set  __stack_pointer
36; CHECK:       block i32
37; CHECK:         br_on_exn 0, __cpp_exception, $[[EXNREF]]
38; CHECK:         rethrow   $[[EXNREF]]
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:       call       $drop=, _Unwind_CallPersonality, $[[EXN]]
44; CHECK:       block
45; CHECK:         br_if     0
46; CHECK:         call      $drop=, __cxa_begin_catch
47; CHECK:         call      __cxa_end_catch
48; CHECK:         br        1
49; CHECK:       end_block
50; CHECK:       rethrow   $[[EXNREF]]
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 = %catch, %entry
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     $[[EXNREF:[0-9]+]]=
96; CHECK:   global.set  __stack_pointer
97; CHECK:   call      $drop=, _ZN4TempD2Ev
98; CHECK:   rethrow   $[[EXNREF]]
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:   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:         i32.const  ${{.*}}=, 0
142; CHECK:         call      __clang_call_terminate
143; CHECK:         unreachable
144; CHECK:       end_block
145; CHECK:       call      __clang_call_terminate
146; CHECK:       unreachable
147; CHECK:     end_try
148; CHECK:     rethrow
149; CHECK:   end_try
150; CHECK:   call      __cxa_end_catch
151; CHECK: end_try
152define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
153entry:
154  invoke void @foo()
155          to label %try.cont unwind label %catch.dispatch
156
157catch.dispatch:                                   ; preds = %entry
158  %0 = catchswitch within none [label %catch.start] unwind to caller
159
160catch.start:                                      ; preds = %catch.dispatch
161  %1 = catchpad within %0 [i8* null]
162  %2 = call i8* @llvm.wasm.get.exception(token %1)
163  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
164  %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
165  invoke void @foo() [ "funclet"(token %1) ]
166          to label %invoke.cont1 unwind label %ehcleanup
167
168invoke.cont1:                                     ; preds = %catch.start
169  call void @__cxa_end_catch() [ "funclet"(token %1) ]
170  catchret from %1 to label %try.cont
171
172try.cont:                                         ; preds = %invoke.cont1, %entry
173  ret void
174
175ehcleanup:                                        ; preds = %catch.start
176  %5 = cleanuppad within %1 []
177  invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
178          to label %invoke.cont2 unwind label %terminate
179
180invoke.cont2:                                     ; preds = %ehcleanup
181  cleanupret from %5 unwind to caller
182
183terminate:                                        ; preds = %ehcleanup
184  %6 = cleanuppad within %5 []
185  %7 = call i8* @llvm.wasm.get.exception(token %6)
186  call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ]
187  unreachable
188}
189
190; Tests prologues and epilogues are not generated within EH scopes.
191; They should not be treated as funclets; BBs starting with a catch instruction
192; should not have a prologue, and BBs ending with a catchret/cleanupret should
193; not have an epilogue. This is separate from __stack_pointer restoring
194; instructions after a catch instruction.
195;
196; void bar(int) noexcept;
197; void test_no_prolog_epilog_in_ehpad() {
198;   int stack_var = 0;
199;   bar(stack_var);
200;   try {
201;     foo();
202;   } catch (int) {
203;     foo();
204;   }
205; }
206
207; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
208; CHECK:     try
209; CHECK:       call      foo
210; CHECK:     catch
211; CHECK-NOT:   global.get  $push{{.+}}=, __stack_pointer
212; CHECK:       global.set  __stack_pointer
213; CHECK:       block
214; CHECK:         block
215; CHECK:           br_if     0
216; CHECK:           call      $drop=, __cxa_begin_catch
217; CHECK:           try
218; CHECK:             call      foo
219; CHECK:           catch
220; CHECK-NOT:         global.get  $push{{.+}}=, __stack_pointer
221; CHECK:             global.set  __stack_pointer
222; CHECK:             call      __cxa_end_catch
223; CHECK:             rethrow
224; CHECK-NOT:         global.set  __stack_pointer, $pop{{.+}}
225; CHECK:           end_try
226; CHECK:         end_block
227; CHECK:         rethrow
228; CHECK:       end_block
229; CHECK-NOT:   global.set  __stack_pointer, $pop{{.+}}
230; CHECK:       call      __cxa_end_catch
231; CHECK:     end_try
232define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
233entry:
234  %stack_var = alloca i32, align 4
235  call void @bar(i32* %stack_var)
236  invoke void @foo()
237          to label %try.cont unwind label %catch.dispatch
238
239catch.dispatch:                                   ; preds = %entry
240  %0 = catchswitch within none [label %catch.start] unwind to caller
241
242catch.start:                                      ; preds = %catch.dispatch
243  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
244  %2 = call i8* @llvm.wasm.get.exception(token %1)
245  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
246  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
247  %matches = icmp eq i32 %3, %4
248  br i1 %matches, label %catch, label %rethrow
249
250catch:                                            ; preds = %catch.start
251  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
252  %6 = bitcast i8* %5 to float*
253  %7 = load float, float* %6, align 4
254  invoke void @foo() [ "funclet"(token %1) ]
255          to label %invoke.cont1 unwind label %ehcleanup
256
257invoke.cont1:                                     ; preds = %catch
258  call void @__cxa_end_catch() [ "funclet"(token %1) ]
259  catchret from %1 to label %try.cont
260
261rethrow:                                          ; preds = %catch.start
262  call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
263  unreachable
264
265try.cont:                                         ; preds = %invoke.cont1, %entry
266  ret void
267
268ehcleanup:                                        ; preds = %catch
269  %8 = cleanuppad within %1 []
270  call void @__cxa_end_catch() [ "funclet"(token %8) ]
271  cleanupret from %8 unwind to caller
272}
273
274; When a function does not have stack-allocated objects, it does not need to
275; store SP back to __stack_pointer global at the epilog.
276;
277; void foo();
278; void test_no_sp_writeback() {
279;   try {
280;     foo();
281;   } catch (...) {
282;   }
283; }
284
285; CHECK-LABEL: test_no_sp_writeback
286; CHECK:     try
287; CHECK:       call      foo
288; CHECK:     catch
289; CHECK:       call      $drop=, __cxa_begin_catch
290; CHECK:       call      __cxa_end_catch
291; CHECK:     end_try
292; CHECK-NOT: global.set  __stack_pointer
293; CHECK:     return
294define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
295entry:
296  invoke void @foo()
297          to label %try.cont unwind label %catch.dispatch
298
299catch.dispatch:                                   ; preds = %entry
300  %0 = catchswitch within none [label %catch.start] unwind to caller
301
302catch.start:                                      ; preds = %catch.dispatch
303  %1 = catchpad within %0 [i8* null]
304  %2 = call i8* @llvm.wasm.get.exception(token %1)
305  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
306  %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
307  call void @__cxa_end_catch() [ "funclet"(token %1) ]
308  catchret from %1 to label %try.cont
309
310try.cont:                                         ; preds = %catch.start, %entry
311  ret void
312}
313
314; When the result of @llvm.wasm.get.exception is not used. This is created to
315; fix a bug in LateEHPrepare and this should not crash.
316define void @test_get_exception_wo_use() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
317entry:
318  invoke void @foo()
319          to label %try.cont unwind label %catch.dispatch
320
321catch.dispatch:                                   ; preds = %entry
322  %0 = catchswitch within none [label %catch.start] unwind to caller
323
324catch.start:                                      ; preds = %catch.dispatch
325  %1 = catchpad within %0 [i8* null]
326  %2 = call i8* @llvm.wasm.get.exception(token %1)
327  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
328  catchret from %1 to label %try.cont
329
330try.cont:                                         ; preds = %catch.start, %entry
331  ret void
332}
333
334; Tests a case when a cleanup region (cleanuppad ~ clanupret) contains another
335; catchpad
336define void @test_complex_cleanup_region() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
337entry:
338  invoke void @foo()
339          to label %invoke.cont unwind label %ehcleanup
340
341invoke.cont:                                      ; preds = %entry
342  ret void
343
344ehcleanup:                                        ; preds = %entry
345  %0 = cleanuppad within none []
346  invoke void @foo() [ "funclet"(token %0) ]
347          to label %ehcleanupret unwind label %catch.dispatch
348
349catch.dispatch:                                   ; preds = %ehcleanup
350  %1 = catchswitch within %0 [label %catch.start] unwind label %ehcleanup.1
351
352catch.start:                                      ; preds = %catch.dispatch
353  %2 = catchpad within %1 [i8* null]
354  %3 = call i8* @llvm.wasm.get.exception(token %2)
355  %4 = call i32 @llvm.wasm.get.ehselector(token %2)
356  catchret from %2 to label %ehcleanupret
357
358ehcleanup.1:                                      ; preds = %catch.dispatch
359  %5 = cleanuppad within %0 []
360  unreachable
361
362ehcleanupret:                                     ; preds = %catch.start, %ehcleanup
363  cleanupret from %0 unwind to caller
364}
365
366declare void @foo()
367declare void @bar(i32*)
368declare i32 @__gxx_wasm_personality_v0(...)
369declare void @llvm.wasm.throw(i32, i8*)
370declare i8* @llvm.wasm.get.exception(token)
371declare i32 @llvm.wasm.get.ehselector(token)
372declare void @llvm.wasm.rethrow.in.catch()
373declare i32 @llvm.eh.typeid.for(i8*)
374declare i8* @__cxa_begin_catch(i8*)
375declare void @__cxa_end_catch()
376declare void @__clang_call_terminate(i8*)
377declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
378
379; CHECK: __cpp_exception:
380; CHECK: .eventtype  __cpp_exception i32
381