Clang Project

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