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 %cs1 = catchswitch within none [label %catch1.body, label %catch2.body] unwind label %finally.pad
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
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
72 ; CHECK-NEXT: [[L_after_f3:.+]]:
73 invoke void @f(i32 3) [ "funclet"(token %catch1) ]
74 to label %catch1.ret unwind label %finally.pad
76 catchret from %catch1 to label %finally.clone
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
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
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
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
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
123 cleanupret from %fault unwind label %finally.pad
125 catchret from %catch2 to label %finally.clone
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
146 ; CHECK: [[L_end:.*func_end.*]]:
149 ; FIXME: Verify that the new clauses are correct and re-enable these checks.
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)