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