1557 lines · plain
1; REQUIRES: asserts2; 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 %s3; 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-memory4; 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=NOOPT5; 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=NOSORT6; 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-LOCALS7 8target triple = "wasm32-unknown-unknown"9 10@_ZTIi = external constant ptr11@_ZTId = external constant ptr12 13%class.Object = type { i8 }14%class.MyClass = type { i32 }15 16; Simple test case with two catch clauses17;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: block29; CHECK: block () -> (i32, exnref)30; CHECK: try_table (catch_ref __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]31; CHECK: call foo32; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]33; CHECK: end_try_table34; CHECK: end_block # label[[L0]]:35; CHECK: local.set 236; CHECK: local.set 137; CHECK: local.get 038; CHECK: call _Unwind_CallPersonality39; CHECK: block40; CHECK: br_if 0 # 0: down to label[[L2:[0-9]+]]41; CHECK: call __cxa_begin_catch42; CHECK: call __cxa_end_catch43; CHECK: br 1 # 1: down to label[[L1]]44; CHECK: end_block # label[[L2]]:45; CHECK: block46; CHECK: br_if 0 # 0: down to label[[L3:[0-9]+]]47; CHECK: call __cxa_begin_catch48; CHECK: call __cxa_end_catch49; CHECK: br 1 # 1: down to label[[L1]]50; CHECK: end_block # label[[L3]]:51; CHECK: throw_ref52; 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.dispatch57 58catch.dispatch: ; preds = %entry59 %0 = catchswitch within none [label %catch.start] unwind to caller60 61catch.start: ; preds = %catch.dispatch62 %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, %467 br i1 %matches, label %catch2, label %catch.fallthrough68 69catch2: ; preds = %catch.start70 %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.cont73 74catch.fallthrough: ; preds = %catch.start75 %6 = call i32 @llvm.eh.typeid.for(ptr @_ZTId)76 %matches1 = icmp eq i32 %3, %677 br i1 %matches1, label %catch, label %rethrow78 79catch: ; preds = %catch.fallthrough80 %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.cont83 84rethrow: ; preds = %catch.fallthrough85 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]86 unreachable87 88try.cont: ; preds = %catch, %catch2, %entry89 ret void90}91 92; Nested try-catches within a catch93; 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 exnref107; CHECK: block108; CHECK: block () -> (i32, exnref)109; CHECK: try_table (catch_ref __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]110; CHECK: call foo111; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]112; CHECK: end_try_table113; CHECK: end_block # label[[L0]]:114; CHECK: call _Unwind_CallPersonality115; CHECK: block116; CHECK: block117; CHECK: br_if 0 # 0: down to label[[L2:[0-9]+]]118; CHECK: call __cxa_begin_catch119; CHECK: block exnref120; 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 foo124; CHECK: br 5 # 5: down to label[[L5:[0-9]+]]125; CHECK: end_try_table126; CHECK: end_block # label[[L4]]:127; CHECK: call _Unwind_CallPersonality128; CHECK: block129; CHECK: block130; CHECK: br_if 0 # 0: down to label[[L6:[0-9]+]]131; CHECK: call __cxa_begin_catch132; CHECK: block exnref133; CHECK: try_table (catch_all_ref 0) # 0: down to label[[L7:[0-9]+]]134; CHECK: call foo135; CHECK: br 3 # 3: down to label[[L8:[0-9]+]]136; CHECK: end_try_table137; 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_catch140; CHECK: end_try_table141; CHECK: throw_ref142; CHECK: end_block # label[[L6]]:143; CHECK: throw_ref144; CHECK: end_block # label[[L8]]:145; CHECK: try_table (catch_all_ref 5) # 5: down to label[[L9]]146; CHECK: call __cxa_end_catch147; CHECK: end_try_table148; CHECK: br 3 # 3: down to label[[L5]]149; CHECK: end_try_table150; CHECK: end_block # label[[L3]]:151; CHECK: call __cxa_end_catch152; CHECK: throw_ref153; CHECK: end_block # label[[L2]]:154; CHECK: throw_ref155; CHECK: end_block # label[[L5]]:156; CHECK: call __cxa_end_catch157; CHECK: end_block # label[[L1]]:158; CHECK: return159; CHECK: end_block # label[[L9]]:160; CHECK: throw_ref161define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {162entry:163 invoke void @foo()164 to label %try.cont11 unwind label %catch.dispatch165 166catch.dispatch: ; preds = %entry167 %0 = catchswitch within none [label %catch.start] unwind to caller168 169catch.start: ; preds = %catch.dispatch170 %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, %4175 br i1 %matches, label %catch, label %rethrow176 177catch: ; preds = %catch.start178 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]179 %6 = load i32, ptr %5, align 4180 invoke void @foo() [ "funclet"(token %1) ]181 to label %try.cont unwind label %catch.dispatch2182 183catch.dispatch2: ; preds = %catch184 %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9185 186catch.start3: ; preds = %catch.dispatch2187 %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, %11192 br i1 %matches4, label %catch6, label %rethrow5193 194catch6: ; preds = %catch.start3195 %12 = call ptr @__cxa_begin_catch(ptr %9) [ "funclet"(token %8) ]196 %13 = load i32, ptr %12, align 4197 invoke void @foo() [ "funclet"(token %8) ]198 to label %invoke.cont8 unwind label %ehcleanup199 200invoke.cont8: ; preds = %catch6201 call void @__cxa_end_catch() [ "funclet"(token %8) ]202 catchret from %8 to label %try.cont203 204rethrow5: ; preds = %catch.start3205 invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]206 to label %unreachable unwind label %ehcleanup9207 208try.cont: ; preds = %invoke.cont8, %catch209 call void @__cxa_end_catch() [ "funclet"(token %1) ]210 catchret from %1 to label %try.cont11211 212rethrow: ; preds = %catch.start213 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]214 unreachable215 216try.cont11: ; preds = %try.cont, %entry217 ret void218 219ehcleanup: ; preds = %catch6220 %14 = cleanuppad within %8 []221 call void @__cxa_end_catch() [ "funclet"(token %14) ]222 cleanupret from %14 unwind label %ehcleanup9223 224ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2225 %15 = cleanuppad within %1 []226 call void @__cxa_end_catch() [ "funclet"(token %15) ]227 cleanupret from %15 unwind to caller228 229unreachable: ; preds = %rethrow5230 unreachable231}232 233; Nested try-catches within a try234; void nested_try() {235; try {236; try {237; foo();238; } catch (...) {239; }240; } catch (...) {241; }242; }243 244; CHECK-LABEL: nested_try:245; CHECK: block246; CHECK: block i32247; CHECK: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]248; CHECK: block i32249; CHECK: try_table (catch __cpp_exception 0) # 0: down to label[[L1:[0-9]+]]250; CHECK: call foo251; CHECK: br 4 # 4: down to label[[L2:[0-9]+]]252; CHECK: end_try_table253; CHECK: end_block # label[[L1]]:254; CHECK: call __cxa_begin_catch255; CHECK: call __cxa_end_catch256; CHECK: br 2 # 2: down to label[[L2]]257; CHECK: end_try_table258; CHECK: end_block # label[[L0]]:259; CHECK: call __cxa_begin_catch260; CHECK: call __cxa_end_catch261; 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.dispatch266 267catch.dispatch: ; preds = %entry268 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch2269 270catch.start: ; preds = %catch.dispatch271 %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.dispatch2277 278catch.dispatch2: ; preds = %catch.start, %catch.dispatch279 %5 = catchswitch within none [label %catch.start3] unwind to caller280 281catch.start3: ; preds = %catch.dispatch2282 %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.cont7288 289try.cont7: ; preds = %entry, %invoke.cont1, %catch.start3290 ret void291 292invoke.cont1: ; preds = %catch.start293 catchret from %1 to label %try.cont7294}295 296 297; CHECK-LABEL: loop_within_catch:298; CHECK: block299; CHECK: block i32300; CHECK: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]301; CHECK: call foo302; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]303; CHECK: end_try_table304; CHECK: end_block # label[[L0]]:305; CHECK: call __cxa_begin_catch306; CHECK: loop # label[[L2:[0-9]+]]:307; CHECK: block308; CHECK: block309; CHECK: br_if 0 # 0: down to label[[L3:[0-9]+]]310; CHECK: block exnref311; CHECK: try_table (catch_all_ref 0) # 0: down to label[[L4:[0-9]+]]312; CHECK: call foo313; CHECK: br 3 # 3: down to label[[L5:[0-9]+]]314; CHECK: end_try_table315; CHECK: end_block # label[[L4]]:316; CHECK: block317; CHECK: block318; CHECK: try_table (catch_all 0) # 0: down to label[[L6:[0-9]+]]319; CHECK: call __cxa_end_catch320; CHECK: br 2 # 2: down to label[[L7:[0-9]+]]321; CHECK: end_try_table322; CHECK: end_block # label[[L6]]:323; CHECK: call _ZSt9terminatev324; CHECK: unreachable325; CHECK: end_block # label[[L7]]:326; CHECK: throw_ref327; CHECK: end_block # label[[L3]]:328; CHECK: call __cxa_end_catch329; 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_loop333; 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.dispatch338 339catch.dispatch: ; preds = %entry340 %0 = catchswitch within none [label %catch.start] unwind to caller341 342catch.start: ; preds = %catch.dispatch343 %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.cond348 349for.cond: ; preds = %for.inc, %catch.start350 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]351 %cmp = icmp slt i32 %i.0, 50352 br i1 %cmp, label %for.body, label %for.end353 354for.body: ; preds = %for.cond355 invoke void @foo() [ "funclet"(token %1) ]356 to label %for.inc unwind label %ehcleanup357 358for.inc: ; preds = %for.body359 %inc = add nsw i32 %i.0, 1360 br label %for.cond361 362for.end: ; preds = %for.cond363 call void @__cxa_end_catch() [ "funclet"(token %1) ]364 catchret from %1 to label %try.cont365 366try.cont: ; preds = %for.end, %entry367 ret void368 369ehcleanup: ; preds = %for.body370 %5 = cleanuppad within %1 []371 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]372 to label %invoke.cont2 unwind label %terminate373 374invoke.cont2: ; preds = %ehcleanup375 cleanupret from %5 unwind to caller376 377terminate: ; preds = %ehcleanup378 %6 = cleanuppad within %5 []379 call void @_ZSt9terminatev() [ "funclet"(token %6) ]380 unreachable381}382 383; Tests if block and try_table markers are correctly placed. Even if two384; predecessors of the EH pad are bb2 and bb3 and their nearest common dominator385; is bb1, the TRY_TABLE marker should be placed at bb0 because there's a branch386; from bb0 to bb2, and scopes cannot be interleaved.387; NOOPT-LABEL: block_try_table_markers:388; NOOPT: block389; NOOPT: block i32390; NOOPT: try_table (catch __cpp_exception 0)391; NOOPT: block392; NOOPT: block393; NOOPT: block394; NOOPT: end_block395; NOOPT: end_block396; NOOPT: call foo397; NOOPT: end_block398; NOOPT: call bar399; NOOPT: end_try_table400; NOOPT: end_block401; NOOPT: end_block402define void @block_try_table_markers() personality ptr @__gxx_wasm_personality_v0 {403bb0:404 br i1 undef, label %bb1, label %bb2405 406bb1: ; preds = %bb0407 br i1 undef, label %bb3, label %bb4408 409bb2: ; preds = %bb0410 br label %try.cont411 412bb3: ; preds = %bb1413 invoke void @foo()414 to label %try.cont unwind label %catch.dispatch415 416bb4: ; preds = %bb1417 invoke void @bar()418 to label %try.cont unwind label %catch.dispatch419 420catch.dispatch: ; preds = %bb4, %bb3421 %0 = catchswitch within none [label %catch.start] unwind to caller422 423catch.start: ; preds = %catch.dispatch424 %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.cont428 429try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2430 ret void431}432 433; Tests if try_table/end_try_table markers are placed correctly wrt434; loop/end_loop markers, when try_table and loop markers are in the same BB and435; end_try_table and end_loop are in another BB.436; CHECK-LABEL: loop_try_table_markers:437; CHECK: loop438; CHECK: block i32439; CHECK: try_table (catch __cpp_exception 0)440; CHECK: call foo441; CHECK: end_try_table442; CHECK: end_block443; CHECK: end_loop444define void @loop_try_table_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {445entry:446 store volatile i32 0, ptr %p447 br label %loop448 449loop: ; preds = %try.cont, %entry450 store volatile i32 1, ptr %p451 invoke void @foo()452 to label %try.cont unwind label %catch.dispatch453 454catch.dispatch: ; preds = %loop455 %0 = catchswitch within none [label %catch.start] unwind to caller456 457catch.start: ; preds = %catch.dispatch458 %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.cont462 463try.cont: ; preds = %catch.start, %loop464 br label %loop465}466 467; Some of test cases below are hand-tweaked by deleting some library calls to468; simplify tests and changing the order of basic blocks to cause unwind469; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum470; number of mismatches in several tests below.471 472; - Call unwind mismatch473; 'call bar''s original unwind destination was 'C0', but after control flow474; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by475; wrapping the call with a nested try_table-end_try_table that targets 'C0'.476; - Catch unwind mismatch477; If 'call foo' throws a foreign exception, it will not be caught by C1, and478; should be rethrown to the caller. But after control flow linearization, it479; will instead unwind to C0, an incorrect next EH pad. We wrap the whole480; try_table-end_try_table with another try_table-end_try_table that jumps to a481; trampoline BB, from which we rethrow the exception to the caller to fix this.482 483; NOSORT-LABEL: unwind_mismatches_0:484; NOSORT: block exnref485; NOSORT: block486; NOSORT: block i32487; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]488; NOSORT: block exnref489; NOSORT: block i32490; --- 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 foo494; --- 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 bar497; NOSORT: end_try_table498; --- nested try_table-end_try_table ends (call unwind mismatch)499; NOSORT: end_try_table500; NOSORT: end_try_table501; --- 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_ref505; NOSORT: end_try_table506; NOSORT: end_block # label[[L0]]:507; NOSORT: end_block508; NOSORT: return509; NOSORT: end_block # label[[L1]]:510; NOSORT: throw_ref511define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {512bb0:513 invoke void @foo()514 to label %bb1 unwind label %catch.dispatch0515 516bb1: ; preds = %bb0517 invoke void @bar()518 to label %try.cont unwind label %catch.dispatch1519 520catch.dispatch0: ; preds = %bb0521 %0 = catchswitch within none [label %catch.start0] unwind to caller522 523catch.start0: ; preds = %catch.dispatch0524 %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.cont528 529catch.dispatch1: ; preds = %bb1530 %4 = catchswitch within none [label %catch.start1] unwind to caller531 532catch.start1: ; preds = %catch.dispatch1533 %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.cont537 538try.cont: ; preds = %catch.start1, %catch.start0, %bb1539 ret void540}541 542; 'call bar' and 'call baz''s original unwind destination was the caller, but543; after control flow linearization, their unwind destination incorrectly becomes544; 'C0'. We fix this by wrapping the calls with a nested try_table-end_try_table545; 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 split548; during fixing unwind mismatches.549 550; NOSORT-LABEL: unwind_mismatches_1:551; NOSORT: block exnref552; NOSORT: block i32553; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]554; NOSORT: call foo555; --- 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 bar558; NOSORT: call baz559; NOSORT: local.set [[LOCAL:[0-9]+]]560; NOSORT: end_try_table561; --- nested try_table-end_try_table ends (call unwind mismatch)562; NOSORT: local.get [[LOCAL]]563; NOSORT: call nothrow564; NOSORT: return565; NOSORT: end_try_table566; NOSORT: end_block # label[[L0]]:567; NOSORT: return568; NOSORT: end_block # label[[L1]]:569; NOSORT: throw_ref570define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {571bb0:572 invoke void @foo()573 to label %bb1 unwind label %catch.dispatch0574 575bb1: ; preds = %bb0576 call void @bar()577 %call = call i32 @baz()578 call void @nothrow(i32 %call) #0579 ret void580 581catch.dispatch0: ; preds = %bb0582 %0 = catchswitch within none [label %catch.start0] unwind to caller583 584catch.start0: ; preds = %catch.dispatch0585 %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.cont589 590try.cont: ; preds = %catch.start0591 ret void592}593 594; The same as unwind_mismatches_0, but we have one more call 'call @foo' in bb1595; 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 exnref600; NOSORT: block601; NOSORT: block i32602; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]603; NOSORT: block exnref604; NOSORT: block i32605; --- 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 foo609; --- 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 foo612; NOSORT: end_try_table613; --- 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 bar617; NOSORT: end_try_table618; --- nested try_table-end_try_table ends (call unwind mismatch)619; NOSORT: end_try_table620; NOSORT: end_try_table621; --- 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_ref625; NOSORT: end_try_table626; NOSORT: end_block # label[[L0]]:627; NOSORT: end_block628; NOSORT: return629; NOSORT: end_block # label[[L1]]:630; NOSORT: throw_ref631define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {632bb0:633 invoke void @foo()634 to label %bb1 unwind label %catch.dispatch0635 636bb1: ; preds = %bb0637 call void @foo()638 invoke void @bar()639 to label %try.cont unwind label %catch.dispatch1640 641catch.dispatch0: ; preds = %bb0642 %0 = catchswitch within none [label %catch.start0] unwind to caller643 644catch.start0: ; preds = %catch.dispatch0645 %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.cont649 650catch.dispatch1: ; preds = %bb1651 %4 = catchswitch within none [label %catch.start1] unwind to caller652 653catch.start1: ; preds = %catch.dispatch1654 %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.cont658 659try.cont: ; preds = %catch.start1, %catch.start0, %bb1660 ret void661}662 663; Similar situation as @unwind_mismatches_1. Here 'call @qux''s original unwind664; destination was the caller, but after control flow linearization, their unwind665; destination incorrectly becomes 'C0' within the function. We fix this by666; wrapping the call with a nested try_table-end_try_table that jumps to a667; trampoline BB where rethrow the exception to the caller.668 669; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the670; nested 'try_table' should be placed before `i32.const 5', not between671; 'i32.const 5' and 'call @qux'.672 673; NOSORT-LABEL: unwind_mismatches_3:674; NOSORT: block exnref675; NOSORT: block i32676; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]677; NOSORT: call foo678; --- 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 5681; NOSORT: call qux682; NOSORT: end_try_table683; --- nested try_table-end_try_table ends (call unwind mismatch)684; NOSORT: return685; NOSORT: end_try_table686; NOSORT: end_block # label[[L0]]:687; NOSORT: return688; NOSORT: end_block # label[[L1]]:689; NOSORT: throw_ref690define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {691bb0:692 invoke void @foo()693 to label %bb1 unwind label %catch.dispatch0694 695bb1: ; preds = %bb0696 %0 = call i32 @qux(i32 5)697 ret i32 %0698 699catch.dispatch0: ; preds = %bb0700 %1 = catchswitch within none [label %catch.start0] unwind to caller701 702catch.start0: ; preds = %catch.dispatch0703 %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.cont707 708try.cont: ; preds = %catch.start0709 ret i32 0710}711 712; We have two call unwind unwind mismatches:713; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the714; CFG, when it is supposed to unwind to another EH pad.715; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the716; 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 the718; first catch because it is a non-C++ exception, it shouldn't unwind to the next719; catch, but it should unwind to the caller.720 721; NOSORT-LABEL: unwind_mismatches_4:722; NOSORT: block exnref723; NOSORT: block724; NOSORT: block i32725; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]726; NOSORT: block exnref727; NOSORT: block i32728; --- 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 foo732; --- 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 bar735; NOSORT: end_try_table736; --- nested try_table-end_try_table ends (call unwind mismatch)737; NOSORT: end_try_table738; NOSORT: end_try_table739; --- nested try_table-end_try_table ends (catch unwind mismatch)740; NOSORT: end_block # label[[L2]]:741; NOSORT: call __cxa_begin_catch742; --- 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_catch745; NOSORT: end_try_table746; --- nested try_table-end_try_table ends (call unwind mismatch)747; NOSORT: end_block # label[[L3]]:748; NOSORT: throw_ref749; NOSORT: end_try_table750; NOSORT: end_block # label[[L0]]:751; NOSORT: call __cxa_begin_catch752; NOSORT: call __cxa_end_catch753; NOSORT: end_block # label74:754; NOSORT: return755; NOSORT: end_block # label[[L1]]:756; NOSORT: throw_ref757define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {758bb0:759 invoke void @foo()760 to label %bb1 unwind label %catch.dispatch0761 762bb1: ; preds = %bb0763 invoke void @bar()764 to label %try.cont unwind label %catch.dispatch1765 766catch.dispatch0: ; preds = %bb0767 %0 = catchswitch within none [label %catch.start0] unwind to caller768 769catch.start0: ; preds = %catch.dispatch0770 %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.cont776 777catch.dispatch1: ; preds = %bb1778 %5 = catchswitch within none [label %catch.start1] unwind to caller779 780catch.start1: ; preds = %catch.dispatch1781 %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.cont787 788try.cont: ; preds = %catch.start1, %catch.start0, %bb1789 ret void790}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 created794; 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 exnref799; CHECK: block800; CHECK: block exnref801; 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 baz805; CHECK: end_try_table806; --- nested try_table-end_try_table ends (call unwind mismatch)807; CHECK: call quux808; CHECK: end_try_table809; CHECK: end_block # label[[L0]]:810; CHECK: throw_ref811; CHECK: end_block812; CHECK: unreachable813; CHECK: end_block # label[[L1]]:814; CHECK: throw_ref815define 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 %ehcleanup820 821ehcleanup: ; preds = %entry822 %0 = cleanuppad within none []823 cleanupret from %0 unwind to caller824 825invoke.cont: ; preds = %entry826 unreachable827}828 829; The structure is similar to unwind_mismatches_0, where the call to 'bar''s830; original unwind destination is catch.dispatch1 but after placing markers it831; unwinds to catch.dispatch0, which we fix. This additionally has a loop before832; the real unwind destination (catch.dispatch1). This makes sure the code833; generation works when the unwind destination has an end_loop before834; end_try_table before the mismatch fixing.835 836; NOSORT-LABEL: unwind_mismatches_with_loop:837; NOSORT: block exnref838; NOSORT: block i32839; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]840; NOSORT: block exnref841; NOSORT: block842; NOSORT: block i32843; --- 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 foo847; --- 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 bar850; NOSORT: end_try_table851; --- nested try_table-end_try_table ends (call unwind mismatch)852; NOSORT: end_try_table853; NOSORT: end_try_table854; --- nested try_table-end_try_table ends (catch unwind mismatch)855; NOSORT: end_block # label[[L2]]:856; NOSORT: end_block857; NOSORT: loop858; NOSORT: call foo859; NOSORT: end_loop860; NOSORT: unreachable861; NOSORT: end_block # label[[L3]]:862; NOSORT: throw_ref863; NOSORT: end_try_table864; NOSORT: end_block # label[[L0]]:865; NOSORT: return866; NOSORT: end_block # label[[L1]]:867; NOSORT: throw_ref868define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {869bb0:870 invoke void @foo()871 to label %bb1 unwind label %catch.dispatch0872 873bb1: ; preds = %bb0874 invoke void @bar()875 to label %bb2 unwind label %catch.dispatch1876 877catch.dispatch0: ; preds = %bb0878 %0 = catchswitch within none [label %catch.start0] unwind to caller879 880catch.start0: ; preds = %catch.dispatch0881 %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 %bb2885 886bb2:887 invoke void @foo()888 to label %bb3 unwind label %catch.dispatch1889 890bb3: ; preds = %bb14891 br label %bb2892 893catch.dispatch1: ; preds = %bb1894 %4 = catchswitch within none [label %catch.start1] unwind to caller895 896catch.start1: ; preds = %catch.dispatch1897 %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.cont901 902try.cont: ; preds = %catch.start1, %catch.start0, %bb1903 ret void904}905 906; Tests the case when TEE stackifies a register in RegStackify but it gets907; 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.dispatch0914 915bb1: ; preds = %bb0916 %t = add i32 %x, 4917 ; 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 dest919 ; registers, the first of which is stackified and the second is not.920 ; But when we introduce a nested try_table-end_try_table in921 ; fixCallUnwindMismatches in CFGStackify, we end up unstackifying the first922 ; dest register. In that case, we convert that tee into a copy.923 %addr = inttoptr i32 %t to ptr924 %load = load i32, ptr %addr925 %call = call i32 @baz()926 %add = add i32 %load, %call927 store i32 %add, ptr %addr928 ret void929; NOSORT-LOCALS: i32.add930; NOSORT-LOCALS-NOT: local.tee931; NOSORT-LOCALS-NEXT: local.set932 933catch.dispatch0: ; preds = %bb0934 %0 = catchswitch within none [label %catch.start0] unwind to caller935 936catch.start0: ; preds = %catch.dispatch0937 %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.cont941 942try.cont: ; preds = %catch.start0943 ret void944}945 946; In CFGSort, EH pads should be sorted as soon as it is available and947; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are948; in the middle of sorting another region that does not contain the EH pad. In949; this example, 'catch.start' should be sorted right after 'if.then' is sorted950; (before 'cont' is sorted) and there should not be any unwind destination951; mismatches in CFGStackify.952 953; NOOPT-LABEL: cfg_sort_order:954; NOOPT: block955; NOOPT: block956; NOOPT: block i32957; NOOPT: try_table (catch __cpp_exception 0)958; NOOPT: call foo959; NOOPT: end_try_table960; NOOPT: end_block961; NOOPT: call __cxa_begin_catch962; NOOPT: call __cxa_end_catch963; NOOPT: end_block964; NOOPT: call foo965; NOOPT: end_block966; NOOPT: return967define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {968entry:969 %tobool = icmp ne i32 %arg, 0970 br i1 %tobool, label %if.then, label %if.end971 972catch.dispatch: ; preds = %if.then973 %0 = catchswitch within none [label %catch.start] unwind to caller974 975catch.start: ; preds = %catch.dispatch976 %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.end982 983if.then: ; preds = %entry984 invoke void @foo()985 to label %cont unwind label %catch.dispatch986 987cont: ; preds = %if.then988 call void @foo()989 br label %if.end990 991if.end: ; preds = %cont, %catch.start, %entry992 ret void993}994 995; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into996; 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 in999; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or1000; unwinding destination mismatches in CFGStackify.1001 1002; NOSORT-LABEL: mem_intrinsics:1003; NOSORT: block exnref1004; NOSORT: try_table (catch_all_ref 0)1005; NOSORT: call foo1006; NOSORT: call memcpy1007; NOSORT: call memmove1008; NOSORT: call memset1009; NOSORT: return1010; NOSORT: end_try_table1011; NOSORT: end_block1012; NOSORT: throw_ref1013define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {1014entry:1015 %o = alloca %class.Object, align 11016 invoke void @foo()1017 to label %invoke.cont unwind label %ehcleanup1018 1019invoke.cont: ; preds = %entry1020 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 void1025 1026ehcleanup: ; preds = %entry1027 %0 = cleanuppad within none []1028 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]1029 cleanupret from %0 unwind to caller1030}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 to1034; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is1035; 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_i321040; CHECK: call fun1041define 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 %terminate1046 1047invoke.cont: ; preds = %entry1048 ret void1049 1050terminate: ; preds = %entry1051 %0 = cleanuppad within none []1052 call void @_ZSt9terminatev() [ "funclet"(token %0) ]1053 unreachable1054}1055 1056; This crashed on debug mode (= when NDEBUG is not defined) when the logic for1057; computing the innermost region was not correct, in which a loop region1058; 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 41062 br label %for.cond1063 1064for.cond: ; preds = %for.inc, %entry1065 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]1066 %cmp = icmp slt i32 %i.0, 91067 br i1 %cmp, label %for.body, label %for.end1068 1069for.body: ; preds = %for.cond1070 invoke void @quux(i32 %i.0)1071 to label %for.inc unwind label %catch.dispatch1072 1073catch.dispatch: ; preds = %for.body1074 %0 = catchswitch within none [label %catch.start] unwind to caller1075 1076catch.start: ; preds = %catch.dispatch1077 %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, %41082 br i1 %matches, label %catch, label %rethrow1083 1084catch: ; preds = %catch.start1085 %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 41089 invoke void @quux(i32 %7) [ "funclet"(token %1) ]1090 to label %invoke.cont2 unwind label %ehcleanup1091 1092invoke.cont2: ; preds = %catch1093 %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.inc1096 1097rethrow: ; preds = %catch.start1098 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]1099 unreachable1100 1101for.inc: ; preds = %invoke.cont2, %for.body1102 %inc = add nsw i32 %i.0, 11103 br label %for.cond1104 1105ehcleanup: ; preds = %catch1106 %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 %terminate71110 1111invoke.cont6: ; preds = %ehcleanup1112 cleanupret from %8 unwind to caller1113 1114for.end: ; preds = %for.cond1115 ret void1116 1117terminate7: ; preds = %ehcleanup1118 %9 = cleanuppad within %8 []1119 call void @_ZSt9terminatev() [ "funclet"(token %9) ]1120 unreachable1121}1122 1123; Here exceptions are semantically contained in a loop. 'ehcleanup' BB belongs1124; to the exception, but does not belong to the loop (because it does not have a1125; path back to the loop header), and is placed after the loop latch block1126; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed1127; correctly not right after 'invoke.cont' part but after 'ehcleanup' part.1128; NOSORT-LABEL: loop_contains_exception:1129; NOSORT: loop1130; NOSORT: try_table (catch __cpp_exception 0)1131; NOSORT: end_try_table1132; NOSORT: try_table (catch_all 0)1133; NOSORT: end_try_table1134; NOSORT: end_loop1135define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {1136entry:1137 br label %while.cond1138 1139while.cond: ; preds = %invoke.cont, %entry1140 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]1141 %tobool = icmp ne i32 %n.addr.0, 01142 br i1 %tobool, label %while.body, label %while.end1143 1144while.body: ; preds = %while.cond1145 %dec = add nsw i32 %n.addr.0, -11146 invoke void @foo()1147 to label %while.end unwind label %catch.dispatch1148 1149catch.dispatch: ; preds = %while.body1150 %0 = catchswitch within none [label %catch.start] unwind to caller1151 1152catch.start: ; preds = %catch.dispatch1153 %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 %ehcleanup1159 1160invoke.cont: ; preds = %catch.start1161 catchret from %1 to label %while.cond1162 1163ehcleanup: ; preds = %catch.start1164 %5 = cleanuppad within %1 []1165 call void @_ZSt9terminatev() [ "funclet"(token %5) ]1166 unreachable1167 1168while.end: ; preds = %while.body, %while.cond1169 ret void1170}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.dispatch1179 1180catch.dispatch: ; preds = %entry1181 %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup221182 1183catch.start: ; preds = %catch.dispatch1184 %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.dispatch21189 1190catch.dispatch2: ; preds = %catch.start1191 %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup1192 1193catch.start3: ; preds = %catch.dispatch21194 %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.cont1198 1199try.cont: ; preds = %catch.start31200 invoke void @foo() [ "funclet"(token %1) ]1201 to label %invoke.cont8 unwind label %ehcleanup1202 1203invoke.cont8: ; preds = %try.cont1204 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]1205 to label %unreachable unwind label %catch.dispatch111206 1207catch.dispatch11: ; preds = %invoke.cont81208 %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup1209 1210catch.start12: ; preds = %catch.dispatch111211 %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 unreachable1215 1216invoke.cont: ; preds = %entry1217 unreachable1218 1219ehcleanup: ; preds = %catch.dispatch11, %try.cont, %catch.dispatch21220 %12 = cleanuppad within %1 []1221 cleanupret from %12 unwind label %ehcleanup221222 1223ehcleanup22: ; preds = %ehcleanup, %catch.dispatch1224 %13 = cleanuppad within none []1225 cleanupret from %13 unwind to caller1226 1227unreachable: ; preds = %invoke.cont8, %catch.start1228 unreachable1229}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 catchswitches1242; are removed, EH pad catch.start2 is dominated by catch.start, but because1243; catch.start2 is the unwind destination of catch.start, it should not be1244; included in catch.start's exception. Also, after we take catch.start2's1245; exception out of catch.start's exception, we have to take out try.cont8 out of1246; 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) #01250 store i32 0, ptr %exception, align 161251 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #11252 to label %unreachable unwind label %catch.dispatch1253 1254catch.dispatch: ; preds = %entry1255 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch11256 1257catch.start: ; preds = %catch.dispatch1258 %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) #01262 %matches = icmp eq i32 %3, %41263 br i1 %matches, label %catch, label %rethrow1264 1265catch: ; preds = %catch.start1266 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]1267 %6 = load i32, ptr %5, align 41268 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]1269 catchret from %1 to label %catchret.dest1270 1271catchret.dest: ; preds = %catch1272 br label %try.cont1273 1274rethrow: ; preds = %catch.start1275 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]1276 to label %unreachable unwind label %catch.dispatch11277 1278catch.dispatch1: ; preds = %rethrow, %catch.dispatch1279 %7 = catchswitch within none [label %catch.start2] unwind to caller1280 1281catch.start2: ; preds = %catch.dispatch11282 %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) #01286 %matches3 = icmp eq i32 %10, %111287 br i1 %matches3, label %catch5, label %rethrow41288 1289catch5: ; preds = %catch.start21290 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]1291 %13 = load i32, ptr %12, align 41292 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]1293 catchret from %8 to label %catchret.dest71294 1295catchret.dest7: ; preds = %catch51296 br label %try.cont81297 1298rethrow4: ; preds = %catch.start21299 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]1300 unreachable1301 1302try.cont8: ; preds = %try.cont, %catchret.dest71303 ret void1304 1305try.cont: ; preds = %catchret.dest1306 br label %try.cont81307 1308unreachable: ; preds = %rethrow, %entry1309 unreachable1310}1311 1312; Test for WebAssemblyException grouping. This test is hand-modified to generate1313; 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 exception1317; (> 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's1322; exception first, before taking out catch.start12's exception out of1323; catch.start4's exception; otherwise we end up with an incorrect relationship1324; 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.dispatch1329 1330invoke.cont: ; preds = %entry1331 invoke void @foo()1332 to label %invoke.cont1 unwind label %catch.dispatch1333 1334invoke.cont1: ; preds = %invoke.cont1335 invoke void @foo()1336 to label %try.cont18 unwind label %catch.dispatch1337 1338catch.dispatch11: ; preds = %rethrow6, %catch.dispatch31339 %0 = catchswitch within none [label %catch.start12] unwind to caller1340 1341catch.start12: ; preds = %catch.dispatch111342 %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) #01346 %matches13 = icmp eq i32 %3, %41347 br i1 %matches13, label %catch15, label %rethrow141348 1349catch15: ; preds = %catch.start121350 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]1351 %6 = load i32, ptr %5, align 41352 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]1353 catchret from %1 to label %try.cont181354 1355rethrow14: ; preds = %catch.start121356 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]1357 unreachable1358 1359catch.dispatch3: ; preds = %rethrow, %catch.dispatch1360 %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch111361 1362catch.start4: ; preds = %catch.dispatch31363 %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) #01367 %matches5 = icmp eq i32 %10, %111368 br i1 %matches5, label %catch7, label %rethrow61369 1370catch7: ; preds = %catch.start41371 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]1372 %13 = load i32, ptr %12, align 41373 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]1374 catchret from %8 to label %try.cont181375 1376rethrow6: ; preds = %catch.start41377 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]1378 to label %unreachable unwind label %catch.dispatch111379 1380catch.dispatch: ; preds = %invoke.cont1, %invoke.cont, %entry1381 %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch31382 1383catch.start: ; preds = %catch.dispatch1384 %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) #01388 %matches = icmp eq i32 %17, %181389 br i1 %matches, label %catch, label %rethrow1390 1391catch: ; preds = %catch.start1392 %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]1393 %20 = load i32, ptr %19, align 41394 call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]1395 catchret from %15 to label %try.cont181396 1397rethrow: ; preds = %catch.start1398 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]1399 to label %unreachable unwind label %catch.dispatch31400 1401try.cont18: ; preds = %catch, %catch7, %catch15, %invoke.cont11402 ret void1403 1404unreachable: ; preds = %rethrow, %rethrow61405 unreachable1406}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) are1424; dominated by EH pad (a) (catch.start), even though they are not semantically1425; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s1426; exception is taken out of (a)'s. But because (c) is reachable from (b), we1427; 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) #11431 store i32 0, ptr %exception, align 161432 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #31433 to label %unreachable unwind label %catch.dispatch1434 1435catch.dispatch: ; preds = %entry1436 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch11437 1438catch.start: ; preds = %catch.dispatch1439 %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) #11443 %matches = icmp eq i32 %3, %41444 br i1 %matches, label %catch, label %rethrow1445 1446catch: ; preds = %catch.start1447 %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]1448 %6 = load i32, ptr %5, align 41449 call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]1450 catchret from %1 to label %try.cont81451 1452rethrow: ; preds = %catch.start1453 invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]1454 to label %unreachable unwind label %catch.dispatch11455 1456catch.dispatch1: ; preds = %rethrow, %catch.dispatch1457 %7 = catchswitch within none [label %catch.start2] unwind to caller1458 1459catch.start2: ; preds = %catch.dispatch11460 %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) #11464 %matches3 = icmp eq i32 %10, %111465 br i1 %matches3, label %catch5, label %rethrow41466 1467catch5: ; preds = %catch.start21468 %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]1469 %13 = load i32, ptr %12, align 41470 call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]1471 catchret from %8 to label %try.cont81472 1473rethrow4: ; preds = %catch.start21474 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]1475 unreachable1476 1477try.cont8: ; preds = %catch, %catch51478 invoke void @foo()1479 to label %try.cont16 unwind label %catch.dispatch91480 1481catch.dispatch9: ; preds = %try.cont81482 %14 = catchswitch within none [label %catch.start10] unwind to caller1483 1484catch.start10: ; preds = %catch.dispatch91485 %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) #11489 %matches11 = icmp eq i32 %17, %181490 br i1 %matches11, label %catch13, label %rethrow121491 1492catch13: ; preds = %catch.start101493 %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]1494 %20 = load i32, ptr %19, align 41495 call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]1496 catchret from %15 to label %try.cont161497 1498rethrow12: ; preds = %catch.start101499 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]1500 unreachable1501 1502try.cont16: ; preds = %try.cont8, %catch131503 ret void1504 1505unreachable: ; preds = %rethrow, %entry1506 unreachable1507}1508 1509; Check if the unwind destination mismatch stats are correct1510; NOSORT: 23 wasm-cfg-stackify - Number of call unwind mismatches found1511; NOSORT: 4 wasm-cfg-stackify - Number of catch unwind mismatches found1512 1513declare void @foo()1514declare void @bar()1515declare i32 @baz()1516declare i32 @qux(i32)1517declare void @quux(i32)1518declare void @fun(i32)1519; Function Attrs: nounwind1520declare void @nothrow(i32) #01521; Function Attrs: nounwind1522declare i32 @nothrow_i32() #01523 1524; Function Attrs: nounwind1525declare ptr @_ZN6ObjectD2Ev(ptr returned) #01526@_ZTI7MyClass = external constant { ptr, ptr }, align 41527; Function Attrs: nounwind1528declare ptr @_ZN7MyClassD2Ev(ptr returned) #01529; Function Attrs: nounwind1530declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #01531 1532declare i32 @__gxx_wasm_personality_v0(...)1533; Function Attrs: nounwind1534declare ptr @llvm.wasm.get.exception(token) #01535; Function Attrs: nounwind1536declare i32 @llvm.wasm.get.ehselector(token) #01537declare ptr @__cxa_allocate_exception(i32) #01538declare void @__cxa_throw(ptr, ptr, ptr)1539; Function Attrs: noreturn1540declare void @llvm.wasm.rethrow() #11541; Function Attrs: nounwind1542declare i32 @llvm.eh.typeid.for(ptr) #01543 1544declare ptr @__cxa_begin_catch(ptr)1545declare void @__cxa_end_catch()1546declare ptr @__cxa_get_exception_ptr(ptr)1547declare void @_ZSt9terminatev()1548; Function Attrs: nounwind1549declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #01550; Function Attrs: nounwind1551declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #01552; Function Attrs: nounwind1553declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #01554 1555attributes #0 = { nounwind }1556attributes #1 = { noreturn }1557