xref: /llvm-project/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll (revision 473ef10b0fc93eeb2cbb3b2cf2f1b748eac6ddd9)
1; RUN: opt < %s -win-eh-prepare -demote-catchswitch-only -wasm-eh-prepare -S | FileCheck %s
2; RUN: opt < %s -win-eh-prepare -demote-catchswitch-only -wasm-eh-prepare -S --mattr=+atomics,+bulk-memory | FileCheck %s
3; RUN: opt < %s -passes='win-eh-prepare<demote-catchswitch-only>,wasm-eh-prepare' -S | FileCheck %s
4; RUN: opt < %s -passes='win-eh-prepare<demote-catchswitch-only>,wasm-eh-prepare' -S --mattr=+atomics,+bulk-memory | FileCheck %s
5; RUN: llc < %s -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -stop-after=wasm-eh-prepare | FileCheck %s
6
7target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
8target triple = "wasm32-unknown-unknown"
9
10; CHECK: @__wasm_lpad_context = external thread_local global { i32, ptr, i32 }
11
12@_ZTIi = external constant ptr
13%struct.Temp = type { i8 }
14
15; A single 'catch (int)' clause.
16; A wasm.catch() call, wasm.lsda() call, and personality call to generate a
17; selector should all be genereated after the catchpad.
18;
19; void foo();
20; void test0() {
21;   try {
22;     foo();
23;   } catch (int) {
24;   }
25; }
26define void @test0() personality ptr @__gxx_wasm_personality_v0 {
27; CHECK-LABEL: @test0()
28entry:
29  invoke void @foo()
30          to label %try.cont unwind label %catch.dispatch
31
32catch.dispatch:                                   ; preds = %entry
33  %0 = catchswitch within none [label %catch.start] unwind to caller
34
35catch.start:                                      ; preds = %catch.dispatch
36  %1 = catchpad within %0 [ptr @_ZTIi]
37  %2 = call ptr @llvm.wasm.get.exception(token %1)
38  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
39  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
40  %matches = icmp eq i32 %3, %4
41  br i1 %matches, label %catch, label %rethrow
42; CHECK: catch.start:
43; CHECK-NEXT:   %[[CATCHPAD:.*]] = catchpad
44; CHECK-NEXT:   %[[EXN:.*]] = call ptr @llvm.wasm.catch(i32 0)
45; CHECK-NEXT:   call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 0)
46; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
47; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
48; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
49; CHECK-NEXT:   call i32 @_Unwind_CallPersonality(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
50; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
51; CHECK:   icmp eq i32 %[[SELECTOR]]
52
53catch:                                            ; preds = %catch.start
54  %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
55  call void @__cxa_end_catch() [ "funclet"(token %1) ]
56  catchret from %1 to label %try.cont
57; CHECK: catch:
58; CHECK-NEXT:  call ptr @__cxa_begin_catch(ptr %[[EXN]])
59
60rethrow:                                          ; preds = %catch.start
61  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
62  unreachable
63
64try.cont:                                         ; preds = %entry, %catch
65  ret void
66}
67
68; Two try-catches.
69; For the catchpad with a single 'catch (...)', only a wasm.catch() call should
70; be generated after the catchpad; wasm.landingpad.index() and personality call
71; should NOT be generated. For the other catchpad, the argument of
72; wasm.landingpad.index() should be not 1 but 0.
73;
74; void foo();
75; void test1() {
76;   try {
77;     foo();
78;   } catch (...) {
79;   }
80;   try {
81;     foo();
82;   } catch (int) {
83;   }
84; }
85define void @test1() personality ptr @__gxx_wasm_personality_v0 {
86; CHECK-LABEL: @test1()
87entry:
88  invoke void @foo()
89          to label %try.cont unwind label %catch.dispatch
90
91catch.dispatch:                                   ; preds = %entry
92  %0 = catchswitch within none [label %catch.start] unwind to caller
93
94catch.start:                                      ; preds = %catch.dispatch
95  %1 = catchpad within %0 [ptr null]
96  %2 = call ptr @llvm.wasm.get.exception(token %1)
97  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
98  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
99  call void @__cxa_end_catch() [ "funclet"(token %1) ]
100  catchret from %1 to label %try.cont
101; CHECK: catch.start:
102; CHECK-NEXT:   catchpad within %0 [ptr null]
103; CHECK-NOT:   call void @llvm.wasm.landingpad.index
104; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
105; CHECK-NOT:   call ptr @llvm.wasm.lsda()
106; CHECK-NOT:   call i32 @_Unwind_CallPersonality
107; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
108
109try.cont:                                         ; preds = %entry, %catch.start
110  invoke void @foo()
111          to label %try.cont7 unwind label %catch.dispatch2
112
113catch.dispatch2:                                  ; preds = %try.cont
114  %5 = catchswitch within none [label %catch.start3] unwind to caller
115
116catch.start3:                                     ; preds = %catch.dispatch2
117  %6 = catchpad within %5 [ptr @_ZTIi]
118  %7 = call ptr @llvm.wasm.get.exception(token %6)
119  %8 = call i32 @llvm.wasm.get.ehselector(token %6)
120  %9 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
121  %matches = icmp eq i32 %8, %9
122  br i1 %matches, label %catch4, label %rethrow
123; CHECK: catch.start3:
124; CHECK:   call void @llvm.wasm.landingpad.index(token %{{.+}}, i32 0)
125
126catch4:                                           ; preds = %catch.start3
127  %10 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
128  call void @__cxa_end_catch() [ "funclet"(token %6) ]
129  catchret from %6 to label %try.cont7
130
131rethrow:                                          ; preds = %catch.start3
132  call void @llvm.wasm.rethrow() [ "funclet"(token %6) ]
133  unreachable
134
135try.cont7:                                        ; preds = %try.cont, %catch4
136  ret void
137}
138
139; PHI demotion test. Only the phi before catchswitch should be demoted; the phi
140; before cleanuppad should NOT.
141;
142; void foo();
143; int bar(int) noexcept;
144; struct Temp {
145;   ~Temp() {}
146; };
147;
148; void test2() {
149;   int num;
150;   try {
151;     Temp t;
152;     num = 1;
153;     foo();
154;     num = 2;
155;     foo();
156;   } catch (...) {
157;     bar(num);
158;   }
159;   try {
160;     foo();
161;     num = 1;
162;     foo();
163;     num = 2;
164;   } catch (...) {
165;     bar(num);
166;   }
167; }
168define void @test2() personality ptr @__gxx_wasm_personality_v0 {
169; CHECK-LABEL: @test2
170entry:
171  %t = alloca %struct.Temp, align 1
172  invoke void @foo()
173          to label %invoke.cont unwind label %ehcleanup
174
175invoke.cont:                                      ; preds = %entry
176  invoke void @foo()
177          to label %invoke.cont1 unwind label %ehcleanup
178
179invoke.cont1:                                     ; preds = %invoke.cont
180  %call = call ptr @_ZN4TempD2Ev(ptr %t)
181  br label %try.cont
182
183ehcleanup:                                        ; preds = %invoke.cont, %entry
184  %num.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ]
185  %0 = cleanuppad within none []
186  %call2 = call ptr @_ZN4TempD2Ev(ptr %t) [ "funclet"(token %0) ]
187  cleanupret from %0 unwind label %catch.dispatch
188; CHECK: ehcleanup:
189; CHECK-NEXT:   = phi
190
191catch.dispatch:                                   ; preds = %ehcleanup
192  %1 = catchswitch within none [label %catch.start] unwind to caller
193
194catch.start:                                      ; preds = %catch.dispatch
195  %2 = catchpad within %1 [ptr null]
196  %3 = call ptr @llvm.wasm.get.exception(token %2)
197  %4 = call i32 @llvm.wasm.get.ehselector(token %2)
198  %5 = call ptr @__cxa_begin_catch(ptr %3) [ "funclet"(token %2) ]
199  call void @bar(i32 %num.0) [ "funclet"(token %2) ]
200  call void @__cxa_end_catch() [ "funclet"(token %2) ]
201  catchret from %2 to label %try.cont
202
203try.cont:                                         ; preds = %catch.start, %invoke.cont1
204  invoke void @foo()
205          to label %invoke.cont3 unwind label %catch.dispatch5
206
207invoke.cont3:                                     ; preds = %try.cont
208  invoke void @foo()
209          to label %try.cont10 unwind label %catch.dispatch5
210
211catch.dispatch5:                                  ; preds = %invoke.cont3, %try.cont
212  %num.1 = phi i32 [ 2, %invoke.cont3 ], [ 1, %try.cont ]
213  %6 = catchswitch within none [label %catch.start6] unwind to caller
214; CHECK: catch.dispatch5:
215; CHECK-NOT:   = phi
216
217catch.start6:                                     ; preds = %catch.dispatch5
218  %7 = catchpad within %6 [ptr null]
219  %8 = call ptr @llvm.wasm.get.exception(token %7)
220  %9 = call i32 @llvm.wasm.get.ehselector(token %7)
221  %10 = call ptr @__cxa_begin_catch(ptr %8) [ "funclet"(token %7) ]
222  call void @bar(i32 %num.1) [ "funclet"(token %7) ]
223  call void @__cxa_end_catch() [ "funclet"(token %7) ]
224  catchret from %7 to label %try.cont10
225
226try.cont10:                                       ; preds = %invoke.cont3, %catch.start6
227  ret void
228}
229
230; Tests if instructions after a call to @llvm.wasm.throw are deleted and the
231; BB's dead children are deleted.
232
233; CHECK-LABEL: @test3
234define i32 @test3(i1 %b, ptr %p) {
235entry:
236  br i1 %b, label %bb.true, label %bb.false
237
238; CHECK:      bb.true:
239; CHECK-NEXT:   call void @llvm.wasm.throw(i32 0, ptr %p)
240; CHECK-NEXT:   unreachable
241bb.true:                                          ; preds = %entry
242  call void @llvm.wasm.throw(i32 0, ptr %p)
243  br label %bb.true.0
244
245; CHECK-NOT:  bb.true.0
246bb.true.0:                                        ; preds = %bb.true
247  br label %merge
248
249bb.false:                                         ; preds = %entry
250  br label %merge
251
252; CHECK:      merge
253merge:                                            ; preds = %bb.true.0, %bb.false
254  ret i32 0
255}
256
257declare void @foo()
258declare void @bar(i32)
259declare ptr @_ZN4TempD2Ev(ptr returned)
260declare i32 @__gxx_wasm_personality_v0(...)
261; Function Attrs: nounwind
262declare ptr @llvm.wasm.get.exception(token) #0
263; Function Attrs: nounwind
264declare i32 @llvm.wasm.get.ehselector(token) #0
265; Function Attrs: nounwind
266declare i32 @llvm.eh.typeid.for(ptr) #0
267; Function Attrs: noreturn
268declare void @llvm.wasm.throw(i32, ptr) #1
269; Function Attrs: noreturn
270declare void @llvm.wasm.rethrow() #1
271declare ptr @__cxa_begin_catch(ptr)
272declare void @__cxa_end_catch()
273declare void @_ZSt9terminatev()
274
275attributes #0 = { nounwind }
276attributes #1 = { noreturn }
277
278; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
279; CHECK-DAG: declare ptr @llvm.wasm.lsda()
280; CHECK-DAG: declare i32 @_Unwind_CallPersonality(ptr)
281