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: [[L_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: [[L_before_f1:.+]]: 39; CHECK-NEXT: movl $1, %ecx 40; CHECK-NEXT: callq f 41; CHECK-NEXT: [[L_after_f1:.+]]: 42 invoke void @f(i32 1) 43 to label %inner_try unwind label %finally.pad 44inner_try: 45; CHECK: # %inner_try 46; CHECK: [[L_before_f2:.+]]: 47; CHECK-NEXT: movl $2, %ecx 48; CHECK-NEXT: callq f 49; CHECK-NEXT: [[L_after_f2:.+]]: 50 invoke void @f(i32 2) 51 to label %finally.clone unwind label %catch1.pad 52catch1.pad: 53 %cs1 = catchswitch within none [label %catch1.body, label %catch2.body] unwind label %finally.pad 54catch1.body: 55 %catch1 = catchpad within %cs1 [i32 1] 56; CHECK: .seh_proc [[L_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 %catch1) 68 call void @g(i8 addrspace(1)* %exn1) [ "funclet"(token %catch1) ] 69; CHECK: [[L_before_f3:.+]]: 70; CHECK-NEXT: movl $3, %ecx 71; CHECK-NEXT: callq f 72; CHECK-NEXT: [[L_after_f3:.+]]: 73 invoke void @f(i32 3) [ "funclet"(token %catch1) ] 74 to label %catch1.ret unwind label %finally.pad 75catch1.ret: 76 catchret from %catch1 to label %finally.clone 77catch2.body: 78 %catch2 = catchpad within %cs1 [i32 2] 79; CHECK: .seh_proc [[L_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 %catch2) 91 call void @g(i8 addrspace(1)* %exn2) [ "funclet"(token %catch2) ] 92; CHECK: [[L_before_f4:.+]]: 93; CHECK-NEXT: movl $4, %ecx 94; CHECK-NEXT: callq f 95; CHECK-NEXT: [[L_after_f4:.+]]: 96 invoke void @f(i32 4) [ "funclet"(token %catch2) ] 97 to label %try_in_catch unwind label %finally.pad 98try_in_catch: 99; CHECK: # %try_in_catch 100; CHECK: [[L_before_f5:.+]]: 101; CHECK-NEXT: movl $5, %ecx 102; CHECK-NEXT: callq f 103; CHECK-NEXT: [[L_after_f5:.+]]: 104 invoke void @f(i32 5) [ "funclet"(token %catch2) ] 105 to label %catch2.ret unwind label %fault.pad 106fault.pad: 107; CHECK: .seh_proc [[L_fault:[^ ]+]] 108 %fault = cleanuppad within none [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: [[L_before_f6:.+]]: 117; CHECK-NEXT: movl $6, %ecx 118; CHECK-NEXT: callq f 119; CHECK-NEXT: [[L_after_f6:.+]]: 120 invoke void @f(i32 6) [ "funclet"(token %fault) ] 121 to label %fault.ret unwind label %finally.pad 122fault.ret: 123 cleanupret from %fault unwind label %finally.pad 124catch2.ret: 125 catchret from %catch2 to label %finally.clone 126finally.clone: 127 call void @f(i32 7) 128 br label %tail 129finally.pad: 130; CHECK: .seh_proc [[L_finally:[^ ]+]] 131 %finally = 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) ] 142 cleanupret from %finally unwind to caller 143tail: 144 call void @f(i32 8) 145 ret void 146; CHECK: [[L_end:.*func_end.*]]: 147} 148 149; FIXME: Verify that the new clauses are correct and re-enable these checks. 150 151; Now check for EH table in xdata (following standard xdata) 152; CHECKX-LABEL: .section .xdata 153; standard xdata comes here 154; CHECKX: .long 4{{$}} 155; ^ number of funclets 156; CHECKX-NEXT: .long [[L_catch1]]-[[L_begin]] 157; ^ offset from L_begin to start of 1st funclet 158; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]] 159; ^ offset from L_begin to start of 2nd funclet 160; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]] 161; ^ offset from L_begin to start of 3rd funclet 162; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] 163; ^ offset from L_begin to start of 4th funclet 164; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] 165; ^ offset from L_begin to end of last funclet 166; CHECKX-NEXT: .long 7 167; ^ number of EH clauses 168; Clause 1: call f(2) is guarded by catch1 169; CHECKX-NEXT: .long 0 170; ^ flags (0 => catch handler) 171; CHECKX-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1 172; ^ offset of start of clause 173; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 174; ^ offset of end of clause 175; CHECKX-NEXT: .long [[L_catch1]]-[[L_begin]] 176; ^ offset of start of handler 177; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]] 178; ^ offset of end of handler 179; CHECKX-NEXT: .long 1 180; ^ type token of catch (from catchpad) 181; Clause 2: call f(2) is also guarded by catch2 182; CHECKX-NEXT: .long 0 183; ^ flags (0 => catch handler) 184; CHECKX-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1 185; ^ offset of start of clause 186; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 187; ^ offset of end of clause 188; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]] 189; ^ offset of start of handler 190; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]] 191; ^ offset of end of handler 192; CHECKX-NEXT: .long 2 193; ^ type token of catch (from catchpad) 194; Clause 3: calls f(1) and f(2) are guarded by finally 195; CHECKX-NEXT: .long 2 196; ^ flags (2 => finally handler) 197; CHECKX-NEXT: .long ([[L_before_f1]]-[[L_begin]])+1 198; ^ offset of start of clause 199; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 200; ^ offset of end of clause 201; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] 202; ^ offset of start of handler 203; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] 204; ^ offset of end of handler 205; CHECKX-NEXT: .long 0 206; ^ type token slot (null for finally) 207; Clause 4: call f(3) is guarded by finally 208; This is a "duplicate" because the protected range (f(3)) 209; is in funclet catch1 but the finally's immediate parent 210; is the main function, not that funclet. 211; CHECKX-NEXT: .long 10 212; ^ flags (2 => finally handler | 8 => duplicate) 213; CHECKX-NEXT: .long ([[L_before_f3]]-[[L_begin]])+1 214; ^ offset of start of clause 215; CHECKX-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1 216; ^ offset of end of clause 217; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] 218; ^ offset of start of handler 219; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] 220; ^ offset of end of handler 221; CHECKX-NEXT: .long 0 222; ^ type token slot (null for finally) 223; Clause 5: call f(5) is guarded by fault 224; CHECKX-NEXT: .long 4 225; ^ flags (4 => fault handler) 226; CHECKX-NEXT: .long ([[L_before_f5]]-[[L_begin]])+1 227; ^ offset of start of clause 228; CHECKX-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1 229; ^ offset of end of clause 230; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]] 231; ^ offset of start of handler 232; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] 233; ^ offset of end of handler 234; CHECKX-NEXT: .long 0 235; ^ type token slot (null for fault) 236; Clause 6: calls f(4) and f(5) are guarded by finally 237; This is a "duplicate" because the protected range (f(4)-f(5)) 238; is in funclet catch2 but the finally's immediate parent 239; is the main function, not that funclet. 240; CHECKX-NEXT: .long 10 241; ^ flags (2 => finally handler | 8 => duplicate) 242; CHECKX-NEXT: .long ([[L_before_f4]]-[[L_begin]])+1 243; ^ offset of start of clause 244; CHECKX-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1 245; ^ offset of end of clause 246; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] 247; ^ offset of start of handler 248; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] 249; ^ offset of end of handler 250; CHECKX-NEXT: .long 0 251; ^ type token slot (null for finally) 252; Clause 7: call f(6) is guarded by finally 253; This is a "duplicate" because the protected range (f(3)) 254; is in funclet catch1 but the finally's immediate parent 255; is the main function, not that funclet. 256; CHECKX-NEXT: .long 10 257; ^ flags (2 => finally handler | 8 => duplicate) 258; CHECKX-NEXT: .long ([[L_before_f6]]-[[L_begin]])+1 259; ^ offset of start of clause 260; CHECKX-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1 261; ^ offset of end of clause 262; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] 263; ^ offset of start of handler 264; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] 265; ^ offset of end of handler 266; CHECKX-NEXT: .long 0 267; ^ type token slot (null for finally) 268