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