1; RUN: llc -mtriple=x86_64-pc-windows-coreclr -verify-machineinstrs < %s | FileCheck %s 2 3declare void @ProcessCLRException() 4declare void @f(i32) 5declare void @g(i8 addrspace(1)*) 6declare i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(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: # @test1 31; CHECK-NEXT: [[test1_begin:.*func_begin.*]]: 32define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { 33entry: 34; CHECK: # %entry 35; CHECK: leaq [[FPOffset:[0-9]+]](%rsp), %rbp 36; CHECK: .seh_endprologue 37; CHECK: movq %rsp, [[PSPSymOffset:[0-9]+]](%rsp) 38; CHECK: [[test1_before_f1:.+]]: 39; CHECK-NEXT: movl $1, %ecx 40; CHECK-NEXT: callq f 41; CHECK-NEXT: [[test1_after_f1:.+]]: 42 invoke void @f(i32 1) 43 to label %inner_try unwind label %finally 44inner_try: 45; CHECK: # %inner_try 46; CHECK: [[test1_before_f2:.+]]: 47; CHECK-NEXT: movl $2, %ecx 48; CHECK-NEXT: callq f 49; CHECK-NEXT: [[test1_after_f2:.+]]: 50 invoke void @f(i32 2) 51 to label %finally.clone unwind label %exn.dispatch 52exn.dispatch: 53 %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally 54catch1: 55 %catch.pad1 = catchpad within %catchswitch [i32 1] 56; CHECK: .seh_proc [[test1_catch1:[^ ]+]] 57; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] 58; ^ all funclets use the same frame size 59; CHECK: movq [[PSPSymOffset]](%rcx), %rcx 60; ^ establisher frame pointer passed in rcx 61; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) 62; CHECK: leaq [[FPOffset]](%rcx), %rbp 63; CHECK: .seh_endprologue 64; CHECK: movq %rdx, %rcx 65; ^ exception pointer passed in rdx 66; CHECK-NEXT: callq g 67 %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad1) 68 call void @g(i8 addrspace(1)* %exn1) [ "funclet"(token %catch.pad1) ] 69; CHECK: [[test1_before_f3:.+]]: 70; CHECK-NEXT: movl $3, %ecx 71; CHECK-NEXT: callq f 72; CHECK-NEXT: [[test1_after_f3:.+]]: 73 invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ] 74 to label %catch1.ret unwind label %finally 75catch1.ret: 76 catchret from %catch.pad1 to label %finally.clone 77catch2: 78 %catch.pad2 = catchpad within %catchswitch [i32 2] 79; CHECK: .seh_proc [[test1_catch2:[^ ]+]] 80; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] 81; ^ all funclets use the same frame size 82; CHECK: movq [[PSPSymOffset]](%rcx), %rcx 83; ^ establisher frame pointer passed in rcx 84; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) 85; CHECK: leaq [[FPOffset]](%rcx), %rbp 86; CHECK: .seh_endprologue 87; CHECK: movq %rdx, %rcx 88; ^ exception pointer passed in rdx 89; CHECK-NEXT: callq g 90 %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad2) 91 call void @g(i8 addrspace(1)* %exn2) [ "funclet"(token %catch.pad2) ] 92; CHECK: [[test1_before_f4:.+]]: 93; CHECK-NEXT: movl $4, %ecx 94; CHECK-NEXT: callq f 95; CHECK-NEXT: [[test1_after_f4:.+]]: 96 invoke void @f(i32 4) [ "funclet"(token %catch.pad2) ] 97 to label %try_in_catch unwind label %finally 98try_in_catch: 99; CHECK: # %try_in_catch 100; CHECK: [[test1_before_f5:.+]]: 101; CHECK-NEXT: movl $5, %ecx 102; CHECK-NEXT: callq f 103; CHECK-NEXT: [[test1_after_f5:.+]]: 104 invoke void @f(i32 5) [ "funclet"(token %catch.pad2) ] 105 to label %catch2.ret unwind label %fault 106fault: 107; CHECK: .seh_proc [[test1_fault:[^ ]+]] 108 %fault.pad = cleanuppad within %catch.pad2 [i32 undef] 109; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] 110; ^ all funclets use the same frame size 111; CHECK: movq [[PSPSymOffset]](%rcx), %rcx 112; ^ establisher frame pointer passed in rcx 113; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) 114; CHECK: leaq [[FPOffset]](%rcx), %rbp 115; CHECK: .seh_endprologue 116; CHECK: [[test1_before_f6:.+]]: 117; CHECK-NEXT: movl $6, %ecx 118; CHECK-NEXT: callq f 119; CHECK-NEXT: [[test1_after_f6:.+]]: 120 invoke void @f(i32 6) [ "funclet"(token %fault.pad) ] 121 to label %fault.ret unwind label %finally 122fault.ret: 123 cleanupret from %fault.pad unwind label %finally 124catch2.ret: 125 catchret from %catch.pad2 to label %finally.clone 126finally.clone: 127 call void @f(i32 7) 128 br label %tail 129finally: 130; CHECK: .seh_proc [[test1_finally:[^ ]+]] 131 %finally.pad = cleanuppad within none [] 132; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] 133; ^ all funclets use the same frame size 134; CHECK: movq [[PSPSymOffset]](%rcx), %rcx 135; ^ establisher frame pointer passed in rcx 136; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) 137; CHECK: leaq [[FPOffset]](%rcx), %rbp 138; CHECK: .seh_endprologue 139; CHECK-NEXT: movl $7, %ecx 140; CHECK-NEXT: callq f 141 call void @f(i32 7) [ "funclet"(token %finally.pad) ] 142 cleanupret from %finally.pad unwind to caller 143tail: 144 call void @f(i32 8) 145 ret void 146; CHECK: [[test1_end:.*func_end.*]]: 147} 148 149; Now check for EH table in xdata (following standard xdata) 150; CHECK-LABEL: .section .xdata 151; standard xdata comes here 152; CHECK: .long 4{{$}} 153; ^ number of funclets 154; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]] 155; ^ offset from L_begin to start of 1st funclet 156; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]] 157; ^ offset from L_begin to start of 2nd funclet 158; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]] 159; ^ offset from L_begin to start of 3rd funclet 160; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]] 161; ^ offset from L_begin to start of 4th funclet 162; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]] 163; ^ offset from L_begin to end of last funclet 164; CHECK-NEXT: .long 7 165; ^ number of EH clauses 166; Clause 1: call f(2) is guarded by catch1 167; CHECK-NEXT: .long 0 168; ^ flags (0 => catch handler) 169; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1 170; ^ offset of start of clause 171; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1 172; ^ offset of end of clause 173; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]] 174; ^ offset of start of handler 175; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]] 176; ^ offset of end of handler 177; CHECK-NEXT: .long 1 178; ^ type token of catch (from catchpad) 179; Clause 2: call f(2) is also guarded by catch2 180; CHECK-NEXT: .long 0 181; ^ flags (0 => catch handler) 182; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1 183; ^ offset of start of clause 184; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1 185; ^ offset of end of clause 186; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]] 187; ^ offset of start of handler 188; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]] 189; ^ offset of end of handler 190; CHECK-NEXT: .long 2 191; ^ type token of catch (from catchpad) 192; Clause 3: calls f(1) and f(2) are guarded by finally 193; CHECK-NEXT: .long 2 194; ^ flags (2 => finally handler) 195; CHECK-NEXT: .long ([[test1_before_f1]]-[[test1_begin]])+1 196; ^ offset of start of clause 197; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1 198; ^ offset of end of clause 199; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]] 200; ^ offset of start of handler 201; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]] 202; ^ offset of end of handler 203; CHECK-NEXT: .long 0 204; ^ type token slot (null for finally) 205; Clause 4: call f(3) is guarded by finally 206; This is a "duplicate" because the protected range (f(3)) 207; is in funclet catch1 but the finally's immediate parent 208; is the main function, not that funclet. 209; CHECK-NEXT: .long 10 210; ^ flags (2 => finally handler | 8 => duplicate) 211; CHECK-NEXT: .long ([[test1_before_f3]]-[[test1_begin]])+1 212; ^ offset of start of clause 213; CHECK-NEXT: .long ([[test1_after_f3]]-[[test1_begin]])+1 214; ^ offset of end of clause 215; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]] 216; ^ offset of start of handler 217; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]] 218; ^ offset of end of handler 219; CHECK-NEXT: .long 0 220; ^ type token slot (null for finally) 221; Clause 5: call f(5) is guarded by fault 222; CHECK-NEXT: .long 4 223; ^ flags (4 => fault handler) 224; CHECK-NEXT: .long ([[test1_before_f5]]-[[test1_begin]])+1 225; ^ offset of start of clause 226; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1 227; ^ offset of end of clause 228; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]] 229; ^ offset of start of handler 230; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]] 231; ^ offset of end of handler 232; CHECK-NEXT: .long 0 233; ^ type token slot (null for fault) 234; Clause 6: calls f(4) and f(5) are guarded by finally 235; This is a "duplicate" because the protected range (f(4)-f(5)) 236; is in funclet catch2 but the finally's immediate parent 237; is the main function, not that funclet. 238; CHECK-NEXT: .long 10 239; ^ flags (2 => finally handler | 8 => duplicate) 240; CHECK-NEXT: .long ([[test1_before_f4]]-[[test1_begin]])+1 241; ^ offset of start of clause 242; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1 243; ^ offset of end of clause 244; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]] 245; ^ offset of start of handler 246; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]] 247; ^ offset of end of handler 248; CHECK-NEXT: .long 0 249; ^ type token slot (null for finally) 250; Clause 7: call f(6) is guarded by finally 251; This is a "duplicate" because the protected range (f(3)) 252; is in funclet catch1 but the finally's immediate parent 253; is the main function, not that funclet. 254; CHECK-NEXT: .long 10 255; ^ flags (2 => finally handler | 8 => duplicate) 256; CHECK-NEXT: .long ([[test1_before_f6]]-[[test1_begin]])+1 257; ^ offset of start of clause 258; CHECK-NEXT: .long ([[test1_after_f6]]-[[test1_begin]])+1 259; ^ offset of end of clause 260; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]] 261; ^ offset of start of handler 262; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]] 263; ^ offset of end of handler 264; CHECK-NEXT: .long 0 265; ^ type token slot (null for finally) 266 267; Test with a cleanup that has no cleanupret, and thus needs its unwind dest 268; inferred from an inner catchswitch 269; 270; corresponds to C# along the lines of: 271; void test2() { 272; try { 273; try { 274; f(1); 275; } fault { 276; try { 277; f(2); 278; } catch(type1) { 279; } 280; __unreachable(); 281; } 282; } catch(type2) { 283; } 284; } 285; 286define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { 287entry: 288 invoke void @f(i32 1) 289 to label %exit unwind label %fault 290fault: 291 %fault.pad = cleanuppad within none [i32 undef] 292 invoke void @f(i32 2) ["funclet"(token %fault.pad)] 293 to label %unreachable unwind label %exn.dispatch.inner 294exn.dispatch.inner: 295 %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer 296catch1: 297 %catch.pad1 = catchpad within %catchswitch.inner [i32 1] 298 catchret from %catch.pad1 to label %unreachable 299exn.dispatch.outer: 300 %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller 301catch2: 302 %catch.pad2 = catchpad within %catchswitch.outer [i32 2] 303 catchret from %catch.pad2 to label %exit 304exit: 305 ret void 306unreachable: 307 unreachable 308} 309; CHECK-LABEL: test2: # @test2 310; CHECK-NEXT: [[test2_begin:.*func_begin.*]]: 311; CHECK: .seh_endprologue 312; CHECK: [[test2_before_f1:.+]]: 313; CHECK-NEXT: movl $1, %ecx 314; CHECK-NEXT: callq f 315; CHECK-NEXT: [[test2_after_f1:.+]]: 316; CHECK: .seh_proc [[test2_catch1:[^ ]+]] 317; CHECK: .seh_proc [[test2_catch2:[^ ]+]] 318; CHECK: .seh_proc [[test2_fault:[^ ]+]] 319; CHECK: .seh_endprologue 320; CHECK: [[test2_before_f2:.+]]: 321; CHECK-NEXT: movl $2, %ecx 322; CHECK-NEXT: callq f 323; CHECK-NEXT: [[test2_after_f2:.+]]: 324; CHECK: int3 325; CHECK: [[test2_end:.*func_end.*]]: 326 327 328; Now check for EH table in xdata (following standard xdata) 329; CHECK-LABEL: .section .xdata 330; standard xdata comes here 331; CHECK: .long 3{{$}} 332; ^ number of funclets 333; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]] 334; ^ offset from L_begin to start of 2nd funclet 335; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 336; ^ offset from L_begin to start of 3rd funclet 337; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]] 338; ^ offset from L_begin to start of 1st funclet 339; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]] 340; ^ offset from L_begin to end of last funclet 341; CHECK-NEXT: .long 4 342; ^ number of EH clauses 343; Clause 1: call f(1) is guarded by fault 344; CHECK-NEXT: .long 4 345; ^ flags (4 => fault handler) 346; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1 347; ^ offset of start of clause 348; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1 349; ^ offset of end of clause 350; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]] 351; ^ offset of start of handler 352; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]] 353; ^ offset of end of handler 354; CHECK-NEXT: .long 0 355; ^ type token slot (null for fault) 356; Clause 2: call f(1) is also guarded by catch2 357; CHECK-NEXT: .long 0 358; ^ flags (0 => catch handler) 359; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1 360; ^ offset of start of clause 361; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1 362; ^ offset of end of clause 363; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 364; ^ offset of start of handler 365; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]] 366; ^ offset of end of handler 367; CHECK-NEXT: .long 2 368; ^ type token of catch (from catchpad) 369; Clause 3: calls f(2) is guarded by catch1 370; CHECK-NEXT: .long 0 371; ^ flags (0 => catch handler) 372; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1 373; ^ offset of start of clause 374; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1 375; ^ offset of end of clause 376; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]] 377; ^ offset of start of handler 378; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 379; ^ offset of end of handler 380; CHECK-NEXT: .long 1 381; ^ type token of catch (from catchpad) 382; Clause 4: call f(2) is also guarded by catch2 383; This is a "duplicate" because the protected range (f(2)) 384; is in funclet fault but catch2's immediate parent 385; is the main function, not that funclet. 386; CHECK-NEXT: .long 8 387; ^ flags (0 => catch handler | 8 => duplicate) 388; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1 389; ^ offset of start of clause 390; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1 391; ^ offset of end of clause 392; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 393; ^ offset of start of handler 394; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]] 395; ^ offset of end of handler 396; CHECK-NEXT: .long 2 397; ^ type token of catch (from catchpad) 398 399; Test with several cleanups that need to infer their unwind dests from each 400; other, the inner one needing to make the inference from an invoke, ignoring 401; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some 402; internal invokes/catchswitches 403; 404; Corresponds to something like: 405; void test3() { 406; try { 407; f(1); 408; } fault { // fault1 409; try { 410; try { 411; f(2); 412; __unreachable(); 413; } fault { // fault2 414; try { 415; f(3); 416; } fault { // fault3 417; try { 418; f(4); 419; } fault { // fault4 420; f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but 421; didn't bother to specify nounwind) 422; try { 423; try { 424; f(6); 425; } catch(type 1) { 426; goto __unreachable; 427; } 428; } catch (type 2) { // marked "unwinds to caller" because we allow 429; // that if the unwind won't be taken (see 430; // SimplifyUnreachable & RemoveUnwindEdge) 431; goto _unreachable; 432; } 433; f(7); 434; __unreachable(); 435; } 436; } 437; } 438; } fault { // fault 5 439; } 440; } 441; } 442; 443; CHECK-LABEL: test3: # @test3 444; CHECK-NEXT: [[test3_begin:.*func_begin.*]]: 445define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { 446entry: 447; CHECK: .seh_endprologue 448; CHECK: [[test3_before_f1:.+]]: 449; CHECK-NEXT: movl $1, %ecx 450; CHECK-NEXT: callq f 451; CHECK-NEXT: [[test3_after_f1:.+]]: 452 invoke void @f(i32 1) 453 to label %exit unwind label %fault1 454fault1: 455 ; check lines below since this gets reordered to end-of-func 456 %fault.pad1 = cleanuppad within none [i32 undef] 457 invoke void @f(i32 2) ["funclet"(token %fault.pad1)] 458 to label %unreachable unwind label %fault2 459fault2: 460 ; check lines below since this gets reordered to end-of-func 461 %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef] 462 invoke void @f(i32 3) ["funclet"(token %fault.pad2)] 463 to label %unreachable unwind label %fault3 464fault3: 465 ; check lines below since this gets reordered to end-of-func 466 %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef] 467 invoke void @f(i32 4) ["funclet"(token %fault.pad3)] 468 to label %unreachable unwind label %fault4 469fault4: 470; CHECK: .seh_proc [[test3_fault4:[^ ]+]] 471 %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef] 472; CHECK: .seh_endprologue 473 call void @f(i32 5) ["funclet"(token %fault.pad4)] 474; CHECK: [[test3_before_f6:.+]]: 475; CHECK-NEXT: movl $6, %ecx 476; CHECK-NEXT: callq f 477; CHECK-NEXT: [[test3_after_f6:.+]]: 478 invoke void @f(i32 6) ["funclet"(token %fault.pad4)] 479 to label %fault4.cont unwind label %exn.dispatch1 480fault4.cont: 481; CHECK: # %fault4.cont 482; CHECK: [[test3_before_f7:.+]]: 483; CHECK-NEXT: movl $7, %ecx 484; CHECK-NEXT: callq f 485; CHECK-NEXT: [[test3_after_f7:.+]]: 486 invoke void @f(i32 7) ["funclet"(token %fault.pad4)] 487 to label %unreachable unwind label %fault5 488exn.dispatch1: 489 %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2 490catch1: 491 %catch.pad1 = catchpad within %catchswitch1 [i32 1] 492; CHECK: .seh_proc [[test3_catch1:[^ ]+]] 493 catchret from %catch.pad1 to label %unreachable 494exn.dispatch2: 495 %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller 496catch2: 497 %catch.pad2 = catchpad within %catchswitch2 [i32 2] 498; CHECK: .seh_proc [[test3_catch2:[^ ]+]] 499 catchret from %catch.pad2 to label %unreachable 500fault5: 501; CHECK: .seh_proc [[test3_fault5:[^ ]+]] 502 %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef] 503; CHECK: .seh_endprologue 504cleanupret from %fault.pad5 unwind to caller 505exit: 506 ret void 507unreachable: 508 unreachable 509; CHECK: .seh_proc [[test3_fault3:[^ ]+]] 510; CHECK: # %fault3 511; CHECK: .seh_endprologue 512; CHECK: [[test3_before_f4:.+]]: 513; CHECK-NEXT: movl $4, %ecx 514; CHECK-NEXT: callq f 515; CHECK-NEXT: [[test3_after_f4:.+]]: 516; CHECK: int3 517; CHECK: .seh_proc [[test3_fault2:[^ ]+]] 518; CHECK: # %fault2 519; CHECK: .seh_endprologue 520; CHECK: [[test3_before_f3:.+]]: 521; CHECK-NEXT: movl $3, %ecx 522; CHECK-NEXT: callq f 523; CHECK-NEXT: [[test3_after_f3:.+]]: 524; CHECK: int3 525; CHECK: .seh_proc [[test3_fault1:[^ ]+]] 526; CHECK: # %fault1 527; CHECK: .seh_endprologue 528; CHECK: [[test3_before_f2:.+]]: 529; CHECK-NEXT: movl $2, %ecx 530; CHECK-NEXT: callq f 531; CHECK-NEXT: [[test3_after_f2:.+]]: 532; CHECK: int3 533; CHECK: [[test3_end:.*func_end.*]]: 534} 535 536; Now check for EH table in xdata (following standard xdata) 537; CHECK-LABEL: .section .xdata 538; standard xdata comes here 539; CHECK: .long 7{{$}} 540; ^ number of funclets 541; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]] 542; ^ offset from L_begin to start of 1st funclet 543; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]] 544; ^ offset from L_begin to start of 2nd funclet 545; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]] 546; ^ offset from L_begin to start of 3rd funclet 547; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 548; ^ offset from L_begin to start of 4th funclet 549; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 550; ^ offset from L_begin to start of 5th funclet 551; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]] 552; ^ offset from L_begin to start of 6th funclet 553; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]] 554; ^ offset from L_begin to start of 7th funclet 555; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]] 556; ^ offset from L_begin to end of last funclet 557; CHECK-NEXT: .long 10 558; ^ number of EH clauses 559; Clause 1: call f(1) is guarded by fault1 560; CHECK-NEXT: .long 4 561; ^ flags (4 => fault handler) 562; CHECK-NEXT: .long ([[test3_before_f1]]-[[test3_begin]])+1 563; ^ offset of start of clause 564; CHECK-NEXT: .long ([[test3_after_f1]]-[[test3_begin]])+1 565; ^ offset of end of clause 566; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]] 567; ^ offset of start of handler 568; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]] 569; ^ offset of end of handler 570; CHECK-NEXT: .long 0 571; ^ type token slot (null for fault) 572; Clause 3: call f(6) is guarded by catch1 573; CHECK-NEXT: .long 0 574; ^ flags (0 => catch handler) 575; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1 576; ^ offset of start of clause 577; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1 578; ^ offset of end of clause 579; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]] 580; ^ offset of start of handler 581; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]] 582; ^ offset of end of handler 583; CHECK-NEXT: .long 1 584; ^ type token of catch (from catchpad) 585; Clause 3: call f(6) is also guarded by catch2 586; CHECK-NEXT: .long 0 587; ^ flags (0 => catch handler) 588; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1 589; ^ offset of start of clause 590; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1 591; ^ offset of end of clause 592; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]] 593; ^ offset of start of handler 594; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 595; ^ offset of end of handler 596; CHECK-NEXT: .long 2 597; ^ type token of catch (from catchpad) 598; Clause 4: call f(7) is guarded by fault5 599; This is a "duplicate" because the protected range (f(6)-f(7)) 600; is in funclet fault4 but fault5's immediate parent 601; is fault1, not that funclet. 602; CHECK-NEXT: .long 12 603; ^ flags (4 => fault handler | 8 => duplicate) 604; CHECK-NEXT: .long ([[test3_before_f7]]-[[test3_begin]])+1 605; ^ offset of start of clause 606; CHECK-NEXT: .long ([[test3_after_f7]]-[[test3_begin]])+1 607; ^ offset of end of clause 608; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 609; ^ offset of start of handler 610; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 611; ^ offset of end of handler 612; CHECK-NEXT: .long 0 613; ^ type token slot (null for fault) 614; Clause 5: call f(4) is guarded by fault4 615; CHECK-NEXT: .long 4 616; ^ flags (4 => fault handler) 617; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1 618; ^ offset of start of clause 619; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1 620; ^ offset of end of clause 621; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]] 622; ^ offset of start of handler 623; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]] 624; ^ offset of end of handler 625; CHECK-NEXT: .long 0 626; ^ type token slot (null for fault) 627; Clause 6: call f(4) is also guarded by fault5 628; This is a "duplicate" because the protected range (f(4)) 629; is in funclet fault3 but fault5's immediate parent 630; is fault1, not that funclet. 631; CHECK-NEXT: .long 12 632; ^ flags (4 => fault handler) 633; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1 634; ^ offset of start of clause 635; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1 636; ^ offset of end of clause 637; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 638; ^ offset of start of handler 639; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 640; ^ offset of end of handler 641; CHECK-NEXT: .long 0 642; ^ type token slot (null for fault) 643; Clause 7: call f(3) is guarded by fault3 644; CHECK-NEXT: .long 4 645; ^ flags (4 => fault handler) 646; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1 647; ^ offset of start of clause 648; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1 649; ^ offset of end of clause 650; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 651; ^ offset of start of handler 652; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]] 653; ^ offset of end of handler 654; CHECK-NEXT: .long 0 655; ^ type token slot (null for fault) 656; Clause 8: call f(3) is guarded by fault5 657; This is a "duplicate" because the protected range (f(3)) 658; is in funclet fault2 but fault5's immediate parent 659; is fault1, not that funclet. 660; CHECK-NEXT: .long 12 661; ^ flags (4 => fault handler | 8 => duplicate) 662; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1 663; ^ offset of start of clause 664; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1 665; ^ offset of end of clause 666; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 667; ^ offset of start of handler 668; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 669; ^ offset of end of handler 670; CHECK-NEXT: .long 0 671; ^ type token slot (null for fault) 672; Clause 9: call f(2) is guarded by fault2 673; CHECK-NEXT: .long 4 674; ^ flags (4 => fault handler) 675; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1 676; ^ offset of start of clause 677; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1 678; ^ offset of end of clause 679; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]] 680; ^ offset of start of handler 681; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]] 682; ^ offset of end of handler 683; CHECK-NEXT: .long 0 684; ^ type token slot (null for fault) 685; Clause 10: call f(2) is guarded by fault5 686; CHECK-NEXT: .long 4 687; ^ flags (4 => fault handler) 688; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1 689; ^ offset of start of clause 690; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1 691; ^ offset of end of clause 692; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 693; ^ offset of start of handler 694; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 695; ^ offset of end of handler 696; CHECK-NEXT: .long 0 697; ^ type token slot (null for fault) 698