1 // REQUIRES: webassembly-registered-target
2 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s
3 // RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -emit-llvm -o - -std=c++11 | FileCheck %s
4 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -S -o - -std=c++11 | FileCheck %s --check-prefix=ASSEMBLY
5 
6 void may_throw();
7 void dont_throw() noexcept;
8 
9 struct Cleanup {
10   ~Cleanup() { dont_throw(); }
11 };
12 
13 // Multiple catch clauses w/o catch-all
14 void test0() {
15   try {
16     may_throw();
17   } catch (int) {
18     dont_throw();
19   } catch (double) {
20     dont_throw();
21   }
22 }
23 
24 // CHECK-LABEL: define void @_Z5test0v() {{.*}} personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*)
25 
26 // CHECK:   %[[INT_ALLOCA:.*]] = alloca i32
27 // CHECK:   invoke void @_Z9may_throwv()
28 // CHECK-NEXT:           to label %[[NORMAL_BB:.*]] unwind label %[[CATCHDISPATCH_BB:.*]]
29 
30 // CHECK: [[CATCHDISPATCH_BB]]:
31 // CHECK-NEXT:   %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller
32 
33 // CHECK: [[CATCHSTART_BB]]:
34 // CHECK-NEXT:   %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)]
35 // CHECK-NEXT:   %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CATCHPAD]])
36 // CHECK-NEXT:   store i8* %[[EXN]], i8** %exn.slot
37 // CHECK-NEXT:   %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector(token %[[CATCHPAD]])
38 // CHECK-NEXT:   %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #2
39 // CHECK-NEXT:   %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]]
40 // CHECK-NEXT:   br i1 %[[MATCHES]], label %[[CATCH_INT_BB:.*]], label %[[CATCH_FALLTHROUGH_BB:.*]]
41 
42 // CHECK: [[CATCH_INT_BB]]:
43 // CHECK-NEXT:   %[[EXN:.*]] = load i8*, i8** %exn.slot
44 // CHECK-NEXT:   %[[ADDR:.*]] = call i8* @__cxa_begin_catch(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
45 // CHECK-NEXT:   %[[ADDR_CAST:.*]] = bitcast i8* %[[ADDR]] to i32*
46 // CHECK-NEXT:   %[[INT_VAL:.*]] = load i32, i32* %[[ADDR_CAST]]
47 // CHECK-NEXT:   store i32 %[[INT_VAL]], i32* %[[INT_ALLOCA]]
48 // CHECK-NEXT:   call void @_Z10dont_throwv() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
49 // CHECK-NEXT:   call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
50 // CHECK-NEXT:   catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB0:.*]]
51 
52 // CHECK: [[CATCHRET_DEST_BB0]]:
53 // CHECK-NEXT:   br label %[[TRY_CONT_BB:.*]]
54 
55 // CHECK: [[CATCH_FALLTHROUGH_BB]]
56 // CHECK-NEXT:   %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) #2
57 // CHECK-NEXT:   %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]]
58 // CHECK-NEXT:   br i1 %[[MATCHES]], label %[[CATCH_FLOAT_BB:.*]], label %[[RETHROW_BB:.*]]
59 
60 // CHECK: [[CATCH_FLOAT_BB]]:
61 // CHECK:   catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB1:.*]]
62 
63 // CHECK: [[CATCHRET_DEST_BB1]]:
64 // CHECK-NEXT:   br label %[[TRY_CONT_BB]]
65 
66 // CHECK: [[RETHROW_BB]]:
67 // CHECK-NEXT:   call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
68 // CHECK-NEXT:   unreachable
69 
70 // Single catch-all
71 void test1() {
72   try {
73     may_throw();
74   } catch (...) {
75     dont_throw();
76   }
77 }
78 
79 // CATCH-LABEL: @_Z5test1v()
80 
81 // CHECK:   %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller
82 
83 // CHECK: [[CATCHSTART_BB]]:
84 // CHECK-NEXT:   %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null]
85 // CHECK:   br label %[[CATCH_ALL_BB:.*]]
86 
87 // CHECK: [[CATCH_ALL_BB]]:
88 // CHECK:   catchret from %[[CATCHPAD]] to label
89 
90 // Multiple catch clauses w/ catch-all
91 void test2() {
92   try {
93     may_throw();
94   } catch (int) {
95     dont_throw();
96   } catch (...) {
97     dont_throw();
98   }
99 }
100 
101 // CHECK-LABEL: @_Z5test2v()
102 
103 // CHECK:   %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller
104 
105 // CHECK: [[CATCHSTART_BB]]:
106 // CHECK-NEXT:   %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null]
107 // CHECK:   br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[CATCH_ALL_BB:.*]]
108 
109 // CHECK: [[CATCH_INT_BB]]:
110 // CHECK:   catchret from %[[CATCHPAD]] to label
111 
112 // CHECK: [[CATCH_ALL_BB]]:
113 // CHECK:   catchret from %[[CATCHPAD]] to label
114 
115 // Cleanup
116 void test3() {
117   Cleanup c;
118   may_throw();
119 }
120 
121 // CHECK-LABEL: @_Z5test3v()
122 
123 // CHECK:   invoke void @_Z9may_throwv()
124 // CHECK-NEXT:           to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]]
125 
126 // CHECK: [[EHCLEANUP_BB]]:
127 // CHECK-NEXT:   %[[CLEANUPPAD:.*]] = cleanuppad within none []
128 // CHECK-NEXT:   call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ]
129 // CHECK-NEXT:   cleanupret from %[[CLEANUPPAD]] unwind to caller
130 
131 // Possibly throwing function call within a catch
132 void test4() {
133   try {
134     may_throw();
135   } catch (int) {
136     may_throw();
137   }
138 }
139 
140 // CHECK-LABEL: @_Z5test4v()
141 
142 // CHECK:   %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller
143 
144 // CHECK: [[CATCHSTART_BB]]:
145 // CHECK:   %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)]
146 
147 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]
148 // CHECK-NEXT:           to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB:.*]]
149 
150 // CHECK: [[INVOKE_CONT_BB]]:
151 // CHECK-NEXT:   call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
152 // CHECK-NEXT:   catchret from %[[CATCHPAD]] to label
153 
154 // CHECK: [[EHCLEANUP_BB]]:
155 // CHECK-NEXT:   %[[CLEANUPPAD:.*]] = cleanuppad within %[[CATCHPAD]] []
156 // CHECK-NEXT:   call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ]
157 // CHECK-NEXT:   cleanupret from %[[CLEANUPPAD]] unwind to caller
158 
159 // Possibly throwing function call within a catch-all
160 void test5() {
161   try {
162     may_throw();
163   } catch (...) {
164     may_throw();
165   }
166 }
167 
168 // CHECK-LABEL: @_Z5test5v()
169 
170 // CHECK:   %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller
171 
172 // CHECK: [[CATCHSTART_BB]]:
173 // CHECK:   %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null]
174 
175 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]
176 // CHECK-NEXT:           to label %[[INVOKE_CONT_BB0:.*]] unwind label %[[EHCLEANUP_BB:.*]]
177 
178 // CHECK: [[INVOKE_CONT_BB0]]:
179 // CHECK-NEXT:   call void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD]]) ]
180 // CHECK-NEXT:   catchret from %[[CATCHPAD]] to label
181 
182 // CHECK: [[EHCLEANUP_BB]]:
183 // CHECK-NEXT:   %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD]] []
184 // CHECK-NEXT:   invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD0]]) ]
185 // CHECK-NEXT:           to label %[[INVOKE_CONT_BB1:.*]] unwind label %[[TERMINATE_BB:.*]]
186 
187 // CHECK: [[INVOKE_CONT_BB1]]:
188 // CHECK-NEXT:   cleanupret from %[[CLEANUPPAD0]] unwind to caller
189 
190 // CHECK: [[TERMINATE_BB]]:
191 // CHECK-NEXT:   %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CLEANUPPAD0]] []
192 // CHECK-NEXT:   %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CLEANUPPAD1]])
193 // CHECK-NEXT:   call void @__clang_call_terminate(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ]
194 // CHECK-NEXT:   unreachable
195 
196 // CHECK-LABEL: define {{.*}} void @__clang_call_terminate(i8* %0)
197 // CHECK-NEXT:   call i8* @__cxa_begin_catch(i8* %{{.*}})
198 // CHECK-NEXT:   call void @_ZSt9terminatev()
199 // CHECK-NEXT:   unreachable
200 
201 // Try-catch with cleanups
202 void test6() {
203   Cleanup c1;
204   try {
205     Cleanup c2;
206     may_throw();
207   } catch (int) {
208     Cleanup c3;
209     may_throw();
210   }
211 }
212 
213 // CHECK-LABEL: @_Z5test6v()
214 // CHECK:   invoke void @_Z9may_throwv()
215 // CHECK-NEXT:           to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]]
216 
217 // CHECK: [[EHCLEANUP_BB0]]:
218 // CHECK-NEXT:   %[[CLEANUPPAD0:.*]] = cleanuppad within none []
219 // CHECK-NEXT:   call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ]
220 // CHECK-NEXT:   cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]]
221 
222 // CHECK: [[CATCH_DISPATCH_BB]]:
223 // CHECK-NEXT:  %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]]
224 
225 // CHECK: [[CATCHSTART_BB]]:
226 // CHECK-NEXT:   %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)]
227 // CHECK:   br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]]
228 
229 // CHECK: [[CATCH_INT_BB]]:
230 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]
231 // CHECK-NEXT:           to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]]
232 
233 // CHECK: [[INVOKE_CONT_BB]]:
234 // CHECK:   catchret from %[[CATCHPAD]] to label %{{.*}}
235 
236 // CHECK: [[RETHROW_BB]]:
237 // CHECK-NEXT:   invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
238 // CHECK-NEXT:          to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]]
239 
240 // CHECK: [[EHCLEANUP_BB2]]:
241 // CHECK-NEXT:   %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] []
242 // CHECK-NEXT:   call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ]
243 // CHECK-NEXT:   cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]]
244 
245 // CHECK: [[EHCLEANUP_BB3]]:
246 // CHECK-NEXT:   %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] []
247 // CHECK:   cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]]
248 
249 // CHECK: [[EHCLEANUP_BB1]]:
250 // CHECK-NEXT:   %[[CLEANUPPAD1:.*]] = cleanuppad within none []
251 // CHECK-NEXT:   call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ]
252 // CHECK-NEXT:   cleanupret from %[[CLEANUPPAD1]] unwind to caller
253 
254 // CHECK: [[UNREACHABLE_BB]]:
255 // CHECK-NEXT:   unreachable
256 
257 // Nested try-catches within a try with cleanups
258 void test7() {
259   Cleanup c1;
260   may_throw();
261   try {
262     Cleanup c2;
263     may_throw();
264     try {
265       Cleanup c3;
266       may_throw();
267     } catch (int) {
268       may_throw();
269     } catch (double) {
270       may_throw();
271     }
272   } catch (int) {
273     may_throw();
274   } catch (...) {
275     may_throw();
276   }
277 }
278 
279 // CHECK-LABEL: @_Z5test7v()
280 // CHECK:   invoke void @_Z9may_throwv()
281 
282 // CHECK:   invoke void @_Z9may_throwv()
283 
284 // CHECK:   invoke void @_Z9may_throwv()
285 
286 // CHECK:   %[[CLEANUPPAD0:.*]] = cleanuppad within none []
287 // CHECK:   cleanupret from %[[CLEANUPPAD0]] unwind label
288 
289 // CHECK:   %[[CATCHSWITCH0:.*]] = catchswitch within none
290 
291 // CHECK:   %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)]
292 
293 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]
294 
295 // CHECK:   catchret from %[[CATCHPAD0]] to label
296 
297 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]
298 
299 // CHECK:   catchret from %[[CATCHPAD0]] to label
300 
301 // CHECK:   invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ]
302 
303 // CHECK:   %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] []
304 // CHECK:   cleanupret from %[[CLEANUPPAD1]] unwind label
305 
306 // CHECK:   %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] []
307 // CHECK:   cleanupret from %[[CLEANUPPAD2]] unwind label
308 
309 // CHECK:   %[[CLEANUPPAD3:.*]] = cleanuppad within none []
310 // CHECK:   cleanupret from %[[CLEANUPPAD3]] unwind label
311 
312 // CHECK:   %[[CATCHSWITCH1:.*]] = catchswitch within none
313 
314 // CHECK:   %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null]
315 
316 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]
317 
318 // CHECK:   catchret from %[[CATCHPAD1]] to label
319 
320 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]
321 
322 // CHECK:   invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ]
323 
324 // CHECK:   catchret from %[[CATCHPAD1]] to label
325 
326 // CHECK:   %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] []
327 // CHECK:   invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ]
328 
329 // CHECK:   cleanupret from %[[CLEANUPPAD4]] unwind label
330 
331 // CHECK:   %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] []
332 // CHECK:   cleanupret from %[[CLEANUPPAD5]] unwind label
333 
334 // CHECK:   %[[CLEANUPPAD6:.*]] = cleanuppad within none []
335 // CHECK:   cleanupret from %[[CLEANUPPAD6]] unwind to caller
336 
337 // CHECK:   unreachable
338 
339 // CHECK:   %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] []
340 // CHECK:   call void @__clang_call_terminate(i8* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ]
341 // CHECK:   unreachable
342 
343 // Nested try-catches within a catch
344 void test8() {
345   try {
346     may_throw();
347   } catch (int) {
348     try {
349       may_throw();
350     } catch (int) {
351       may_throw();
352     }
353   }
354 }
355 
356 // CHECK-LABEL: @_Z5test8v()
357 // CHECK:   invoke void @_Z9may_throwv()
358 
359 // CHECK:   %[[CATCHSWITCH0:.*]] = catchswitch within none
360 
361 // CHECK:   %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*)]
362 
363 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]
364 
365 // CHECK:   %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]]
366 
367 // CHECK:   %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*)]
368 
369 // CHECK:   invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]
370 
371 // CHECK:   catchret from %[[CATCHPAD1]] to label
372 
373 // CHECK:   invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ]
374 
375 // CHECK:   catchret from %[[CATCHPAD0]] to label
376 
377 // CHECK:   call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ]
378 // CHECK:   unreachable
379 
380 // CHECK:   %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] []
381 // CHECK:   cleanupret from %[[CLEANUPPAD0]] unwind label
382 
383 // CHECK:   %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] []
384 // CHECK:   cleanupret from %[[CLEANUPPAD1]] unwind to caller
385 
386 // CHECK:   unreachable
387 
388 // Here we only check if the command enables wasm exception handling in the
389 // backend so that exception handling instructions can be generated in .s file.
390 
391 // ASSEMBLY: try
392 // ASSEMBLY: catch
393 // ASSEMBLY: rethrow
394 // ASSEMBLY: end_try
395