1; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm
2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -exception-model=wasm -mattr=+exception-handling | FileCheck -allow-deprecated-dag-overlap %s
3
4target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
5target triple = "wasm32-unknown-unknown"
6
7%struct.Cleanup = type { i8 }
8
9@_ZTIi = external constant i8*
10
11declare void @llvm.wasm.throw(i32, i8*)
12
13; CHECK-LABEL: test_throw:
14; CHECK-NEXT: i32.const $push0=, 0
15; CHECK-NEXT: throw 0, $pop0
16define void @test_throw() {
17  call void @llvm.wasm.throw(i32 0, i8* null)
18  ret void
19}
20
21; CHECK-LABEL: test_catch_rethrow:
22; CHECK:   get_global  $push{{.+}}=, __stack_pointer@GLOBAL
23; CHECK:   try
24; CHECK:   call      foo@FUNCTION
25; CHECK:   i32.catch     $push{{.+}}=, 0
26; CHECK:   set_global  __stack_pointer@GLOBAL
27; CHECK-DAG:   i32.store  __wasm_lpad_context
28; CHECK-DAG:   i32.store  __wasm_lpad_context+4
29; CHECK:   i32.call  $push{{.+}}=, _Unwind_CallPersonality@FUNCTION
30; CHECK:   i32.call  $push{{.+}}=, __cxa_begin_catch@FUNCTION
31; CHECK:   call      __cxa_end_catch@FUNCTION
32; CHECK:   call      __cxa_rethrow@FUNCTION
33; CHECK-NEXT:   rethrow
34; CHECK:   end_try
35define void @test_catch_rethrow() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
36entry:
37  invoke void @foo()
38          to label %try.cont unwind label %catch.dispatch
39
40catch.dispatch:                                   ; preds = %entry
41  %0 = catchswitch within none [label %catch.start] unwind to caller
42
43catch.start:                                      ; preds = %catch.dispatch
44  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
45  %2 = call i8* @llvm.wasm.get.exception(token %1)
46  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
47  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
48  %matches = icmp eq i32 %3, %4
49  br i1 %matches, label %catch, label %rethrow
50
51catch:                                            ; preds = %catch.start
52  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
53  call void @__cxa_end_catch() [ "funclet"(token %1) ]
54  catchret from %1 to label %try.cont
55
56rethrow:                                          ; preds = %catch.start
57  call void @__cxa_rethrow() [ "funclet"(token %1) ]
58  unreachable
59
60try.cont:                                         ; preds = %entry, %catch
61  ret void
62}
63
64; CHECK-LABEL: test_cleanup:
65; CHECK:   try
66; CHECK:   call      foo@FUNCTION
67; CHECK:   catch_all
68; CHECK:   set_global  __stack_pointer@GLOBAL
69; CHECK:   i32.call  $push{{.+}}=, _ZN7CleanupD1Ev@FUNCTION
70; CHECK:   rethrow
71; CHECK:   end_try
72define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
73entry:
74  %c = alloca %struct.Cleanup, align 1
75  invoke void @foo()
76          to label %invoke.cont unwind label %ehcleanup
77
78invoke.cont:                                      ; preds = %entry
79  %call = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c)
80  ret void
81
82ehcleanup:                                        ; preds = %entry
83  %0 = cleanuppad within none []
84  %call1 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %0) ]
85  cleanupret from %0 unwind to caller
86}
87
88; - Tests multple terminate pads are merged into one
89; - Tests a catch_all terminate pad is created after a catch terminate pad
90
91; CHECK-LABEL: test_terminatepad
92; CHECK:  i32.catch
93; CHECK:  call      __clang_call_terminate@FUNCTION
94; CHECK:  unreachable
95; CHECK:  catch_all
96; CHECK:  call      _ZSt9terminatev@FUNCTION
97; CHECK-NOT:  call      __clang_call_terminate@FUNCTION
98define hidden i32 @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
99entry:
100  %c = alloca %struct.Cleanup, align 1
101  %c1 = alloca %struct.Cleanup, align 1
102  invoke void @foo()
103          to label %invoke.cont unwind label %ehcleanup
104
105invoke.cont:                                      ; preds = %entry
106  %call = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c1)
107          to label %try.cont unwind label %catch.dispatch
108
109ehcleanup:                                        ; preds = %entry
110  %0 = cleanuppad within none []
111  %call4 = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c1) [ "funclet"(token %0) ]
112          to label %invoke.cont3 unwind label %terminate
113
114invoke.cont3:                                     ; preds = %ehcleanup
115  cleanupret from %0 unwind label %catch.dispatch
116
117catch.dispatch:                                   ; preds = %invoke.cont3, %invoke.cont
118  %1 = catchswitch within none [label %catch.start] unwind label %ehcleanup7
119
120catch.start:                                      ; preds = %catch.dispatch
121  %2 = catchpad within %1 [i8* null]
122  %3 = call i8* @llvm.wasm.get.exception(token %2)
123  %4 = call i32 @llvm.wasm.get.ehselector(token %2)
124  %5 = call i8* @__cxa_begin_catch(i8* %3) [ "funclet"(token %2) ]
125  invoke void @__cxa_end_catch() [ "funclet"(token %2) ]
126          to label %invoke.cont5 unwind label %ehcleanup7
127
128invoke.cont5:                                     ; preds = %catch.start
129  catchret from %2 to label %try.cont
130
131try.cont:                                         ; preds = %invoke.cont5, %invoke.cont
132  %call6 = call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c)
133  ret i32 0
134
135ehcleanup7:                                       ; preds = %catch.start, %catch.dispatch
136  %6 = cleanuppad within none []
137  %call9 = invoke %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %c) [ "funclet"(token %6) ]
138          to label %invoke.cont8 unwind label %terminate10
139
140invoke.cont8:                                     ; preds = %ehcleanup7
141  cleanupret from %6 unwind to caller
142
143terminate:                                        ; preds = %ehcleanup
144  %7 = cleanuppad within %0 []
145  %8 = call i8* @llvm.wasm.get.exception(token %7)
146  call void @__clang_call_terminate(i8* %8) [ "funclet"(token %7) ]
147  unreachable
148
149terminate10:                                      ; preds = %ehcleanup7
150  %9 = cleanuppad within %6 []
151  %10 = call i8* @llvm.wasm.get.exception(token %9)
152  call void @__clang_call_terminate(i8* %10) [ "funclet"(token %9) ]
153  unreachable
154}
155
156; Tests prologues and epilogues are not generated within EH scopes.
157; They should not be treated as funclets; BBs starting with a catch instruction
158; should not have a prologue, and BBs ending with a catchret/cleanupret should
159; not have an epilogue. This is separate from __stack_pointer restoring
160; instructions after a catch instruction.
161
162; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
163; CHECK:  try
164; CHECK:  call      foo@FUNCTION
165; CHECK:  i32.catch
166; CHECK-NOT:  get_global  $push{{.+}}=, __stack_pointer@GLOBAL
167; CHECK:  set_global  __stack_pointer@GLOBAL
168; CHECK:  try
169; CHECK:  call      foo@FUNCTION
170; CHECK:  catch_all
171; CHECK-NOT:  get_global  $push{{.+}}=, __stack_pointer@GLOBAL
172; CHECK:  set_global  __stack_pointer@GLOBAL
173; CHECK:  call      __cxa_end_catch@FUNCTION
174; CHECK-NOT:  set_global  __stack_pointer@GLOBAL, $pop{{.+}}
175; CHECK:  end_try
176; CHECK-NOT:  set_global  __stack_pointer@GLOBAL, $pop{{.+}}
177; CHECK:  end_try
178define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
179entry:
180  %stack_var = alloca i32, align 4
181  call void @bar(i32* %stack_var)
182  invoke void @foo()
183          to label %try.cont unwind label %catch.dispatch
184
185catch.dispatch:                                   ; preds = %entry
186  %0 = catchswitch within none [label %catch.start] unwind to caller
187
188catch.start:                                      ; preds = %catch.dispatch
189  %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
190  %2 = call i8* @llvm.wasm.get.exception(token %1)
191  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
192  %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
193  %matches = icmp eq i32 %3, %4
194  br i1 %matches, label %catch, label %rethrow
195
196catch:                                            ; preds = %catch.start
197  %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
198  %6 = bitcast i8* %5 to float*
199  %7 = load float, float* %6, align 4
200  invoke void @foo() [ "funclet"(token %1) ]
201          to label %invoke.cont1 unwind label %ehcleanup
202
203invoke.cont1:                                     ; preds = %catch
204  call void @__cxa_end_catch() [ "funclet"(token %1) ]
205  catchret from %1 to label %try.cont
206
207rethrow:                                          ; preds = %catch.start
208  call void @__cxa_rethrow() [ "funclet"(token %1) ]
209  unreachable
210
211try.cont:                                         ; preds = %entry, %invoke.cont1
212  ret void
213
214ehcleanup:                                        ; preds = %catch
215  %8 = cleanuppad within %1 []
216  call void @__cxa_end_catch() [ "funclet"(token %8) ]
217  cleanupret from %8 unwind to caller
218}
219
220; When a function does not have stack-allocated objects, it does not need to
221; store SP back to __stack_pointer global at the epilog.
222
223; CHECK-LABEL: no_sp_writeback
224; CHECK:  try
225; CHECK:  call foo@FUNCTION
226; CHECK:  end_try
227; CHECK-NOT:  set_global  __stack_pointer@GLOBAL
228; CHECK:  return
229define void @no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
230entry:
231  invoke void @foo()
232          to label %try.cont unwind label %catch.dispatch
233
234catch.dispatch:                                   ; preds = %entry
235  %0 = catchswitch within none [label %catch.start] unwind to caller
236
237catch.start:                                      ; preds = %catch.dispatch
238  %1 = catchpad within %0 [i8* null]
239  %2 = call i8* @llvm.wasm.get.exception(token %1)
240  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
241  %4 = call i8* @__cxa_begin_catch(i8* %2) #2 [ "funclet"(token %1) ]
242  call void @__cxa_end_catch() [ "funclet"(token %1) ]
243  catchret from %1 to label %try.cont
244
245try.cont:                                         ; preds = %entry, %catch.start
246  ret void
247}
248
249declare void @foo()
250declare void @bar(i32*)
251declare i32 @__gxx_wasm_personality_v0(...)
252declare i8* @llvm.wasm.get.exception(token)
253declare i32 @llvm.wasm.get.ehselector(token)
254declare i32 @llvm.eh.typeid.for(i8*)
255declare i8* @__cxa_begin_catch(i8*)
256declare void @__cxa_end_catch()
257declare void @__cxa_rethrow()
258declare void @__clang_call_terminate(i8*)
259declare void @_ZSt9terminatev()
260declare %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* returned)
261