[WinEH] Find root frame correctly in CLR funclets
[oota-llvm.git] / test / CodeGen / WinEH / wineh-coreclr.ll
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 ; CHECK: .seh_proc [[L_catch1:[^ ]+]]
54   %catch1 = catchpad [i32 1]
55     to label %catch1.body unwind label %catch2.pad
56 catch1.body:
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)
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)
74     to label %catch1.ret unwind label %catch.end
75 catch1.ret:
76   catchret %catch1 to label %finally.clone
77 catch2.pad:
78 ; CHECK: .seh_proc [[L_catch2:[^ ]+]]
79   %catch2 = catchpad [i32 2]
80     to label %catch2.body unwind label %catch.end
81 catch2.body:
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
91 ; CHECK-NEXT: callq g
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
96 ; CHECK-NEXT: callq f
97 ; CHECK-NEXT: [[L_after_f4:.+]]:
98   invoke void @f(i32 4)
99     to label %try_in_catch unwind label %catch.end
100 try_in_catch:
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
108 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
124 fault.ret:
125   cleanupret %fault unwind label %catch.end
126 fault.end:
127   cleanupendpad %fault unwind label %catch.end
128 catch2.ret:
129   catchret %catch2 to label %finally.clone
130 catch.end:
131   catchendpad unwind label %finally.pad
132 finally.clone:
133   call void @f(i32 7)
134   br label %tail
135 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
151 finally.ret:
152   cleanupret %finally unwind to caller
153 finally.end:
154    cleanupendpad %finally unwind to caller
155 tail:
156   call void @f(i32 8)
157   ret void
158 ; CHECK: [[L_end:.*func_end.*]]:
159 }
160
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)