1 ; RUN: llc -mtriple=x86_64-pc-windows-coreclr -verify-machineinstrs < %s | FileCheck %s
3 declare void @ProcessCLRException()
5 declare void @g(i8 addrspace(1)*)
6 declare i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token)
8 ; Simplified IR for pseudo-C# like the following:
30 ; CHECK-LABEL: test1: # @test1
31 ; CHECK-NEXT: [[L_begin:.*func_begin.*]]:
32 define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
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
41 ; CHECK-NEXT: [[L_after_f1:.+]]:
43 to label %inner_try unwind label %finally.pad
46 ; CHECK: [[L_before_f2:.+]]:
47 ; CHECK-NEXT: movl $2, %ecx
49 ; CHECK-NEXT: [[L_after_f2:.+]]:
51 to label %finally.clone unwind label %catch1.pad
53 ; CHECK: .seh_proc [[L_catch1:[^ ]+]]
54 %catch1 = catchpad [i32 1]
55 to label %catch1.body unwind label %catch2.pad
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
67 %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch1)
68 call void @g(i8 addrspace(1)* %exn1)
69 ; CHECK: [[L_before_f3:.+]]:
70 ; CHECK-NEXT: movl $3, %ecx
72 ; CHECK-NEXT: [[L_after_f3:.+]]:
74 to label %catch1.ret unwind label %catch.end
76 catchret %catch1 to label %finally.clone
78 ; CHECK: .seh_proc [[L_catch2:[^ ]+]]
79 %catch2 = catchpad [i32 2]
80 to label %catch2.body unwind label %catch.end
82 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
83 ; ^ all funclets use the same frame size
84 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
85 ; ^ establisher frame pointer passed in rcx
86 ; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
87 ; CHECK: leaq [[FPOffset]](%rcx), %rbp
88 ; CHECK: .seh_endprologue
89 ; CHECK: movq %rdx, %rcx
90 ; ^ exception pointer passed in rdx
92 %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch2)
93 call void @g(i8 addrspace(1)* %exn2)
94 ; CHECK: [[L_before_f4:.+]]:
95 ; CHECK-NEXT: movl $4, %ecx
97 ; CHECK-NEXT: [[L_after_f4:.+]]:
99 to label %try_in_catch unwind label %catch.end
101 ; CHECK: # %try_in_catch
102 ; CHECK: [[L_before_f5:.+]]:
103 ; CHECK-NEXT: movl $5, %ecx
104 ; CHECK-NEXT: callq f
105 ; CHECK-NEXT: [[L_after_f5:.+]]:
106 invoke void @f(i32 5)
107 to label %catch2.ret unwind label %fault.pad
109 ; CHECK: .seh_proc [[L_fault:[^ ]+]]
110 %fault = cleanuppad [i32 undef]
111 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
112 ; ^ all funclets use the same frame size
113 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
114 ; ^ establisher frame pointer passed in rcx
115 ; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
116 ; CHECK: leaq [[FPOffset]](%rcx), %rbp
117 ; CHECK: .seh_endprologue
118 ; CHECK: [[L_before_f6:.+]]:
119 ; CHECK-NEXT: movl $6, %ecx
120 ; CHECK-NEXT: callq f
121 ; CHECK-NEXT: [[L_after_f6:.+]]:
122 invoke void @f(i32 6)
123 to label %fault.ret unwind label %fault.end
125 cleanupret %fault unwind label %catch.end
127 cleanupendpad %fault unwind label %catch.end
129 catchret %catch2 to label %finally.clone
131 catchendpad unwind label %finally.pad
136 ; CHECK: .seh_proc [[L_finally:[^ ]+]]
137 %finally = cleanuppad []
138 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
139 ; ^ all funclets use the same frame size
140 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
141 ; ^ establisher frame pointer passed in rcx
142 ; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
143 ; CHECK: leaq [[FPOffset]](%rcx), %rbp
144 ; CHECK: .seh_endprologue
145 ; CHECK: [[L_before_f7:.+]]:
146 ; CHECK-NEXT: movl $7, %ecx
147 ; CHECK-NEXT: callq f
148 ; CHECK-NEXT: [[L_after_f7:.+]]:
149 invoke void @f(i32 7)
150 to label %finally.ret unwind label %finally.end
152 cleanupret %finally unwind to caller
154 cleanupendpad %finally unwind to caller
158 ; CHECK: [[L_end:.*func_end.*]]:
161 ; Now check for EH table in xdata (following standard xdata)
162 ; CHECK-LABEL: .section .xdata
163 ; standard xdata comes here
164 ; CHECK: .long 4{{$}}
165 ; ^ number of funclets
166 ; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]]
167 ; ^ offset from L_begin to start of 1st funclet
168 ; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
169 ; ^ offset from L_begin to start of 2nd funclet
170 ; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
171 ; ^ offset from L_begin to start of 3rd funclet
172 ; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
173 ; ^ offset from L_begin to start of 4th funclet
174 ; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
175 ; ^ offset from L_begin to end of last funclet
176 ; CHECK-NEXT: .long 7
177 ; ^ number of EH clauses
178 ; Clause 1: call f(2) is guarded by catch1
179 ; CHECK-NEXT: .long 0
180 ; ^ flags (0 => catch handler)
181 ; CHECK-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1
182 ; ^ offset of start of clause
183 ; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
184 ; ^ offset of end of clause
185 ; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]]
186 ; ^ offset of start of handler
187 ; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
188 ; ^ offset of end of handler
189 ; CHECK-NEXT: .long 1
190 ; ^ type token of catch (from catchpad)
191 ; Clause 2: call f(2) is also guarded by catch2
192 ; CHECK-NEXT: .long 0
193 ; ^ flags (0 => catch handler)
194 ; CHECK-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1
195 ; ^ offset of start of clause
196 ; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
197 ; ^ offset of end of clause
198 ; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
199 ; ^ offset of start of handler
200 ; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
201 ; ^ offset of end of handler
202 ; CHECK-NEXT: .long 2
203 ; ^ type token of catch (from catchpad)
204 ; Clause 3: calls f(1) and f(2) are guarded by finally
205 ; CHECK-NEXT: .long 2
206 ; ^ flags (2 => finally handler)
207 ; CHECK-NEXT: .long ([[L_before_f1]]-[[L_begin]])+1
208 ; ^ offset of start of clause
209 ; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
210 ; ^ offset of end of clause
211 ; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
212 ; ^ offset of start of handler
213 ; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
214 ; ^ offset of end of handler
215 ; CHECK-NEXT: .long 0
216 ; ^ type token slot (null for finally)
217 ; Clause 4: call f(3) is guarded by finally
218 ; This is a "duplicate" because the protected range (f(3))
219 ; is in funclet catch1 but the finally's immediate parent
220 ; is the main function, not that funclet.
221 ; CHECK-NEXT: .long 10
222 ; ^ flags (2 => finally handler | 8 => duplicate)
223 ; CHECK-NEXT: .long ([[L_before_f3]]-[[L_begin]])+1
224 ; ^ offset of start of clause
225 ; CHECK-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1
226 ; ^ offset of end of clause
227 ; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
228 ; ^ offset of start of handler
229 ; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
230 ; ^ offset of end of handler
231 ; CHECK-NEXT: .long 0
232 ; ^ type token slot (null for finally)
233 ; Clause 5: call f(5) is guarded by fault
234 ; CHECK-NEXT: .long 4
235 ; ^ flags (4 => fault handler)
236 ; CHECK-NEXT: .long ([[L_before_f5]]-[[L_begin]])+1
237 ; ^ offset of start of clause
238 ; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
239 ; ^ offset of end of clause
240 ; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
241 ; ^ offset of start of handler
242 ; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
243 ; ^ offset of end of handler
244 ; CHECK-NEXT: .long 0
245 ; ^ type token slot (null for fault)
246 ; Clause 6: calls f(4) and f(5) are guarded by finally
247 ; This is a "duplicate" because the protected range (f(4)-f(5))
248 ; is in funclet catch2 but the finally's immediate parent
249 ; is the main function, not that funclet.
250 ; CHECK-NEXT: .long 10
251 ; ^ flags (2 => finally handler | 8 => duplicate)
252 ; CHECK-NEXT: .long ([[L_before_f4]]-[[L_begin]])+1
253 ; ^ offset of start of clause
254 ; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
255 ; ^ offset of end of clause
256 ; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
257 ; ^ offset of start of handler
258 ; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
259 ; ^ offset of end of handler
260 ; CHECK-NEXT: .long 0
261 ; ^ type token slot (null for finally)
262 ; Clause 7: call f(6) is guarded by finally
263 ; This is a "duplicate" because the protected range (f(3))
264 ; is in funclet catch1 but the finally's immediate parent
265 ; is the main function, not that funclet.
266 ; CHECK-NEXT: .long 10
267 ; ^ flags (2 => finally handler | 8 => duplicate)
268 ; CHECK-NEXT: .long ([[L_before_f6]]-[[L_begin]])+1
269 ; ^ offset of start of clause
270 ; CHECK-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1
271 ; ^ offset of end of clause
272 ; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
273 ; ^ offset of start of handler
274 ; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
275 ; ^ offset of end of handler
276 ; CHECK-NEXT: .long 0
277 ; ^ type token slot (null for finally)