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 5 void may_throw(); 6 void dont_throw() noexcept; 7 8 struct Cleanup { 9 ~Cleanup() { dont_throw(); } 10 }; 11 12 // Multiple catch clauses w/o catch-all 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.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 67 // CHECK-NEXT: unreachable 68 69 // Single catch-all 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 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 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 %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 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 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: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CLEANUPPAD1]]) 192 // CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] 193 // CHECK-NEXT: unreachable 194 195 // CHECK-LABEL: define {{.*}} void @__clang_call_terminate(i8* %0) 196 // CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %{{.*}}) 197 // CHECK-NEXT: call void @_ZSt9terminatev() 198 // CHECK-NEXT: unreachable 199 200 // Try-catch with cleanups 201 void test6() { 202 Cleanup c1; 203 try { 204 Cleanup c2; 205 may_throw(); 206 } catch (int) { 207 Cleanup c3; 208 may_throw(); 209 } 210 } 211 212 // CHECK-LABEL: @_Z5test6v() 213 // CHECK: invoke void @_Z9may_throwv() 214 // CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]] 215 216 // CHECK: [[EHCLEANUP_BB0]]: 217 // CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] 218 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ] 219 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]] 220 221 // CHECK: [[CATCH_DISPATCH_BB]]: 222 // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]] 223 224 // CHECK: [[CATCHSTART_BB]]: 225 // CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] 226 // CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]] 227 228 // CHECK: [[CATCH_INT_BB]]: 229 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] 230 // CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]] 231 232 // CHECK: [[INVOKE_CONT_BB]]: 233 // CHECK: catchret from %[[CATCHPAD]] to label %{{.*}} 234 235 // CHECK: [[RETHROW_BB]]: 236 // CHECK-NEXT: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] 237 // CHECK-NEXT: to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]] 238 239 // CHECK: [[EHCLEANUP_BB2]]: 240 // CHECK-NEXT: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] [] 241 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{[^,]*}} %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ] 242 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]] 243 244 // CHECK: [[EHCLEANUP_BB3]]: 245 // CHECK-NEXT: %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] [] 246 // CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]] 247 248 // CHECK: [[EHCLEANUP_BB1]]: 249 // CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within none [] 250 // CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{[^,]*}} %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] 251 // CHECK-NEXT: cleanupret from %[[CLEANUPPAD1]] unwind to caller 252 253 // CHECK: [[UNREACHABLE_BB]]: 254 // CHECK-NEXT: unreachable 255 256 // Nested try-catches within a try with cleanups 257 void test7() { 258 Cleanup c1; 259 may_throw(); 260 try { 261 Cleanup c2; 262 may_throw(); 263 try { 264 Cleanup c3; 265 may_throw(); 266 } catch (int) { 267 may_throw(); 268 } catch (double) { 269 may_throw(); 270 } 271 } catch (int) { 272 may_throw(); 273 } catch (...) { 274 may_throw(); 275 } 276 } 277 278 // CHECK-LABEL: @_Z5test7v() 279 // CHECK: invoke void @_Z9may_throwv() 280 281 // CHECK: invoke void @_Z9may_throwv() 282 283 // CHECK: invoke void @_Z9may_throwv() 284 285 // CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] 286 // CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label 287 288 // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none 289 290 // CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] 291 292 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] 293 294 // CHECK: catchret from %[[CATCHPAD0]] to label 295 296 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] 297 298 // CHECK: catchret from %[[CATCHPAD0]] to label 299 300 // CHECK: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] 301 302 // CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] 303 // CHECK: cleanupret from %[[CLEANUPPAD1]] unwind label 304 305 // CHECK: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] [] 306 // CHECK: cleanupret from %[[CLEANUPPAD2]] unwind label 307 308 // CHECK: %[[CLEANUPPAD3:.*]] = cleanuppad within none [] 309 // CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label 310 311 // CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within none 312 313 // CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] 314 315 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] 316 317 // CHECK: catchret from %[[CATCHPAD1]] to label 318 319 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] 320 321 // CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ] 322 323 // CHECK: catchret from %[[CATCHPAD1]] to label 324 325 // CHECK: %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] [] 326 // CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ] 327 328 // CHECK: cleanupret from %[[CLEANUPPAD4]] unwind label 329 330 // CHECK: %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] [] 331 // CHECK: cleanupret from %[[CLEANUPPAD5]] unwind label 332 333 // CHECK: %[[CLEANUPPAD6:.*]] = cleanuppad within none [] 334 // CHECK: cleanupret from %[[CLEANUPPAD6]] unwind to caller 335 336 // CHECK: unreachable 337 338 // CHECK: %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] [] 339 // CHECK: call void @__clang_call_terminate(i8* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ] 340 // CHECK: unreachable 341 342 // Nested try-catches within a catch 343 void test8() { 344 try { 345 may_throw(); 346 } catch (int) { 347 try { 348 may_throw(); 349 } catch (int) { 350 may_throw(); 351 } 352 } 353 } 354 355 // CHECK-LABEL: @_Z5test8v() 356 // CHECK: invoke void @_Z9may_throwv() 357 358 // CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none 359 360 // CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*)] 361 362 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] 363 364 // CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]] 365 366 // CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*)] 367 368 // CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] 369 370 // CHECK: catchret from %[[CATCHPAD1]] to label 371 372 // CHECK: invoke void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ] 373 374 // CHECK: catchret from %[[CATCHPAD0]] to label 375 376 // CHECK: call void @llvm.wasm.rethrow.in.catch() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] 377 // CHECK: unreachable 378 379 // CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] [] 380 // CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label 381 382 // CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] 383 // CHECK: cleanupret from %[[CLEANUPPAD1]] unwind to caller 384 385 // CHECK: unreachable 386 387 // 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 2>&1 | FileCheck %s --check-prefix=WARNING-DEFAULT 388 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -Wwasm-exception-spec -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=WARNING-ON 389 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -fwasm-exceptions -target-feature +exception-handling -Wno-wasm-exception-spec -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=WARNING-OFF 390 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fexceptions -fcxx-exceptions -emit-llvm -o - -std=c++11 2>&1 | FileCheck %s --check-prefix=NOT-WASM-EH 391 392 // Wasm EH ignores dynamic exception specifications with types at the moment. 393 // This is controlled by -Wwasm-exception-spec, which is on by default. This 394 // warning can be suppressed with -Wno-wasm-exception-spec. Checks if a warning 395 // message is correctly printed or not printed depending on the options. 396 void test9() throw(int) { 397 } 398 // WARNING-DEFAULT: warning: dynamic exception specifications with types are currently ignored in wasm 399 // WARNING-ON: warning: dynamic exception specifications with types are currently ignored in wasm 400 // WARNING-OFF-NOT: warning: dynamic exception specifications with types are currently ignored in wasm 401 // NOT-WASM-EH-NOT: warning: dynamic exception specifications with types are currently ignored in wasm 402 403 // Wasm curremtly treats 'throw()' in the same way as 'noexept'. Check if the 404 // same warning message is printed as if when a 'noexcept' function throws. 405 void test10() throw() { 406 throw 3; 407 } 408 // WARNING-DEFAULT: warning: 'test10' has a non-throwing exception specification but can still throw 409 // WARNING-DEFAULT: function declared non-throwing here 410 411 // Here we only check if the command enables wasm exception handling in the 412 // backend so that exception handling instructions can be generated in .s file. 413 414 // 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 415 416 // ASSEMBLY: try 417 // ASSEMBLY: catch 418 // ASSEMBLY: rethrow 419 // ASSEMBLY: end_try 420