1 ; RUN: opt < %s -wasm-lower-em-ehsjlj -enable-emscripten-cxx-exceptions -enable-emscripten-sjlj -S | FileCheck %s
2 ; RUN: llc < %s -enable-emscripten-cxx-exceptions -enable-emscripten-sjlj -verify-machineinstrs
4 ; Tests for cases when exception handling and setjmp/longjmp handling are mixed.
6 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
7 target triple = "wasm32-unknown-unknown"
9 %struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
10 @_ZTIi = external constant ptr
12 ; There is a function call (@foo) that can either throw an exception or longjmp
13 ; and there is also a setjmp call. When @foo throws, we have to check both for
14 ; exception and longjmp and jump to exception or longjmp handling BB depending
16 define void @setjmp_longjmp_exception() personality ptr @__gxx_personality_v0 {
17 ; CHECK-LABEL: @setjmp_longjmp_exception
19 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
20 %call = call i32 @setjmp(ptr %buf) #0
22 to label %try.cont unwind label %lpad
24 ; CHECK: entry.split.split:
25 ; CHECK: %[[CMP0:.*]] = icmp ne i32 %__THREW__.val, 0
26 ; CHECK-NEXT: %__threwValue.val = load i32, ptr @__threwValue
27 ; CHECK-NEXT: %[[CMP1:.*]] = icmp ne i32 %__threwValue.val, 0
28 ; CHECK-NEXT: %[[CMP:.*]] = and i1 %[[CMP0]], %[[CMP1]]
29 ; CHECK-NEXT: br i1 %[[CMP]], label %if.then1, label %if.else1
31 ; This is exception checking part. %if.else1 leads here
32 ; CHECK: entry.split.split.split:
33 ; CHECK-NEXT: %[[CMP:.*]] = icmp eq i32 %__THREW__.val, 1
34 ; CHECK-NEXT: br i1 %[[CMP]], label %lpad, label %try.cont
37 lpad: ; preds = %entry
38 %0 = landingpad { ptr, i32 }
40 %1 = extractvalue { ptr, i32 } %0, 0
41 %2 = extractvalue { ptr, i32 } %0, 1
42 ; CHECK-NOT: call {{.*}} void @__invoke_void(ptr @__cxa_end_catch)
43 %3 = call ptr @__cxa_begin_catch(ptr %1) #2
44 call void @__cxa_end_catch()
47 try.cont: ; preds = %lpad, %entry
50 ; longjmp checking part
52 ; CHECK: call i32 @__wasm_setjmp_test
55 ; @foo can either throw an exception or longjmp. Because this function doesn't
56 ; have any setjmp calls, we only handle exceptions in this function. But because
57 ; sjlj is enabled, we check if the thrown value is longjmp and if so rethrow it
58 ; by calling @emscripten_longjmp.
59 define void @rethrow_longjmp() personality ptr @__gxx_personality_v0 {
60 ; CHECK-LABEL: @rethrow_longjmp
63 to label %try.cont unwind label %lpad
65 ; CHECK: %cmp.eq.one = icmp eq i32 %__THREW__.val, 1
66 ; CHECK-NEXT: %cmp.eq.zero = icmp eq i32 %__THREW__.val, 0
67 ; CHECK-NEXT: %or = or i1 %cmp.eq.zero, %cmp.eq.one
68 ; CHECK-NEXT: br i1 %or, label %tail, label %rethrow.longjmp
71 ; CHECK-NEXT: %phi = phi i32 [ undef, %tail ], [ undef, %lpad ]
72 ; CHECK-NEXT: ret void
74 ; CHECK: rethrow.longjmp:
75 ; CHECK-NEXT: %threw.phi = phi i32 [ %__THREW__.val, %entry ]
76 ; CHECK-NEXT: %__threwValue.val = load i32, ptr @__threwValue, align 4
77 ; CHECK-NEXT: call void @emscripten_longjmp(i32 %threw.phi, i32 %__threwValue.val
78 ; CHECK-NEXT: unreachable
81 ; CHECK-NEXT: %cmp = icmp eq i32 %__THREW__.val, 1
82 ; CHECK-NEXT: br i1 %cmp, label %lpad, label %try.cont
84 lpad: ; preds = %entry
85 %0 = landingpad { ptr, i32 }
87 %1 = extractvalue { ptr, i32 } %0, 0
88 %2 = extractvalue { ptr, i32 } %0, 1
89 %3 = call ptr @__cxa_begin_catch(ptr %1) #5
90 call void @__cxa_end_catch()
93 try.cont: ; preds = %lpad, %entry
94 %phi = phi i32 [ undef, %entry ], [ undef, %lpad ]
98 ; This function contains a setjmp call and no invoke, so we only handle longjmp
99 ; here. But @foo can also throw an exception, so we check if an exception is
100 ; thrown and if so rethrow it by calling @__resumeException. Also we have to
101 ; free the setjmpTable buffer before calling @__resumeException.
102 define void @rethrow_exception() {
103 ; CHECK-LABEL: @rethrow_exception
105 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
106 %call = call i32 @setjmp(ptr %buf) #0
107 %cmp = icmp ne i32 %call, 0
108 br i1 %cmp, label %return, label %if.end
110 if.end: ; preds = %entry
115 ; CHECK: %cmp.eq.one = icmp eq i32 %__THREW__.val, 1
116 ; CHECK-NEXT: br i1 %cmp.eq.one, label %rethrow.exn, label %normal
118 ; CHECK: rethrow.exn:
119 ; CHECK-NEXT: %exn = call ptr @__cxa_find_matching_catch_2()
120 ; CHECK-NEXT: call void @__resumeException(ptr %exn)
121 ; CHECK-NEXT: unreachable
124 ; CHECK-NEXT: icmp ne i32 %__THREW__.val, 0
126 return: ; preds = %if.end, %entry
130 ; The same as 'rethrow_exception' but contains a __cxa_throw call. We have to
131 ; free the setjmpTable buffer before calling __cxa_throw.
132 define void @rethrow_exception2() {
133 ; CHECK-LABEL: @rethrow_exception2
135 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
136 %call = call i32 @setjmp(ptr %buf) #0
137 %cmp = icmp ne i32 %call, 0
138 br i1 %cmp, label %throw, label %if.end
140 if.end: ; preds = %entry
144 throw: ; preds = %if.end, %entry
145 call void @__cxa_throw(ptr null, ptr null, ptr null) #1
149 ; CHECK-NEXT: call void @__cxa_throw(ptr null, ptr null, ptr null)
150 ; CHECK-NEXT: unreachable
153 ; The same case with @rethrow_longjmp, but there are multiple function calls
154 ; that can possibly longjmp (instead of throwing exception) so we have to
155 ; rethrow them. Here we test if we correclty generate only one 'rethrow.longjmp'
156 ; BB and share it for multiple calls.
157 define void @rethrow_longjmp_multi() personality ptr @__gxx_personality_v0 {
158 ; CHECK-LABEL: @rethrow_longjmp_multi
161 to label %bb unwind label %lpad
165 to label %try.cont unwind label %lpad
167 lpad: ; preds = %bb, %entry
168 %0 = landingpad { ptr, i32 }
170 %1 = extractvalue { ptr, i32 } %0, 0
171 %2 = extractvalue { ptr, i32 } %0, 1
172 %3 = call ptr @__cxa_begin_catch(ptr %1) #5
173 call void @__cxa_end_catch()
176 try.cont: ; preds = %lpad, %bb
177 %phi = phi i32 [ undef, %bb ], [ undef, %lpad ]
180 ; CHECK: rethrow.longjmp:
181 ; CHECK-NEXT: %threw.phi = phi i32 [ %__THREW__.val, %entry ], [ %__THREW__.val1, %bb ]
182 ; CHECK-NEXT: %__threwValue.val = load i32, ptr @__threwValue, align 4
183 ; CHECK-NEXT: call void @emscripten_longjmp(i32 %threw.phi, i32 %__threwValue.val)
184 ; CHECK-NEXT: unreachable
187 ; The same case with @rethrow_exception, but there are multiple function calls
188 ; that can possibly throw (instead of longjmping) so we have to rethrow them.
189 ; Here we test if correctly we generate only one 'rethrow.exn' BB and share it
190 ; for multiple calls.
191 define void @rethrow_exception_multi() {
192 ; CHECK-LABEL: @rethrow_exception_multi
194 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
195 %call = call i32 @setjmp(ptr %buf) #0
196 %cmp = icmp ne i32 %call, 0
197 br i1 %cmp, label %return, label %if.end
199 if.end: ; preds = %entry
204 return: ; preds = %entry, %if.end
207 ; CHECK: rethrow.exn:
208 ; CHECK-NEXT: %exn = call ptr @__cxa_find_matching_catch_2()
209 ; CHECK-NEXT: call void @__resumeException(ptr %exn)
210 ; CHECK-NEXT: unreachable
213 ; int jmpval = setjmp(buf);
220 define void @setjmp_with_throw_try_catch() personality ptr @__gxx_personality_v0 {
221 ; CHECK-LABEL: @setjmp_with_throw_try_catch
223 %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
224 %call = call i32 @setjmp(ptr %buf) #0
225 %cmp = icmp ne i32 %call, 0
226 br i1 %cmp, label %try.cont, label %if.end
228 if.end: ; preds = %entry
229 %exception = call ptr @__cxa_allocate_exception(i32 4) #2
230 store i32 3, ptr %exception, align 16
231 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
232 to label %unreachable unwind label %lpad
233 ; When invoke @__cxa_throw is converted to a call to the invoke wrapper,
234 ; "noreturn" attribute should be removed, and there should be no 'free' call
235 ; before the call. We insert a 'free' call that frees 'setjmpTable' before every
236 ; function-exiting instruction. And invoke wrapper calls shouldn't be treated as
237 ; noreturn instructions, because they are supposed to return.
239 ; CHECK-NOT: tail call void @free
240 ; CHECK-NOT: call cc99 void @__invoke_void_ptr_ptr_ptr(ptr @__cxa_throw, ptr %exception, ptr @_ZTIi, ptr null) #
241 ; CHECK: call cc99 void @__invoke_void_ptr_ptr_ptr(ptr @__cxa_throw, ptr %exception, ptr @_ZTIi, ptr null)
243 lpad: ; preds = %if.end
244 %0 = landingpad { ptr, i32 }
246 %1 = extractvalue { ptr, i32 } %0, 0
247 %2 = extractvalue { ptr, i32 } %0, 1
248 %3 = call ptr @__cxa_begin_catch(ptr %1) #2
249 call void @__cxa_end_catch()
252 try.cont: ; preds = %entry, %lpad
255 unreachable: ; preds = %if.end
260 ; Function Attrs: returns_twice
261 declare i32 @setjmp(ptr)
262 ; Function Attrs: noreturn
263 declare void @longjmp(ptr, i32)
264 declare i32 @__gxx_personality_v0(...)
265 declare ptr @__cxa_begin_catch(ptr)
266 declare void @__cxa_end_catch()
267 declare void @__cxa_throw(ptr, ptr, ptr)
268 declare ptr @__cxa_allocate_exception(i32)
270 attributes #0 = { returns_twice }
271 attributes #1 = { noreturn }
272 attributes #2 = { nounwind }