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