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 -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 4; 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 | FileCheck %s --check-prefix=NOSORT 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-STAT 6 7target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 8target triple = "wasm32-unknown-unknown" 9 10@_ZTIi = external constant i8* 11@_ZTId = external constant i8* 12 13; Simple test case with two catch clauses 14; 15; void foo(); 16; void test0() { 17; try { 18; foo(); 19; } catch (int) { 20; } catch (double) { 21; } 22; } 23 24; CHECK-LABEL: test0 25; CHECK: try 26; CHECK: call foo 27; CHECK: catch 28; CHECK: block 29; CHECK: br_if 0, {{.*}} # 0: down to label2 30; CHECK: call $drop=, __cxa_begin_catch 31; CHECK: call __cxa_end_catch 32; CHECK: br 1 # 1: down to label0 33; CHECK: end_block # label2: 34; CHECK: block 35; CHECK: br_if 0, {{.*}} # 0: down to label3 36; CHECK: call $drop=, __cxa_begin_catch 37; CHECK: call __cxa_end_catch 38; CHECK: br 1 # 1: down to label0 39; CHECK: end_block # label3: 40; CHECK: rethrow {{.*}} # to caller 41; CHECK: end_try # label0: 42define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 43entry: 44 invoke void @foo() 45 to label %try.cont unwind label %catch.dispatch 46 47catch.dispatch: ; preds = %entry 48 %0 = catchswitch within none [label %catch.start] unwind to caller 49 50catch.start: ; preds = %catch.dispatch 51 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] 52 %2 = call i8* @llvm.wasm.get.exception(token %1) 53 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 54 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 55 %matches = icmp eq i32 %3, %4 56 br i1 %matches, label %catch2, label %catch.fallthrough 57 58catch2: ; preds = %catch.start 59 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 60 call void @__cxa_end_catch() [ "funclet"(token %1) ] 61 catchret from %1 to label %try.cont 62 63catch.fallthrough: ; preds = %catch.start 64 %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) 65 %matches1 = icmp eq i32 %3, %6 66 br i1 %matches1, label %catch, label %rethrow 67 68catch: ; preds = %catch.fallthrough 69 %7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 70 call void @__cxa_end_catch() [ "funclet"(token %1) ] 71 catchret from %1 to label %try.cont 72 73rethrow: ; preds = %catch.fallthrough 74 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] 75 unreachable 76 77try.cont: ; preds = %catch, %catch2, %entry 78 ret void 79} 80 81; Nested try-catches within a catch 82; void test1() { 83; try { 84; foo(); 85; } catch (int) { 86; try { 87; foo(); 88; } catch (int) { 89; foo(); 90; } 91; } 92; } 93 94; CHECK-LABEL: test1 95; CHECK: try 96; CHECK: call foo 97; CHECK: catch 98; CHECK: block 99; CHECK: block 100; CHECK: br_if 0, {{.*}} # 0: down to label7 101; CHECK: call $drop=, __cxa_begin_catch 102; CHECK: try 103; CHECK: call foo 104; CHECK: br 2 # 2: down to label6 105; CHECK: catch 106; CHECK: try 107; CHECK: block 108; CHECK: br_if 0, {{.*}} # 0: down to label11 109; CHECK: call $drop=, __cxa_begin_catch 110; CHECK: try 111; CHECK: call foo 112; CHECK: br 2 # 2: down to label9 113; CHECK: catch 114; CHECK: call __cxa_end_catch 115; CHECK: rethrow {{.*}} # down to catch3 116; CHECK: end_try 117; CHECK: end_block # label11: 118; CHECK: rethrow {{.*}} # down to catch3 119; CHECK: catch {{.*}} # catch3: 120; CHECK: call __cxa_end_catch 121; CHECK: rethrow {{.*}} # to caller 122; CHECK: end_try # label9: 123; CHECK: call __cxa_end_catch 124; CHECK: br 2 # 2: down to label6 125; CHECK: end_try 126; CHECK: end_block # label7: 127; CHECK: rethrow {{.*}} # to caller 128; CHECK: end_block # label6: 129; CHECK: call __cxa_end_catch 130; CHECK: end_try 131define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 132entry: 133 invoke void @foo() 134 to label %try.cont11 unwind label %catch.dispatch 135 136catch.dispatch: ; preds = %entry 137 %0 = catchswitch within none [label %catch.start] unwind to caller 138 139catch.start: ; preds = %catch.dispatch 140 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)] 141 %2 = call i8* @llvm.wasm.get.exception(token %1) 142 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 143 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 144 %matches = icmp eq i32 %3, %4 145 br i1 %matches, label %catch, label %rethrow 146 147catch: ; preds = %catch.start 148 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 149 %6 = bitcast i8* %5 to i32* 150 %7 = load i32, i32* %6, align 4 151 invoke void @foo() [ "funclet"(token %1) ] 152 to label %try.cont unwind label %catch.dispatch2 153 154catch.dispatch2: ; preds = %catch 155 %8 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9 156 157catch.start3: ; preds = %catch.dispatch2 158 %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)] 159 %10 = call i8* @llvm.wasm.get.exception(token %9) 160 %11 = call i32 @llvm.wasm.get.ehselector(token %9) 161 %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 162 %matches4 = icmp eq i32 %11, %12 163 br i1 %matches4, label %catch6, label %rethrow5 164 165catch6: ; preds = %catch.start3 166 %13 = call i8* @__cxa_begin_catch(i8* %10) [ "funclet"(token %9) ] 167 %14 = bitcast i8* %13 to i32* 168 %15 = load i32, i32* %14, align 4 169 invoke void @foo() [ "funclet"(token %9) ] 170 to label %invoke.cont8 unwind label %ehcleanup 171 172invoke.cont8: ; preds = %catch6 173 call void @__cxa_end_catch() [ "funclet"(token %9) ] 174 catchret from %9 to label %try.cont 175 176rethrow5: ; preds = %catch.start3 177 invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %9) ] 178 to label %unreachable unwind label %ehcleanup9 179 180try.cont: ; preds = %invoke.cont8, %catch 181 call void @__cxa_end_catch() [ "funclet"(token %1) ] 182 catchret from %1 to label %try.cont11 183 184rethrow: ; preds = %catch.start 185 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] 186 unreachable 187 188try.cont11: ; preds = %try.cont, %entry 189 ret void 190 191ehcleanup: ; preds = %catch6 192 %16 = cleanuppad within %9 [] 193 call void @__cxa_end_catch() [ "funclet"(token %16) ] 194 cleanupret from %16 unwind label %ehcleanup9 195 196ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2 197 %17 = cleanuppad within %1 [] 198 call void @__cxa_end_catch() [ "funclet"(token %17) ] 199 cleanupret from %17 unwind to caller 200 201unreachable: ; preds = %rethrow5 202 unreachable 203} 204 205; Nested loop within a catch clause 206; void test2() { 207; try { 208; foo(); 209; } catch (...) { 210; for (int i = 0; i < 50; i++) 211; foo(); 212; } 213; } 214 215; CHECK-LABEL: test2 216; CHECK: try 217; CHECK: call foo 218; CHECK: catch 219; CHECK: call $drop=, __cxa_begin_catch 220; CHECK: loop # label15: 221; CHECK: block 222; CHECK: block 223; CHECK: br_if 0, {{.*}} # 0: down to label17 224; CHECK: try 225; CHECK: call foo 226; CHECK: br 2 # 2: down to label16 227; CHECK: catch 228; CHECK: try 229; CHECK: call __cxa_end_catch 230; CHECK: catch 231; CHECK: call __clang_call_terminate 232; CHECK: unreachable 233; CHECK: end_try 234; CHECK: rethrow {{.*}} # to caller 235; CHECK: end_try 236; CHECK: end_block # label17: 237; CHECK: call __cxa_end_catch 238; CHECK: br 2 # 2: down to label13 239; CHECK: end_block # label16: 240; CHECK: br 0 # 0: up to label15 241; CHECK: end_loop 242; CHECK: end_try # label13: 243define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 244entry: 245 invoke void @foo() 246 to label %try.cont unwind label %catch.dispatch 247 248catch.dispatch: ; preds = %entry 249 %0 = catchswitch within none [label %catch.start] unwind to caller 250 251catch.start: ; preds = %catch.dispatch 252 %1 = catchpad within %0 [i8* null] 253 %2 = call i8* @llvm.wasm.get.exception(token %1) 254 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 255 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 256 br label %for.cond 257 258for.cond: ; preds = %for.inc, %catch.start 259 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ] 260 %cmp = icmp slt i32 %i.0, 50 261 br i1 %cmp, label %for.body, label %for.end 262 263for.body: ; preds = %for.cond 264 invoke void @foo() [ "funclet"(token %1) ] 265 to label %for.inc unwind label %ehcleanup 266 267for.inc: ; preds = %for.body 268 %inc = add nsw i32 %i.0, 1 269 br label %for.cond 270 271for.end: ; preds = %for.cond 272 call void @__cxa_end_catch() [ "funclet"(token %1) ] 273 catchret from %1 to label %try.cont 274 275try.cont: ; preds = %for.end, %entry 276 ret void 277 278ehcleanup: ; preds = %for.body 279 %5 = cleanuppad within %1 [] 280 invoke void @__cxa_end_catch() [ "funclet"(token %5) ] 281 to label %invoke.cont2 unwind label %terminate 282 283invoke.cont2: ; preds = %ehcleanup 284 cleanupret from %5 unwind to caller 285 286terminate: ; preds = %ehcleanup 287 %6 = cleanuppad within %5 [] 288 %7 = call i8* @llvm.wasm.get.exception(token %6) 289 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 290 unreachable 291} 292 293; Tests if block and try markers are correctly placed. Even if two predecessors 294; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the 295; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2, 296; and scopes cannot be interleaved. 297 298; NOOPT-LABEL: test3 299; NOOPT: try 300; NOOPT: block 301; NOOPT: block 302; NOOPT: block 303; NOOPT: end_block 304; NOOPT: end_block 305; NOOPT: call foo 306; NOOPT: end_block 307; NOOPT: call bar 308; NOOPT: catch {{.*}} 309; NOOPT: end_try 310define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 311bb0: 312 br i1 undef, label %bb1, label %bb2 313 314bb1: ; preds = %bb0 315 br i1 undef, label %bb3, label %bb4 316 317bb2: ; preds = %bb0 318 br label %try.cont 319 320bb3: ; preds = %bb1 321 invoke void @foo() 322 to label %try.cont unwind label %catch.dispatch 323 324bb4: ; preds = %bb1 325 invoke void @bar() 326 to label %try.cont unwind label %catch.dispatch 327 328catch.dispatch: ; preds = %bb4, %bb3 329 %0 = catchswitch within none [label %catch.start] unwind to caller 330 331catch.start: ; preds = %catch.dispatch 332 %1 = catchpad within %0 [i8* null] 333 %2 = call i8* @llvm.wasm.get.exception(token %1) 334 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 335 catchret from %1 to label %try.cont 336 337try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2 338 ret void 339} 340 341; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers, 342; when try and loop markers are in the same BB and end_try and end_loop are in 343; another BB. 344; CHECK: loop 345; CHECK: try 346; CHECK: call foo 347; CHECK: catch 348; CHECK: end_try 349; CHECK: end_loop 350define void @test4(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 351entry: 352 store volatile i32 0, i32* %p 353 br label %loop 354 355loop: ; preds = %try.cont, %entry 356 store volatile i32 1, i32* %p 357 invoke void @foo() 358 to label %try.cont unwind label %catch.dispatch 359 360catch.dispatch: ; preds = %loop 361 %0 = catchswitch within none [label %catch.start] unwind to caller 362 363catch.start: ; preds = %catch.dispatch 364 %1 = catchpad within %0 [i8* null] 365 %2 = call i8* @llvm.wasm.get.exception(token %1) 366 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 367 catchret from %1 to label %try.cont 368 369try.cont: ; preds = %catch.start, %loop 370 br label %loop 371} 372 373; Some of test cases below are hand-tweaked by deleting some library calls to 374; simplify tests and changing the order of basic blocks to cause unwind 375; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum 376; number of mismatches in several tests below. 377 378; 'call bar''s original unwind destination was 'catch14', but after control flow 379; linearization, its unwind destination incorrectly becomes 'catch15'. We fix 380; this by wrapping the call with a nested try/catch/end_try and branching to the 381; right destination (label32). 382 383; NOSORT-LABEL: test5 384; NOSORT: block 385; NOSORT: try 386; NOSORT: try 387; NOSORT: call foo 388; --- Nested try/catch/end_try starts 389; NOSORT: try 390; NOSORT: call bar 391; NOSORT: catch $drop= 392; NOSORT: br 2 # 2: down to label32 393; NOSORT: end_try 394; --- Nested try/catch/end_try ends 395; NOSORT: br 2 # 2: down to label31 396; NOSORT: catch $drop= # catch15: 397; NOSORT: br 2 # 2: down to label31 398; NOSORT: end_try 399; NOSORT: catch $drop= # catch14: 400; NOSORT: end_try # label32: 401; NOSORT: end_block # label31: 402; NOSORT: return 403 404define void @test5() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 405bb0: 406 invoke void @foo() 407 to label %bb1 unwind label %catch.dispatch0 408 409bb1: ; preds = %bb0 410 invoke void @bar() 411 to label %try.cont unwind label %catch.dispatch1 412 413catch.dispatch0: ; preds = %bb0 414 %0 = catchswitch within none [label %catch.start0] unwind to caller 415 416catch.start0: ; preds = %catch.dispatch0 417 %1 = catchpad within %0 [i8* null] 418 %2 = call i8* @llvm.wasm.get.exception(token %1) 419 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 420 catchret from %1 to label %try.cont 421 422catch.dispatch1: ; preds = %bb1 423 %4 = catchswitch within none [label %catch.start1] unwind to caller 424 425catch.start1: ; preds = %catch.dispatch1 426 %5 = catchpad within %4 [i8* null] 427 %6 = call i8* @llvm.wasm.get.exception(token %5) 428 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 429 catchret from %5 to label %try.cont 430 431try.cont: ; preds = %catch.start1, %catch.start0, %bb1 432 ret void 433} 434 435; Two 'call bar''s original unwind destination was the caller, but after control 436; flow linearization, their unwind destination incorrectly becomes 'catch17'. We 437; fix this by wrapping the call with a nested try/catch/end_try and branching to 438; the right destination (label4), from which we rethrow the exception to the 439; caller. 440 441; And the return value of 'baz' should NOT be stackified because the BB is split 442; during fixing unwind mismatches. 443 444; NOSORT-LABEL: test6 445; NOSORT: try 446; NOSORT: call foo 447; --- Nested try/catch/end_try starts 448; NOSORT: try 449; NOSORT: call bar 450; NOSORT: call ${{[0-9]+}}=, baz 451; NOSORT-NOT: call $push{{.*}}=, baz 452; NOSORT: catch $[[REG:[0-9]+]]= 453; NOSORT: br 1 # 1: down to label35 454; NOSORT: end_try 455; --- Nested try/catch/end_try ends 456; NOSORT: return 457; NOSORT: catch $drop= # catch17: 458; NOSORT: return 459; NOSORT: end_try # label35: 460; NOSORT: rethrow $[[REG]] # to caller 461 462define void @test6() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 463bb0: 464 invoke void @foo() 465 to label %bb1 unwind label %catch.dispatch0 466 467bb1: ; preds = %bb0 468 call void @bar() 469 %call = call i32 @baz() 470 call void @nothrow(i32 %call) #0 471 ret void 472 473catch.dispatch0: ; preds = %bb0 474 %0 = catchswitch within none [label %catch.start0] unwind to caller 475 476catch.start0: ; preds = %catch.dispatch0 477 %1 = catchpad within %0 [i8* null] 478 %2 = call i8* @llvm.wasm.get.exception(token %1) 479 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 480 catchret from %1 to label %try.cont 481 482try.cont: ; preds = %catch.start0 483 ret void 484} 485 486; Similar situation as @test6. Here 'call @qux''s original unwind destination 487; was the caller, but after control flow linearization, their unwind destination 488; incorrectly becomes another catch within the function. We fix this by wrapping 489; the call with a nested try/catch/end_try and branching to the right 490; destination, from which we rethrow the exception to the caller. 491 492; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the 493; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5' 494; and 'call @qux'. 495 496; NOSORT-LABEL: test7 497; NOSORT: try 498; NOSORT: call foo 499; --- Nested try/catch/end_try starts 500; NOSORT: try 501; NOSORT-NEXT: i32.const $push{{[0-9]+}}=, 5 502; NOSORT-NEXT: call ${{[0-9]+}}=, qux 503; NOSORT: catch $[[REG:[0-9]+]]= 504; NOSORT: br 1 # 1: down to label37 505; NOSORT: end_try 506; --- Nested try/catch/end_try ends 507; NOSORT: return 508; NOSORT: catch $drop= # catch19: 509; NOSORT: return 510; NOSORT: end_try # label37: 511; NOSORT: rethrow $[[REG]] # to caller 512 513define i32 @test7() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 514bb0: 515 invoke void @foo() 516 to label %bb1 unwind label %catch.dispatch0 517 518bb1: ; preds = %bb0 519 %0 = call i32 @qux(i32 5) 520 ret i32 %0 521 522catch.dispatch0: ; preds = %bb0 523 %1 = catchswitch within none [label %catch.start0] unwind to caller 524 525catch.start0: ; preds = %catch.dispatch0 526 %2 = catchpad within %1 [i8* null] 527 %3 = call i8* @llvm.wasm.get.exception(token %2) 528 %j = call i32 @llvm.wasm.get.ehselector(token %2) 529 catchret from %2 to label %try.cont 530 531try.cont: ; preds = %catch.start0 532 ret i32 0 533} 534 535; If not for the unwind destination mismatch, the LOOP marker here would have an 536; i32 signature. But because we add a rethrow instruction at the end of the 537; appendix block, now the LOOP marker does not have a signature (= has a void 538; signature). Here the two calls two 'bar' are supposed to throw up to the 539; caller, but incorrectly unwind to 'catch19' after linearizing the CFG. 540 541; NOSORT-LABEL: test8 542; NOSORT: block 543; NOSORT-NOT: loop i32 544; NOSORT: loop # label40: 545; NOSORT: try 546; NOSORT: call foo 547; --- Nested try/catch/end_try starts 548; NOSORT: try 549; NOSORT: call bar 550; NOSORT: call bar 551; NOSORT: catch $[[REG:[0-9]+]]= 552; NOSORT: br 1 # 1: down to label41 553; NOSORT: end_try 554; --- Nested try/catch/end_try ends 555; NOSORT: return {{.*}} 556; NOSORT: catch $drop= # catch21: 557; NOSORT: br 1 # 1: up to label40 558; NOSORT: end_try # label41: 559; NOSORT: end_loop 560; NOSORT: end_block 561; NOSORT: rethrow $[[REG]] # to caller 562 563define i32 @test8(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 564entry: 565 store volatile i32 0, i32* %p 566 br label %loop 567 568loop: ; preds = %try.cont, %entry 569 store volatile i32 1, i32* %p 570 invoke void @foo() 571 to label %bb unwind label %catch.dispatch 572 573bb: ; preds = %loop 574 call void @bar() 575 call void @bar() 576 ret i32 0 577 578catch.dispatch: ; preds = %loop 579 %0 = catchswitch within none [label %catch.start] unwind to caller 580 581catch.start: ; preds = %catch.dispatch 582 %1 = catchpad within %0 [i8* null] 583 %2 = call i8* @llvm.wasm.get.exception(token %1) 584 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 585 catchret from %1 to label %try.cont 586 587try.cont: ; preds = %catch.start 588 br label %loop 589} 590 591; When we have both kinds of EH pad unwind mismatches: 592; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 593; CFG, when it is supposed to unwind to another EH pad. 594; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 595; CFG, when it is supposed to unwind to the caller. 596 597; NOSORT-LABEL: test9 598; NOSORT: block 599; NOSORT: block 600; NOSORT: try 601; NOSORT: try 602; NOSORT: call foo 603; --- Nested try/catch/end_try starts 604; NOSORT: try 605; NOSORT: call bar 606; NOSORT: catch $[[REG0:[0-9]+]]= 607; NOSORT: br 2 # 2: down to label45 608; NOSORT: end_try 609; --- Nested try/catch/end_try ends 610; NOSORT: br 2 # 2: down to label44 611; NOSORT: catch {{.*}} 612; NOSORT: block i32 613; NOSORT: br_on_exn 0, {{.*}} # 0: down to label48 614; --- Nested try/catch/end_try starts 615; NOSORT: try 616; NOSORT: rethrow {{.*}} # down to catch26 617; NOSORT: catch $[[REG1:[0-9]+]]= # catch26: 618; NOSORT: br 5 # 5: down to label43 619; NOSORT: end_try 620; --- Nested try/catch/end_try ends 621; NOSORT: end_block # label48: 622; NOSORT: call $drop=, __cxa_begin_catch 623; --- Nested try/catch/end_try starts 624; NOSORT: try 625; NOSORT: call __cxa_end_catch 626; NOSORT: catch $[[REG1]]= 627; NOSORT: br 4 # 4: down to label43 628; NOSORT: end_try 629; --- Nested try/catch/end_try ends 630; NOSORT: br 2 # 2: down to label44 631; NOSORT: end_try 632; NOSORT: catch $[[REG0]]= 633; NOSORT: end_try # label45: 634; NOSORT: call $drop=, __cxa_begin_catch 635; NOSORT: call __cxa_end_catch 636; NOSORT: end_block # label44: 637; NOSORT: return 638; NOSORT: end_block # label43: 639; NOSORT: rethrow $[[REG1]] # to caller 640define void @test9() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 641bb0: 642 invoke void @foo() 643 to label %bb1 unwind label %catch.dispatch0 644 645bb1: ; preds = %bb0 646 invoke void @bar() 647 to label %try.cont unwind label %catch.dispatch1 648 649catch.dispatch0: ; preds = %bb0 650 %0 = catchswitch within none [label %catch.start0] unwind to caller 651 652catch.start0: ; preds = %catch.dispatch0 653 %1 = catchpad within %0 [i8* null] 654 %2 = call i8* @llvm.wasm.get.exception(token %1) 655 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 656 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 657 call void @__cxa_end_catch() [ "funclet"(token %1) ] 658 catchret from %1 to label %try.cont 659 660catch.dispatch1: ; preds = %bb1 661 %5 = catchswitch within none [label %catch.start1] unwind to caller 662 663catch.start1: ; preds = %catch.dispatch1 664 %6 = catchpad within %5 [i8* null] 665 %7 = call i8* @llvm.wasm.get.exception(token %6) 666 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 667 %9 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ] 668 call void @__cxa_end_catch() [ "funclet"(token %6) ] 669 catchret from %6 to label %try.cont 670 671try.cont: ; preds = %catch.start1, %catch.start0, %bb1 672 ret void 673} 674 675; In CFGSort, EH pads should be sorted as soon as it is available and 676; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are 677; in the middle of sorting another region that does not contain the EH pad. In 678; this example, 'catch.start' should be sorted right after 'if.then' is sorted 679; (before 'cont' is sorted) and there should not be any unwind destination 680; mismatches in CFGStackify. 681 682; NOOPT: block 683; NOOPT: try 684; NOOPT: call foo 685; NOOPT: catch 686; NOOPT: end_try 687; NOOPT: call foo 688; NOOPT: end_block 689; NOOPT: return 690define void @test10(i32 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 691entry: 692 %tobool = icmp ne i32 %arg, 0 693 br i1 %tobool, label %if.then, label %if.end 694 695catch.dispatch: ; preds = %if.then 696 %0 = catchswitch within none [label %catch.start] unwind to caller 697 698catch.start: ; preds = %catch.dispatch 699 %1 = catchpad within %0 [i8* null] 700 %2 = call i8* @llvm.wasm.get.exception(token %1) 701 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 702 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 703 call void @__cxa_end_catch() [ "funclet"(token %1) ] 704 catchret from %1 to label %if.end 705 706if.then: ; preds = %entry 707 invoke void @foo() 708 to label %cont unwind label %catch.dispatch 709 710cont: ; preds = %if.then 711 call void @foo() 712 br label %if.end 713 714if.end: ; preds = %cont, %catch.start, %entry 715 ret void 716} 717 718%class.Object = type { i8 } 719 720; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into 721; calls to external symbols (not global addresses) in instruction selection, 722; which will be eventually lowered to library function calls. 723; Because this test runs with -wasm-disable-ehpad-sort, these library calls in 724; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or 725; unwinding destination mismatches in CFGStackify. 726 727; NOSORT-LABEL: test11 728; NOSORT: try 729; NOSORT: call foo 730; NOSORT: call {{.*}} memcpy 731; NOSORT: call {{.*}} memmove 732; NOSORT: call {{.*}} memset 733; NOSORT: return 734; NOSORT: catch 735; NOSORT: rethrow 736; NOSORT: end_try 737define void @test11(i8* %a, i8* %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 738entry: 739 %o = alloca %class.Object, align 1 740 invoke void @foo() 741 to label %invoke.cont unwind label %ehcleanup 742 743invoke.cont: ; preds = %entry 744 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 745 call void @llvm.memmove.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 746 call void @llvm.memset.p0i8.i32(i8* %a, i8 0, i32 100, i1 false) 747 %call = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 748 ret void 749 750ehcleanup: ; preds = %entry 751 %0 = cleanuppad within none [] 752 %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 [ "funclet"(token %0) ] 753 cleanupret from %0 unwind to caller 754} 755 756; Tests if 'try' marker is placed correctly. In this test, 'try' should be 757; placed before the call to 'nothrow_i32' and not between the call to 758; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is 759; stackified and pushed onto the stack to be consumed by the call to 'fun'. 760 761; CHECK-LABEL: test12 762; CHECK: try 763; CHECK: call $push{{.*}}=, nothrow_i32 764; CHECK: call fun, $pop{{.*}} 765define void @test12() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 766entry: 767 %call = call i32 @nothrow_i32() 768 invoke void @fun(i32 %call) 769 to label %invoke.cont unwind label %terminate 770 771invoke.cont: ; preds = %entry 772 ret void 773 774terminate: ; preds = %entry 775 %0 = cleanuppad within none [] 776 %1 = tail call i8* @llvm.wasm.get.exception(token %0) 777 call void @__clang_call_terminate(i8* %1) [ "funclet"(token %0) ] 778 unreachable 779} 780 781%class.MyClass = type { i32 } 782 783; This crashed on debug mode (= when NDEBUG is not defined) when the logic for 784; computing the innermost region was not correct, in which a loop region 785; contains an exception region. This should pass CFGSort without crashing. 786define void @test13() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 787entry: 788 %e = alloca %class.MyClass, align 4 789 br label %for.cond 790 791for.cond: ; preds = %for.inc, %entry 792 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] 793 %cmp = icmp slt i32 %i.0, 9 794 br i1 %cmp, label %for.body, label %for.end 795 796for.body: ; preds = %for.cond 797 invoke void @quux(i32 %i.0) 798 to label %for.inc unwind label %catch.dispatch 799 800catch.dispatch: ; preds = %for.body 801 %0 = catchswitch within none [label %catch.start] unwind to caller 802 803catch.start: ; preds = %catch.dispatch 804 %1 = catchpad within %0 [i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)] 805 %2 = call i8* @llvm.wasm.get.exception(token %1) 806 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 807 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)) #3 808 %matches = icmp eq i32 %3, %4 809 br i1 %matches, label %catch, label %rethrow 810 811catch: ; preds = %catch.start 812 %5 = call i8* @__cxa_get_exception_ptr(i8* %2) #3 [ "funclet"(token %1) ] 813 %6 = bitcast i8* %5 to %class.MyClass* 814 %call = call %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* %e, %class.MyClass* dereferenceable(4) %6) [ "funclet"(token %1) ] 815 %7 = call i8* @__cxa_begin_catch(i8* %2) #3 [ "funclet"(token %1) ] 816 %x = getelementptr inbounds %class.MyClass, %class.MyClass* %e, i32 0, i32 0 817 %8 = load i32, i32* %x, align 4 818 invoke void @quux(i32 %8) [ "funclet"(token %1) ] 819 to label %invoke.cont2 unwind label %ehcleanup 820 821invoke.cont2: ; preds = %catch 822 %call3 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) #3 [ "funclet"(token %1) ] 823 call void @__cxa_end_catch() [ "funclet"(token %1) ] 824 catchret from %1 to label %for.inc 825 826rethrow: ; preds = %catch.start 827 call void @llvm.wasm.rethrow.in.catch() #6 [ "funclet"(token %1) ] 828 unreachable 829 830for.inc: ; preds = %invoke.cont2, %for.body 831 %inc = add nsw i32 %i.0, 1 832 br label %for.cond 833 834ehcleanup: ; preds = %catch 835 %9 = cleanuppad within %1 [] 836 %call4 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) #3 [ "funclet"(token %9) ] 837 invoke void @__cxa_end_catch() [ "funclet"(token %9) ] 838 to label %invoke.cont6 unwind label %terminate7 839 840invoke.cont6: ; preds = %ehcleanup 841 cleanupret from %9 unwind to caller 842 843for.end: ; preds = %for.cond 844 ret void 845 846terminate7: ; preds = %ehcleanup 847 %10 = cleanuppad within %9 [] 848 %11 = call i8* @llvm.wasm.get.exception(token %10) 849 call void @__clang_call_terminate(i8* %11) #7 [ "funclet"(token %10) ] 850 unreachable 851} 852 853; We don't need to call placeBlockMarker after fixUnwindMismatches unless the 854; destination is the appendix BB at the very end. This should not crash. 855define void @test16(i32* %p, i32 %a, i32 %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 856entry: 857 br label %loop 858 859loop: 860 invoke void @foo() 861 to label %bb0 unwind label %catch.dispatch0 862 863bb0: 864 %cmp = icmp ne i32 %a, %b 865 br i1 %cmp, label %bb1, label %last 866 867bb1: ; preds = %bb0 868 invoke void @bar() 869 to label %try.cont unwind label %catch.dispatch1 870 871catch.dispatch0: ; preds = %loop 872 %0 = catchswitch within none [label %catch.start0] unwind to caller 873 874catch.start0: ; preds = %catch.dispatch0 875 %1 = catchpad within %0 [i8* null] 876 %2 = call i8* @llvm.wasm.get.exception(token %1) 877 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 878 catchret from %1 to label %try.cont 879 880catch.dispatch1: ; preds = %bb1 881 %4 = catchswitch within none [label %catch.start1] unwind to caller 882 883catch.start1: ; preds = %catch.dispatch1 884 %5 = catchpad within %4 [i8* null] 885 %6 = call i8* @llvm.wasm.get.exception(token %5) 886 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 887 catchret from %5 to label %try.cont 888 889try.cont: ; preds = %catch.start, %loop 890 br label %loop 891 892last: 893 ret void 894} 895 896; Check if the unwind destination mismatch stats are correct 897; NOSORT-STAT: 16 wasm-cfg-stackify - Number of EH pad unwind mismatches found 898 899declare void @foo() 900declare void @bar() 901declare i32 @baz() 902declare i32 @qux(i32) 903declare void @quux(i32) 904declare void @fun(i32) 905; Function Attrs: nounwind 906declare void @nothrow(i32) #0 907declare i32 @nothrow_i32() #0 908 909; Function Attrs: nounwind 910declare %class.Object* @_ZN6ObjectD2Ev(%class.Object* returned) #0 911@_ZTI7MyClass = external constant { i8*, i8* }, align 4 912; Function Attrs: nounwind 913declare %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* returned) #0 914; Function Attrs: nounwind 915declare %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* returned, %class.MyClass* dereferenceable(4)) #0 916 917declare i32 @__gxx_wasm_personality_v0(...) 918declare i8* @llvm.wasm.get.exception(token) 919declare i32 @llvm.wasm.get.ehselector(token) 920declare void @llvm.wasm.rethrow.in.catch() 921declare i32 @llvm.eh.typeid.for(i8*) 922declare i8* @__cxa_begin_catch(i8*) 923declare void @__cxa_end_catch() 924declare i8* @__cxa_get_exception_ptr(i8*) 925declare void @__clang_call_terminate(i8*) 926declare void @_ZSt9terminatev() 927; Function Attrs: nounwind 928declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0 929; Function Attrs: nounwind 930declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i1 immarg) #0 931; Function Attrs: nounwind 932declare void @llvm.memset.p0i8.i32(i8* nocapture writeonly, i8, i32, i1 immarg) #0 933 934attributes #0 = { nounwind } 935