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; If not for the unwind destination mismatch, the LOOP marker here would have an 487; i32 signature. But because we add a rethrow instruction at the end of the 488; appendix block, now the LOOP marker does not have a signature (= has a void 489; signature). Here the two calls two 'bar' are supposed to throw up to the 490; caller, but incorrectly unwind to 'catch19' after linearizing the CFG. 491 492; NOSORT-LABEL: test7 493; NOSORT: block 494; NOSORT-NOT: loop i32 495; NOSORT: loop # label38: 496; NOSORT: try 497; NOSORT: call foo 498; --- Nested try/catch/end_try starts 499; NOSORT: try 500; NOSORT: call bar 501; NOSORT: call bar 502; NOSORT: catch $[[REG:[0-9]+]]= 503; NOSORT: br 1 # 1: down to label39 504; NOSORT: end_try 505; --- Nested try/catch/end_try ends 506; NOSORT: return {{.*}} 507; NOSORT: catch $drop= # catch19: 508; NOSORT: br 1 # 1: up to label38 509; NOSORT: end_try # label39: 510; NOSORT: end_loop 511; NOSORT: end_block 512; NOSORT: rethrow $[[REG]] # to caller 513 514define i32 @test7(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 515entry: 516 store volatile i32 0, i32* %p 517 br label %loop 518 519loop: ; preds = %try.cont, %entry 520 store volatile i32 1, i32* %p 521 invoke void @foo() 522 to label %bb unwind label %catch.dispatch 523 524bb: ; preds = %loop 525 call void @bar() 526 call void @bar() 527 ret i32 0 528 529catch.dispatch: ; preds = %loop 530 %0 = catchswitch within none [label %catch.start] unwind to caller 531 532catch.start: ; preds = %catch.dispatch 533 %1 = catchpad within %0 [i8* null] 534 %2 = call i8* @llvm.wasm.get.exception(token %1) 535 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 536 catchret from %1 to label %try.cont 537 538try.cont: ; preds = %catch.start 539 br label %loop 540} 541 542; When we have both kinds of EH pad unwind mismatches: 543; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 544; CFG, when it is supposed to unwind to another EH pad. 545; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 546; CFG, when it is supposed to unwind to the caller. 547 548; NOSORT-LABEL: test8 549; NOSORT: block 550; NOSORT: block 551; NOSORT: try 552; NOSORT: try 553; NOSORT: call foo 554; --- Nested try/catch/end_try starts 555; NOSORT: try 556; NOSORT: call bar 557; NOSORT: catch $[[REG0:[0-9]+]]= 558; NOSORT: br 2 # 2: down to label43 559; NOSORT: end_try 560; --- Nested try/catch/end_try ends 561; NOSORT: br 2 # 2: down to label42 562; NOSORT: catch {{.*}} 563; NOSORT: block i32 564; NOSORT: br_on_exn 0, {{.*}} # 0: down to label46 565; --- Nested try/catch/end_try starts 566; NOSORT: try 567; NOSORT: rethrow {{.*}} # down to catch24 568; NOSORT: catch $[[REG1:[0-9]+]]= # catch24: 569; NOSORT: br 5 # 5: down to label41 570; NOSORT: end_try 571; --- Nested try/catch/end_try ends 572; NOSORT: end_block # label46: 573; NOSORT: call $drop=, __cxa_begin_catch 574; --- Nested try/catch/end_try starts 575; NOSORT: try 576; NOSORT: call __cxa_end_catch 577; NOSORT: catch $[[REG1]]= 578; NOSORT: br 4 # 4: down to label41 579; NOSORT: end_try 580; --- Nested try/catch/end_try ends 581; NOSORT: br 2 # 2: down to label42 582; NOSORT: end_try 583; NOSORT: catch $[[REG0]]= 584; NOSORT: end_try # label43: 585; NOSORT: call $drop=, __cxa_begin_catch 586; NOSORT: call __cxa_end_catch 587; NOSORT: end_block # label42: 588; NOSORT: return 589; NOSORT: end_block # label41: 590; NOSORT: rethrow $[[REG1]] # to caller 591define void @test8() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 592bb0: 593 invoke void @foo() 594 to label %bb1 unwind label %catch.dispatch0 595 596bb1: ; preds = %bb0 597 invoke void @bar() 598 to label %try.cont unwind label %catch.dispatch1 599 600catch.dispatch0: ; preds = %bb0 601 %0 = catchswitch within none [label %catch.start0] unwind to caller 602 603catch.start0: ; preds = %catch.dispatch0 604 %1 = catchpad within %0 [i8* null] 605 %2 = call i8* @llvm.wasm.get.exception(token %1) 606 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 607 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 608 call void @__cxa_end_catch() [ "funclet"(token %1) ] 609 catchret from %1 to label %try.cont 610 611catch.dispatch1: ; preds = %bb1 612 %5 = catchswitch within none [label %catch.start1] unwind to caller 613 614catch.start1: ; preds = %catch.dispatch1 615 %6 = catchpad within %5 [i8* null] 616 %7 = call i8* @llvm.wasm.get.exception(token %6) 617 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 618 %9 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ] 619 call void @__cxa_end_catch() [ "funclet"(token %6) ] 620 catchret from %6 to label %try.cont 621 622try.cont: ; preds = %catch.start1, %catch.start0, %bb1 623 ret void 624} 625 626; In CFGSort, EH pads should be sorted as soon as it is available and 627; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are 628; in the middle of sorting another region that does not contain the EH pad. In 629; this example, 'catch.start' should be sorted right after 'if.then' is sorted 630; (before 'cont' is sorted) and there should not be any unwind destination 631; mismatches in CFGStackify. 632 633; NOOPT: block 634; NOOPT: try 635; NOOPT: call foo 636; NOOPT: catch 637; NOOPT: end_try 638; NOOPT: call foo 639; NOOPT: end_block 640; NOOPT: return 641define void @test9(i32 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 642entry: 643 %tobool = icmp ne i32 %arg, 0 644 br i1 %tobool, label %if.then, label %if.end 645 646catch.dispatch: ; preds = %if.then 647 %0 = catchswitch within none [label %catch.start] unwind to caller 648 649catch.start: ; preds = %catch.dispatch 650 %1 = catchpad within %0 [i8* null] 651 %2 = call i8* @llvm.wasm.get.exception(token %1) 652 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 653 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 654 call void @__cxa_end_catch() [ "funclet"(token %1) ] 655 catchret from %1 to label %if.end 656 657if.then: ; preds = %entry 658 invoke void @foo() 659 to label %cont unwind label %catch.dispatch 660 661cont: ; preds = %if.then 662 call void @foo() 663 br label %if.end 664 665if.end: ; preds = %cont, %catch.start, %entry 666 ret void 667} 668 669%class.Object = type { i8 } 670 671; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into 672; calls to external symbols (not global addresses) in instruction selection, 673; which will be eventually lowered to library function calls. 674; Because this test runs with -wasm-disable-ehpad-sort, these library calls in 675; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or 676; unwinding destination mismatches in CFGStackify. 677 678; NOSORT-LABEL: test10 679; NOSORT: try 680; NOSORT: call foo 681; NOSORT: call {{.*}} memcpy 682; NOSORT: call {{.*}} memmove 683; NOSORT: call {{.*}} memset 684; NOSORT: return 685; NOSORT: catch 686; NOSORT: rethrow 687; NOSORT: end_try 688define void @test10(i8* %a, i8* %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 689entry: 690 %o = alloca %class.Object, align 1 691 invoke void @foo() 692 to label %invoke.cont unwind label %ehcleanup 693 694invoke.cont: ; preds = %entry 695 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 696 call void @llvm.memmove.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 697 call void @llvm.memset.p0i8.i32(i8* %a, i8 0, i32 100, i1 false) 698 %call = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 699 ret void 700 701ehcleanup: ; preds = %entry 702 %0 = cleanuppad within none [] 703 %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 [ "funclet"(token %0) ] 704 cleanupret from %0 unwind to caller 705} 706 707; Tests if 'try' marker is placed correctly. In this test, 'try' should be 708; placed before the call to 'nothrow_i32' and not between the call to 709; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is 710; stackified and pushed onto the stack to be consumed by the call to 'fun'. 711 712; CHECK-LABEL: test11 713; CHECK: try 714; CHECK: call $push{{.*}}=, nothrow_i32 715; CHECK: call fun, $pop{{.*}} 716define void @test11() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 717entry: 718 %call = call i32 @nothrow_i32() 719 invoke void @fun(i32 %call) 720 to label %invoke.cont unwind label %terminate 721 722invoke.cont: ; preds = %entry 723 ret void 724 725terminate: ; preds = %entry 726 %0 = cleanuppad within none [] 727 %1 = tail call i8* @llvm.wasm.get.exception(token %0) 728 call void @__clang_call_terminate(i8* %1) [ "funclet"(token %0) ] 729 unreachable 730} 731 732%class.MyClass = type { i32 } 733 734; This crashed on debug mode (= when NDEBUG is not defined) when the logic for 735; computing the innermost region was not correct, in which a loop region 736; contains an exception region. This should pass CFGSort without crashing. 737define void @test12() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 738entry: 739 %e = alloca %class.MyClass, align 4 740 br label %for.cond 741 742for.cond: ; preds = %for.inc, %entry 743 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] 744 %cmp = icmp slt i32 %i.0, 9 745 br i1 %cmp, label %for.body, label %for.end 746 747for.body: ; preds = %for.cond 748 invoke void @quux(i32 %i.0) 749 to label %for.inc unwind label %catch.dispatch 750 751catch.dispatch: ; preds = %for.body 752 %0 = catchswitch within none [label %catch.start] unwind to caller 753 754catch.start: ; preds = %catch.dispatch 755 %1 = catchpad within %0 [i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)] 756 %2 = call i8* @llvm.wasm.get.exception(token %1) 757 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 758 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)) #3 759 %matches = icmp eq i32 %3, %4 760 br i1 %matches, label %catch, label %rethrow 761 762catch: ; preds = %catch.start 763 %5 = call i8* @__cxa_get_exception_ptr(i8* %2) #3 [ "funclet"(token %1) ] 764 %6 = bitcast i8* %5 to %class.MyClass* 765 %call = call %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* %e, %class.MyClass* dereferenceable(4) %6) [ "funclet"(token %1) ] 766 %7 = call i8* @__cxa_begin_catch(i8* %2) #3 [ "funclet"(token %1) ] 767 %x = getelementptr inbounds %class.MyClass, %class.MyClass* %e, i32 0, i32 0 768 %8 = load i32, i32* %x, align 4 769 invoke void @quux(i32 %8) [ "funclet"(token %1) ] 770 to label %invoke.cont2 unwind label %ehcleanup 771 772invoke.cont2: ; preds = %catch 773 %call3 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) #3 [ "funclet"(token %1) ] 774 call void @__cxa_end_catch() [ "funclet"(token %1) ] 775 catchret from %1 to label %for.inc 776 777rethrow: ; preds = %catch.start 778 call void @llvm.wasm.rethrow.in.catch() #6 [ "funclet"(token %1) ] 779 unreachable 780 781for.inc: ; preds = %invoke.cont2, %for.body 782 %inc = add nsw i32 %i.0, 1 783 br label %for.cond 784 785ehcleanup: ; preds = %catch 786 %9 = cleanuppad within %1 [] 787 %call4 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) #3 [ "funclet"(token %9) ] 788 invoke void @__cxa_end_catch() [ "funclet"(token %9) ] 789 to label %invoke.cont6 unwind label %terminate7 790 791invoke.cont6: ; preds = %ehcleanup 792 cleanupret from %9 unwind to caller 793 794for.end: ; preds = %for.cond 795 ret void 796 797terminate7: ; preds = %ehcleanup 798 %10 = cleanuppad within %9 [] 799 %11 = call i8* @llvm.wasm.get.exception(token %10) 800 call void @__clang_call_terminate(i8* %11) #7 [ "funclet"(token %10) ] 801 unreachable 802} 803 804; Check if the unwind destination mismatch stats are correct 805; NOSORT-STAT: 14 wasm-cfg-stackify - Number of EH pad unwind mismatches found 806 807declare void @foo() 808declare void @bar() 809declare i32 @baz() 810declare void @quux(i32) 811declare void @fun(i32) 812; Function Attrs: nounwind 813declare void @nothrow(i32) #0 814declare i32 @nothrow_i32() #0 815 816; Function Attrs: nounwind 817declare %class.Object* @_ZN6ObjectD2Ev(%class.Object* returned) #0 818@_ZTI7MyClass = external constant { i8*, i8* }, align 4 819; Function Attrs: nounwind 820declare %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* returned) #0 821; Function Attrs: nounwind 822declare %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* returned, %class.MyClass* dereferenceable(4)) #0 823 824declare i32 @__gxx_wasm_personality_v0(...) 825declare i8* @llvm.wasm.get.exception(token) 826declare i32 @llvm.wasm.get.ehselector(token) 827declare void @llvm.wasm.rethrow.in.catch() 828declare i32 @llvm.eh.typeid.for(i8*) 829declare i8* @__cxa_begin_catch(i8*) 830declare void @__cxa_end_catch() 831declare i8* @__cxa_get_exception_ptr(i8*) 832declare void @__clang_call_terminate(i8*) 833declare void @_ZSt9terminatev() 834; Function Attrs: nounwind 835declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0 836; Function Attrs: nounwind 837declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i1 immarg) #0 838; Function Attrs: nounwind 839declare void @llvm.memset.p0i8.i32(i8* nocapture writeonly, i8, i32, i1 immarg) #0 840 841attributes #0 = { nounwind } 842