1; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s
2
3target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
4target triple = "wasm32-unknown-unknown"
5
6@_ZTIi = external constant i8*
7@_ZTId = external constant i8*
8
9; Simple test case with two catch clauses
10
11; CHECK-LABEL: test0
12; CHECK:   call      foo@FUNCTION
13; CHECK: .LBB0_1:
14; CHECK:   i32.catch
15; CHECK:   i32.call  $drop=, _Unwind_CallPersonality@FUNCTION
16; CHECK:   i32.call  $drop=, __cxa_begin_catch@FUNCTION
17; CHECK:   call      bar@FUNCTION
18; CHECK:   call      __cxa_end_catch@FUNCTION
19; CHECK: .LBB0_3:
20; CHECK:   i32.call  $drop=, __cxa_begin_catch@FUNCTION
21; CHECK:   call      __cxa_end_catch@FUNCTION
22; CHECK: .LBB0_5:
23; CHECK:   call      __cxa_rethrow@FUNCTION
24; CHECK: .LBB0_6:
25; CHECK:   return
26define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
27entry:
28  invoke void @foo()
29          to label %try.cont unwind label %catch.dispatch
30
31catch.dispatch:                                   ; preds = %entry
32  %0 = catchswitch within none [label %catch.start] unwind to caller
33
34catch.start:                                      ; preds = %catch.dispatch
35  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)]
36  %2 = call i8* @llvm.wasm.get.exception(token %1)
37  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
38  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
39  %matches = icmp eq i32 %3, %4
40  br i1 %matches, label %catch2, label %catch.fallthrough
41
42catch2:                                           ; preds = %catch.start
43  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
44  %6 = bitcast i8* %5 to i32*
45  %7 = load i32, i32* %6, align 4
46  call void @bar() [ "funclet"(token %1) ]
47  call void @__cxa_end_catch() [ "funclet"(token %1) ]
48  catchret from %1 to label %try.cont
49
50catch.fallthrough:                                ; preds = %catch.start
51  %8 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*))
52  %matches1 = icmp eq i32 %3, %8
53  br i1 %matches1, label %catch, label %rethrow
54
55catch:                                            ; preds = %catch.fallthrough
56  %9 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
57  %10 = bitcast i8* %9 to double*
58  %11 = load double, double* %10, align 8
59  call void @__cxa_end_catch() [ "funclet"(token %1) ]
60  catchret from %1 to label %try.cont
61
62rethrow:                                          ; preds = %catch.fallthrough
63  call void @__cxa_rethrow() [ "funclet"(token %1) ]
64  unreachable
65
66try.cont:                                         ; preds = %entry, %catch, %catch2
67  ret void
68}
69
70; Nested try-catches within a catch
71
72; CHECK-LABEL: test1
73; CHECK:   call      foo@FUNCTION
74; CHECK: .LBB1_1:
75; CHECK:   i32.catch     $0=, 0
76; CHECK:   i32.call  $drop=, _Unwind_CallPersonality@FUNCTION, $0
77; CHECK:   i32.call  $drop=, __cxa_begin_catch@FUNCTION, $0
78; CHECK:   call      foo@FUNCTION
79; CHECK: .LBB1_3:
80; CHECK:   i32.catch     $0=, 0
81; CHECK:   i32.call  $drop=, _Unwind_CallPersonality@FUNCTION, $0
82; CHECK:   i32.call  $drop=, __cxa_begin_catch@FUNCTION, $0
83; CHECK:   call      foo@FUNCTION
84; CHECK: .LBB1_5:
85; CHECK:   catch_all
86; CHECK:   call      __cxa_end_catch@FUNCTION
87; CHECK:   rethrow
88; CHECK: .LBB1_6:
89; CHECK:   call      __cxa_rethrow@FUNCTION
90; CHECK:   rethrow
91; CHECK: .LBB1_7:
92; CHECK:   call      __cxa_end_catch@FUNCTION
93; CHECK: .LBB1_8:
94; CHECK:   catch_all
95; CHECK:   call      __cxa_end_catch@FUNCTION
96; CHECK: .LBB1_9:
97; CHECK:   call      __cxa_rethrow@FUNCTION
98; CHECK:   rethrow
99; CHECK: .LBB1_10:
100; CHECK:   call      __cxa_end_catch@FUNCTION
101; CHECK: .LBB1_11:
102; CHECK:   return
103define hidden void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
104entry:
105  invoke void @foo()
106          to label %try.cont11 unwind label %catch.dispatch
107
108catch.dispatch:                                   ; preds = %entry
109  %0 = catchswitch within none [label %catch.start] unwind to caller
110
111catch.start:                                      ; preds = %catch.dispatch
112  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
113  %2 = call i8* @llvm.wasm.get.exception(token %1)
114  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
115  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
116  %matches = icmp eq i32 %3, %4
117  br i1 %matches, label %catch, label %rethrow
118
119catch:                                            ; preds = %catch.start
120  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
121  %6 = bitcast i8* %5 to i32*
122  %7 = load i32, i32* %6, align 4
123  invoke void @foo() [ "funclet"(token %1) ]
124          to label %try.cont unwind label %catch.dispatch2
125
126catch.dispatch2:                                  ; preds = %catch
127  %8 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9
128
129catch.start3:                                     ; preds = %catch.dispatch2
130  %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)]
131  %10 = call i8* @llvm.wasm.get.exception(token %9)
132  %11 = call i32 @llvm.wasm.get.ehselector(token %9)
133  %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
134  %matches4 = icmp eq i32 %11, %12
135  br i1 %matches4, label %catch6, label %rethrow5
136
137catch6:                                           ; preds = %catch.start3
138  %13 = call i8* @__cxa_begin_catch(i8* %10) [ "funclet"(token %9) ]
139  %14 = bitcast i8* %13 to i32*
140  %15 = load i32, i32* %14, align 4
141  invoke void @foo() [ "funclet"(token %9) ]
142          to label %invoke.cont8 unwind label %ehcleanup
143
144invoke.cont8:                                     ; preds = %catch6
145  call void @__cxa_end_catch() [ "funclet"(token %9) ]
146  catchret from %9 to label %try.cont
147
148rethrow5:                                         ; preds = %catch.start3
149  invoke void @__cxa_rethrow() [ "funclet"(token %9) ]
150          to label %unreachable unwind label %ehcleanup9
151
152try.cont:                                         ; preds = %catch, %invoke.cont8
153  call void @__cxa_end_catch() [ "funclet"(token %1) ]
154  catchret from %1 to label %try.cont11
155
156rethrow:                                          ; preds = %catch.start
157  call void @__cxa_rethrow() [ "funclet"(token %1) ]
158  unreachable
159
160try.cont11:                                       ; preds = %entry, %try.cont
161  ret void
162
163ehcleanup:                                        ; preds = %catch6
164  %16 = cleanuppad within %9 []
165  call void @__cxa_end_catch() [ "funclet"(token %16) ]
166  cleanupret from %16 unwind label %ehcleanup9
167
168ehcleanup9:                                       ; preds = %ehcleanup, %rethrow5, %catch.dispatch2
169  %17 = cleanuppad within %1 []
170  call void @__cxa_end_catch() [ "funclet"(token %17) ]
171  cleanupret from %17 unwind to caller
172
173unreachable:                                      ; preds = %rethrow5
174  unreachable
175}
176
177; Nested loop within a catch clause
178
179; CHECK-LABEL: test2
180; CHECK:   call      foo@FUNCTION
181; CHECK: .LBB2_1:
182; CHECK:   i32.catch
183; CHECK:   i32.call  $drop=, __cxa_begin_catch@FUNCTION
184; CHECK: .LBB2_2:
185; CHECK:   call      foo@FUNCTION
186; CHECK: .LBB2_4:
187; CHECK:   catch_all
188; CHECK:   call      __cxa_end_catch@FUNCTION
189; CHECK: .LBB2_5:
190; CHECK:   i32.catch
191; CHECK:   call      __clang_call_terminate@FUNCTION
192; CHECK:   unreachable
193; CHECK: .LBB2_6:
194; CHECK:   catch_all
195; CHECK:   call      _ZSt9terminatev@FUNCTION
196; CHECK:   unreachable
197; CHECK: .LBB2_7:
198; CHECK:   rethrow
199; CHECK: .LBB2_8:
200; CHECK:   call      __cxa_end_catch@FUNCTION
201; CHECK: .LBB2_10:
202; CHECK:   return
203define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
204entry:
205  invoke void @foo()
206          to label %try.cont unwind label %catch.dispatch
207
208catch.dispatch:                                   ; preds = %entry
209  %0 = catchswitch within none [label %catch.start] unwind to caller
210
211catch.start:                                      ; preds = %catch.dispatch
212  %1 = catchpad within %0 [i8* null]
213  %2 = call i8* @llvm.wasm.get.exception(token %1)
214  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
215  %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
216  br label %for.cond
217
218for.cond:                                         ; preds = %for.inc, %catch.start
219  %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
220  %cmp = icmp slt i32 %i.0, 50
221  br i1 %cmp, label %for.body, label %for.end
222
223for.body:                                         ; preds = %for.cond
224  invoke void @foo() [ "funclet"(token %1) ]
225          to label %for.inc unwind label %ehcleanup
226
227for.inc:                                          ; preds = %for.body
228  %inc = add nsw i32 %i.0, 1
229  br label %for.cond
230
231for.end:                                          ; preds = %for.cond
232  call void @__cxa_end_catch() [ "funclet"(token %1) ]
233  catchret from %1 to label %try.cont
234
235try.cont:                                         ; preds = %for.end, %entry
236  ret void
237
238ehcleanup:                                        ; preds = %for.body
239  %5 = cleanuppad within %1 []
240  invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
241          to label %invoke.cont2 unwind label %terminate
242
243invoke.cont2:                                     ; preds = %ehcleanup
244  cleanupret from %5 unwind to caller
245
246terminate:                                        ; preds = %ehcleanup
247  %6 = cleanuppad within %5 []
248  %7 = call i8* @llvm.wasm.get.exception(token %6)
249  call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ]
250  unreachable
251}
252
253declare void @foo()
254declare void @bar()
255declare i32 @__gxx_wasm_personality_v0(...)
256declare i8* @llvm.wasm.get.exception(token)
257declare i32 @llvm.wasm.get.ehselector(token)
258declare i32 @llvm.eh.typeid.for(i8*)
259declare i8* @__cxa_begin_catch(i8*)
260declare void @__cxa_end_catch()
261declare void @__cxa_rethrow()
262declare void @__clang_call_terminate(i8*)
263declare void @_ZSt9terminatev()
264