712 lines · plain
1; RUN: llc -mtriple=x86_64-pc-windows-coreclr -verify-machineinstrs < %s | FileCheck %s2 3declare void @ProcessCLRException()4declare void @f(i32)5declare void @g(ptr addrspace(1))6declare ptr addrspace(1) @llvm.eh.exceptionpointer.p1(token)7 8; Simplified IR for pseudo-C# like the following:9; void test1() {10; try {11; f(1);12; try {13; f(2);14; } catch (type1) {15; f(3);16; } catch (type2) {17; f(4);18; try {19; f(5);20; } fault {21; f(6);22; }23; }24; } finally {25; f(7);26; }27; f(8);28; }29;30; CHECK-LABEL: test1: # @test131; CHECK-NEXT: [[test1_begin:.*func_begin.*]]:32define void @test1() personality ptr @ProcessCLRException {33entry:34; CHECK: # %entry35; CHECK: leaq [[FPOffset:[0-9]+]](%rsp), %rbp36; CHECK: .seh_endprologue37; CHECK: movq %rsp, [[PSPSymOffset:[0-9]+]](%rsp)38; CHECK: [[test1_before_f1:.+]]:39; CHECK-NEXT: movl $1, %ecx40; CHECK-NEXT: callq f41; CHECK-NEXT: nop42; CHECK-NEXT: [[test1_after_f1:.+]]:43 invoke void @f(i32 1)44 to label %inner_try unwind label %finally45inner_try:46; CHECK: # %inner_try47; CHECK: [[test1_before_f2:.+]]:48; CHECK-NEXT: movl $2, %ecx49; CHECK-NEXT: callq f50; CHECK-NEXT: nop51; CHECK-NEXT: [[test1_after_f2:.+]]:52 invoke void @f(i32 2)53 to label %finally.clone unwind label %exn.dispatch54exn.dispatch:55 %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally56catch1:57 %catch.pad1 = catchpad within %catchswitch [i32 1]58; CHECK: .seh_proc [[test1_catch1:[^ ]+]]59; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]60; ^ all funclets use the same frame size61; CHECK: movq [[PSPSymOffset]](%rcx), %rcx62; ^ establisher frame pointer passed in rcx63; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)64; CHECK: leaq [[FPOffset]](%rcx), %rbp65; CHECK: .seh_endprologue66; CHECK: movq %rdx, %rcx67; ^ exception pointer passed in rdx68; CHECK-NEXT: callq g69 %exn1 = call ptr addrspace(1) @llvm.eh.exceptionpointer.p1(token %catch.pad1)70 call void @g(ptr addrspace(1) %exn1) [ "funclet"(token %catch.pad1) ]71; CHECK: [[test1_before_f3:.+]]:72; CHECK-NEXT: movl $3, %ecx73; CHECK-NEXT: callq f74; CHECK-NEXT: nop75; CHECK-NEXT: [[test1_after_f3:.+]]:76 invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ]77 to label %catch1.ret unwind label %finally78catch1.ret:79 catchret from %catch.pad1 to label %finally.clone80catch2:81 %catch.pad2 = catchpad within %catchswitch [i32 2]82; CHECK: .seh_proc [[test1_catch2:[^ ]+]]83; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]84; ^ all funclets use the same frame size85; CHECK: movq [[PSPSymOffset]](%rcx), %rcx86; ^ establisher frame pointer passed in rcx87; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)88; CHECK: leaq [[FPOffset]](%rcx), %rbp89; CHECK: .seh_endprologue90; CHECK: movq %rdx, %rcx91; ^ exception pointer passed in rdx92; CHECK-NEXT: callq g93 %exn2 = call ptr addrspace(1) @llvm.eh.exceptionpointer.p1(token %catch.pad2)94 call void @g(ptr addrspace(1) %exn2) [ "funclet"(token %catch.pad2) ]95; CHECK: [[test1_before_f4:.+]]:96; CHECK-NEXT: movl $4, %ecx97; CHECK-NEXT: callq f98; CHECK-NEXT: nop99; CHECK-NEXT: [[test1_after_f4:.+]]:100 invoke void @f(i32 4) [ "funclet"(token %catch.pad2) ]101 to label %try_in_catch unwind label %finally102try_in_catch:103; CHECK: # %try_in_catch104; CHECK: [[test1_before_f5:.+]]:105; CHECK-NEXT: movl $5, %ecx106; CHECK-NEXT: callq f107; CHECK-NEXT: nop108; CHECK-NEXT: [[test1_after_f5:.+]]:109 invoke void @f(i32 5) [ "funclet"(token %catch.pad2) ]110 to label %catch2.ret unwind label %fault111fault:112; CHECK: .seh_proc [[test1_fault:[^ ]+]]113 %fault.pad = cleanuppad within %catch.pad2 [i32 undef]114; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]115; ^ all funclets use the same frame size116; CHECK: movq [[PSPSymOffset]](%rcx), %rcx117; ^ establisher frame pointer passed in rcx118; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)119; CHECK: leaq [[FPOffset]](%rcx), %rbp120; CHECK: .seh_endprologue121; CHECK: [[test1_before_f6:.+]]:122; CHECK-NEXT: movl $6, %ecx123; CHECK-NEXT: callq f124; CHECK-NEXT: nop125; CHECK-NEXT: [[test1_after_f6:.+]]:126 invoke void @f(i32 6) [ "funclet"(token %fault.pad) ]127 to label %fault.ret unwind label %finally128fault.ret:129 cleanupret from %fault.pad unwind label %finally130catch2.ret:131 catchret from %catch.pad2 to label %finally.clone132finally.clone:133 call void @f(i32 7)134 br label %tail135finally:136; CHECK: .seh_proc [[test1_finally:[^ ]+]]137 %finally.pad = cleanuppad within none []138; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]139; ^ all funclets use the same frame size140; CHECK: movq [[PSPSymOffset]](%rcx), %rcx141; ^ establisher frame pointer passed in rcx142; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)143; CHECK: leaq [[FPOffset]](%rcx), %rbp144; CHECK: .seh_endprologue145; CHECK-NEXT: movl $7, %ecx146; CHECK-NEXT: callq f147 call void @f(i32 7) [ "funclet"(token %finally.pad) ]148 cleanupret from %finally.pad unwind to caller149tail:150 call void @f(i32 8)151 ret void152; CHECK: [[test1_end:.*func_end.*]]:153}154 155; Now check for EH table in xdata (following standard xdata)156; CHECK-LABEL: .section .xdata157; standard xdata comes here158; CHECK: .long 4{{$}}159; ^ number of funclets160; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]161; ^ offset from L_begin to start of 1st funclet162; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]163; ^ offset from L_begin to start of 2nd funclet164; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]165; ^ offset from L_begin to start of 3rd funclet166; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]167; ^ offset from L_begin to start of 4th funclet168; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]169; ^ offset from L_begin to end of last funclet170; CHECK-NEXT: .long 7171; ^ number of EH clauses172; Clause 1: call f(2) is guarded by catch1173; CHECK-NEXT: .long 0174; ^ flags (0 => catch handler)175; CHECK-NEXT: .long [[test1_before_f2]]-[[test1_begin]]+1176; ^ offset of start of clause177; CHECK-NEXT: .long [[test1_after_f2]]-[[test1_begin]]+1178; ^ offset of end of clause179; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]180; ^ offset of start of handler181; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]182; ^ offset of end of handler183; CHECK-NEXT: .long 1184; ^ type token of catch (from catchpad)185; Clause 2: call f(2) is also guarded by catch2186; CHECK-NEXT: .long 0187; ^ flags (0 => catch handler)188; CHECK-NEXT: .long [[test1_before_f2]]-[[test1_begin]]+1189; ^ offset of start of clause190; CHECK-NEXT: .long [[test1_after_f2]]-[[test1_begin]]+1191; ^ offset of end of clause192; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]193; ^ offset of start of handler194; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]195; ^ offset of end of handler196; CHECK-NEXT: .long 2197; ^ type token of catch (from catchpad)198; Clause 3: calls f(1) and f(2) are guarded by finally199; CHECK-NEXT: .long 2200; ^ flags (2 => finally handler)201; CHECK-NEXT: .long [[test1_before_f1]]-[[test1_begin]]+1202; ^ offset of start of clause203; CHECK-NEXT: .long [[test1_after_f2]]-[[test1_begin]]+1204; ^ offset of end of clause205; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]206; ^ offset of start of handler207; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]208; ^ offset of end of handler209; CHECK-NEXT: .long 0210; ^ type token slot (null for finally)211; Clause 4: call f(3) is guarded by finally212; This is a "duplicate" because the protected range (f(3))213; is in funclet catch1 but the finally's immediate parent214; is the main function, not that funclet.215; CHECK-NEXT: .long 10216; ^ flags (2 => finally handler | 8 => duplicate)217; CHECK-NEXT: .long [[test1_before_f3]]-[[test1_begin]]+1218; ^ offset of start of clause219; CHECK-NEXT: .long [[test1_after_f3]]-[[test1_begin]]+1220; ^ offset of end of clause221; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]222; ^ offset of start of handler223; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]224; ^ offset of end of handler225; CHECK-NEXT: .long 0226; ^ type token slot (null for finally)227; Clause 5: call f(5) is guarded by fault228; CHECK-NEXT: .long 4229; ^ flags (4 => fault handler)230; CHECK-NEXT: .long [[test1_before_f5]]-[[test1_begin]]+1231; ^ offset of start of clause232; CHECK-NEXT: .long [[test1_after_f5]]-[[test1_begin]]+1233; ^ offset of end of clause234; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]235; ^ offset of start of handler236; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]237; ^ offset of end of handler238; CHECK-NEXT: .long 0239; ^ type token slot (null for fault)240; Clause 6: calls f(4) and f(5) are guarded by finally241; This is a "duplicate" because the protected range (f(4)-f(5))242; is in funclet catch2 but the finally's immediate parent243; is the main function, not that funclet.244; CHECK-NEXT: .long 10245; ^ flags (2 => finally handler | 8 => duplicate)246; CHECK-NEXT: .long [[test1_before_f4]]-[[test1_begin]]+1247; ^ offset of start of clause248; CHECK-NEXT: .long [[test1_after_f5]]-[[test1_begin]]+1249; ^ offset of end of clause250; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]251; ^ offset of start of handler252; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]253; ^ offset of end of handler254; CHECK-NEXT: .long 0255; ^ type token slot (null for finally)256; Clause 7: call f(6) is guarded by finally257; This is a "duplicate" because the protected range (f(3))258; is in funclet catch1 but the finally's immediate parent259; is the main function, not that funclet.260; CHECK-NEXT: .long 10261; ^ flags (2 => finally handler | 8 => duplicate)262; CHECK-NEXT: .long [[test1_before_f6]]-[[test1_begin]]+1263; ^ offset of start of clause264; CHECK-NEXT: .long [[test1_after_f6]]-[[test1_begin]]+1265; ^ offset of end of clause266; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]267; ^ offset of start of handler268; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]269; ^ offset of end of handler270; CHECK-NEXT: .long 0271; ^ type token slot (null for finally)272 273; Test with a cleanup that has no cleanupret, and thus needs its unwind dest274; inferred from an inner catchswitch275;276; corresponds to C# along the lines of:277; void test2() {278; try {279; try {280; f(1);281; } fault {282; try {283; f(2);284; } catch(type1) {285; }286; __unreachable();287; }288; } catch(type2) {289; }290; }291;292define void @test2() personality ptr @ProcessCLRException {293entry:294 invoke void @f(i32 1)295 to label %exit unwind label %fault296fault:297 %fault.pad = cleanuppad within none [i32 undef]298 invoke void @f(i32 2) ["funclet"(token %fault.pad)]299 to label %unreachable unwind label %exn.dispatch.inner300exn.dispatch.inner:301 %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer302catch1:303 %catch.pad1 = catchpad within %catchswitch.inner [i32 1]304 catchret from %catch.pad1 to label %unreachable305exn.dispatch.outer:306 %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller307catch2:308 %catch.pad2 = catchpad within %catchswitch.outer [i32 2]309 catchret from %catch.pad2 to label %exit310exit:311 ret void312unreachable:313 unreachable314}315; CHECK-LABEL: test2: # @test2316; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:317; CHECK: .seh_endprologue318; CHECK: [[test2_before_f1:.+]]:319; CHECK-NEXT: movl $1, %ecx320; CHECK-NEXT: callq f321; CHECK-NEXT: nop322; CHECK-NEXT: [[test2_after_f1:.+]]:323; CHECK: .seh_proc [[test2_catch1:[^ ]+]]324; CHECK: .seh_proc [[test2_catch2:[^ ]+]]325; CHECK: .seh_proc [[test2_fault:[^ ]+]]326; CHECK: .seh_endprologue327; CHECK: [[test2_before_f2:.+]]:328; CHECK-NEXT: movl $2, %ecx329; CHECK-NEXT: callq f330; CHECK-NEXT: nop331; CHECK-NEXT: [[test2_after_f2:.+]]:332; CHECK: int3333; CHECK: [[test2_end:.*func_end.*]]:334 335 336; Now check for EH table in xdata (following standard xdata)337; CHECK-LABEL: .section .xdata338; standard xdata comes here339; CHECK: .long 3{{$}}340; ^ number of funclets341; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]342; ^ offset from L_begin to start of 2nd funclet343; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]344; ^ offset from L_begin to start of 3rd funclet345; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]346; ^ offset from L_begin to start of 1st funclet347; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]348; ^ offset from L_begin to end of last funclet349; CHECK-NEXT: .long 4350; ^ number of EH clauses351; Clause 1: call f(1) is guarded by fault352; CHECK-NEXT: .long 4353; ^ flags (4 => fault handler)354; CHECK-NEXT: .long [[test2_before_f1]]-[[test2_begin]]+1355; ^ offset of start of clause356; CHECK-NEXT: .long [[test2_after_f1]]-[[test2_begin]]+1357; ^ offset of end of clause358; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]359; ^ offset of start of handler360; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]361; ^ offset of end of handler362; CHECK-NEXT: .long 0363; ^ type token slot (null for fault)364; Clause 2: call f(1) is also guarded by catch2365; CHECK-NEXT: .long 0366; ^ flags (0 => catch handler)367; CHECK-NEXT: .long [[test2_before_f1]]-[[test2_begin]]+1368; ^ offset of start of clause369; CHECK-NEXT: .long [[test2_after_f1]]-[[test2_begin]]+1370; ^ offset of end of clause371; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]372; ^ offset of start of handler373; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]374; ^ offset of end of handler375; CHECK-NEXT: .long 2376; ^ type token of catch (from catchpad)377; Clause 3: calls f(2) is guarded by catch1378; CHECK-NEXT: .long 0379; ^ flags (0 => catch handler)380; CHECK-NEXT: .long [[test2_before_f2]]-[[test2_begin]]+1381; ^ offset of start of clause382; CHECK-NEXT: .long [[test2_after_f2]]-[[test2_begin]]+1383; ^ offset of end of clause384; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]385; ^ offset of start of handler386; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]387; ^ offset of end of handler388; CHECK-NEXT: .long 1389; ^ type token of catch (from catchpad)390; Clause 4: call f(2) is also guarded by catch2391; This is a "duplicate" because the protected range (f(2))392; is in funclet fault but catch2's immediate parent393; is the main function, not that funclet.394; CHECK-NEXT: .long 8395; ^ flags (0 => catch handler | 8 => duplicate)396; CHECK-NEXT: .long [[test2_before_f2]]-[[test2_begin]]+1397; ^ offset of start of clause398; CHECK-NEXT: .long [[test2_after_f2]]-[[test2_begin]]+1399; ^ offset of end of clause400; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]401; ^ offset of start of handler402; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]403; ^ offset of end of handler404; CHECK-NEXT: .long 2405; ^ type token of catch (from catchpad)406 407; Test with several cleanups that need to infer their unwind dests from each408; other, the inner one needing to make the inference from an invoke, ignoring409; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some410; internal invokes/catchswitches411;412; Corresponds to something like:413; void test3() {414; try {415; f(1);416; } fault { // fault1417; try {418; try {419; f(2);420; __unreachable();421; } fault { // fault2422; try {423; f(3);424; } fault { // fault3425; try {426; f(4);427; } fault { // fault4428; f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but429; didn't bother to specify nounwind)430; try {431; try {432; f(6);433; } catch(type 1) {434; goto __unreachable;435; }436; } catch (type 2) { // marked "unwinds to caller" because we allow437; // that if the unwind won't be taken (see438; // SimplifyUnreachable & RemoveUnwindEdge)439; goto _unreachable;440; }441; f(7);442; __unreachable();443; }444; }445; }446; } fault { // fault 5447; }448; }449; }450;451; CHECK-LABEL: test3: # @test3452; CHECK-NEXT: [[test3_begin:.*func_begin.*]]:453define void @test3() personality ptr @ProcessCLRException {454entry:455; CHECK: .seh_endprologue456; CHECK: [[test3_before_f1:.+]]:457; CHECK-NEXT: movl $1, %ecx458; CHECK-NEXT: callq f459; CHECK-NEXT: nop460; CHECK-NEXT: [[test3_after_f1:.+]]:461 invoke void @f(i32 1)462 to label %exit unwind label %fault1463fault1:464 ; check lines below since this gets reordered to end-of-func465 %fault.pad1 = cleanuppad within none [i32 undef]466 invoke void @f(i32 2) ["funclet"(token %fault.pad1)]467 to label %unreachable unwind label %fault2468fault2:469 ; check lines below since this gets reordered to end-of-func470 %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef]471 invoke void @f(i32 3) ["funclet"(token %fault.pad2)]472 to label %unreachable unwind label %fault3473fault3:474 ; check lines below since this gets reordered to end-of-func475 %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef]476 invoke void @f(i32 4) ["funclet"(token %fault.pad3)]477 to label %unreachable unwind label %fault4478fault4:479; CHECK: .seh_proc [[test3_fault4:[^ ]+]]480 %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef]481; CHECK: .seh_endprologue482 call void @f(i32 5) ["funclet"(token %fault.pad4)]483; CHECK: [[test3_before_f6:.+]]:484; CHECK-NEXT: movl $6, %ecx485; CHECK-NEXT: callq f486; CHECK-NEXT: nop487; CHECK-NEXT: [[test3_after_f6:.+]]:488 invoke void @f(i32 6) ["funclet"(token %fault.pad4)]489 to label %fault4.cont unwind label %exn.dispatch1490fault4.cont:491; CHECK: # %fault4.cont492; CHECK: [[test3_before_f7:.+]]:493; CHECK-NEXT: movl $7, %ecx494; CHECK-NEXT: callq f495; CHECK-NEXT: nop496; CHECK-NEXT: [[test3_after_f7:.+]]:497 invoke void @f(i32 7) ["funclet"(token %fault.pad4)]498 to label %unreachable unwind label %fault5499exn.dispatch1:500 %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2501catch1:502 %catch.pad1 = catchpad within %catchswitch1 [i32 1]503; CHECK: .seh_proc [[test3_catch1:[^ ]+]]504 catchret from %catch.pad1 to label %unreachable505exn.dispatch2:506 %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller507catch2:508 %catch.pad2 = catchpad within %catchswitch2 [i32 2]509; CHECK: .seh_proc [[test3_catch2:[^ ]+]]510 catchret from %catch.pad2 to label %unreachable511fault5:512; CHECK: .seh_proc [[test3_fault5:[^ ]+]]513 %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef]514; CHECK: .seh_endprologue515cleanupret from %fault.pad5 unwind to caller516exit:517 ret void518unreachable:519 unreachable520; CHECK: .seh_proc [[test3_fault3:[^ ]+]]521; CHECK: # %fault3522; CHECK: .seh_endprologue523; CHECK: [[test3_before_f4:.+]]:524; CHECK-NEXT: movl $4, %ecx525; CHECK-NEXT: callq f526; CHECK-NEXT: nop527; CHECK-NEXT: [[test3_after_f4:.+]]:528; CHECK: int3529; CHECK: .seh_proc [[test3_fault2:[^ ]+]]530; CHECK: # %fault2531; CHECK: .seh_endprologue532; CHECK: [[test3_before_f3:.+]]:533; CHECK-NEXT: movl $3, %ecx534; CHECK-NEXT: callq f535; CHECK-NEXT: nop536; CHECK-NEXT: [[test3_after_f3:.+]]:537; CHECK: int3538; CHECK: .seh_proc [[test3_fault1:[^ ]+]]539; CHECK: # %fault1540; CHECK: .seh_endprologue541; CHECK: [[test3_before_f2:.+]]:542; CHECK-NEXT: movl $2, %ecx543; CHECK-NEXT: callq f544; CHECK-NEXT: nop545; CHECK-NEXT: [[test3_after_f2:.+]]:546; CHECK: int3547; CHECK: [[test3_end:.*func_end.*]]:548}549 550; Now check for EH table in xdata (following standard xdata)551; CHECK-LABEL: .section .xdata552; standard xdata comes here553; CHECK: .long 7{{$}}554; ^ number of funclets555; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]556; ^ offset from L_begin to start of 1st funclet557; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]558; ^ offset from L_begin to start of 2nd funclet559; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]560; ^ offset from L_begin to start of 3rd funclet561; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]562; ^ offset from L_begin to start of 4th funclet563; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]564; ^ offset from L_begin to start of 5th funclet565; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]566; ^ offset from L_begin to start of 6th funclet567; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]568; ^ offset from L_begin to start of 7th funclet569; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]570; ^ offset from L_begin to end of last funclet571; CHECK-NEXT: .long 10572; ^ number of EH clauses573; Clause 1: call f(1) is guarded by fault1574; CHECK-NEXT: .long 4575; ^ flags (4 => fault handler)576; CHECK-NEXT: .long [[test3_before_f1]]-[[test3_begin]]+1577; ^ offset of start of clause578; CHECK-NEXT: .long [[test3_after_f1]]-[[test3_begin]]+1579; ^ offset of end of clause580; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]581; ^ offset of start of handler582; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]583; ^ offset of end of handler584; CHECK-NEXT: .long 0585; ^ type token slot (null for fault)586; Clause 3: call f(6) is guarded by catch1587; CHECK-NEXT: .long 0588; ^ flags (0 => catch handler)589; CHECK-NEXT: .long [[test3_before_f6]]-[[test3_begin]]+1590; ^ offset of start of clause591; CHECK-NEXT: .long [[test3_after_f6]]-[[test3_begin]]+1592; ^ offset of end of clause593; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]594; ^ offset of start of handler595; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]596; ^ offset of end of handler597; CHECK-NEXT: .long 1598; ^ type token of catch (from catchpad)599; Clause 3: call f(6) is also guarded by catch2600; CHECK-NEXT: .long 0601; ^ flags (0 => catch handler)602; CHECK-NEXT: .long [[test3_before_f6]]-[[test3_begin]]+1603; ^ offset of start of clause604; CHECK-NEXT: .long [[test3_after_f6]]-[[test3_begin]]+1605; ^ offset of end of clause606; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]607; ^ offset of start of handler608; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]609; ^ offset of end of handler610; CHECK-NEXT: .long 2611; ^ type token of catch (from catchpad)612; Clause 4: call f(7) is guarded by fault5613; This is a "duplicate" because the protected range (f(6)-f(7))614; is in funclet fault4 but fault5's immediate parent615; is fault1, not that funclet.616; CHECK-NEXT: .long 12617; ^ flags (4 => fault handler | 8 => duplicate)618; CHECK-NEXT: .long [[test3_before_f7]]-[[test3_begin]]+1619; ^ offset of start of clause620; CHECK-NEXT: .long [[test3_after_f7]]-[[test3_begin]]+1621; ^ offset of end of clause622; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]623; ^ offset of start of handler624; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]625; ^ offset of end of handler626; CHECK-NEXT: .long 0627; ^ type token slot (null for fault)628; Clause 5: call f(4) is guarded by fault4629; CHECK-NEXT: .long 4630; ^ flags (4 => fault handler)631; CHECK-NEXT: .long [[test3_before_f4]]-[[test3_begin]]+1632; ^ offset of start of clause633; CHECK-NEXT: .long [[test3_after_f4]]-[[test3_begin]]+1634; ^ offset of end of clause635; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]636; ^ offset of start of handler637; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]638; ^ offset of end of handler639; CHECK-NEXT: .long 0640; ^ type token slot (null for fault)641; Clause 6: call f(4) is also guarded by fault5642; This is a "duplicate" because the protected range (f(4))643; is in funclet fault3 but fault5's immediate parent644; is fault1, not that funclet.645; CHECK-NEXT: .long 12646; ^ flags (4 => fault handler)647; CHECK-NEXT: .long [[test3_before_f4]]-[[test3_begin]]+1648; ^ offset of start of clause649; CHECK-NEXT: .long [[test3_after_f4]]-[[test3_begin]]+1650; ^ offset of end of clause651; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]652; ^ offset of start of handler653; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]654; ^ offset of end of handler655; CHECK-NEXT: .long 0656; ^ type token slot (null for fault)657; Clause 7: call f(3) is guarded by fault3658; CHECK-NEXT: .long 4659; ^ flags (4 => fault handler)660; CHECK-NEXT: .long [[test3_before_f3]]-[[test3_begin]]+1661; ^ offset of start of clause662; CHECK-NEXT: .long [[test3_after_f3]]-[[test3_begin]]+1663; ^ offset of end of clause664; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]665; ^ offset of start of handler666; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]667; ^ offset of end of handler668; CHECK-NEXT: .long 0669; ^ type token slot (null for fault)670; Clause 8: call f(3) is guarded by fault5671; This is a "duplicate" because the protected range (f(3))672; is in funclet fault2 but fault5's immediate parent673; is fault1, not that funclet.674; CHECK-NEXT: .long 12675; ^ flags (4 => fault handler | 8 => duplicate)676; CHECK-NEXT: .long [[test3_before_f3]]-[[test3_begin]]+1677; ^ offset of start of clause678; CHECK-NEXT: .long [[test3_after_f3]]-[[test3_begin]]+1679; ^ offset of end of clause680; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]681; ^ offset of start of handler682; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]683; ^ offset of end of handler684; CHECK-NEXT: .long 0685; ^ type token slot (null for fault)686; Clause 9: call f(2) is guarded by fault2687; CHECK-NEXT: .long 4688; ^ flags (4 => fault handler)689; CHECK-NEXT: .long [[test3_before_f2]]-[[test3_begin]]+1690; ^ offset of start of clause691; CHECK-NEXT: .long [[test3_after_f2]]-[[test3_begin]]+1692; ^ offset of end of clause693; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]694; ^ offset of start of handler695; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]696; ^ offset of end of handler697; CHECK-NEXT: .long 0698; ^ type token slot (null for fault)699; Clause 10: call f(2) is guarded by fault5700; CHECK-NEXT: .long 4701; ^ flags (4 => fault handler)702; CHECK-NEXT: .long [[test3_before_f2]]-[[test3_begin]]+1703; ^ offset of start of clause704; CHECK-NEXT: .long [[test3_after_f2]]-[[test3_begin]]+1705; ^ offset of end of clause706; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]707; ^ offset of start of handler708; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]709; ^ offset of end of handler710; CHECK-NEXT: .long 0711; ^ type token slot (null for fault)712