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; 286; CHECK-LABEL: test2: # @test2 287; CHECK-NEXT: [[test2_begin:.*func_begin.*]]: 288define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { 289entry: 290; CHECK: .seh_endprologue 291; CHECK: [[test2_before_f1:.+]]: 292; CHECK-NEXT: movl $1, %ecx 293; CHECK-NEXT: callq f 294; CHECK-NEXT: [[test2_after_f1:.+]]: 295 invoke void @f(i32 1) 296 to label %exit unwind label %fault 297fault: 298; CHECK: .seh_proc [[test2_fault:[^ ]+]] 299 %fault.pad = cleanuppad within none [i32 undef] 300; CHECK: .seh_endprologue 301; CHECK: [[test2_before_f2:.+]]: 302; CHECK-NEXT: movl $2, %ecx 303; CHECK-NEXT: callq f 304; CHECK-NEXT: [[test2_after_f2:.+]]: 305 invoke void @f(i32 2) ["funclet"(token %fault.pad)] 306 to label %unreachable unwind label %exn.dispatch.inner 307exn.dispatch.inner: 308 %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer 309catch1: 310 %catch.pad1 = catchpad within %catchswitch.inner [i32 1] 311; CHECK: .seh_proc [[test2_catch1:[^ ]+]] 312 catchret from %catch.pad1 to label %unreachable 313exn.dispatch.outer: 314 %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller 315catch2: 316 %catch.pad2 = catchpad within %catchswitch.outer [i32 2] 317; CHECK: .seh_proc [[test2_catch2:[^ ]+]] 318 catchret from %catch.pad2 to label %exit 319exit: 320 ret void 321unreachable: 322 unreachable 323; CHECK: [[test2_end:.*func_end.*]]: 324} 325 326; Now check for EH table in xdata (following standard xdata) 327; CHECK-LABEL: .section .xdata 328; standard xdata comes here 329; CHECK: .long 3{{$}} 330; ^ number of funclets 331; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]] 332; ^ offset from L_begin to start of 1st funclet 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_end]]-[[test2_begin]] 338; ^ offset from L_begin to end of last funclet 339; CHECK-NEXT: .long 4 340; ^ number of EH clauses 341; Clause 1: call f(1) is guarded by fault 342; CHECK-NEXT: .long 4 343; ^ flags (4 => fault handler) 344; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1 345; ^ offset of start of clause 346; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1 347; ^ offset of end of clause 348; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]] 349; ^ offset of start of handler 350; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]] 351; ^ offset of end of handler 352; CHECK-NEXT: .long 0 353; ^ type token slot (null for fault) 354; Clause 2: call f(1) is also guarded by catch2 355; CHECK-NEXT: .long 0 356; ^ flags (0 => catch handler) 357; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1 358; ^ offset of start of clause 359; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1 360; ^ offset of end of clause 361; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 362; ^ offset of start of handler 363; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]] 364; ^ offset of end of handler 365; CHECK-NEXT: .long 2 366; ^ type token of catch (from catchpad) 367; Clause 3: calls f(2) is guarded by catch1 368; CHECK-NEXT: .long 0 369; ^ flags (0 => catch handler) 370; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1 371; ^ offset of start of clause 372; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1 373; ^ offset of end of clause 374; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]] 375; ^ offset of start of handler 376; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 377; ^ offset of end of handler 378; CHECK-NEXT: .long 1 379; ^ type token of catch (from catchpad) 380; Clause 4: call f(2) is also guarded by catch2 381; This is a "duplicate" because the protected range (f(2)) 382; is in funclet fault but catch2's immediate parent 383; is the main function, not that funclet. 384; CHECK-NEXT: .long 8 385; ^ flags (0 => catch handler | 8 => duplicate) 386; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1 387; ^ offset of start of clause 388; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1 389; ^ offset of end of clause 390; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]] 391; ^ offset of start of handler 392; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]] 393; ^ offset of end of handler 394; CHECK-NEXT: .long 2 395; ^ type token of catch (from catchpad) 396 397; Test with several cleanups that need to infer their unwind dests from each 398; other, the inner one needing to make the inference from an invoke, ignoring 399; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some 400; internal invokes/catchswitches 401; 402; Corresponds to something like: 403; void test3() { 404; try { 405; f(1); 406; } fault { // fault1 407; try { 408; try { 409; f(2); 410; __unreachable(); 411; } fault { // fault2 412; try { 413; f(3); 414; } fault { // fault3 415; try { 416; f(4); 417; } fault { // fault4 418; f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but 419; didn't bother to specify nounwind) 420; try { 421; try { 422; f(6); 423; } catch(type 1) { 424; goto __unreachable; 425; } 426; } catch (type 2) { // marked "unwinds to caller" because we allow 427; // that if the unwind won't be taken (see 428; // SimplifyUnreachable & RemoveUnwindEdge) 429; goto _unreachable; 430; } 431; f(7); 432; __unreachable(); 433; } 434; } 435; } 436; } fault { // fault 5 437; } 438; } 439; } 440; 441; CHECK-LABEL: test3: # @test3 442; CHECK-NEXT: [[test3_begin:.*func_begin.*]]: 443define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { 444entry: 445; CHECK: .seh_endprologue 446; CHECK: [[test3_before_f1:.+]]: 447; CHECK-NEXT: movl $1, %ecx 448; CHECK-NEXT: callq f 449; CHECK-NEXT: [[test3_after_f1:.+]]: 450 invoke void @f(i32 1) 451 to label %exit unwind label %fault1 452fault1: 453 ; check lines below since this gets reordered to end-of-func 454 %fault.pad1 = cleanuppad within none [i32 undef] 455 invoke void @f(i32 2) ["funclet"(token %fault.pad1)] 456 to label %unreachable unwind label %fault2 457fault2: 458 ; check lines below since this gets reordered to end-of-func 459 %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef] 460 invoke void @f(i32 3) ["funclet"(token %fault.pad2)] 461 to label %unreachable unwind label %fault3 462fault3: 463 ; check lines below since this gets reordered to end-of-func 464 %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef] 465 invoke void @f(i32 4) ["funclet"(token %fault.pad3)] 466 to label %unreachable unwind label %fault4 467fault4: 468; CHECK: .seh_proc [[test3_fault4:[^ ]+]] 469 %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef] 470; CHECK: .seh_endprologue 471 call void @f(i32 5) ["funclet"(token %fault.pad4)] 472; CHECK: [[test3_before_f6:.+]]: 473; CHECK-NEXT: movl $6, %ecx 474; CHECK-NEXT: callq f 475; CHECK-NEXT: [[test3_after_f6:.+]]: 476 invoke void @f(i32 6) ["funclet"(token %fault.pad4)] 477 to label %fault4.cont unwind label %exn.dispatch1 478fault4.cont: 479; CHECK: # %fault4.cont 480; CHECK: [[test3_before_f7:.+]]: 481; CHECK-NEXT: movl $7, %ecx 482; CHECK-NEXT: callq f 483; CHECK-NEXT: [[test3_after_f7:.+]]: 484 invoke void @f(i32 7) ["funclet"(token %fault.pad4)] 485 to label %unreachable unwind label %fault5 486exn.dispatch1: 487 %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2 488catch1: 489 %catch.pad1 = catchpad within %catchswitch1 [i32 1] 490; CHECK: .seh_proc [[test3_catch1:[^ ]+]] 491 catchret from %catch.pad1 to label %unreachable 492exn.dispatch2: 493 %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller 494catch2: 495 %catch.pad2 = catchpad within %catchswitch2 [i32 2] 496; CHECK: .seh_proc [[test3_catch2:[^ ]+]] 497 catchret from %catch.pad2 to label %unreachable 498fault5: 499; CHECK: .seh_proc [[test3_fault5:[^ ]+]] 500 %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef] 501; CHECK: .seh_endprologue 502cleanupret from %fault.pad5 unwind to caller 503exit: 504 ret void 505unreachable: 506 unreachable 507; CHECK: .seh_proc [[test3_fault3:[^ ]+]] 508; CHECK: # %fault3 509; CHECK: .seh_endprologue 510; CHECK: [[test3_before_f4:.+]]: 511; CHECK-NEXT: movl $4, %ecx 512; CHECK-NEXT: callq f 513; CHECK-NEXT: [[test3_after_f4:.+]]: 514; CHECK: .seh_proc [[test3_fault2:[^ ]+]] 515; CHECK: # %fault2 516; CHECK: .seh_endprologue 517; CHECK: [[test3_before_f3:.+]]: 518; CHECK-NEXT: movl $3, %ecx 519; CHECK-NEXT: callq f 520; CHECK-NEXT: [[test3_after_f3:.+]]: 521; CHECK: .seh_proc [[test3_fault1:[^ ]+]] 522; CHECK: # %fault1 523; CHECK: .seh_endprologue 524; CHECK: [[test3_before_f2:.+]]: 525; CHECK-NEXT: movl $2, %ecx 526; CHECK-NEXT: callq f 527; CHECK-NEXT: [[test3_after_f2:.+]]: 528; CHECK: [[test3_end:.*func_end.*]]: 529} 530 531; Now check for EH table in xdata (following standard xdata) 532; CHECK-LABEL: .section .xdata 533; standard xdata comes here 534; CHECK: .long 7{{$}} 535; ^ number of funclets 536; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]] 537; ^ offset from L_begin to start of 1st funclet 538; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]] 539; ^ offset from L_begin to start of 2nd funclet 540; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]] 541; ^ offset from L_begin to start of 3rd funclet 542; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 543; ^ offset from L_begin to start of 4th funclet 544; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 545; ^ offset from L_begin to start of 5th funclet 546; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]] 547; ^ offset from L_begin to start of 6th funclet 548; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]] 549; ^ offset from L_begin to start of 7th funclet 550; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]] 551; ^ offset from L_begin to end of last funclet 552; CHECK-NEXT: .long 10 553; ^ number of EH clauses 554; Clause 1: call f(1) is guarded by fault1 555; CHECK-NEXT: .long 4 556; ^ flags (4 => fault handler) 557; CHECK-NEXT: .long ([[test3_before_f1]]-[[test3_begin]])+1 558; ^ offset of start of clause 559; CHECK-NEXT: .long ([[test3_after_f1]]-[[test3_begin]])+1 560; ^ offset of end of clause 561; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]] 562; ^ offset of start of handler 563; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]] 564; ^ offset of end of handler 565; CHECK-NEXT: .long 0 566; ^ type token slot (null for fault) 567; Clause 3: call f(6) is guarded by catch1 568; CHECK-NEXT: .long 0 569; ^ flags (0 => catch handler) 570; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1 571; ^ offset of start of clause 572; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1 573; ^ offset of end of clause 574; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]] 575; ^ offset of start of handler 576; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]] 577; ^ offset of end of handler 578; CHECK-NEXT: .long 1 579; ^ type token of catch (from catchpad) 580; Clause 3: call f(6) is also guarded by catch2 581; CHECK-NEXT: .long 0 582; ^ flags (0 => catch handler) 583; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1 584; ^ offset of start of clause 585; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1 586; ^ offset of end of clause 587; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]] 588; ^ offset of start of handler 589; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 590; ^ offset of end of handler 591; CHECK-NEXT: .long 2 592; ^ type token of catch (from catchpad) 593; Clause 4: call f(7) is guarded by fault5 594; This is a "duplicate" because the protected range (f(6)-f(7)) 595; is in funclet fault4 but fault5's immediate parent 596; is fault1, not that funclet. 597; CHECK-NEXT: .long 12 598; ^ flags (4 => fault handler | 8 => duplicate) 599; CHECK-NEXT: .long ([[test3_before_f7]]-[[test3_begin]])+1 600; ^ offset of start of clause 601; CHECK-NEXT: .long ([[test3_after_f7]]-[[test3_begin]])+1 602; ^ offset of end of clause 603; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 604; ^ offset of start of handler 605; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 606; ^ offset of end of handler 607; CHECK-NEXT: .long 0 608; ^ type token slot (null for fault) 609; Clause 5: call f(4) is guarded by fault4 610; CHECK-NEXT: .long 4 611; ^ flags (4 => fault handler) 612; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1 613; ^ offset of start of clause 614; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1 615; ^ offset of end of clause 616; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]] 617; ^ offset of start of handler 618; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]] 619; ^ offset of end of handler 620; CHECK-NEXT: .long 0 621; ^ type token slot (null for fault) 622; Clause 6: call f(4) is also guarded by fault5 623; This is a "duplicate" because the protected range (f(4)) 624; is in funclet fault3 but fault5's immediate parent 625; is fault1, not that funclet. 626; CHECK-NEXT: .long 12 627; ^ flags (4 => fault handler) 628; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1 629; ^ offset of start of clause 630; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1 631; ^ offset of end of clause 632; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 633; ^ offset of start of handler 634; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 635; ^ offset of end of handler 636; CHECK-NEXT: .long 0 637; ^ type token slot (null for fault) 638; Clause 7: call f(3) is guarded by fault3 639; CHECK-NEXT: .long 4 640; ^ flags (4 => fault handler) 641; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1 642; ^ offset of start of clause 643; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1 644; ^ offset of end of clause 645; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 646; ^ offset of start of handler 647; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]] 648; ^ offset of end of handler 649; CHECK-NEXT: .long 0 650; ^ type token slot (null for fault) 651; Clause 8: call f(3) is guarded by fault5 652; This is a "duplicate" because the protected range (f(3)) 653; is in funclet fault2 but fault5's immediate parent 654; is fault1, not that funclet. 655; CHECK-NEXT: .long 12 656; ^ flags (4 => fault handler | 8 => duplicate) 657; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1 658; ^ offset of start of clause 659; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1 660; ^ offset of end of clause 661; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 662; ^ offset of start of handler 663; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 664; ^ offset of end of handler 665; CHECK-NEXT: .long 0 666; ^ type token slot (null for fault) 667; Clause 9: call f(2) is guarded by fault2 668; CHECK-NEXT: .long 4 669; ^ flags (4 => fault handler) 670; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1 671; ^ offset of start of clause 672; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1 673; ^ offset of end of clause 674; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]] 675; ^ offset of start of handler 676; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]] 677; ^ offset of end of handler 678; CHECK-NEXT: .long 0 679; ^ type token slot (null for fault) 680; Clause 10: call f(2) is guarded by fault5 681; CHECK-NEXT: .long 4 682; ^ flags (4 => fault handler) 683; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1 684; ^ offset of start of clause 685; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1 686; ^ offset of end of clause 687; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]] 688; ^ offset of start of handler 689; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]] 690; ^ offset of end of handler 691; CHECK-NEXT: .long 0 692; ^ type token slot (null for fault) 693