Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / llvm / test / CodeGen / WebAssembly / lower-wasm-sjlj.ll
blobb8d2230fac9f5bb33bd4cbac1d56d10da95dcfa0
1 ; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-sjlj -S | FileCheck %s -DPTR=i32
2 ; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-sjlj -S --mattr=+atomics,+bulk-memory | FileCheck %s -DPTR=i32
3 ; RUN: opt < %s -wasm-lower-em-ehsjlj -wasm-enable-sjlj --mtriple=wasm64-unknown-unknown -data-layout="e-m:e-p:64:64-i64:64-n32:64-S128" -S | FileCheck %s -DPTR=i64
5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6 target triple = "wasm32-unknown-unknown"
8 %struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
10 ; These variables are only used in Emscripten EH/SjLj, so they shouldn't be
11 ; generated.
12 ; CHECK-NOT: @__THREW__ =
13 ; CHECK-NOT: @__threwValue =
15 @global_longjmp_ptr = global ptr @longjmp, align 4
16 ; CHECK-DAG: @global_longjmp_ptr = global ptr @__wasm_longjmp
18 ; Test a simple setjmp - longjmp sequence
19 define void @setjmp_longjmp() {
20 ; CHECK-LABEL: @setjmp_longjmp()
21 entry:
22   %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
23   %call = call i32 @setjmp(ptr %buf) #0
24   call void @longjmp(ptr %buf, i32 1) #1
25   unreachable
27 ; CHECK:    entry:
28 ; CHECK-NEXT: %setjmpTable = tail call ptr @malloc([[PTR]] 40)
29 ; CHECK-NEXT: store i32 0, ptr %setjmpTable, align 4
30 ; CHECK-NEXT: %setjmpTableSize = add i32 4, 0
31 ; CHECK-NEXT: br label %setjmp.dispatch
33 ; CHECK:    setjmp.dispatch:
34 ; CHECK-NEXT: %[[VAL2:.*]] = phi i32 [ %val, %if.end ], [ undef, %entry ]
35 ; CHECK-NEXT: %[[BUF:.*]] = phi ptr [ %[[BUF2:.*]], %if.end ], [ undef, %entry ]
36 ; CHECK-NEXT: %[[SETJMPTABLESIZE2:.*]] = phi i32 [ %[[SETJMPTABLESIZE3:.*]], %if.end ], [ %setjmpTableSize, %entry ]
37 ; CHECK-NEXT: %[[SETJMPTABLE2:.*]] = phi ptr [ %[[SETJMPTABLE3:.*]], %if.end ], [ %setjmpTable, %entry ]
38 ; CHECK-NEXT: %label.phi = phi i32 [ %label, %if.end ], [ -1, %entry ]
39 ; CHECK-NEXT: switch i32 %label.phi, label %entry.split [
40 ; CHECK-NEXT:   i32 1, label %entry.split.split
41 ; CHECK-NEXT: ]
43 ; CHECK:    entry.split:
44 ; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
45 ; CHECK-NEXT: %[[SETJMPTABLE4:.*]] = call ptr @saveSetjmp(ptr %buf, i32 1, ptr %[[SETJMPTABLE2]], i32 %[[SETJMPTABLESIZE2]])
46 ; CHECK-NEXT: %[[SETJMPTABLESIZE4:.*]] = call i32 @getTempRet0()
47 ; CHECK-NEXT: br label %entry.split.split
49 ; CHECK:    entry.split.split:
50 ; CHECK-NEXT: %[[BUF2]] = phi ptr [ %[[BUF]], %setjmp.dispatch ], [ %buf, %entry.split ]
51 ; CHECK-NEXT: %[[SETJMPTABLESIZE3]] = phi i32 [ %[[SETJMPTABLESIZE4]], %entry.split ], [ %[[SETJMPTABLESIZE2]], %setjmp.dispatch ]
52 ; CHECK-NEXT: %[[SETJMPTABLE3]] = phi ptr [ %[[SETJMPTABLE4]], %entry.split ], [ %[[SETJMPTABLE2]], %setjmp.dispatch ]
53 ; CHECK-NEXT: %setjmp.ret = phi i32 [ 0, %entry.split ], [ %[[VAL2]], %setjmp.dispatch ]
54 ; CHECK-NEXT: invoke void @__wasm_longjmp(ptr %[[BUF2]], i32 1)
55 ; CHECK-NEXT:         to label %.noexc unwind label %catch.dispatch.longjmp
57 ; CHECK:    .noexc:
58 ; CHECK-NEXT: unreachable
60 ; CHECK:    catch.dispatch.longjmp:
61 ; CHECK-NEXT: %0 = catchswitch within none [label %catch.longjmp] unwind to caller
63 ; CHECK:    catch.longjmp:
64 ; CHECK-NEXT: %1 = catchpad within %0 []
65 ; CHECK-NEXT: %thrown = call ptr @llvm.wasm.catch(i32 1)
66 ; CHECK-NEXT: %env_gep = getelementptr { ptr, i32 }, ptr %thrown, i32 0, i32 0
67 ; CHECK-NEXT: %val_gep = getelementptr { ptr, i32 }, ptr %thrown, i32 0, i32 1
68 ; CHECK-NEXT: %env = load ptr, ptr %env_gep, align {{.*}}
69 ; CHECK-NEXT: %val = load i32, ptr %val_gep, align 4
70 ; CHECK-NEXT: %setjmp.id = load [[PTR]], ptr %env, align {{.*}}
71 ; CHECK-NEXT: %label = call i32 @testSetjmp([[PTR]] %setjmp.id, ptr %[[SETJMPTABLE3]], i32 %[[SETJMPTABLESIZE3]]) [ "funclet"(token %1) ]
72 ; CHECK-NEXT: %2 = icmp eq i32 %label, 0
73 ; CHECK-NEXT: br i1 %2, label %if.then, label %if.end
75 ; CHECK:    if.then:
76 ; CHECK-NEXT: tail call void @free(ptr %[[SETJMPTABLE3]]) [ "funclet"(token %1) ]
77 ; CHECK-NEXT: call void @__wasm_longjmp(ptr %env, i32 %val) [ "funclet"(token %1) ]
78 ; CHECK-NEXT: unreachable
80 ; CHECK:    if.end:
81 ; CHECK-NEXT: catchret from %1 to label %setjmp.dispatch
84 ; When there are multiple longjmpable calls after setjmp. This will turn each of
85 ; longjmpable call into an invoke whose unwind destination is
86 ; 'catch.dispatch.longjmp' BB.
87 define void @setjmp_multiple_longjmpable_calls() {
88 ; CHECK-LABEL: @setjmp_multiple_longjmpable_calls
89 entry:
90   %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
91   %call = call i32 @setjmp(ptr %buf) #0
92   call void @foo()
93   call void @foo()
94   ret void
96 ; CHECK: entry.split.split:
97 ; CHECK:   invoke void @foo()
98 ; CHECK:           to label %{{.*}} unwind label %catch.dispatch.longjmp
100 ; CHECK: .noexc:
101 ; CHECK:   invoke void @foo()
102 ; CHECK:           to label %{{.*}} unwind label %catch.dispatch.longjmp
105 ; Tests cases where longjmp function pointer is used in other ways than direct
106 ; calls. longjmps should be replaced with (void(*)(jmp_buf*, int))__wasm_longjmp.
107 declare void @take_longjmp(ptr %arg_ptr)
108 define void @indirect_longjmp() {
109 ; CHECK-LABEL: @indirect_longjmp
110 entry:
111   %local_longjmp_ptr = alloca ptr, align 4
112   %buf0 = alloca [1 x %struct.__jmp_buf_tag], align 16
113   %buf1 = alloca [1 x %struct.__jmp_buf_tag], align 16
115   ; Store longjmp in a local variable, load it, and call it
116   store ptr @longjmp, ptr %local_longjmp_ptr, align 4
117   ; CHECK: store ptr @__wasm_longjmp, ptr %local_longjmp_ptr, align 4
118   %longjmp_from_local_ptr = load ptr, ptr %local_longjmp_ptr, align 4
119   call void %longjmp_from_local_ptr(ptr %buf0, i32 0)
121   ; Load longjmp from a global variable and call it
122   %longjmp_from_global_ptr = load ptr, ptr @global_longjmp_ptr, align 4
123   call void %longjmp_from_global_ptr(ptr %buf1, i32 0)
125   ; Pass longjmp as a function argument. This is a call but longjmp is not a
126   ; callee but an argument.
127   call void @take_longjmp(ptr @longjmp)
128   ; CHECK: call void @take_longjmp(ptr @__wasm_longjmp)
129   ret void
132 ; Function Attrs: nounwind
133 declare void @foo() #2
134 ; The pass removes the 'nounwind' attribute, so there should be no attributes
135 ; CHECK-NOT: declare void @foo #{{.*}}
136 ; Function Attrs: returns_twice
137 declare i32 @setjmp(ptr) #0
138 ; Function Attrs: noreturn
139 declare void @longjmp(ptr, i32) #1
140 declare i32 @__gxx_personality_v0(...)
141 declare ptr @__cxa_begin_catch(ptr)
142 declare void @__cxa_end_catch()
143 declare void @free(ptr)
145 ; JS glue function declarations
146 ; CHECK-DAG: declare i32 @getTempRet0()
147 ; CHECK-DAG: declare ptr @saveSetjmp(ptr, i32, ptr, i32)
148 ; CHECK-DAG: declare i32 @testSetjmp([[PTR]], ptr, i32)
149 ; CHECK-DAG: declare void @__wasm_longjmp(ptr, i32)
151 attributes #0 = { returns_twice }
152 attributes #1 = { noreturn }
153 attributes #2 = { nounwind }