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
12 ; int jmpval = setjmp(buf);
20 define void @setjmp_and_try() personality ptr @__gxx_wasm_personality_v0 {
21 ; CHECK-LABEL: @setjmp_and_try
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
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
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
49 ; CHECK-NEXT: catchret from [[T0]] to label {{.*}}
51 return: ; preds = %catch.start, %if.end, %entry
54 ; CHECK: catch.dispatch.longjmp:
55 ; CHECK-NEXT: catchswitch within none [label %catch.longjmp] unwind to caller
58 ; void setjmp_within_try() {
61 ; int jmpval = setjmp(buf);
68 define void @setjmp_within_try() personality ptr @__gxx_wasm_personality_v0 {
69 ; CHECK-LABEL: @setjmp_within_try
71 %jmpval = alloca i32, align 4
72 %exn.slot = alloca ptr, align 4
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
89 if.end: ; preds = %invoke.cont1
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)
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
119 try.cont: ; preds = %invoke.cont2, %catchret.dest, %if.then
122 invoke.cont2: ; preds = %if.end
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);
143 define void @setjmp_and_nested_try() personality ptr @__gxx_wasm_personality_v0 {
144 ; CHECK-LABEL: @setjmp_and_nested_try
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
152 to label %invoke.cont unwind label %catch.dispatch5
154 invoke.cont: ; preds = %if.end
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
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
211 ; CHECK-NEXT: catchret from [[T1]] to label {{.*}}
213 try.cont10: ; preds = %catch.start6, %invoke.cont3, %invoke.cont, %entry
216 terminate: ; preds = %ehcleanup
217 %11 = cleanuppad within %5 []
218 call void @terminate() #3 [ "funclet"(token %11) ]
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
232 ; CHECK-NEXT: invoke void @terminate() {{.*}} [ "funclet"(token [[T2]]) ]
233 ; CHECK-NEXT: to label %[[NOEXC:.*]] unwind label %catch.dispatch5
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 {
247 define void @cleanuppad_no_parent() personality ptr @__gxx_wasm_personality_v0 {
248 ; CHECK-LABEL: @cleanuppad_no_parent
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
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
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
285 ; CHECK: invoke.cont:
286 ; The remaining setjmp call is converted to constant 0, because setjmp returns 0
287 ; when called directly.
290 ehcleanup: ; preds = %entry
291 %0 = cleanuppad within none []
292 cleanupret from %0 unwind to caller
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 }