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