1; REQUIRES: asserts 2; TODO Reenable disabled lines after updating the backend to the new spec 3; 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 4; 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 5; 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 6; R UN: 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 7; R UN: 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 8; R UN: 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 9 10target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 11target triple = "wasm32-unknown-unknown" 12 13@_ZTIi = external constant i8* 14@_ZTId = external constant i8* 15 16%class.Object = type { i8 } 17%class.MyClass = type { i32 } 18 19; Simple test case with two catch clauses 20; 21; void foo(); 22; void test0() { 23; try { 24; foo(); 25; } catch (int) { 26; } catch (double) { 27; } 28; } 29 30; CHECK-LABEL: test0 31; CHECK: try 32; CHECK: call foo 33; CHECK: catch 34; CHECK: block 35; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]] 36; CHECK: call $drop=, __cxa_begin_catch 37; CHECK: call __cxa_end_catch 38; CHECK: br 1 # 1: down to label[[L1:[0-9]+]] 39; CHECK: end_block # label[[L0]]: 40; CHECK: block 41; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]] 42; CHECK: call $drop=, __cxa_begin_catch 43; CHECK: call __cxa_end_catch 44; CHECK: br 1 # 1: down to label[[L1]] 45; CHECK: end_block # label[[L2]]: 46; CHECK: rethrow 0 # to caller 47; CHECK: end_try # label[[L1]]: 48define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 49entry: 50 invoke void @foo() 51 to label %try.cont unwind label %catch.dispatch 52 53catch.dispatch: ; preds = %entry 54 %0 = catchswitch within none [label %catch.start] unwind to caller 55 56catch.start: ; preds = %catch.dispatch 57 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] 58 %2 = call i8* @llvm.wasm.get.exception(token %1) 59 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 60 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) 61 %matches = icmp eq i32 %3, %4 62 br i1 %matches, label %catch2, label %catch.fallthrough 63 64catch2: ; preds = %catch.start 65 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 66 call void @__cxa_end_catch() [ "funclet"(token %1) ] 67 catchret from %1 to label %try.cont 68 69catch.fallthrough: ; preds = %catch.start 70 %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) 71 %matches1 = icmp eq i32 %3, %6 72 br i1 %matches1, label %catch, label %rethrow 73 74catch: ; preds = %catch.fallthrough 75 %7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 76 call void @__cxa_end_catch() [ "funclet"(token %1) ] 77 catchret from %1 to label %try.cont 78 79rethrow: ; preds = %catch.fallthrough 80 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ] 81 unreachable 82 83try.cont: ; preds = %catch, %catch2, %entry 84 ret void 85} 86 87; Nested try-catches within a catch 88; void test1() { 89; try { 90; foo(); 91; } catch (int) { 92; try { 93; foo(); 94; } catch (int) { 95; foo(); 96; } 97; } 98; } 99 100; CHECK-LABEL: test1 101; CHECK: try 102; CHECK: call foo 103; CHECK: catch 104; CHECK: block 105; CHECK: block 106; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]] 107; CHECK: call $drop=, __cxa_begin_catch 108; CHECK: try 109; CHECK: call foo 110; CHECK: br 2 # 2: down to label[[L1:[0-9]+]] 111; CHECK: catch 112; CHECK: try 113; CHECK: block 114; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]] 115; CHECK: call $drop=, __cxa_begin_catch 116; CHECK: try 117; CHECK: call foo 118; CHECK: br 2 # 2: down to label[[L3:[0-9]+]] 119; CHECK: catch 120; CHECK: call __cxa_end_catch 121; CHECK: rethrow 0 # down to catch[[C0:[0-9]+]] 122; CHECK: end_try 123; CHECK: end_block # label[[L2]]: 124; CHECK: rethrow 0 # down to catch[[C0]] 125; CHECK: catch_all # catch[[C0]]: 126; CHECK: call __cxa_end_catch 127; CHECK: rethrow 0 # to caller 128; CHECK: end_try # label[[L3]]: 129; CHECK: call __cxa_end_catch 130; CHECK: br 2 # 2: down to label[[L1]] 131; CHECK: end_try 132; CHECK: end_block # label[[L0]]: 133; CHECK: rethrow 0 # 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 237; CHECK: call __clang_call_terminate 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 %7 = call i8* @llvm.wasm.get.exception(token %6) 295 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ] 296 unreachable 297} 298 299; Tests if block and try markers are correctly placed. Even if two predecessors 300; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the 301; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2, 302; and scopes cannot be interleaved. 303 304; NOOPT-LABEL: test3 305; NOOPT: try 306; NOOPT: block 307; NOOPT: block 308; NOOPT: block 309; NOOPT: end_block 310; NOOPT: end_block 311; NOOPT: call foo 312; NOOPT: end_block 313; NOOPT: call bar 314; NOOPT: catch {{.*}} 315; NOOPT: end_try 316define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 317bb0: 318 br i1 undef, label %bb1, label %bb2 319 320bb1: ; preds = %bb0 321 br i1 undef, label %bb3, label %bb4 322 323bb2: ; preds = %bb0 324 br label %try.cont 325 326bb3: ; preds = %bb1 327 invoke void @foo() 328 to label %try.cont unwind label %catch.dispatch 329 330bb4: ; preds = %bb1 331 invoke void @bar() 332 to label %try.cont unwind label %catch.dispatch 333 334catch.dispatch: ; preds = %bb4, %bb3 335 %0 = catchswitch within none [label %catch.start] unwind to caller 336 337catch.start: ; preds = %catch.dispatch 338 %1 = catchpad within %0 [i8* null] 339 %2 = call i8* @llvm.wasm.get.exception(token %1) 340 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 341 catchret from %1 to label %try.cont 342 343try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2 344 ret void 345} 346 347; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers, 348; when try and loop markers are in the same BB and end_try and end_loop are in 349; another BB. 350; CHECK: loop 351; CHECK: try 352; CHECK: call foo 353; CHECK: catch 354; CHECK: end_try 355; CHECK: end_loop 356define void @test4(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 357entry: 358 store volatile i32 0, i32* %p 359 br label %loop 360 361loop: ; preds = %try.cont, %entry 362 store volatile i32 1, i32* %p 363 invoke void @foo() 364 to label %try.cont unwind label %catch.dispatch 365 366catch.dispatch: ; preds = %loop 367 %0 = catchswitch within none [label %catch.start] unwind to caller 368 369catch.start: ; preds = %catch.dispatch 370 %1 = catchpad within %0 [i8* null] 371 %2 = call i8* @llvm.wasm.get.exception(token %1) 372 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 373 catchret from %1 to label %try.cont 374 375try.cont: ; preds = %catch.start, %loop 376 br label %loop 377} 378 379; Some of test cases below are hand-tweaked by deleting some library calls to 380; simplify tests and changing the order of basic blocks to cause unwind 381; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum 382; number of mismatches in several tests below. 383 384; 'call bar''s original unwind destination was 'C1', but after control flow 385; linearization, its unwind destination incorrectly becomes 'C0'. We fix this by 386; wrapping the call with a nested try/catch/end_try and branching to the right 387; destination (L0). 388 389; NOSORT-LABEL: test5 390; NOSORT: block 391; NOSORT: try 392; NOSORT: try 393; NOSORT: call foo 394; --- Nested try/catch/end_try starts 395; NOSORT: try 396; NOSORT: call bar 397; NOSORT: catch $drop= 398; NOSORT: br 2 # 2: down to label[[L0:[0-9]+]] 399; NOSORT: end_try 400; --- Nested try/catch/end_try ends 401; NOSORT: br 2 # 2: down to label[[L1:[0-9]+]] 402; NOSORT: catch $drop= # catch[[C0:[0-9]+]]: 403; NOSORT: br 2 # 2: down to label[[L1]] 404; NOSORT: end_try 405; NOSORT: catch $drop= # catch[[C1:[0-9]+]]: 406; NOSORT: end_try # label[[L0]]: 407; NOSORT: end_block # label[[L1]]: 408; NOSORT: return 409 410define void @test5() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 411bb0: 412 invoke void @foo() 413 to label %bb1 unwind label %catch.dispatch0 414 415bb1: ; preds = %bb0 416 invoke void @bar() 417 to label %try.cont unwind label %catch.dispatch1 418 419catch.dispatch0: ; preds = %bb0 420 %0 = catchswitch within none [label %catch.start0] unwind to caller 421 422catch.start0: ; preds = %catch.dispatch0 423 %1 = catchpad within %0 [i8* null] 424 %2 = call i8* @llvm.wasm.get.exception(token %1) 425 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 426 catchret from %1 to label %try.cont 427 428catch.dispatch1: ; preds = %bb1 429 %4 = catchswitch within none [label %catch.start1] unwind to caller 430 431catch.start1: ; preds = %catch.dispatch1 432 %5 = catchpad within %4 [i8* null] 433 %6 = call i8* @llvm.wasm.get.exception(token %5) 434 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 435 catchret from %5 to label %try.cont 436 437try.cont: ; preds = %catch.start1, %catch.start0, %bb1 438 ret void 439} 440 441; Two 'call bar''s original unwind destination was the caller, but after control 442; flow linearization, their unwind destination incorrectly becomes 'C0'. We fix 443; this by wrapping the call with a nested try/catch/end_try and branching to the 444; right destination (L0), from which we rethrow the exception to the caller. 445 446; And the return value of 'baz' should NOT be stackified because the BB is split 447; during fixing unwind mismatches. 448 449; NOSORT-LABEL: test6 450; NOSORT: try 451; NOSORT: call foo 452; --- Nested try/catch/end_try starts 453; NOSORT: try 454; NOSORT: call bar 455; NOSORT: call ${{[0-9]+}}=, baz 456; NOSORT-NOT: call $push{{.*}}=, baz 457; NOSORT: catch $[[REG:[0-9]+]]= 458; NOSORT: br 1 # 1: down to label[[L0:[0-9]+]] 459; NOSORT: end_try 460; --- Nested try/catch/end_try ends 461; NOSORT: return 462; NOSORT: catch $drop= # catch[[C0:[0-9]+]]: 463; NOSORT: return 464; NOSORT: end_try # label[[L0]]: 465; NOSORT: rethrow $[[REG]] # to caller 466 467define void @test6() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 468bb0: 469 invoke void @foo() 470 to label %bb1 unwind label %catch.dispatch0 471 472bb1: ; preds = %bb0 473 call void @bar() 474 %call = call i32 @baz() 475 call void @nothrow(i32 %call) #0 476 ret void 477 478catch.dispatch0: ; preds = %bb0 479 %0 = catchswitch within none [label %catch.start0] unwind to caller 480 481catch.start0: ; preds = %catch.dispatch0 482 %1 = catchpad within %0 [i8* null] 483 %2 = call i8* @llvm.wasm.get.exception(token %1) 484 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 485 catchret from %1 to label %try.cont 486 487try.cont: ; preds = %catch.start0 488 ret void 489} 490 491; Similar situation as @test6. Here 'call @qux''s original unwind destination 492; was the caller, but after control flow linearization, their unwind destination 493; incorrectly becomes 'C0' within the function. We fix this by wrapping the call 494; with a nested try/catch/end_try and branching to the right destination, from 495; which we rethrow the exception to the caller. 496 497; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the 498; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5' 499; and 'call @qux'. 500 501; NOSORT-LABEL: test7 502; NOSORT: try 503; NOSORT: call foo 504; --- Nested try/catch/end_try starts 505; NOSORT: try 506; NOSORT-NEXT: i32.const $push{{[0-9]+}}=, 5 507; NOSORT-NEXT: call ${{[0-9]+}}=, qux 508; NOSORT: catch $[[REG:[0-9]+]]= 509; NOSORT: br 1 # 1: down to label[[L0:[0-9]+]] 510; NOSORT: end_try 511; --- Nested try/catch/end_try ends 512; NOSORT: return 513; NOSORT: catch $drop= # catch[[C0:[0-9]+]]: 514; NOSORT: return 515; NOSORT: end_try # label[[L0]]: 516; NOSORT: rethrow $[[REG]] # to caller 517 518define i32 @test7() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 519bb0: 520 invoke void @foo() 521 to label %bb1 unwind label %catch.dispatch0 522 523bb1: ; preds = %bb0 524 %0 = call i32 @qux(i32 5) 525 ret i32 %0 526 527catch.dispatch0: ; preds = %bb0 528 %1 = catchswitch within none [label %catch.start0] unwind to caller 529 530catch.start0: ; preds = %catch.dispatch0 531 %2 = catchpad within %1 [i8* null] 532 %3 = call i8* @llvm.wasm.get.exception(token %2) 533 %j = call i32 @llvm.wasm.get.ehselector(token %2) 534 catchret from %2 to label %try.cont 535 536try.cont: ; preds = %catch.start0 537 ret i32 0 538} 539 540; Tests the case when TEE stackifies a register in RegStackify but it gets 541; unstackified in fixUnwindMismatches in CFGStackify. 542 543; NOSORT-LOCALS-LABEL: test8 544define void @test8(i32 %x) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 545bb0: 546 invoke void @foo() 547 to label %bb1 unwind label %catch.dispatch0 548 549bb1: ; preds = %bb0 550 %t = add i32 %x, 4 551 ; This %addr is used in multiple places, so tee is introduced in RegStackify, 552 ; which stackifies the use of %addr in store instruction. A tee has two dest 553 ; registers, the first of which is stackified and the second is not. 554 ; But when we introduce a nested try-catch in fixUnwindMismatches in 555 ; CFGStackify, it is possible that we end up unstackifying the first dest 556 ; register. In that case, we convert that tee into a copy. 557 %addr = inttoptr i32 %t to i32* 558 %load = load i32, i32* %addr 559 %call = call i32 @baz() 560 %add = add i32 %load, %call 561 store i32 %add, i32* %addr 562 ret void 563; NOSORT-LOCALS: i32.add 564; NOSORT-LOCALS-NOT: local.tee 565; NOSORT-LOCALS-NEXT: local.set 566 567catch.dispatch0: ; preds = %bb0 568 %0 = catchswitch within none [label %catch.start0] unwind to caller 569 570catch.start0: ; preds = %catch.dispatch0 571 %1 = catchpad within %0 [i8* null] 572 %2 = call i8* @llvm.wasm.get.exception(token %1) 573 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 574 catchret from %1 to label %try.cont 575 576try.cont: ; preds = %catch.start0 577 ret void 578} 579 580; If not for the unwind destination mismatch, the LOOP marker here would have an 581; i32 signature. But because we add a rethrow instruction at the end of the 582; appendix block, now the LOOP marker does not have a signature (= has a void 583; signature). Here the two calls two 'bar' are supposed to throw up to the 584; caller, but incorrectly unwind to 'C0' after linearizing the CFG. 585 586; NOSORT-LABEL: test9 587; NOSORT: block 588; NOSORT-NOT: loop i32 589; NOSORT: loop # label[[L0:[0-9]+]]: 590; NOSORT: try 591; NOSORT: call foo 592; --- Nested try/catch/end_try starts 593; NOSORT: try 594; NOSORT: call bar 595; NOSORT: call bar 596; NOSORT: catch $[[REG:[0-9]+]]= 597; NOSORT: br 1 # 1: down to label[[L1:[0-9]+]] 598; NOSORT: end_try 599; --- Nested try/catch/end_try ends 600; NOSORT: return {{.*}} 601; NOSORT: catch $drop= # catch[[C0:[0-9]+]]: 602; NOSORT: br 1 # 1: up to label[[L0]] 603; NOSORT: end_try # label[[L1]]: 604; NOSORT: end_loop 605; NOSORT: end_block 606; NOSORT: rethrow $[[REG]] # to caller 607 608define i32 @test9(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 609entry: 610 store volatile i32 0, i32* %p 611 br label %loop 612 613loop: ; preds = %try.cont, %entry 614 store volatile i32 1, i32* %p 615 invoke void @foo() 616 to label %bb unwind label %catch.dispatch 617 618bb: ; preds = %loop 619 call void @bar() 620 call void @bar() 621 ret i32 0 622 623catch.dispatch: ; preds = %loop 624 %0 = catchswitch within none [label %catch.start] unwind to caller 625 626catch.start: ; preds = %catch.dispatch 627 %1 = catchpad within %0 [i8* null] 628 %2 = call i8* @llvm.wasm.get.exception(token %1) 629 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 630 catchret from %1 to label %try.cont 631 632try.cont: ; preds = %catch.start 633 br label %loop 634} 635 636; When we have both kinds of EH pad unwind mismatches: 637; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 638; CFG, when it is supposed to unwind to another EH pad. 639; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the 640; CFG, when it is supposed to unwind to the caller. 641 642; NOSORT-LABEL: test10 643; NOSORT: block 644; NOSORT: block 645; NOSORT: try 646; NOSORT: try 647; NOSORT: call foo 648; --- Nested try/catch/end_try starts 649; NOSORT: try 650; NOSORT: call bar 651; NOSORT: catch $[[REG0:[0-9]+]]= 652; NOSORT: br 2 # 2: down to label[[L0:[0-9]+]] 653; NOSORT: end_try 654; --- Nested try/catch/end_try ends 655; NOSORT: br 2 # 2: down to label[[L1:[0-9]+]] 656; NOSORT: catch {{.*}} 657; NOSORT: block i32 658; NOSORT: br_on_exn 0, {{.*}} # 0: down to label[[L2:[0-9]+]] 659; --- Nested try/catch/end_try starts 660; NOSORT: try 661; NOSORT: rethrow 0 # down to catch[[C0:[0-9]+]] 662; NOSORT: catch $[[REG1:[0-9]+]]= # catch[[C0]]: 663; NOSORT: br 5 # 5: down to label[[L3:[0-9]+]] 664; NOSORT: end_try 665; --- Nested try/catch/end_try ends 666; NOSORT: end_block # label[[L2]]: 667; NOSORT: call $drop=, __cxa_begin_catch 668; --- Nested try/catch/end_try starts 669; NOSORT: try 670; NOSORT: call __cxa_end_catch 671; NOSORT: catch $[[REG1]]= 672; NOSORT: br 4 # 4: down to label[[L3]] 673; NOSORT: end_try 674; --- Nested try/catch/end_try ends 675; NOSORT: br 2 # 2: down to label[[L1]] 676; NOSORT: end_try 677; NOSORT: catch $[[REG0]]= 678; NOSORT: end_try # label[[L0]]: 679; NOSORT: call $drop=, __cxa_begin_catch 680; NOSORT: call __cxa_end_catch 681; NOSORT: end_block # label[[L1]]: 682; NOSORT: return 683; NOSORT: end_block # label[[L3]]: 684; NOSORT: rethrow $[[REG1]] # to caller 685define void @test10() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 686bb0: 687 invoke void @foo() 688 to label %bb1 unwind label %catch.dispatch0 689 690bb1: ; preds = %bb0 691 invoke void @bar() 692 to label %try.cont unwind label %catch.dispatch1 693 694catch.dispatch0: ; preds = %bb0 695 %0 = catchswitch within none [label %catch.start0] unwind to caller 696 697catch.start0: ; preds = %catch.dispatch0 698 %1 = catchpad within %0 [i8* null] 699 %2 = call i8* @llvm.wasm.get.exception(token %1) 700 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 701 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 702 call void @__cxa_end_catch() [ "funclet"(token %1) ] 703 catchret from %1 to label %try.cont 704 705catch.dispatch1: ; preds = %bb1 706 %5 = catchswitch within none [label %catch.start1] unwind to caller 707 708catch.start1: ; preds = %catch.dispatch1 709 %6 = catchpad within %5 [i8* null] 710 %7 = call i8* @llvm.wasm.get.exception(token %6) 711 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 712 %9 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ] 713 call void @__cxa_end_catch() [ "funclet"(token %6) ] 714 catchret from %6 to label %try.cont 715 716try.cont: ; preds = %catch.start1, %catch.start0, %bb1 717 ret void 718} 719 720; In CFGSort, EH pads should be sorted as soon as it is available and 721; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are 722; in the middle of sorting another region that does not contain the EH pad. In 723; this example, 'catch.start' should be sorted right after 'if.then' is sorted 724; (before 'cont' is sorted) and there should not be any unwind destination 725; mismatches in CFGStackify. 726 727; NOOPT: block 728; NOOPT: try 729; NOOPT: call foo 730; NOOPT: catch 731; NOOPT: end_try 732; NOOPT: call foo 733; NOOPT: end_block 734; NOOPT: return 735define void @test11(i32 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 736entry: 737 %tobool = icmp ne i32 %arg, 0 738 br i1 %tobool, label %if.then, label %if.end 739 740catch.dispatch: ; preds = %if.then 741 %0 = catchswitch within none [label %catch.start] unwind to caller 742 743catch.start: ; preds = %catch.dispatch 744 %1 = catchpad within %0 [i8* null] 745 %2 = call i8* @llvm.wasm.get.exception(token %1) 746 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 747 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 748 call void @__cxa_end_catch() [ "funclet"(token %1) ] 749 catchret from %1 to label %if.end 750 751if.then: ; preds = %entry 752 invoke void @foo() 753 to label %cont unwind label %catch.dispatch 754 755cont: ; preds = %if.then 756 call void @foo() 757 br label %if.end 758 759if.end: ; preds = %cont, %catch.start, %entry 760 ret void 761} 762 763; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into 764; calls to external symbols (not global addresses) in instruction selection, 765; which will be eventually lowered to library function calls. 766; Because this test runs with -wasm-disable-ehpad-sort, these library calls in 767; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or 768; unwinding destination mismatches in CFGStackify. 769 770; NOSORT-LABEL: test12 771; NOSORT: try 772; NOSORT: call foo 773; NOSORT: call {{.*}} memcpy 774; NOSORT: call {{.*}} memmove 775; NOSORT: call {{.*}} memset 776; NOSORT: return 777; NOSORT: catch 778; NOSORT: rethrow 779; NOSORT: end_try 780define void @test12(i8* %a, i8* %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 781entry: 782 %o = alloca %class.Object, align 1 783 invoke void @foo() 784 to label %invoke.cont unwind label %ehcleanup 785 786invoke.cont: ; preds = %entry 787 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 788 call void @llvm.memmove.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false) 789 call void @llvm.memset.p0i8.i32(i8* %a, i8 0, i32 100, i1 false) 790 %call = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 791 ret void 792 793ehcleanup: ; preds = %entry 794 %0 = cleanuppad within none [] 795 %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 [ "funclet"(token %0) ] 796 cleanupret from %0 unwind to caller 797} 798 799; Tests if 'try' marker is placed correctly. In this test, 'try' should be 800; placed before the call to 'nothrow_i32' and not between the call to 801; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is 802; stackified and pushed onto the stack to be consumed by the call to 'fun'. 803 804; CHECK-LABEL: test13 805; CHECK: try 806; CHECK: call $push{{.*}}=, nothrow_i32 807; CHECK: call fun, $pop{{.*}} 808define void @test13() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 809entry: 810 %call = call i32 @nothrow_i32() 811 invoke void @fun(i32 %call) 812 to label %invoke.cont unwind label %terminate 813 814invoke.cont: ; preds = %entry 815 ret void 816 817terminate: ; preds = %entry 818 %0 = cleanuppad within none [] 819 %1 = tail call i8* @llvm.wasm.get.exception(token %0) 820 call void @__clang_call_terminate(i8* %1) [ "funclet"(token %0) ] 821 unreachable 822} 823 824; This crashed on debug mode (= when NDEBUG is not defined) when the logic for 825; computing the innermost region was not correct, in which a loop region 826; contains an exception region. This should pass CFGSort without crashing. 827define void @test14() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 828entry: 829 %e = alloca %class.MyClass, align 4 830 br label %for.cond 831 832for.cond: ; preds = %for.inc, %entry 833 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] 834 %cmp = icmp slt i32 %i.0, 9 835 br i1 %cmp, label %for.body, label %for.end 836 837for.body: ; preds = %for.cond 838 invoke void @quux(i32 %i.0) 839 to label %for.inc unwind label %catch.dispatch 840 841catch.dispatch: ; preds = %for.body 842 %0 = catchswitch within none [label %catch.start] unwind to caller 843 844catch.start: ; preds = %catch.dispatch 845 %1 = catchpad within %0 [i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)] 846 %2 = call i8* @llvm.wasm.get.exception(token %1) 847 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 848 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast ({ i8*, i8* }* @_ZTI7MyClass to i8*)) #3 849 %matches = icmp eq i32 %3, %4 850 br i1 %matches, label %catch, label %rethrow 851 852catch: ; preds = %catch.start 853 %5 = call i8* @__cxa_get_exception_ptr(i8* %2) #3 [ "funclet"(token %1) ] 854 %6 = bitcast i8* %5 to %class.MyClass* 855 %call = call %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* %e, %class.MyClass* dereferenceable(4) %6) [ "funclet"(token %1) ] 856 %7 = call i8* @__cxa_begin_catch(i8* %2) #3 [ "funclet"(token %1) ] 857 %x = getelementptr inbounds %class.MyClass, %class.MyClass* %e, i32 0, i32 0 858 %8 = load i32, i32* %x, align 4 859 invoke void @quux(i32 %8) [ "funclet"(token %1) ] 860 to label %invoke.cont2 unwind label %ehcleanup 861 862invoke.cont2: ; preds = %catch 863 %call3 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) #3 [ "funclet"(token %1) ] 864 call void @__cxa_end_catch() [ "funclet"(token %1) ] 865 catchret from %1 to label %for.inc 866 867rethrow: ; preds = %catch.start 868 call void @llvm.wasm.rethrow() #6 [ "funclet"(token %1) ] 869 unreachable 870 871for.inc: ; preds = %invoke.cont2, %for.body 872 %inc = add nsw i32 %i.0, 1 873 br label %for.cond 874 875ehcleanup: ; preds = %catch 876 %9 = cleanuppad within %1 [] 877 %call4 = call %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* %e) #3 [ "funclet"(token %9) ] 878 invoke void @__cxa_end_catch() [ "funclet"(token %9) ] 879 to label %invoke.cont6 unwind label %terminate7 880 881invoke.cont6: ; preds = %ehcleanup 882 cleanupret from %9 unwind to caller 883 884for.end: ; preds = %for.cond 885 ret void 886 887terminate7: ; preds = %ehcleanup 888 %10 = cleanuppad within %9 [] 889 %11 = call i8* @llvm.wasm.get.exception(token %10) 890 call void @__clang_call_terminate(i8* %11) #7 [ "funclet"(token %10) ] 891 unreachable 892} 893 894; We don't need to call placeBlockMarker after fixUnwindMismatches unless the 895; destination is the appendix BB at the very end. This should not crash. 896define void @test16(i32* %p, i32 %a, i32 %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 897entry: 898 br label %loop 899 900loop: ; preds = %try.cont, %entry 901 invoke void @foo() 902 to label %bb0 unwind label %catch.dispatch0 903 904bb0: ; preds = %loop 905 %cmp = icmp ne i32 %a, %b 906 br i1 %cmp, label %bb1, label %last 907 908bb1: ; preds = %bb0 909 invoke void @bar() 910 to label %try.cont unwind label %catch.dispatch1 911 912catch.dispatch0: ; preds = %loop 913 %0 = catchswitch within none [label %catch.start0] unwind to caller 914 915catch.start0: ; preds = %catch.dispatch0 916 %1 = catchpad within %0 [i8* null] 917 %2 = call i8* @llvm.wasm.get.exception(token %1) 918 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 919 catchret from %1 to label %try.cont 920 921catch.dispatch1: ; preds = %bb1 922 %4 = catchswitch within none [label %catch.start1] unwind to caller 923 924catch.start1: ; preds = %catch.dispatch1 925 %5 = catchpad within %4 [i8* null] 926 %6 = call i8* @llvm.wasm.get.exception(token %5) 927 %7 = call i32 @llvm.wasm.get.ehselector(token %5) 928 catchret from %5 to label %try.cont 929 930try.cont: ; preds = %catch.start1, %catch.start0, %bb1 931 br label %loop 932 933last: ; preds = %bb0 934 ret void 935} 936 937; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches 938; correctly. The code is in the form below, where 'br' is unnecessary because 939; after running the 'try' body the control flow will fall through to bb2 anyway. 940 941; bb0: 942; try 943; ... 944; br bb2 <- Not necessary 945; bb1 (ehpad): 946; catch 947; ... 948; bb2: <- Continuation BB 949; end 950; CHECK-LABEL: test17 951define void @test17(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 952entry: 953 invoke void @foo() 954 to label %for.body unwind label %catch.dispatch 955 956for.body: ; preds = %for.end, %entry 957 %i = phi i32 [ %inc, %for.end ], [ 0, %entry ] 958 invoke void @foo() 959 to label %for.end unwind label %catch.dispatch 960 961; Before going to CFGStackify, this BB will have a conditional branch followed 962; by an unconditional branch. CFGStackify should remove only the unconditional 963; one. 964for.end: ; preds = %for.body 965 %inc = add nuw nsw i32 %i, 1 966 %exitcond = icmp eq i32 %inc, %n 967 br i1 %exitcond, label %try.cont, label %for.body 968; CHECK: br_if 969; CHECK-NOT: br 970; CHECK: end_loop 971; CHECK: catch 972 973catch.dispatch: ; preds = %for.body, %entry 974 %0 = catchswitch within none [label %catch.start] unwind to caller 975 976catch.start: ; preds = %catch.dispatch 977 %1 = catchpad within %0 [i8* null] 978 %2 = call i8* @llvm.wasm.get.exception(token %1) 979 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 980 %4 = call i8* @__cxa_begin_catch(i8* %2) #2 [ "funclet"(token %1) ] 981 call void @__cxa_end_catch() [ "funclet"(token %1) ] 982 catchret from %1 to label %try.cont 983 984try.cont: ; preds = %catch.start, %for.end 985 ret void 986} 987 988; void foo(); 989; void test18() { 990; try { 991; foo(); 992; try { 993; foo(); 994; } catch (...) { 995; } 996; } catch (...) { 997; } 998; } 999; 1000; This tests whether the 'br' can be removed in code in the form as follows. 1001; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this 1002; case, after running an inner try body, the control flow should fall through to 1003; bb3, so the 'br' in the code is unnecessary. 1004 1005; bb0: 1006; try 1007; try 1008; ... 1009; br bb3 <- Not necessary 1010; bb1: 1011; catch 1012; bb2: 1013; end_try 1014; catch 1015; ... 1016; bb3: <- Continuation BB 1017; end 1018; 1019; CHECK-LABEL: test18 1020define void @test18() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1021; CHECK: call foo 1022entry: 1023 invoke void @foo() 1024 to label %invoke.cont unwind label %catch.dispatch3 1025 1026; CHECK: call foo 1027; CHECK-NOT: br 1028invoke.cont: ; preds = %entry 1029 invoke void @foo() 1030 to label %try.cont8 unwind label %catch.dispatch 1031 1032catch.dispatch: ; preds = %invoke.cont 1033 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3 1034 1035; CHECK: catch 1036catch.start: ; preds = %catch.dispatch 1037 %1 = catchpad within %0 [i8* null] 1038 %2 = call i8* @llvm.wasm.get.exception(token %1) 1039 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1040 %4 = call i8* @__cxa_begin_catch(i8* %2) #2 [ "funclet"(token %1) ] 1041 invoke void @__cxa_end_catch() [ "funclet"(token %1) ] 1042 to label %invoke.cont2 unwind label %catch.dispatch3 1043 1044catch.dispatch3: ; preds = %catch.start, %catch.dispatch, %entry 1045 %5 = catchswitch within none [label %catch.start4] unwind to caller 1046 1047catch.start4: ; preds = %catch.dispatch3 1048 %6 = catchpad within %5 [i8* null] 1049 %7 = call i8* @llvm.wasm.get.exception(token %6) 1050 %8 = call i32 @llvm.wasm.get.ehselector(token %6) 1051 %9 = call i8* @__cxa_begin_catch(i8* %7) #2 [ "funclet"(token %6) ] 1052 call void @__cxa_end_catch() [ "funclet"(token %6) ] 1053 catchret from %6 to label %try.cont8 1054 1055try.cont8: ; preds = %invoke.cont, %invoke.cont2, %catch.start4 1056 ret void 1057 1058invoke.cont2: ; preds = %catch.start 1059 catchret from %1 to label %try.cont8 1060} 1061 1062; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs 1063; to the exception, but does not belong to the loop (because it does not have a 1064; path back to the loop header), and is placed after the loop latch block 1065; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed 1066; correctly not right after 'invoke.cont' part but after 'ehcleanup' part, 1067; NOSORT-LABEL: test19 1068; NOSORT: loop 1069; NOSORT: try 1070; NOSORT: end_try 1071; NOSORT: end_loop 1072define void @test19(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1073entry: 1074 br label %while.cond 1075 1076while.cond: ; preds = %invoke.cont, %entry 1077 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ] 1078 %tobool = icmp ne i32 %n.addr.0, 0 1079 br i1 %tobool, label %while.body, label %while.end 1080 1081while.body: ; preds = %while.cond 1082 %dec = add nsw i32 %n.addr.0, -1 1083 invoke void @foo() 1084 to label %while.end unwind label %catch.dispatch 1085 1086catch.dispatch: ; preds = %while.body 1087 %0 = catchswitch within none [label %catch.start] unwind to caller 1088 1089catch.start: ; preds = %catch.dispatch 1090 %1 = catchpad within %0 [i8* null] 1091 %2 = call i8* @llvm.wasm.get.exception(token %1) 1092 %3 = call i32 @llvm.wasm.get.ehselector(token %1) 1093 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ] 1094 invoke void @__cxa_end_catch() [ "funclet"(token %1) ] 1095 to label %invoke.cont unwind label %ehcleanup 1096 1097invoke.cont: ; preds = %catch.start 1098 catchret from %1 to label %while.cond 1099 1100ehcleanup: ; preds = %catch.start 1101 %5 = cleanuppad within %1 [] 1102 %6 = call i8* @llvm.wasm.get.exception(token %5) 1103 call void @__clang_call_terminate(i8* %6) [ "funclet"(token %5) ] 1104 unreachable 1105 1106while.end: ; preds = %while.body, %while.cond 1107 ret void 1108} 1109 1110; When the function return type is non-void and 'end' instructions are at the 1111; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes 1112; the corresponding block/loop/try's type to match the function's return type. 1113; But when a `try`'s type is fixed, we should also check `end` instructions 1114; before its corresponding `catch`, because both `try` and `catch` body should 1115; satisfy the return type requirements. 1116 1117; NOSORT-LABEL: test20 1118; NOSORT: try i32 1119; NOSORT: loop i32 1120; NOSORT: end_loop 1121; NOSORT: catch 1122; NOSORT: end_try 1123; NOSORT-NEXT: end_function 1124define i32 @test20(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { 1125entry: 1126 %t = alloca %class.Object, align 1 1127 br label %for.cond 1128 1129for.cond: ; preds = %for.inc, %entry 1130 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] 1131 %cmp = icmp slt i32 %i.0, %n 1132 br label %for.body 1133 1134for.body: ; preds = %for.cond 1135 %div = sdiv i32 %n, 2 1136 %cmp1 = icmp eq i32 %i.0, %div 1137 br i1 %cmp1, label %if.then, label %for.inc 1138 1139if.then: ; preds = %for.body 1140 %call = invoke i32 @baz() 1141 to label %invoke.cont unwind label %ehcleanup 1142 1143invoke.cont: ; preds = %if.then 1144 %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %t) #4 1145 ret i32 %call 1146 1147for.inc: ; preds = %for.body 1148 %inc = add nsw i32 %i.0, 1 1149 br label %for.cond 1150 1151ehcleanup: ; preds = %if.then 1152 %0 = cleanuppad within none [] 1153 %call3 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %t) #4 [ "funclet"(token %0) ] 1154 cleanupret from %0 unwind to caller 1155} 1156 1157 1158; Check if the unwind destination mismatch stats are correct 1159; NOSORT-STAT: 17 wasm-cfg-stackify - Number of EH pad unwind mismatches found 1160 1161declare void @foo() 1162declare void @bar() 1163declare i32 @baz() 1164declare i32 @qux(i32) 1165declare void @quux(i32) 1166declare void @fun(i32) 1167; Function Attrs: nounwind 1168declare void @nothrow(i32) #0 1169declare i32 @nothrow_i32() #0 1170 1171; Function Attrs: nounwind 1172declare %class.Object* @_ZN6ObjectD2Ev(%class.Object* returned) #0 1173@_ZTI7MyClass = external constant { i8*, i8* }, align 4 1174; Function Attrs: nounwind 1175declare %class.MyClass* @_ZN7MyClassD2Ev(%class.MyClass* returned) #0 1176; Function Attrs: nounwind 1177declare %class.MyClass* @_ZN7MyClassC2ERKS_(%class.MyClass* returned, %class.MyClass* dereferenceable(4)) #0 1178 1179declare i32 @__gxx_wasm_personality_v0(...) 1180declare i8* @llvm.wasm.get.exception(token) 1181declare i32 @llvm.wasm.get.ehselector(token) 1182declare void @llvm.wasm.rethrow() 1183declare i32 @llvm.eh.typeid.for(i8*) 1184declare i8* @__cxa_begin_catch(i8*) 1185declare void @__cxa_end_catch() 1186declare i8* @__cxa_get_exception_ptr(i8*) 1187declare void @__clang_call_terminate(i8*) 1188declare void @_ZSt9terminatev() 1189; Function Attrs: nounwind 1190declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0 1191; Function Attrs: nounwind 1192declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i1 immarg) #0 1193; Function Attrs: nounwind 1194declare void @llvm.memset.p0i8.i32(i8* nocapture writeonly, i8, i32, i1 immarg) #0 1195 1196attributes #0 = { nounwind } 1197