1; REQUIRES: asserts 2; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s 3; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling 4; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -exception-model=wasm -mattr=+exception-handling | FileCheck %s --check-prefix=NOOPT 5; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort -stats 2>&1 | FileCheck %s --check-prefix=NOSORT 6; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT-LOCALS 7 8target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 9target triple = "wasm32-unknown-unknown" 10 11@_ZTIi = external constant i8* 12@_ZTId = external constant i8* 13 14%class.Object = type { i8 } 15%class.MyClass = type { i32 } 16 17; Simple test case with two catch clauses 18; 19; void foo(); 20; void test0() { 21; try { 22; foo(); 23; } catch (int) { 24; } catch (double) { 25; } 26; } 27 28; CHECK-LABEL: test0 29; CHECK: try 30; CHECK: call foo 31; CHECK: catch 32; CHECK: block 33; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]] 34; CHECK: call $drop=, __cxa_begin_catch 35; CHECK: call __cxa_end_catch 36; CHECK: br 1 # 1: down to label[[L1:[0-9]+]] 37; CHECK: end_block # label[[L0]]: 38; CHECK: block 39; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]] 40; CHECK: call $drop=, __cxa_begin_catch 41; CHECK: call __cxa_end_catch 42; CHECK: br 1 # 1: down to label[[L1]] 43; CHECK: end_block # label[[L2]]: 44; CHECK: rethrow 0 # to caller 45; CHECK: end_try # label[[L1]]: 46define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 47entry: 48 invoke void @foo() 49 to label %try.cont unwind label %catch.dispatch 50 51catch.dispatch: ; preds = %entry 52 %0 = catchswitch within none [label %catch.start] unwind to caller 53 54catch.start: ; preds = %catch.dispatch 55 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] 56 %2 = call i8* @llvm.wasm.get.exception(token %1) 57 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 58 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 59 %matches = icmp eq i32 %3, %4 60 br i1 %matches, label %catch2, label %catch.fallthrough 61 62catch2: ; preds = %catch.start 63 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 64 call void @__cxa_end_catch() [ "funclet"(token %1) ] 65 catchret from %1 to label %try.cont 66 67catch.fallthrough: ; preds = %catch.start 68 %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) 69 %matches1 = icmp eq i32 %3, %6 70 br i1 %matches1, label %catch, label %rethrow 71 72catch: ; preds = %catch.fallthrough 73 %7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 74 call void @__cxa_end_catch() [ "funclet"(token %1) ] 75 catchret from %1 to label %try.cont 76 77rethrow: ; preds = %catch.fallthrough 78 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ] 79 unreachable 80 81try.cont: ; preds = %catch, %catch2, %entry 82 ret void 83} 84 85; Nested try-catches within a catch 86; void test1() { 87; try { 88; foo(); 89; } catch (int) { 90; try { 91; foo(); 92; } catch (int) { 93; foo(); 94; } 95; } 96; } 97 98; CHECK-LABEL: test1 99; CHECK: try 100; CHECK: call foo 101; CHECK: catch 102; CHECK: block 103; CHECK: block 104; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9+]]] 105; CHECK: call $drop=, __cxa_begin_catch, $0 106; CHECK: try 107; CHECK: try 108; CHECK: call foo 109; CHECK: br 3 # 3: down to label[[L1:[0-9+]]] 110; CHECK: catch 111; CHECK: block 112; CHECK: block 113; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9+]]] 114; CHECK: call $drop=, __cxa_begin_catch 115; CHECK: try 116; CHECK: call foo 117; CHECK: br 2 # 2: down to label[[L3:[0-9+]]] 118; CHECK: catch_all 119; CHECK: call __cxa_end_catch 120; CHECK: rethrow 0 # down to catch[[L4:[0-9+]]] 121; CHECK: end_try 122; CHECK: end_block # label[[L2]]: 123; CHECK: rethrow 1 # down to catch[[L4]] 124; CHECK: end_block # label[[L3]]: 125; CHECK: call __cxa_end_catch 126; CHECK: br 3 # 3: down to label[[L1]] 127; CHECK: end_try 128; CHECK: catch_all # catch[[L4]]: 129; CHECK: call __cxa_end_catch 130; CHECK: rethrow 0 # to caller 131; CHECK: end_try 132; CHECK: end_block # label[[L0]]: 133; CHECK: rethrow 1 # to caller 134; CHECK: end_block # label[[L1]]: 135; CHECK: call __cxa_end_catch 136; CHECK: end_try 137define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 138entry: 139 invoke void @foo() 140 to label %try.cont11 unwind label %catch.dispatch 141 142catch.dispatch: ; preds = %entry 143 %0 = catchswitch within none [label %catch.start] unwind to caller 144 145catch.start: ; preds = %catch.dispatch 146 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 147 %2 = call i8* @llvm.wasm.get.exception(token %1) 148 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 149 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 150 %matches = icmp eq i32 %3, %4 151 br i1 %matches, label %catch, label %rethrow 152 153catch: ; preds = %catch.start 154 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 155 %6 = bitcast i8* %5 to i32* 156 %7 = load i32, i32* %6, align 4 157 invoke void @foo() [ "funclet"(token %1) ] 158 to label %try.cont unwind label %catch.dispatch2 159 160catch.dispatch2: ; preds = %catch 161 %8 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9 162 163catch.start3: ; preds = %catch.dispatch2 164 %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)] 165 %10 = call i8* @llvm.wasm.get.exception(token %9) 166 %11 = call i32 @llvm.wasm.get.ehselector(token %9) 167 %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 168 %matches4 = icmp eq i32 %11, %12 169 br i1 %matches4, label %catch6, label %rethrow5 170 171catch6: ; preds = %catch.start3 172 %13 = call i8* @__cxa_begin_catch(i8* %10) [ "funclet"(token %9) ] 173 %14 = bitcast i8* %13 to i32* 174 %15 = load i32, i32* %14, align 4 175 invoke void @foo() [ "funclet"(token %9) ] 176 to label %invoke.cont8 unwind label %ehcleanup 177 178invoke.cont8: ; preds = %catch6 179 call void @__cxa_end_catch() [ "funclet"(token %9) ] 180 catchret from %9 to label %try.cont 181 182rethrow5: ; preds = %catch.start3 183 invoke void @llvm.wasm.rethrow() [ "funclet"(token %9) ] 184 to label %unreachable unwind label %ehcleanup9 185 186try.cont: ; preds = %invoke.cont8, %catch 187 call void @__cxa_end_catch() [ "funclet"(token %1) ] 188 catchret from %1 to label %try.cont11 189 190rethrow: ; preds = %catch.start 191 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ] 192 unreachable 193 194try.cont11: ; preds = %try.cont, %entry 195 ret void 196 197ehcleanup: ; preds = %catch6 198 %16 = cleanuppad within %9 [] 199 call void @__cxa_end_catch() [ "funclet"(token %16) ] 200 cleanupret from %16 unwind label %ehcleanup9 201 202ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2 203 %17 = cleanuppad within %1 [] 204 call void @__cxa_end_catch() [ "funclet"(token %17) ] 205 cleanupret from %17 unwind to caller 206 207unreachable: ; preds = %rethrow5 208 unreachable 209} 210 211; Nested loop within a catch clause 212; void test2() { 213; try { 214; foo(); 215; } catch (...) { 216; for (int i = 0; i < 50; i++) 217; foo(); 218; } 219; } 220 221; CHECK-LABEL: test2 222; CHECK: try 223; CHECK: call foo 224; CHECK: catch 225; CHECK: call $drop=, __cxa_begin_catch 226; CHECK: loop # label[[L0:[0-9]+]]: 227; CHECK: block 228; CHECK: block 229; CHECK: br_if 0, {{.*}} # 0: down to label[[L1:[0-9]+]] 230; CHECK: try 231; CHECK: call foo 232; CHECK: br 2 # 2: down to label[[L2:[0-9]+]] 233; CHECK: catch 234; CHECK: try 235; CHECK: call __cxa_end_catch 236; CHECK: catch_all 237; CHECK: call _ZSt9terminatev 238; CHECK: unreachable 239; CHECK: end_try 240; CHECK: rethrow 0 # to caller 241; CHECK: end_try 242; CHECK: end_block # label[[L1]]: 243; CHECK: call __cxa_end_catch 244; CHECK: br 2 # 2: down to label[[L3:[0-9]+]] 245; CHECK: end_block # label[[L2]]: 246; CHECK: br 0 # 0: up to label[[L0]] 247; CHECK: end_loop 248; CHECK: end_try # label[[L3]]: 249define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 250entry: 251 invoke void @foo() 252 to label %try.cont unwind label %catch.dispatch 253 254catch.dispatch: ; preds = %entry 255 %0 = catchswitch within none [label %catch.start] unwind to caller 256 257catch.start: ; preds = %catch.dispatch 258 %1 = catchpad within %0 [i8* null] 259 %2 = call i8* @llvm.wasm.get.exception(token %1) 260 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 261 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 262 br label %for.cond 263 264for.cond: ; preds = %for.inc, %catch.start 265 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ] 266 %cmp = icmp slt i32 %i.0, 50 267 br i1 %cmp, label %for.body, label %for.end 268 269for.body: ; preds = %for.cond 270 invoke void @foo() [ "funclet"(token %1) ] 271 to label %for.inc unwind label %ehcleanup 272 273for.inc: ; preds = %for.body 274 %inc = add nsw i32 %i.0, 1 275 br label %for.cond 276 277for.end: ; preds = %for.cond 278 call void @__cxa_end_catch() [ "funclet"(token %1) ] 279 catchret from %1 to label %try.cont 280 281try.cont: ; preds = %for.end, %entry 282 ret void 283 284ehcleanup: ; preds = %for.body 285 %5 = cleanuppad within %1 [] 286 invoke void @__cxa_end_catch() [ "funclet"(token %5) ] 287 to label %invoke.cont2 unwind label %terminate 288 289invoke.cont2: ; preds = %ehcleanup 290 cleanupret from %5 unwind to caller 291 292terminate: ; preds = %ehcleanup 293 %6 = cleanuppad within %5 [] 294 call void @_ZSt9terminatev() [ "funclet"(token %6) ] 295 unreachable 296} 297 298; Tests if block and try markers are correctly placed. Even if two predecessors 299; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the 300; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2, 301; and scopes cannot be interleaved. 302 303; NOOPT-LABEL: test3 304; NOOPT: try 305; NOOPT: block 306; NOOPT: block 307; NOOPT: block 308; NOOPT: end_block 309; NOOPT: end_block 310; NOOPT: call foo 311; NOOPT: end_block 312; NOOPT: call bar 313; NOOPT: catch {{.*}} 314; NOOPT: end_try 315define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 316bb0: 317 br i1 undef, label %bb1, label %bb2 318 319bb1: ; preds = %bb0 320 br i1 undef, label %bb3, label %bb4 321 322bb2: ; preds = %bb0 323 br label %try.cont 324 325bb3: ; preds = %bb1 326 invoke void @foo() 327 to label %try.cont unwind label %catch.dispatch 328 329bb4: ; preds = %bb1 330 invoke void @bar() 331 to label %try.cont unwind label %catch.dispatch 332 333catch.dispatch: ; preds = %bb4, %bb3 334 %0 = catchswitch within none [label %catch.start] unwind to caller 335 336catch.start: ; preds = %catch.dispatch 337 %1 = catchpad within %0 [i8* null] 338 %2 = call i8* @llvm.wasm.get.exception(token %1) 339 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 340 catchret from %1 to label %try.cont 341 342try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2 343 ret void 344} 345 346; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers, 347; when try and loop markers are in the same BB and end_try and end_loop are in 348; another BB. 349; CHECK: loop 350; CHECK: try 351; CHECK: call foo 352; CHECK: catch 353; CHECK: end_try 354; CHECK: end_loop 355define void @test4(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 356entry: 357 store volatile i32 0, i32* %p 358 br label %loop 359 360loop: ; preds = %try.cont, %entry 361 store volatile i32 1, i32* %p 362 invoke void @foo() 363 to label %try.cont unwind label %catch.dispatch 364 365catch.dispatch: ; preds = %loop 366 %0 = catchswitch within none [label %catch.start] unwind to caller 367 368catch.start: ; preds = %catch.dispatch 369 %1 = catchpad within %0 [i8* null] 370 %2 = call i8* @llvm.wasm.get.exception(token %1) 371 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 372 catchret from %1 to label %try.cont 373 374try.cont: ; preds = %catch.start, %loop 375 br label %loop 376} 377 378; Some of test cases below are hand-tweaked by deleting some library calls to 379; simplify tests and changing the order of basic blocks to cause unwind 380; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum 381; number of mismatches in several tests below. 382 383; - Call unwind mismatch 384; 'call bar''s original unwind destination was 'C0', but after control flow 385; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by 386; wrapping the call with a nested try-delegate that targets 'C0'. 387; - Catch unwind mismatch 388; If 'call foo' throws a foreign exception, it will not be caught by C1, and 389; should be rethrown to the caller. But after control flow linearization, it 390; will instead unwind to C0, an incorrect next EH pad. We wrap the whole 391; try-catch with try-delegate that rethrows an exception to the caller to fix 392; this. 393 394; NOSORT-LABEL: test5 395; NOSORT: try 396; --- try-delegate starts (catch unwind mismatch) 397; NOSORT try 398; NOSORT: try 399; NOSORT: call foo 400; --- try-delegate starts (call unwind mismatch) 401; NOSORT: try 402; NOSORT: call bar 403; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]] 404; --- try-delegate ends (call unwind mismatch) 405; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]: 406; NOSORT: end_try 407; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller 408; --- try-delegate ends (catch unwind mismatch) 409; NOSORT: catch {{.*}} # catch[[C0]]: 410; NOSORT: end_try 411; NOSORT: return 412 413define void @test5() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 414bb0: 415 invoke void @foo() 416 to label %bb1 unwind label %catch.dispatch0 417 418bb1: ; preds = %bb0 419 invoke void @bar() 420 to label %try.cont unwind label %catch.dispatch1 421 422catch.dispatch0: ; preds = %bb0 423 %0 = catchswitch within none [label %catch.start0] unwind to caller 424 425catch.start0: ; preds = %catch.dispatch0 426 %1 = catchpad within %0 [i8* null] 427 %2 = call i8* @llvm.wasm.get.exception(token %1) 428 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 429 catchret from %1 to label %try.cont 430 431catch.dispatch1: ; preds = %bb1 432 %4 = catchswitch within none [label %catch.start1] unwind to caller 433 434catch.start1: ; preds = %catch.dispatch1 435 %5 = catchpad within %4 [i8* null] 436 %6 = call i8* @llvm.wasm.get.exception(token %5) 437 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 438 catchret from %5 to label %try.cont 439 440try.cont: ; preds = %catch.start1, %catch.start0, %bb1 441 ret void 442} 443 444; 'call bar' and 'call baz''s original unwind destination was the caller, but 445; after control flow linearization, their unwind destination incorrectly becomes 446; 'C0'. We fix this by wrapping the calls with a nested try-delegate that 447; rethrows exceptions to the caller. 448 449; And the return value of 'baz' should NOT be stackified because the BB is split 450; during fixing unwind mismatches. 451 452; NOSORT-LABEL: test6 453; NOSORT: try 454; NOSORT: call foo 455; --- try-delegate starts (call unwind mismatch) 456; NOSORT: try 457; NOSORT: call bar 458; NOSORT: call $[[RET:[0-9]+]]=, baz 459; NOSORT-NOT: call $push{{.*}}=, baz 460; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller 461; --- try-delegate ends (call unwind mismatch) 462; NOSORT: call nothrow, $[[RET]] 463; NOSORT: return 464; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]: 465; NOSORT: return 466; NOSORT: end_try 467 468define void @test6() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 469bb0: 470 invoke void @foo() 471 to label %bb1 unwind label %catch.dispatch0 472 473bb1: ; preds = %bb0 474 call void @bar() 475 %call = call i32 @baz() 476 call void @nothrow(i32 %call) #0 477 ret void 478 479catch.dispatch0: ; preds = %bb0 480 %0 = catchswitch within none [label %catch.start0] unwind to caller 481 482catch.start0: ; preds = %catch.dispatch0 483 %1 = catchpad within %0 [i8* null] 484 %2 = call i8* @llvm.wasm.get.exception(token %1) 485 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 486 catchret from %1 to label %try.cont 487 488try.cont: ; preds = %catch.start0 489 ret void 490} 491 492; The same as test5, but we have one more call 'call @foo' in bb1 which unwinds 493; to the caller. IN this case bb1 has two call unwind mismatches: 'call @foo' 494; unwinds to the caller and 'call @bar' unwinds to catch C0. 495 496; NOSORT-LABEL: test7 497; NOSORT: try 498; --- try-delegate starts (catch unwind mismatch) 499; NOSORT try 500; NOSORT: try 501; NOSORT: call foo 502; --- try-delegate starts (call unwind mismatch) 503; NOSORT: try 504; NOSORT: call foo 505; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller 506; --- try-delegate ends (call unwind mismatch) 507; --- try-delegate starts (call unwind mismatch) 508; NOSORT: try 509; NOSORT: call bar 510; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]] 511; --- try-delegate ends (call unwind mismatch) 512; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]: 513; NOSORT: end_try 514; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller 515; --- try-delegate ends (catch unwind mismatch) 516; NOSORT: catch {{.*}} # catch[[C0]]: 517; NOSORT: end_try 518; NOSORT: return 519 520define void @test7() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 521bb0: 522 invoke void @foo() 523 to label %bb1 unwind label %catch.dispatch0 524 525bb1: ; preds = %bb0 526 call void @foo() 527 invoke void @bar() 528 to label %try.cont unwind label %catch.dispatch1 529 530catch.dispatch0: ; preds = %bb0 531 %0 = catchswitch within none [label %catch.start0] unwind to caller 532 533catch.start0: ; preds = %catch.dispatch0 534 %1 = catchpad within %0 [i8* null] 535 %2 = call i8* @llvm.wasm.get.exception(token %1) 536 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 537 catchret from %1 to label %try.cont 538 539catch.dispatch1: ; preds = %bb1 540 %4 = catchswitch within none [label %catch.start1] unwind to caller 541 542catch.start1: ; preds = %catch.dispatch1 543 %5 = catchpad within %4 [i8* null] 544 %6 = call i8* @llvm.wasm.get.exception(token %5) 545 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 546 catchret from %5 to label %try.cont 547 548try.cont: ; preds = %catch.start1, %catch.start0, %bb1 549 ret void 550} 551 552; Similar situation as @test6. Here 'call @qux''s original unwind destination 553; was the caller, but after control flow linearization, their unwind destination 554; incorrectly becomes 'C0' within the function. We fix this by wrapping the call 555; with a nested try-delegate that rethrows the exception to the caller. 556 557; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the 558; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5' 559; and 'call @qux'. 560 561; NOSORT-LABEL: test8 562; NOSORT: try i32 563; NOSORT: call foo 564; --- try-delegate starts (call unwind mismatch) 565; NOSORT: try 566; NOSORT: i32.const $push{{[0-9]+}}=, 5 567; NOSORT: call ${{[0-9]+}}=, qux 568; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller 569; --- try-delegate ends (call unwind mismatch) 570; NOSORT: return 571; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]: 572; NOSORT: return 573; NOSORT: end_try 574 575define i32 @test8() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 576bb0: 577 invoke void @foo() 578 to label %bb1 unwind label %catch.dispatch0 579 580bb1: ; preds = %bb0 581 %0 = call i32 @qux(i32 5) 582 ret i32 %0 583 584catch.dispatch0: ; preds = %bb0 585 %1 = catchswitch within none [label %catch.start0] unwind to caller 586 587catch.start0: ; preds = %catch.dispatch0 588 %2 = catchpad within %1 [i8* null] 589 %3 = call i8* @llvm.wasm.get.exception(token %2) 590 %j = call i32 @llvm.wasm.get.ehselector(token %2) 591 catchret from %2 to label %try.cont 592 593try.cont: ; preds = %catch.start0 594 ret i32 0 595} 596 597; Tests the case when TEE stackifies a register in RegStackify but it gets 598; unstackified in fixCallUnwindMismatches in CFGStackify. 599 600; NOSORT-LOCALS-LABEL: test9 601define void @test9(i32 %x) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 602bb0: 603 invoke void @foo() 604 to label %bb1 unwind label %catch.dispatch0 605 606bb1: ; preds = %bb0 607 %t = add i32 %x, 4 608 ; This %addr is used in multiple places, so tee is introduced in RegStackify, 609 ; which stackifies the use of %addr in store instruction. A tee has two dest 610 ; registers, the first of which is stackified and the second is not. 611 ; But when we introduce a nested try-delegate in fixCallUnwindMismatches in 612 ; CFGStackify, it is possible that we end up unstackifying the first dest 613 ; register. In that case, we convert that tee into a copy. 614 %addr = inttoptr i32 %t to i32* 615 %load = load i32, i32* %addr 616 %call = call i32 @baz() 617 %add = add i32 %load, %call 618 store i32 %add, i32* %addr 619 ret void 620; NOSORT-LOCALS: i32.add 621; NOSORT-LOCALS-NOT: local.tee 622; NOSORT-LOCALS-NEXT: local.set 623 624catch.dispatch0: ; preds = %bb0 625 %0 = catchswitch within none [label %catch.start0] unwind to caller 626 627catch.start0: ; preds = %catch.dispatch0 628 %1 = catchpad within %0 [i8* null] 629 %2 = call i8* @llvm.wasm.get.exception(token %1) 630 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 631 catchret from %1 to label %try.cont 632 633try.cont: ; preds = %catch.start0 634 ret void 635} 636 637; We have two call unwind unwind mismatches: 638; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 639; CFG, when it is supposed to unwind to another EH pad. 640; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 641; CFG, when it is supposed to unwind to the caller. 642; We also have a catch unwind mismatch: If an exception is not caught by the 643; first catch because it is a non-C++ exception, it shouldn't unwind to the next 644; catch, but it should unwind to the caller. 645 646; NOSORT-LABEL: test10 647; NOSORT: try 648; --- try-delegate starts (catch unwind mismatch) 649; NOSORT: try 650; NOSORT: try 651; NOSORT: call foo 652; --- try-delegate starts (call unwind mismatch) 653; NOSORT: try 654; NOSORT: call bar 655; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]] 656; --- try-delegate ends (call unwind mismatch) 657; NOSORT: catch 658; NOSORT: call {{.*}} __cxa_begin_catch 659; --- try-delegate starts (call unwind mismatch) 660; NOSORT: try 661; NOSORT: call __cxa_end_catch 662; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller 663; --- try-delegate ends (call unwind mismatch) 664; NOSORT: end_try 665; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller 666; --- try-delegate ends (catch unwind mismatch) 667; NOSORT: catch {{.*}} # catch[[C0]]: 668; NOSORT: call {{.*}} __cxa_begin_catch 669; NOSORT: call __cxa_end_catch 670; NOSORT: end_try 671; NOSORT: return 672 673define void @test10() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 674bb0: 675 invoke void @foo() 676 to label %bb1 unwind label %catch.dispatch0 677 678bb1: ; preds = %bb0 679 invoke void @bar() 680 to label %try.cont unwind label %catch.dispatch1 681 682catch.dispatch0: ; preds = %bb0 683 %0 = catchswitch within none [label %catch.start0] unwind to caller 684 685catch.start0: ; preds = %catch.dispatch0 686 %1 = catchpad within %0 [i8* null] 687 %2 = call i8* @llvm.wasm.get.exception(token %1) 688 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 689 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 690 call void @__cxa_end_catch() [ "funclet"(token %1) ] 691 catchret from %1 to label %try.cont 692 693catch.dispatch1: ; preds = %bb1 694 %5 = catchswitch within none [label %catch.start1] unwind to caller 695 696catch.start1: ; preds = %catch.dispatch1 697 %6 = catchpad within %5 [i8* null] 698 %7 = call i8* @llvm.wasm.get.exception(token %6) 699 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 700 %9 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ] 701 call void @__cxa_end_catch() [ "funclet"(token %6) ] 702 catchret from %6 to label %try.cont 703 704try.cont: ; preds = %catch.start1, %catch.start0, %bb1 705 ret void 706} 707 708; In CFGSort, EH pads should be sorted as soon as it is available and 709; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are 710; in the middle of sorting another region that does not contain the EH pad. In 711; this example, 'catch.start' should be sorted right after 'if.then' is sorted 712; (before 'cont' is sorted) and there should not be any unwind destination 713; mismatches in CFGStackify. 714 715; NOOPT-LABEL: test11 716; NOOPT: block 717; NOOPT: try 718; NOOPT: call foo 719; NOOPT: catch 720; NOOPT: end_try 721; NOOPT: call foo 722; NOOPT: end_block 723; NOOPT: return 724define void @test11(i32 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 725entry: 726 %tobool = icmp ne i32 %arg, 0 727 br i1 %tobool, label %if.then, label %if.end 728 729catch.dispatch: ; preds = %if.then 730 %0 = catchswitch within none [label %catch.start] unwind to caller 731 732catch.start: ; preds = %catch.dispatch 733 %1 = catchpad within %0 [i8* null] 734 %2 = call i8* @llvm.wasm.get.exception(token %1) 735 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 736 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 737 call void @__cxa_end_catch() [ "funclet"(token %1) ] 738 catchret from %1 to label %if.end 739 740if.then: ; preds = %entry 741 invoke void @foo() 742 to label %cont unwind label %catch.dispatch 743 744cont: ; preds = %if.then 745 call void @foo() 746 br label %if.end 747 748if.end: ; preds = %cont, %catch.start, %entry 749 ret void 750} 751 752; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into 753; calls to external symbols (not global addresses) in instruction selection, 754; which will be eventually lowered to library function calls. 755; Because this test runs with -wasm-disable-ehpad-sort, these library calls in 756; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or 757; unwinding destination mismatches in CFGStackify. 758 759; NOSORT-LABEL: test12 760; NOSORT: try 761; NOSORT: call foo 762; NOSORT: call {{.*}} memcpy 763; NOSORT: call {{.*}} memmove 764; NOSORT: call {{.*}} memset 765; NOSORT: return 766; NOSORT: catch_all 767; NOSORT: rethrow 0 768; NOSORT: end_try 769define void @test12(i8* %a, i8* %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 770entry: 771 %o = alloca %class.Object, align 1 772 invoke void @foo() 773 to label %invoke.cont unwind label %ehcleanup 774 775invoke.cont: ; preds = %entry 776 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 777 call void @llvm.memmove.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 778 call void @llvm.memset.p0i8.i32(i8* %a, i8 0, i32 100, i1 false) 779 %call = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) 780 ret void 781 782ehcleanup: ; preds = %entry 783 %0 = cleanuppad within none [] 784 %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) [ "funclet"(token %0) ] 785 cleanupret from %0 unwind to caller 786} 787 788; Tests if 'try' marker is placed correctly. In this test, 'try' should be 789; placed before the call to 'nothrow_i32' and not between the call to 790; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is 791; stackified and pushed onto the stack to be consumed by the call to 'fun'. 792 793; CHECK-LABEL: test13 794; CHECK: try 795; CHECK: call $push{{.*}}=, nothrow_i32 796; CHECK: call fun, $pop{{.*}} 797define void @test13() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 798entry: 799 %call = call i32 @nothrow_i32() 800 invoke void @fun(i32 %call) 801 to label %invoke.cont unwind label %terminate 802 803invoke.cont: ; preds = %entry 804 ret void 805 806terminate: ; preds = %entry 807 %0 = cleanuppad within none [] 808 call void @_ZSt9terminatev() [ "funclet"(token %0) ] 809 unreachable 810} 811 812; This crashed on debug mode (= when NDEBUG is not defined) when the logic for 813; computing the innermost region was not correct, in which a loop region 814; contains an exception region. This should pass CFGSort without crashing. 815define void @test14() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 816entry: 817 %e = alloca %class.MyClass, align 4 818 br label %for.cond 819 820for.cond: ; preds = %for.inc, %entry 821 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] 822 %cmp = icmp slt i32 %i.0, 9 823 br i1 %cmp, label %for.body, label %for.end 824 825for.body: ; preds = %for.cond 826 invoke void @quux(i32 %i.0) 827 to label %for.inc unwind label %catch.dispatch 828 829catch.dispatch: ; preds = %for.body 830 %0 = catchswitch within none [label %catch.start] unwind to caller 831 832catch.start: ; preds = %catch.dispatch 833 %1 = catchpad within %0 [i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)] 834 %2 = call i8* @llvm.wasm.get.exception(token %1) 835 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 836 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)) 837 %matches = icmp eq i32 %3, %4 838 br i1 %matches, label %catch, label %rethrow 839 840catch: ; preds = %catch.start 841 %5 = call i8* @__cxa_get_exception_ptr(i8* %2) [ "funclet"(token %1) ] 842 %6 = bitcast i8* %5 to %class.MyClass* 843 %call = call %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* %e, %class.MyClass* dereferenceable(4) %6) [ "funclet"(token %1) ] 844 %7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 845 %x = getelementptr inbounds %class.MyClass, %class.MyClass* %e, i32 0, i32 0 846 %8 = load i32, i32* %x, align 4 847 invoke void @quux(i32 %8) [ "funclet"(token %1) ] 848 to label %invoke.cont2 unwind label %ehcleanup 849 850invoke.cont2: ; preds = %catch 851 %call3 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) [ "funclet"(token %1) ] 852 call void @__cxa_end_catch() [ "funclet"(token %1) ] 853 catchret from %1 to label %for.inc 854 855rethrow: ; preds = %catch.start 856 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ] 857 unreachable 858 859for.inc: ; preds = %invoke.cont2, %for.body 860 %inc = add nsw i32 %i.0, 1 861 br label %for.cond 862 863ehcleanup: ; preds = %catch 864 %9 = cleanuppad within %1 [] 865 %call4 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) [ "funclet"(token %9) ] 866 invoke void @__cxa_end_catch() [ "funclet"(token %9) ] 867 to label %invoke.cont6 unwind label %terminate7 868 869invoke.cont6: ; preds = %ehcleanup 870 cleanupret from %9 unwind to caller 871 872for.end: ; preds = %for.cond 873 ret void 874 875terminate7: ; preds = %ehcleanup 876 %10 = cleanuppad within %9 [] 877 call void @_ZSt9terminatev() [ "funclet"(token %10) ] 878 unreachable 879} 880 881; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches 882; correctly. The code is in the form below, where 'br' is unnecessary because 883; after running the 'try' body the control flow will fall through to bb2 anyway. 884 885; bb0: 886; try 887; ... 888; br bb2 <- Not necessary 889; bb1 (ehpad): 890; catch 891; ... 892; bb2: <- Continuation BB 893; end 894; CHECK-LABEL: test15 895define void @test15(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 896entry: 897 invoke void @foo() 898 to label %for.body unwind label %catch.dispatch 899 900for.body: ; preds = %for.end, %entry 901 %i = phi i32 [ %inc, %for.end ], [ 0, %entry ] 902 invoke void @foo() 903 to label %for.end unwind label %catch.dispatch 904 905; Before going to CFGStackify, this BB will have a conditional branch followed 906; by an unconditional branch. CFGStackify should remove only the unconditional 907; one. 908for.end: ; preds = %for.body 909 %inc = add nuw nsw i32 %i, 1 910 %exitcond = icmp eq i32 %inc, %n 911 br i1 %exitcond, label %try.cont, label %for.body 912; CHECK: br_if 913; CHECK-NOT: br 914; CHECK: end_loop 915; CHECK: catch 916 917catch.dispatch: ; preds = %for.body, %entry 918 %0 = catchswitch within none [label %catch.start] unwind to caller 919 920catch.start: ; preds = %catch.dispatch 921 %1 = catchpad within %0 [i8* null] 922 %2 = call i8* @llvm.wasm.get.exception(token %1) 923 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 924 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 925 call void @__cxa_end_catch() [ "funclet"(token %1) ] 926 catchret from %1 to label %try.cont 927 928try.cont: ; preds = %catch.start, %for.end 929 ret void 930} 931 932; void foo(); 933; void test16() { 934; try { 935; foo(); 936; try { 937; foo(); 938; } catch (...) { 939; } 940; } catch (...) { 941; } 942; } 943; 944; This tests whether the 'br' can be removed in code in the form as follows. 945; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this 946; case, after running an inner try body, the control flow should fall through to 947; bb3, so the 'br' in the code is unnecessary. 948 949; bb0: 950; try 951; try 952; ... 953; br bb3 <- Not necessary 954; bb1: 955; catch 956; bb2: 957; end_try 958; catch 959; ... 960; bb3: <- Continuation BB 961; end 962; 963; CHECK-LABEL: test16 964define void @test16() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 965; CHECK: call foo 966entry: 967 invoke void @foo() 968 to label %invoke.cont unwind label %catch.dispatch3 969 970; CHECK: call foo 971; CHECK-NOT: br 972invoke.cont: ; preds = %entry 973 invoke void @foo() 974 to label %try.cont8 unwind label %catch.dispatch 975 976catch.dispatch: ; preds = %invoke.cont 977 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3 978 979; CHECK: catch 980catch.start: ; preds = %catch.dispatch 981 %1 = catchpad within %0 [i8* null] 982 %2 = call i8* @llvm.wasm.get.exception(token %1) 983 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 984 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 985 invoke void @__cxa_end_catch() [ "funclet"(token %1) ] 986 to label %invoke.cont2 unwind label %catch.dispatch3 987 988catch.dispatch3: ; preds = %catch.start, %catch.dispatch, %entry 989 %5 = catchswitch within none [label %catch.start4] unwind to caller 990 991catch.start4: ; preds = %catch.dispatch3 992 %6 = catchpad within %5 [i8* null] 993 %7 = call i8* @llvm.wasm.get.exception(token %6) 994 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 995 %9 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ] 996 call void @__cxa_end_catch() [ "funclet"(token %6) ] 997 catchret from %6 to label %try.cont8 998 999try.cont8: ; preds = %invoke.cont2, %catch.start4, %invoke.cont 1000 ret void 1001 1002invoke.cont2: ; preds = %catch.start 1003 catchret from %1 to label %try.cont8 1004} 1005 1006; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs 1007; to the exception, but does not belong to the loop (because it does not have a 1008; path back to the loop header), and is placed after the loop latch block 1009; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed 1010; correctly not right after 'invoke.cont' part but after 'ehcleanup' part, 1011; NOSORT-LABEL: test17 1012; NOSORT: loop 1013; NOSORT: try 1014; NOSORT: end_try 1015; NOSORT: end_loop 1016define void @test17(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1017entry: 1018 br label %while.cond 1019 1020while.cond: ; preds = %invoke.cont, %entry 1021 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ] 1022 %tobool = icmp ne i32 %n.addr.0, 0 1023 br i1 %tobool, label %while.body, label %while.end 1024 1025while.body: ; preds = %while.cond 1026 %dec = add nsw i32 %n.addr.0, -1 1027 invoke void @foo() 1028 to label %while.end unwind label %catch.dispatch 1029 1030catch.dispatch: ; preds = %while.body 1031 %0 = catchswitch within none [label %catch.start] unwind to caller 1032 1033catch.start: ; preds = %catch.dispatch 1034 %1 = catchpad within %0 [i8* null] 1035 %2 = call i8* @llvm.wasm.get.exception(token %1) 1036 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1037 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 1038 invoke void @__cxa_end_catch() [ "funclet"(token %1) ] 1039 to label %invoke.cont unwind label %ehcleanup 1040 1041invoke.cont: ; preds = %catch.start 1042 catchret from %1 to label %while.cond 1043 1044ehcleanup: ; preds = %catch.start 1045 %5 = cleanuppad within %1 [] 1046 call void @_ZSt9terminatev() [ "funclet"(token %5) ] 1047 unreachable 1048 1049while.end: ; preds = %while.body, %while.cond 1050 ret void 1051} 1052 1053; When the function return type is non-void and 'end' instructions are at the 1054; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes 1055; the corresponding block/loop/try's type to match the function's return type. 1056; But when a `try`'s type is fixed, we should also check `end` instructions 1057; before its corresponding `catch_all`, because both `try` and `catch_all` body 1058; should satisfy the return type requirements. 1059 1060; NOSORT-LABEL: test18 1061; NOSORT: try i32 1062; NOSORT: loop i32 1063; NOSORT: end_loop 1064; NOSORT: catch_all 1065; NOSORT: end_try 1066; NOSORT-NEXT: end_function 1067define i32 @test18(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1068entry: 1069 %t = alloca %class.Object, align 1 1070 br label %for.cond 1071 1072for.cond: ; preds = %for.inc, %entry 1073 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] 1074 %cmp = icmp slt i32 %i.0, %n 1075 br label %for.body 1076 1077for.body: ; preds = %for.cond 1078 %div = sdiv i32 %n, 2 1079 %cmp1 = icmp eq i32 %i.0, %div 1080 br i1 %cmp1, label %if.then, label %for.inc 1081 1082if.then: ; preds = %for.body 1083 %call = invoke i32 @baz() 1084 to label %invoke.cont unwind label %ehcleanup 1085 1086invoke.cont: ; preds = %if.then 1087 %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %t) 1088 ret i32 %call 1089 1090for.inc: ; preds = %for.body 1091 %inc = add nsw i32 %i.0, 1 1092 br label %for.cond 1093 1094ehcleanup: ; preds = %if.then 1095 %0 = cleanuppad within none [] 1096 %call3 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %t) [ "funclet"(token %0) ] 1097 cleanupret from %0 unwind to caller 1098} 1099 1100; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug. 1101; This should not crash and try-delegate has to be created around 'call @baz', 1102; because the initial TRY placement for 'call @quux' was done before 'call @baz' 1103; because 'call @baz''s return value is stackified. 1104 1105; CHECK-LABEL: test19 1106; CHECK: try 1107; CHECK: try 1108; CHECK: call $[[RET:[0-9]+]]=, baz 1109; CHECK: delegate 1 1110; CHECK: call quux, $[[RET]] 1111; CHECK: catch_all 1112; CHECK: end_try 1113define void @test19() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1114entry: 1115 %call = call i32 @baz() 1116 invoke void @quux(i32 %call) 1117 to label %invoke.cont unwind label %ehcleanup 1118 1119ehcleanup: ; preds = %entry 1120 %0 = cleanuppad within none [] 1121 cleanupret from %0 unwind to caller 1122 1123invoke.cont: ; preds = %entry 1124 unreachable 1125} 1126 1127; This tests if invalidated branch destinations after fixing catch unwind 1128; mismatches are correctly remapped. For example, we have this code and suppose 1129; we need to wrap this try-catch-end in this code with a try-delegate to fix a 1130; catch unwind mismatch: 1131 ; - Before: 1132; block 1133; br (a) 1134; try 1135; catch 1136; end_try 1137; end_block 1138; <- (a) 1139; 1140; - After 1141; block 1142; br (a) 1143; try 1144; try 1145; catch 1146; end_try 1147; <- (a) 1148; delegate 1149; end_block 1150; <- (b) 1151; After adding a try-delegate, the 'br's destination BB, where (a) points, 1152; becomes invalid because it incorrectly branches into an inner scope. The 1153; destination should change to the BB where (b) points. 1154 1155; NOSORT-LABEL: test20 1156; NOSORT: try 1157; NOSORT: br_if 0 1158define void @test20(i1 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1159entry: 1160 br i1 %arg, label %bb0, label %dest 1161 1162bb0: ; preds = %entry 1163 invoke void @foo() 1164 to label %bb1 unwind label %catch.dispatch0 1165 1166bb1: ; preds = %bb0 1167 invoke void @bar() 1168 to label %try.cont unwind label %catch.dispatch1 1169 1170catch.dispatch0: ; preds = %bb0 1171 %0 = catchswitch within none [label %catch.start0] unwind to caller 1172 1173catch.start0: ; preds = %catch.dispatch0 1174 %1 = catchpad within %0 [i8* null] 1175 %2 = call i8* @llvm.wasm.get.exception(token %1) 1176 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1177 catchret from %1 to label %try.cont 1178 1179dest: ; preds = %entry 1180 ret void 1181 1182catch.dispatch1: ; preds = %bb1 1183 %4 = catchswitch within none [label %catch.start1] unwind to caller 1184 1185catch.start1: ; preds = %catch.dispatch1 1186 %5 = catchpad within %4 [i8* null] 1187 %6 = call i8* @llvm.wasm.get.exception(token %5) 1188 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 1189 catchret from %5 to label %try.cont 1190 1191try.cont: ; preds = %catch.start1, %catch.start0, %bb1 1192 ret void 1193} 1194 1195; The similar case with test20, but multiple consecutive delegates are 1196; generated: 1197; - Before: 1198; block 1199; br (a) 1200; try 1201; catch 1202; end_try 1203; end_block 1204; <- (a) 1205; 1206; - After 1207; block 1208; br (a) 1209; try 1210; ... 1211; try 1212; try 1213; catch 1214; end_try 1215; <- (a) 1216; delegate 1217; delegate 1218; end_block 1219; <- (b) The br destination should be remapped to here 1220; 1221; The test was reduced by bugpoint and should not crash in CFGStackify. 1222define void @test21() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1223entry: 1224 br i1 undef, label %if.then, label %if.end12 1225 1226if.then: ; preds = %entry 1227 invoke void @__cxa_throw(i8* null, i8* null, i8* null) #1 1228 to label %unreachable unwind label %catch.dispatch 1229 1230catch.dispatch: ; preds = %if.then 1231 %0 = catchswitch within none [label %catch.start] unwind to caller 1232 1233catch.start: ; preds = %catch.dispatch 1234 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 1235 %2 = call i8* @llvm.wasm.get.exception(token %1) 1236 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1237 catchret from %1 to label %catchret.dest 1238 1239catchret.dest: ; preds = %catch.start 1240 invoke void @foo() 1241 to label %invoke.cont unwind label %catch.dispatch4 1242 1243invoke.cont: ; preds = %catchret.dest 1244 invoke void @__cxa_throw(i8* null, i8* null, i8* null) #1 1245 to label %unreachable unwind label %catch.dispatch4 1246 1247catch.dispatch4: ; preds = %invoke.cont, %catchret.dest 1248 %4 = catchswitch within none [label %catch.start5] unwind to caller 1249 1250catch.start5: ; preds = %catch.dispatch4 1251 %5 = catchpad within %4 [i8* bitcast (i8** @_ZTIi to i8*)] 1252 %6 = call i8* @llvm.wasm.get.exception(token %5) 1253 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 1254 unreachable 1255 1256if.end12: ; preds = %entry 1257 invoke void @foo() 1258 to label %invoke.cont14 unwind label %catch.dispatch16 1259 1260catch.dispatch16: ; preds = %if.end12 1261 %8 = catchswitch within none [label %catch.start17] unwind label %ehcleanup 1262 1263catch.start17: ; preds = %catch.dispatch16 1264 %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)] 1265 %10 = call i8* @llvm.wasm.get.exception(token %9) 1266 %11 = call i32 @llvm.wasm.get.ehselector(token %9) 1267 br i1 undef, label %catch20, label %rethrow19 1268 1269catch20: ; preds = %catch.start17 1270 catchret from %9 to label %catchret.dest22 1271 1272catchret.dest22: ; preds = %catch20 1273 br label %try.cont23 1274 1275rethrow19: ; preds = %catch.start17 1276 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ] 1277 to label %unreachable unwind label %ehcleanup 1278 1279try.cont23: ; preds = %invoke.cont14, %catchret.dest22 1280 invoke void @foo() 1281 to label %invoke.cont24 unwind label %ehcleanup 1282 1283invoke.cont24: ; preds = %try.cont23 1284 ret void 1285 1286invoke.cont14: ; preds = %if.end12 1287 br label %try.cont23 1288 1289ehcleanup: ; preds = %try.cont23, %rethrow19, %catch.dispatch16 1290 %12 = cleanuppad within none [] 1291 cleanupret from %12 unwind to caller 1292 1293unreachable: ; preds = %rethrow19, %invoke.cont, %if.then 1294 unreachable 1295} 1296 1297; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc' 1298; should return a vector and not a single BB, which was incorrect. 1299; This was reduced by bugpoint and should not crash in CFGStackify. 1300define void @test22() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1301entry: 1302 invoke void @foo() 1303 to label %invoke.cont unwind label %catch.dispatch 1304 1305catch.dispatch: ; preds = %entry 1306 %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22 1307 1308catch.start: ; preds = %catch.dispatch 1309 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 1310 %2 = call i8* @llvm.wasm.get.exception(token %1) 1311 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1312 invoke void @__cxa_throw(i8* null, i8* null, i8* null) #1 [ "funclet"(token %1) ] 1313 to label %unreachable unwind label %catch.dispatch2 1314 1315catch.dispatch2: ; preds = %catch.start 1316 %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup 1317 1318catch.start3: ; preds = %catch.dispatch2 1319 %5 = catchpad within %4 [i8* bitcast (i8** @_ZTIi to i8*)] 1320 %6 = call i8* @llvm.wasm.get.exception(token %5) 1321 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 1322 catchret from %5 to label %try.cont 1323 1324try.cont: ; preds = %catch.start3 1325 invoke void @foo() [ "funclet"(token %1) ] 1326 to label %invoke.cont8 unwind label %ehcleanup 1327 1328invoke.cont8: ; preds = %try.cont 1329 invoke void @__cxa_throw(i8* null, i8* null, i8* null) #1 [ "funclet"(token %1) ] 1330 to label %unreachable unwind label %catch.dispatch11 1331 1332catch.dispatch11: ; preds = %invoke.cont8 1333 %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup 1334 1335catch.start12: ; preds = %catch.dispatch11 1336 %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)] 1337 %10 = call i8* @llvm.wasm.get.exception(token %9) 1338 %11 = call i32 @llvm.wasm.get.ehselector(token %9) 1339 unreachable 1340 1341invoke.cont: ; preds = %entry 1342 unreachable 1343 1344ehcleanup: ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2 1345 %12 = cleanuppad within %1 [] 1346 cleanupret from %12 unwind label %ehcleanup22 1347 1348ehcleanup22: ; preds = %ehcleanup, %catch.dispatch 1349 %13 = cleanuppad within none [] 1350 cleanupret from %13 unwind to caller 1351 1352unreachable: ; preds = %invoke.cont8, %catch.start 1353 unreachable 1354} 1355 1356; void test23() { 1357; try { 1358; try { 1359; throw 0; 1360; } catch (int) { 1361; } 1362; } catch (int) { 1363; } 1364; } 1365; 1366; Regression test for a WebAssemblyException grouping bug. After catchswitches 1367; are removed, EH pad catch.start2 is dominated by catch.start, but because 1368; catch.start2 is the unwind destination of catch.start, it should not be 1369; included in catch.start's exception. Also, after we take catch.start2's 1370; exception out of catch.start's exception, we have to take out try.cont8 out of 1371; catch.start's exception, because it has a predecessor in catch.start2. 1372define void @test23() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1373entry: 1374 %exception = call i8* @__cxa_allocate_exception(i32 4) #0 1375 %0 = bitcast i8* %exception to i32* 1376 store i32 0, i32* %0, align 16 1377 invoke void @__cxa_throw(i8* %exception, i8* bitcast (i8** @_ZTIi to i8*), i8* null) #1 1378 to label %unreachable unwind label %catch.dispatch 1379 1380catch.dispatch: ; preds = %entry 1381 %1 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1 1382 1383catch.start: ; preds = %catch.dispatch 1384 %2 = catchpad within %1 [i8* bitcast (i8** @_ZTIi to i8*)] 1385 %3 = call i8* @llvm.wasm.get.exception(token %2) 1386 %4 = call i32 @llvm.wasm.get.ehselector(token %2) 1387 %5 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #0 1388 %matches = icmp eq i32 %4, %5 1389 br i1 %matches, label %catch, label %rethrow 1390 1391catch: ; preds = %catch.start 1392 %6 = call i8* @__cxa_begin_catch(i8* %3) #0 [ "funclet"(token %2) ] 1393 %7 = bitcast i8* %6 to i32* 1394 %8 = load i32, i32* %7, align 4 1395 call void @__cxa_end_catch() #0 [ "funclet"(token %2) ] 1396 catchret from %2 to label %catchret.dest 1397 1398catchret.dest: ; preds = %catch 1399 br label %try.cont 1400 1401rethrow: ; preds = %catch.start 1402 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %2) ] 1403 to label %unreachable unwind label %catch.dispatch1 1404 1405catch.dispatch1: ; preds = %rethrow, %catch.dispatch 1406 %9 = catchswitch within none [label %catch.start2] unwind to caller 1407 1408catch.start2: ; preds = %catch.dispatch1 1409 %10 = catchpad within %9 [i8* bitcast (i8** @_ZTIi to i8*)] 1410 %11 = call i8* @llvm.wasm.get.exception(token %10) 1411 %12 = call i32 @llvm.wasm.get.ehselector(token %10) 1412 %13 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #0 1413 %matches3 = icmp eq i32 %12, %13 1414 br i1 %matches3, label %catch5, label %rethrow4 1415 1416catch5: ; preds = %catch.start2 1417 %14 = call i8* @__cxa_begin_catch(i8* %11) #0 [ "funclet"(token %10) ] 1418 %15 = bitcast i8* %14 to i32* 1419 %16 = load i32, i32* %15, align 4 1420 call void @__cxa_end_catch() #0 [ "funclet"(token %10) ] 1421 catchret from %10 to label %catchret.dest7 1422 1423catchret.dest7: ; preds = %catch5 1424 br label %try.cont8 1425 1426rethrow4: ; preds = %catch.start2 1427 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %10) ] 1428 unreachable 1429 1430try.cont8: ; preds = %try.cont, %catchret.dest7 1431 ret void 1432 1433try.cont: ; preds = %catchret.dest 1434 br label %try.cont8 1435 1436unreachable: ; preds = %rethrow, %entry 1437 unreachable 1438} 1439 1440; Test for WebAssemblyException grouping. This test is hand-modified to generate 1441; this structure: 1442; catch.start dominates catch.start4 and catch.start4 dominates catch.start12, 1443; so the after dominator-based grouping, we end up with: 1444; catch.start's exception > catch4.start's exception > catch12.start's exception 1445; (> here represents subexception relationship) 1446; 1447; But the unwind destination chain is catch.start -> catch.start4 -> 1448; catch.start12. So all these subexception relationship should be deconstructed. 1449; We have to make sure to take out catch.start4's exception out of catch.start's 1450; exception first, before taking out catch.start12's exception out of 1451; catch.start4's exception; otherwise we end up with an incorrect relationship 1452; of catch.start's exception > catch.start12's exception. 1453define void @test24() personality i8* bitcast (i32 (...)* 1454@__gxx_wasm_personality_v0 to i8*) { 1455entry: 1456 invoke void @foo() 1457 to label %invoke.cont unwind label %catch.dispatch 1458 1459invoke.cont: ; preds = %entry 1460 invoke void @foo() 1461 to label %invoke.cont1 unwind label %catch.dispatch 1462 1463invoke.cont1: ; preds = %invoke.cont 1464 invoke void @foo() 1465 to label %try.cont18 unwind label %catch.dispatch 1466 1467catch.dispatch11: ; preds = %rethrow6, %catch.dispatch3 1468 %0 = catchswitch within none [label %catch.start12] unwind to caller 1469 1470catch.start12: ; preds = %catch.dispatch11 1471 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 1472 %2 = call i8* @llvm.wasm.get.exception(token %1) 1473 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1474 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #0 1475 %matches13 = icmp eq i32 %3, %4 1476 br i1 %matches13, label %catch15, label %rethrow14 1477 1478catch15: ; preds = %catch.start12 1479 %5 = call i8* @__cxa_begin_catch(i8* %2) #0 [ "funclet"(token %1) ] 1480 %6 = bitcast i8* %5 to i32* 1481 %7 = load i32, i32* %6, align 4 1482 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ] 1483 catchret from %1 to label %try.cont18 1484 1485rethrow14: ; preds = %catch.start12 1486 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ] 1487 unreachable 1488 1489catch.dispatch3: ; preds = %rethrow, %catch.dispatch 1490 %8 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11 1491 1492catch.start4: ; preds = %catch.dispatch3 1493 %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)] 1494 %10 = call i8* @llvm.wasm.get.exception(token %9) 1495 %11 = call i32 @llvm.wasm.get.ehselector(token %9) 1496 %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #0 1497 %matches5 = icmp eq i32 %11, %12 1498 br i1 %matches5, label %catch7, label %rethrow6 1499 1500catch7: ; preds = %catch.start4 1501 %13 = call i8* @__cxa_begin_catch(i8* %10) #0 [ "funclet"(token %9) ] 1502 %14 = bitcast i8* %13 to i32* 1503 %15 = load i32, i32* %14, align 4 1504 call void @__cxa_end_catch() #0 [ "funclet"(token %9) ] 1505 catchret from %9 to label %try.cont18 1506 1507rethrow6: ; preds = %catch.start4 1508 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ] 1509 to label %unreachable unwind label %catch.dispatch11 1510 1511catch.dispatch: ; preds = %invoke.cont1, %invoke.cont, %entry 1512 %16 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3 1513 1514catch.start: ; preds = %catch.dispatch 1515 %17 = catchpad within %16 [i8* bitcast (i8** @_ZTIi to i8*)] 1516 %18 = call i8* @llvm.wasm.get.exception(token %17) 1517 %19 = call i32 @llvm.wasm.get.ehselector(token %17) 1518 %20 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #0 1519 %matches = icmp eq i32 %19, %20 1520 br i1 %matches, label %catch, label %rethrow 1521 1522catch: ; preds = %catch.start 1523 %21 = call i8* @__cxa_begin_catch(i8* %18) #0 [ "funclet"(token %17) ] 1524 %22 = bitcast i8* %21 to i32* 1525 %23 = load i32, i32* %22, align 4 1526 call void @__cxa_end_catch() #0 [ "funclet"(token %17) ] 1527 catchret from %17 to label %try.cont18 1528 1529rethrow: ; preds = %catch.start 1530 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %17) ] 1531 to label %unreachable unwind label %catch.dispatch3 1532 1533try.cont18: ; preds = %catch, %catch7, %catch15, %invoke.cont1 1534 ret void 1535 1536unreachable: ; preds = %rethrow, %rethrow6 1537 unreachable 1538} 1539 1540; void test25() { 1541; try { 1542; try { 1543; throw 0; 1544; } catch (int) { // (a) 1545; } 1546; } catch (int) { // (b) 1547; } 1548; try { 1549; foo(); 1550; } catch (int) { // (c) 1551; } 1552; } 1553; 1554; Regression test for an ExceptionInfo grouping bug. Because the first (inner) 1555; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are 1556; dominated by EH pad (a) (catch.start), even though they are not semantically 1557; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s 1558; exception is taken out of (a)'s. But because (c) is reachable from (b), we 1559; should make sure to take out (c)'s exception out of (a)'s exception too. 1560define void @test25() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1561entry: 1562 %exception = call i8* @__cxa_allocate_exception(i32 4) #1 1563 %0 = bitcast i8* %exception to i32* 1564 store i32 0, i32* %0, align 16 1565 invoke void @__cxa_throw(i8* %exception, i8* bitcast (i8** @_ZTIi to i8*), i8* null) #3 1566 to label %unreachable unwind label %catch.dispatch 1567 1568catch.dispatch: ; preds = %entry 1569 %1 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1 1570 1571catch.start: ; preds = %catch.dispatch 1572 %2 = catchpad within %1 [i8* bitcast (i8** @_ZTIi to i8*)] 1573 %3 = call i8* @llvm.wasm.get.exception(token %2) 1574 %4 = call i32 @llvm.wasm.get.ehselector(token %2) 1575 %5 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #1 1576 %matches = icmp eq i32 %4, %5 1577 br i1 %matches, label %catch, label %rethrow 1578 1579catch: ; preds = %catch.start 1580 %6 = call i8* @__cxa_begin_catch(i8* %3) #1 [ "funclet"(token %2) ] 1581 %7 = bitcast i8* %6 to i32* 1582 %8 = load i32, i32* %7, align 4 1583 call void @__cxa_end_catch() #1 [ "funclet"(token %2) ] 1584 catchret from %2 to label %try.cont8 1585 1586rethrow: ; preds = %catch.start 1587 invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %2) ] 1588 to label %unreachable unwind label %catch.dispatch1 1589 1590catch.dispatch1: ; preds = %rethrow, %catch.dispatch 1591 %9 = catchswitch within none [label %catch.start2] unwind to caller 1592 1593catch.start2: ; preds = %catch.dispatch1 1594 %10 = catchpad within %9 [i8* bitcast (i8** @_ZTIi to i8*)] 1595 %11 = call i8* @llvm.wasm.get.exception(token %10) 1596 %12 = call i32 @llvm.wasm.get.ehselector(token %10) 1597 %13 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #1 1598 %matches3 = icmp eq i32 %12, %13 1599 br i1 %matches3, label %catch5, label %rethrow4 1600 1601catch5: ; preds = %catch.start2 1602 %14 = call i8* @__cxa_begin_catch(i8* %11) #1 [ "funclet"(token %10) ] 1603 %15 = bitcast i8* %14 to i32* 1604 %16 = load i32, i32* %15, align 4 1605 call void @__cxa_end_catch() #1 [ "funclet"(token %10) ] 1606 catchret from %10 to label %try.cont8 1607 1608rethrow4: ; preds = %catch.start2 1609 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %10) ] 1610 unreachable 1611 1612try.cont8: ; preds = %catch, %catch5 1613 invoke void @foo() 1614 to label %try.cont16 unwind label %catch.dispatch9 1615 1616catch.dispatch9: ; preds = %try.cont8 1617 %17 = catchswitch within none [label %catch.start10] unwind to caller 1618 1619catch.start10: ; preds = %catch.dispatch9 1620 %18 = catchpad within %17 [i8* bitcast (i8** @_ZTIi to i8*)] 1621 %19 = call i8* @llvm.wasm.get.exception(token %18) 1622 %20 = call i32 @llvm.wasm.get.ehselector(token %18) 1623 %21 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #1 1624 %matches11 = icmp eq i32 %20, %21 1625 br i1 %matches11, label %catch13, label %rethrow12 1626 1627catch13: ; preds = %catch.start10 1628 %22 = call i8* @__cxa_begin_catch(i8* %19) #1 [ "funclet"(token %18) ] 1629 %23 = bitcast i8* %22 to i32* 1630 %24 = load i32, i32* %23, align 4 1631 call void @__cxa_end_catch() #1 [ "funclet"(token %18) ] 1632 catchret from %18 to label %try.cont16 1633 1634rethrow12: ; preds = %catch.start10 1635 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %18) ] 1636 unreachable 1637 1638try.cont16: ; preds = %try.cont8, %catch13 1639 ret void 1640 1641unreachable: ; preds = %rethrow, %entry 1642 unreachable 1643} 1644 1645; Check if the unwind destination mismatch stats are correct 1646; NOSORT: 23 wasm-cfg-stackify - Number of call unwind mismatches found 1647; NOSORT: 4 wasm-cfg-stackify - Number of catch unwind mismatches found 1648 1649declare void @foo() 1650declare void @bar() 1651declare i32 @baz() 1652declare i32 @qux(i32) 1653declare void @quux(i32) 1654declare void @fun(i32) 1655; Function Attrs: nounwind 1656declare void @nothrow(i32) #0 1657; Function Attrs: nounwind 1658declare i32 @nothrow_i32() #0 1659 1660; Function Attrs: nounwind 1661declare %class.Object* @_ZN6ObjectD2Ev(%class.Object* returned) #0 1662@_ZTI7MyClass = external constant { i8*, i8* }, align 4 1663; Function Attrs: nounwind 1664declare %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* returned) #0 1665; Function Attrs: nounwind 1666declare %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* returned, %class.MyClass* dereferenceable(4)) #0 1667 1668declare i32 @__gxx_wasm_personality_v0(...) 1669; Function Attrs: nounwind 1670declare i8* @llvm.wasm.get.exception(token) #0 1671; Function Attrs: nounwind 1672declare i32 @llvm.wasm.get.ehselector(token) #0 1673declare i8* @__cxa_allocate_exception(i32) #0 1674declare void @__cxa_throw(i8*, i8*, i8*) 1675; Function Attrs: noreturn 1676declare void @llvm.wasm.rethrow() #1 1677; Function Attrs: nounwind 1678declare i32 @llvm.eh.typeid.for(i8*) #0 1679 1680declare i8* @__cxa_begin_catch(i8*) 1681declare void @__cxa_end_catch() 1682declare i8* @__cxa_get_exception_ptr(i8*) 1683declare void @_ZSt9terminatev() 1684; Function Attrs: nounwind 1685declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0 1686; Function Attrs: nounwind 1687declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i1 immarg) #0 1688; Function Attrs: nounwind 1689declare void @llvm.memset.p0i8.i32(i8* nocapture writeonly, i8, i32, i1 immarg) #0 1690 1691attributes #0 = { nounwind } 1692attributes #1 = { noreturn } 1693