• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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