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