1 ; RUN: llc -mtriple=x86_64-pc-windows-coreclr -verify-machineinstrs < %s | FileCheck %s 2 3 declare void @ProcessCLRException() 4 declare void @f(i32) 5 declare void @g(i8 addrspace(1)*) 6 declare 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.*]]: 32 define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { 33 entry: 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 44 inner_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 52 catch1.pad: 53 %cs1 = catchswitch within none [label %catch1.body, label %catch2.body] unwind label %finally.pad 54 catch1.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 75 catch1.ret: 76 catchret from %catch1 to label %finally.clone 77 catch2.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 98 try_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 106 fault.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 122 fault.ret: 123 cleanupret from %fault unwind label %finally.pad 124 catch2.ret: 125 catchret from %catch2 to label %finally.clone 126 finally.clone: 127 call void @f(i32 7) 128 br label %tail 129 finally.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 143 tail: 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