Bump version to 19.1.0-rc3
[llvm-project.git] / llvm / test / CodeGen / WebAssembly / lower-wasm-ehsjlj.ll
blobbd8db83a0e57ed7cb71ef8acb2d4a390b548cfea
1 ; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-eh -wasm-enable-sjlj -S | FileCheck %s
2 ; RUN: llc < %s -wasm-enable-eh -wasm-enable-sjlj -exception-model=wasm -mattr=+exception-handling -verify-machineinstrs
4 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
7 %struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
8 %struct.Temp = type { i8 }
9 @_ZL3buf = internal global [1 x %struct.__jmp_buf_tag] zeroinitializer, align 16
11 ; void test() {
12 ;   int jmpval = setjmp(buf);
13 ;   if (jmpval != 0)
14 ;     return;
15 ;   try {
16 ;     foo();
17 ;   } catch (...) {
18 ;   }
19 ; }
20 define void @setjmp_and_try() personality ptr @__gxx_wasm_personality_v0 {
21 ; CHECK-LABEL: @setjmp_and_try
22 entry:
23   %call = call i32 @setjmp(ptr @_ZL3buf) #0
24   %cmp = icmp ne i32 %call, 0
25   br i1 %cmp, label %return, label %if.end
27 if.end:                                           ; preds = %entry
28   invoke void @foo()
29   to label %return unwind label %catch.dispatch
31 catch.dispatch:                                   ; preds = %if.end
32   %0 = catchswitch within none [label %catch.start] unwind to caller
33 ; CHECK:       catch.dispatch:
34 ; CHECK-NEXT:    catchswitch within none [label %catch.start] unwind label %catch.dispatch.longjmp
36 catch.start:                                      ; preds = %catch.dispatch
37   %1 = catchpad within %0 [ptr null]
38   %2 = call ptr @llvm.wasm.get.exception(token %1)
39   %3 = call i32 @llvm.wasm.get.ehselector(token %1)
40   %4 = call ptr @__cxa_begin_catch(ptr %2) #2 [ "funclet"(token %1) ]
41   call void @__cxa_end_catch() [ "funclet"(token %1) ]
42   catchret from %1 to label %return
43 ; CHECK:       catch.start:
44 ; CHECK:         [[T0:%.*]] = catchpad within {{.*}} [ptr null]
45 ; CHECK:         invoke void @__cxa_end_catch() [ "funclet"(token [[T0]]) ]
46 ; CHECK-NEXT:    to label %.noexc unwind label %catch.dispatch.longjmp
48 ; CHECK:       .noexc:
49 ; CHECK-NEXT:     catchret from [[T0]] to label {{.*}}
51 return:                                           ; preds = %catch.start, %if.end, %entry
52   ret void
54 ; CHECK:       catch.dispatch.longjmp:
55 ; CHECK-NEXT:    catchswitch within none [label %catch.longjmp] unwind to caller
58 ; void setjmp_within_try() {
59 ;   try {
60 ;     foo();
61 ;     int jmpval = setjmp(buf);
62 ;     if (jmpval != 0)
63 ;       return;
64 ;     foo();
65 ;   } catch (...) {
66 ;   }
67 ; }
68 define void @setjmp_within_try() personality ptr @__gxx_wasm_personality_v0 {
69 ; CHECK-LABEL: @setjmp_within_try
70 entry:
71   %jmpval = alloca i32, align 4
72   %exn.slot = alloca ptr, align 4
73   invoke void @foo()
74   to label %invoke.cont unwind label %catch.dispatch
76 invoke.cont:                                      ; preds = %entry
77   %call = invoke i32 @setjmp(ptr @_ZL3buf) #0
78   to label %invoke.cont1 unwind label %catch.dispatch
80 invoke.cont1:                                     ; preds = %invoke.cont
81   store i32 %call, ptr %jmpval, align 4
82   %0 = load i32, ptr %jmpval, align 4
83   %cmp = icmp ne i32 %0, 0
84   br i1 %cmp, label %if.then, label %if.end
86 if.then:                                          ; preds = %invoke.cont1
87   br label %try.cont
89 if.end:                                           ; preds = %invoke.cont1
90   invoke void @foo()
91   to label %invoke.cont2 unwind label %catch.dispatch
93 catch.dispatch:                                   ; preds = %if.end, %invoke.cont, %entry
94   %1 = catchswitch within none [label %catch.start] unwind to caller
96 ; CHECK:       catch.dispatch:
97 ; CHECK:         catchswitch within none [label %catch.start] unwind label %catch.dispatch.longjmp
98 catch.start:                                      ; preds = %catch.dispatch
99   %2 = catchpad within %1 [ptr null]
100   %3 = call ptr @llvm.wasm.get.exception(token %2)
101   store ptr %3, ptr %exn.slot, align 4
102   %4 = call i32 @llvm.wasm.get.ehselector(token %2)
103   br label %catch
105 catch:                                            ; preds = %catch.start
106   %exn = load ptr, ptr %exn.slot, align 4
107   %5 = call ptr @__cxa_begin_catch(ptr %exn) #2 [ "funclet"(token %2) ]
108   call void @__cxa_end_catch() [ "funclet"(token %2) ]
109   catchret from %2 to label %catchret.dest
110 ; CHECK: catch:                                            ; preds = %catch.start
111 ; CHECK-NEXT:   %exn = load ptr, ptr %exn.slot6, align 4
112 ; CHECK-NEXT:   %5 = call ptr @__cxa_begin_catch(ptr %exn) #6 [ "funclet"(token %2) ]
113 ; CHECK-NEXT:   invoke void @__cxa_end_catch() [ "funclet"(token %2) ]
114 ; CHECK-NEXT:           to label %.noexc unwind label %catch.dispatch.longjmp
116 catchret.dest:                                    ; preds = %catch
117   br label %try.cont
119 try.cont:                                         ; preds = %invoke.cont2, %catchret.dest, %if.then
120   ret void
122 invoke.cont2:                                     ; preds = %if.end
123   br label %try.cont
125 ; CHECK:       catch.dispatch.longjmp:
126 ; CHECK-NEXT:    catchswitch within none [label %catch.longjmp] unwind to caller
129 ; void setjmp_and_nested_try() {
130 ;   int jmpval = setjmp(buf);
131 ;   if (jmpval != 0)
132 ;     return;
133 ;   try {
134 ;     foo();
135 ;     try {
136 ;       foo();
137 ;     } catch (...) {
138 ;       foo();
139 ;     }
140 ;   } catch (...) {
141 ;   }
142 ; }
143 define void @setjmp_and_nested_try() personality ptr @__gxx_wasm_personality_v0 {
144 ; CHECK-LABEL: @setjmp_and_nested_try
145 entry:
146   %call = call i32 @setjmp(ptr @_ZL3buf) #0
147   %cmp = icmp ne i32 %call, 0
148   br i1 %cmp, label %try.cont10, label %if.end
150 if.end:                                           ; preds = %entry
151   invoke void @foo()
152   to label %invoke.cont unwind label %catch.dispatch5
154 invoke.cont:                                      ; preds = %if.end
155   invoke void @foo()
156   to label %try.cont10 unwind label %catch.dispatch
158 catch.dispatch:                                   ; preds = %invoke.cont
159   %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch5
161 catch.start:                                      ; preds = %catch.dispatch
162   %1 = catchpad within %0 [ptr null]
163   %2 = call ptr @llvm.wasm.get.exception(token %1)
164   %3 = call i32 @llvm.wasm.get.ehselector(token %1)
165   %4 = call ptr @__cxa_begin_catch(ptr %2) #2 [ "funclet"(token %1) ]
166   invoke void @foo() [ "funclet"(token %1) ]
167   to label %invoke.cont2 unwind label %ehcleanup
169 invoke.cont2:                                     ; preds = %catch.start
170   invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
171   to label %invoke.cont3 unwind label %catch.dispatch5
173 invoke.cont3:                                     ; preds = %invoke.cont2
174   catchret from %1 to label %try.cont10
176 ehcleanup:                                        ; preds = %catch.start
177   %5 = cleanuppad within %1 []
178   invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
179   to label %invoke.cont4 unwind label %terminate
180 ; CHECK:       ehcleanup:
181 ; CHECK-NEXT:    [[T0:%.*]] = cleanuppad within {{.*}} []
182 ; CHECK-NEXT:    invoke void @__cxa_end_catch() [ "funclet"(token [[T0]]) ]
183 ; CHECK-NEXT:    to label %invoke.cont4 unwind label %terminate
185 invoke.cont4:                                     ; preds = %ehcleanup
186   cleanupret from %5 unwind label %catch.dispatch5
187 ; CHECK:       invoke.cont4:
188 ; CHECK-NEXT:    cleanupret from [[T0]] unwind label %catch.dispatch5
190 catch.dispatch5:                                  ; preds = %invoke.cont4, %invoke.cont2, %catch.dispatch, %if.end
191   %6 = catchswitch within none [label %catch.start6] unwind to caller
192 ; CHECK:       catch.dispatch5:
193 ; CHECK-NEXT:    catchswitch within none [label %catch.start6] unwind label %catch.dispatch.longjmp
195 catch.start6:                                     ; preds = %catch.dispatch5
196   %7 = catchpad within %6 [ptr null]
197   %8 = call ptr @llvm.wasm.get.exception(token %7)
198   %9 = call i32 @llvm.wasm.get.ehselector(token %7)
199   %10 = call ptr @__cxa_begin_catch(ptr %8) #2 [ "funclet"(token %7) ]
200   call void @__cxa_end_catch() [ "funclet"(token %7) ]
201   catchret from %7 to label %try.cont10
202 ; CHECK:       catch.start6:
203 ; CHECK-NEXT:    [[T1:%.*]] = catchpad within {{.*}} [ptr null]
204 ; CHECK-NEXT:    call ptr @llvm.wasm.get.exception(token [[T1]])
205 ; CHECK-NEXT:    call i32 @llvm.wasm.get.ehselector(token [[T1]])
206 ; CHECK-NEXT:    call ptr @__cxa_begin_catch(ptr {{.*}}) {{.*}} [ "funclet"(token [[T1]]) ]
207 ; CHECK:         invoke void @__cxa_end_catch() [ "funclet"(token [[T1]]) ]
208 ; CHECK-NEXT:    to label %.noexc unwind label %catch.dispatch.longjmp
210 ; CHECK:       .noexc:
211 ; CHECK-NEXT:    catchret from [[T1]] to label {{.*}}
213 try.cont10:                                       ; preds = %catch.start6, %invoke.cont3, %invoke.cont, %entry
214   ret void
216 terminate:                                        ; preds = %ehcleanup
217   %11 = cleanuppad within %5 []
218   call void @terminate() #3 [ "funclet"(token %11) ]
219   unreachable
220 ; CHECK:       terminate:
221 ; CHECK-NEXT:    [[T2:%.*]] = cleanuppad within [[T0]] []
222 ; Note that this call unwinds not to %catch.dispatch.longjmp but to
223 ; %catch.dispatch5. This call is enclosed in the cleanuppad above, but there is
224 ; no matching catchret, which has the unwind destination. So this checks this
225 ; cleanuppad's parent, which is in 'ehcleanup', and unwinds to its unwind
226 ; destination, %catch.dispatch5.
227 ; This call was originally '_ZSt9terminatev', which is the mangled name for
228 ; 'std::terminate'. But we listed that as "cannot longjmp", we changed
229 ; the name of the function in this test to show the case in which a call has to
230 ; change to an invoke whose unwind destination is determined by its parent
231 ; chain.
232 ; CHECK-NEXT:    invoke void @terminate() {{.*}} [ "funclet"(token [[T2]]) ]
233 ; CHECK-NEXT:    to label %[[NOEXC:.*]] unwind label %catch.dispatch5
235 ; CHECK:       [[NOEXC]]:
236 ; CHECK-NEXT:    unreachable
238 ; CHECK:       catch.dispatch.longjmp:
239 ; CHECK-NEXT:    catchswitch within none [label %catch.longjmp] unwind to caller
242 ; void @cleanuppad_no_parent {
243 ;   jmp_buf buf;
244 ;   Temp t;
245 ;   setjmp(buf);
246 ; }
247 define void @cleanuppad_no_parent() personality ptr @__gxx_wasm_personality_v0 {
248 ; CHECK-LABEL: @cleanuppad_no_parent
249 entry:
250   %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
251   %t = alloca %struct.Temp, align 1
252   %call = invoke i32 @setjmp(ptr noundef %buf) #0
253           to label %invoke.cont unwind label %ehcleanup
255 invoke.cont:                                      ; preds = %entry
256   %call1 = call noundef ptr @_ZN4TempD2Ev(ptr noundef %t) #2
257   ret void
259 ehcleanup:                                        ; preds = %entry
260   %0 = cleanuppad within none []
261   ; After SjLj transformation, this will be converted to an invoke that
262   ; eventually unwinds to %catch.dispatch.longjmp. But in case a call has a
263   ; "funclet" attribute, we should unwind to the funclet's unwind destination
264   ; first to preserve the scoping structure. But this call's parent is %0
265   ; (cleanuppad), whose parent is 'none', so we should unwind directly to
266   ; %catch.dispatch.longjmp.
267   %call2 = call noundef ptr @_ZN4TempD2Ev(ptr noundef %t) #2 [ "funclet"(token %0) ]
268 ; CHECK: %call11 = invoke {{.*}} ptr @_ZN4TempD2Ev(ptr
269 ; CHECK-NEXT:    to label {{.*}} unwind label %catch.dispatch.longjmp
270   cleanupret from %0 unwind to caller
273 ; This case was adapted from @cleanuppad_no_parent by removing allocas and
274 ; destructor calls, to generate a situation that there's only 'invoke @setjmp'
275 ; and no other longjmpable calls.
276 define i32 @setjmp_only() personality ptr @__gxx_wasm_personality_v0 {
277 ; CHECK-LABEL: @setjmp_only
278 entry:
279   %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
280   %call = invoke i32 @setjmp(ptr noundef %buf) #0
281           to label %invoke.cont unwind label %ehcleanup
283 invoke.cont:                                      ; preds = %entry
284   ret i32 %call
285 ; CHECK: invoke.cont:
286 ; The remaining setjmp call is converted to constant 0, because setjmp returns 0
287 ; when called directly.
288 ; CHECK:   ret i32 0
290 ehcleanup:                                        ; preds = %entry
291   %0 = cleanuppad within none []
292   cleanupret from %0 unwind to caller
295 declare void @foo()
296 ; Function Attrs: nounwind
297 declare ptr @_ZN4TempD2Ev(ptr %this) #2
298 ; Function Attrs: returns_twice
299 declare i32 @setjmp(ptr) #0
300 ; Function Attrs: noreturn
301 declare void @longjmp(ptr, i32) #1
302 declare i32 @__gxx_wasm_personality_v0(...)
303 ; Function Attrs: nounwind
304 declare ptr @llvm.wasm.get.exception(token) #2
305 ; Function Attrs: nounwind
306 declare i32 @llvm.wasm.get.ehselector(token) #2
307 declare ptr @__cxa_begin_catch(ptr)
308 declare void @__cxa_end_catch()
309 declare void @terminate()
311 attributes #0 = { returns_twice }
312 attributes #1 = { noreturn }
313 attributes #2 = { nounwind }
314 attributes #3 = { noreturn nounwind }