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: [[test1_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: [[test1_before_f1:.+]]:
39; CHECK-NEXT: movl $1, %ecx
40; CHECK-NEXT: callq f
41; CHECK-NEXT: [[test1_after_f1:.+]]:
42  invoke void @f(i32 1)
43    to label %inner_try unwind label %finally
44inner_try:
45; CHECK: # %inner_try
46; CHECK: [[test1_before_f2:.+]]:
47; CHECK-NEXT: movl $2, %ecx
48; CHECK-NEXT: callq f
49; CHECK-NEXT: [[test1_after_f2:.+]]:
50  invoke void @f(i32 2)
51    to label %finally.clone unwind label %exn.dispatch
52exn.dispatch:
53  %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally
54catch1:
55  %catch.pad1 = catchpad within %catchswitch [i32 1]
56; CHECK: .seh_proc [[test1_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 %catch.pad1)
68  call void @g(i8 addrspace(1)* %exn1) [ "funclet"(token %catch.pad1) ]
69; CHECK: [[test1_before_f3:.+]]:
70; CHECK-NEXT: movl $3, %ecx
71; CHECK-NEXT: callq f
72; CHECK-NEXT: [[test1_after_f3:.+]]:
73  invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ]
74    to label %catch1.ret unwind label %finally
75catch1.ret:
76  catchret from %catch.pad1 to label %finally.clone
77catch2:
78  %catch.pad2 = catchpad within %catchswitch [i32 2]
79; CHECK: .seh_proc [[test1_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 %catch.pad2)
91  call void @g(i8 addrspace(1)* %exn2) [ "funclet"(token %catch.pad2) ]
92; CHECK: [[test1_before_f4:.+]]:
93; CHECK-NEXT: movl $4, %ecx
94; CHECK-NEXT: callq f
95; CHECK-NEXT: [[test1_after_f4:.+]]:
96  invoke void @f(i32 4) [ "funclet"(token %catch.pad2) ]
97    to label %try_in_catch unwind label %finally
98try_in_catch:
99; CHECK: # %try_in_catch
100; CHECK: [[test1_before_f5:.+]]:
101; CHECK-NEXT: movl $5, %ecx
102; CHECK-NEXT: callq f
103; CHECK-NEXT: [[test1_after_f5:.+]]:
104  invoke void @f(i32 5) [ "funclet"(token %catch.pad2) ]
105    to label %catch2.ret unwind label %fault
106fault:
107; CHECK: .seh_proc [[test1_fault:[^ ]+]]
108  %fault.pad = cleanuppad within %catch.pad2 [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: [[test1_before_f6:.+]]:
117; CHECK-NEXT: movl $6, %ecx
118; CHECK-NEXT: callq f
119; CHECK-NEXT: [[test1_after_f6:.+]]:
120  invoke void @f(i32 6) [ "funclet"(token %fault.pad) ]
121    to label %fault.ret unwind label %finally
122fault.ret:
123  cleanupret from %fault.pad unwind label %finally
124catch2.ret:
125  catchret from %catch.pad2 to label %finally.clone
126finally.clone:
127  call void @f(i32 7)
128  br label %tail
129finally:
130; CHECK: .seh_proc [[test1_finally:[^ ]+]]
131  %finally.pad = 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.pad) ]
142  cleanupret from %finally.pad unwind to caller
143tail:
144  call void @f(i32 8)
145  ret void
146; CHECK: [[test1_end:.*func_end.*]]:
147}
148
149; Now check for EH table in xdata (following standard xdata)
150; CHECK-LABEL: .section .xdata
151; standard xdata comes here
152; CHECK:      .long 4{{$}}
153;                   ^ number of funclets
154; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]
155;                   ^ offset from L_begin to start of 1st funclet
156; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
157;                   ^ offset from L_begin to start of 2nd funclet
158; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
159;                   ^ offset from L_begin to start of 3rd funclet
160; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
161;                   ^ offset from L_begin to start of 4th funclet
162; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
163;                   ^ offset from L_begin to end of last funclet
164; CHECK-NEXT: .long 7
165;                   ^ number of EH clauses
166; Clause 1: call f(2) is guarded by catch1
167; CHECK-NEXT: .long 0
168;                   ^ flags (0 => catch handler)
169; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1
170;                   ^ offset of start of clause
171; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
172;                   ^ offset of end of clause
173; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]
174;                   ^ offset of start of handler
175; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
176;                   ^ offset of end of handler
177; CHECK-NEXT: .long 1
178;                   ^ type token of catch (from catchpad)
179; Clause 2: call f(2) is also guarded by catch2
180; CHECK-NEXT: .long 0
181;                   ^ flags (0 => catch handler)
182; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1
183;                   ^ offset of start of clause
184; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
185;                   ^ offset of end of clause
186; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
187;                   ^ offset of start of handler
188; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
189;                   ^ offset of end of handler
190; CHECK-NEXT: .long 2
191;                   ^ type token of catch (from catchpad)
192; Clause 3: calls f(1) and f(2) are guarded by finally
193; CHECK-NEXT: .long 2
194;                   ^ flags (2 => finally handler)
195; CHECK-NEXT: .long ([[test1_before_f1]]-[[test1_begin]])+1
196;                   ^ offset of start of clause
197; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
198;                   ^ offset of end of clause
199; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
200;                   ^ offset of start of handler
201; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
202;                   ^ offset of end of handler
203; CHECK-NEXT: .long 0
204;                   ^ type token slot (null for finally)
205; Clause 4: call f(3) is guarded by finally
206;           This is a "duplicate" because the protected range (f(3))
207;           is in funclet catch1 but the finally's immediate parent
208;           is the main function, not that funclet.
209; CHECK-NEXT: .long 10
210;                   ^ flags (2 => finally handler | 8 => duplicate)
211; CHECK-NEXT: .long ([[test1_before_f3]]-[[test1_begin]])+1
212;                   ^ offset of start of clause
213; CHECK-NEXT: .long ([[test1_after_f3]]-[[test1_begin]])+1
214;                   ^ offset of end of clause
215; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
216;                   ^ offset of start of handler
217; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
218;                   ^ offset of end of handler
219; CHECK-NEXT: .long 0
220;                   ^ type token slot (null for finally)
221; Clause 5: call f(5) is guarded by fault
222; CHECK-NEXT: .long 4
223;                   ^ flags (4 => fault handler)
224; CHECK-NEXT: .long ([[test1_before_f5]]-[[test1_begin]])+1
225;                   ^ offset of start of clause
226; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1
227;                   ^ offset of end of clause
228; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
229;                   ^ offset of start of handler
230; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
231;                   ^ offset of end of handler
232; CHECK-NEXT: .long 0
233;                   ^ type token slot (null for fault)
234; Clause 6: calls f(4) and f(5) are guarded by finally
235;           This is a "duplicate" because the protected range (f(4)-f(5))
236;           is in funclet catch2 but the finally's immediate parent
237;           is the main function, not that funclet.
238; CHECK-NEXT: .long 10
239;                   ^ flags (2 => finally handler | 8 => duplicate)
240; CHECK-NEXT: .long ([[test1_before_f4]]-[[test1_begin]])+1
241;                   ^ offset of start of clause
242; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1
243;                   ^ offset of end of clause
244; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
245;                   ^ offset of start of handler
246; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
247;                   ^ offset of end of handler
248; CHECK-NEXT: .long 0
249;                   ^ type token slot (null for finally)
250; Clause 7: call f(6) is guarded by finally
251;           This is a "duplicate" because the protected range (f(3))
252;           is in funclet catch1 but the finally's immediate parent
253;           is the main function, not that funclet.
254; CHECK-NEXT: .long 10
255;                   ^ flags (2 => finally handler | 8 => duplicate)
256; CHECK-NEXT: .long ([[test1_before_f6]]-[[test1_begin]])+1
257;                   ^ offset of start of clause
258; CHECK-NEXT: .long ([[test1_after_f6]]-[[test1_begin]])+1
259;                   ^ offset of end of clause
260; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
261;                   ^ offset of start of handler
262; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
263;                   ^ offset of end of handler
264; CHECK-NEXT: .long 0
265;                   ^ type token slot (null for finally)
266
267; Test with a cleanup that has no cleanupret, and thus needs its unwind dest
268; inferred from an inner catchswitch
269;
270; corresponds to C# along the lines of:
271; void test2() {
272;   try {
273;     try {
274;       f(1);
275;     } fault {
276;       try {
277;         f(2);
278;       } catch(type1) {
279;       }
280;       __unreachable();
281;     }
282;   } catch(type2) {
283;   }
284; }
285;
286; CHECK-LABEL: test2:     # @test2
287; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:
288define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
289entry:
290; CHECK: .seh_endprologue
291; CHECK: [[test2_before_f1:.+]]:
292; CHECK-NEXT: movl $1, %ecx
293; CHECK-NEXT: callq f
294; CHECK-NEXT: [[test2_after_f1:.+]]:
295  invoke void @f(i32 1)
296    to label %exit unwind label %fault
297fault:
298; CHECK: .seh_proc [[test2_fault:[^ ]+]]
299  %fault.pad = cleanuppad within none [i32 undef]
300; CHECK: .seh_endprologue
301; CHECK: [[test2_before_f2:.+]]:
302; CHECK-NEXT: movl $2, %ecx
303; CHECK-NEXT: callq f
304; CHECK-NEXT: [[test2_after_f2:.+]]:
305  invoke void @f(i32 2) ["funclet"(token %fault.pad)]
306    to label %unreachable unwind label %exn.dispatch.inner
307exn.dispatch.inner:
308  %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer
309catch1:
310  %catch.pad1 = catchpad within %catchswitch.inner [i32 1]
311; CHECK: .seh_proc [[test2_catch1:[^ ]+]]
312  catchret from %catch.pad1 to label %unreachable
313exn.dispatch.outer:
314  %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller
315catch2:
316  %catch.pad2 = catchpad within %catchswitch.outer [i32 2]
317; CHECK: .seh_proc [[test2_catch2:[^ ]+]]
318  catchret from %catch.pad2 to label %exit
319exit:
320  ret void
321unreachable:
322  unreachable
323; CHECK: [[test2_end:.*func_end.*]]:
324}
325
326; Now check for EH table in xdata (following standard xdata)
327; CHECK-LABEL: .section .xdata
328; standard xdata comes here
329; CHECK:      .long 3{{$}}
330;                   ^ number of funclets
331; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
332;                   ^ offset from L_begin to start of 1st funclet
333; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
334;                   ^ offset from L_begin to start of 2nd funclet
335; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
336;                   ^ offset from L_begin to start of 3rd funclet
337; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
338;                   ^ offset from L_begin to end of last funclet
339; CHECK-NEXT: .long 4
340;                   ^ number of EH clauses
341; Clause 1: call f(1) is guarded by fault
342; CHECK-NEXT: .long 4
343;                   ^ flags (4 => fault handler)
344; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
345;                   ^ offset of start of clause
346; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
347;                   ^ offset of end of clause
348; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
349;                   ^ offset of start of handler
350; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
351;                   ^ offset of end of handler
352; CHECK-NEXT: .long 0
353;                   ^ type token slot (null for fault)
354; Clause 2: call f(1) is also guarded by catch2
355; CHECK-NEXT: .long 0
356;                   ^ flags (0 => catch handler)
357; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
358;                   ^ offset of start of clause
359; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
360;                   ^ offset of end of clause
361; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
362;                   ^ offset of start of handler
363; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
364;                   ^ offset of end of handler
365; CHECK-NEXT: .long 2
366;                   ^ type token of catch (from catchpad)
367; Clause 3: calls f(2) is guarded by catch1
368; CHECK-NEXT: .long 0
369;                   ^ flags (0 => catch handler)
370; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
371;                   ^ offset of start of clause
372; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
373;                   ^ offset of end of clause
374; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
375;                   ^ offset of start of handler
376; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
377;                   ^ offset of end of handler
378; CHECK-NEXT: .long 1
379;                   ^ type token of catch (from catchpad)
380; Clause 4: call f(2) is also guarded by catch2
381;           This is a "duplicate" because the protected range (f(2))
382;           is in funclet fault but catch2's immediate parent
383;           is the main function, not that funclet.
384; CHECK-NEXT: .long 8
385;                   ^ flags (0 => catch handler | 8 => duplicate)
386; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
387;                   ^ offset of start of clause
388; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
389;                   ^ offset of end of clause
390; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
391;                   ^ offset of start of handler
392; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
393;                   ^ offset of end of handler
394; CHECK-NEXT: .long 2
395;                   ^ type token of catch (from catchpad)
396
397; Test with several cleanups that need to infer their unwind dests from each
398; other, the inner one needing to make the inference from an invoke, ignoring
399; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some
400; internal invokes/catchswitches
401;
402; Corresponds to something like:
403; void test3() {
404;   try {
405;     f(1);
406;   } fault { // fault1
407;     try {
408;       try {
409;         f(2);
410;         __unreachable();
411;       } fault { // fault2
412;         try {
413;           f(3);
414;         } fault { // fault3
415;           try {
416;             f(4);
417;           } fault { // fault4
418;             f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but
419;                    didn't bother to specify nounwind)
420;             try {
421;               try {
422;                 f(6);
423;               } catch(type 1) {
424;                 goto __unreachable;
425;               }
426;             } catch (type 2) { // marked "unwinds to caller" because we allow
427;                                // that if the unwind won't be taken (see
428;                                // SimplifyUnreachable & RemoveUnwindEdge)
429;               goto _unreachable;
430;             }
431;             f(7);
432;             __unreachable();
433;           }
434;         }
435;       }
436;     } fault { // fault 5
437;     }
438;   }
439; }
440;
441; CHECK-LABEL: test3:     # @test3
442; CHECK-NEXT: [[test3_begin:.*func_begin.*]]:
443define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
444entry:
445; CHECK: .seh_endprologue
446; CHECK: [[test3_before_f1:.+]]:
447; CHECK-NEXT: movl $1, %ecx
448; CHECK-NEXT: callq f
449; CHECK-NEXT: [[test3_after_f1:.+]]:
450  invoke void @f(i32 1)
451    to label %exit unwind label %fault1
452fault1:
453 ; check lines below since this gets reordered to end-of-func
454  %fault.pad1 = cleanuppad within none [i32 undef]
455  invoke void @f(i32 2) ["funclet"(token %fault.pad1)]
456    to label %unreachable unwind label %fault2
457fault2:
458 ; check lines below since this gets reordered to end-of-func
459  %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef]
460  invoke void @f(i32 3) ["funclet"(token %fault.pad2)]
461    to label %unreachable unwind label %fault3
462fault3:
463 ; check lines below since this gets reordered to end-of-func
464  %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef]
465  invoke void @f(i32 4) ["funclet"(token %fault.pad3)]
466    to label %unreachable unwind label %fault4
467fault4:
468; CHECK: .seh_proc [[test3_fault4:[^ ]+]]
469  %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef]
470; CHECK: .seh_endprologue
471  call void @f(i32 5) ["funclet"(token %fault.pad4)]
472; CHECK: [[test3_before_f6:.+]]:
473; CHECK-NEXT: movl $6, %ecx
474; CHECK-NEXT: callq f
475; CHECK-NEXT: [[test3_after_f6:.+]]:
476  invoke void @f(i32 6) ["funclet"(token %fault.pad4)]
477    to label %fault4.cont unwind label %exn.dispatch1
478fault4.cont:
479; CHECK: # %fault4.cont
480; CHECK: [[test3_before_f7:.+]]:
481; CHECK-NEXT: movl $7, %ecx
482; CHECK-NEXT: callq f
483; CHECK-NEXT: [[test3_after_f7:.+]]:
484  invoke void @f(i32 7) ["funclet"(token %fault.pad4)]
485    to label %unreachable unwind label %fault5
486exn.dispatch1:
487  %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2
488catch1:
489  %catch.pad1 = catchpad within %catchswitch1 [i32 1]
490; CHECK: .seh_proc [[test3_catch1:[^ ]+]]
491  catchret from %catch.pad1 to label %unreachable
492exn.dispatch2:
493  %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller
494catch2:
495  %catch.pad2 = catchpad within %catchswitch2 [i32 2]
496; CHECK: .seh_proc [[test3_catch2:[^ ]+]]
497  catchret from %catch.pad2 to label %unreachable
498fault5:
499; CHECK: .seh_proc [[test3_fault5:[^ ]+]]
500  %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef]
501; CHECK: .seh_endprologue
502cleanupret from %fault.pad5 unwind to caller
503exit:
504  ret void
505unreachable:
506  unreachable
507; CHECK: .seh_proc [[test3_fault3:[^ ]+]]
508; CHECK: # %fault3
509; CHECK: .seh_endprologue
510; CHECK: [[test3_before_f4:.+]]:
511; CHECK-NEXT: movl $4, %ecx
512; CHECK-NEXT: callq f
513; CHECK-NEXT: [[test3_after_f4:.+]]:
514; CHECK: .seh_proc [[test3_fault2:[^ ]+]]
515; CHECK: # %fault2
516; CHECK: .seh_endprologue
517; CHECK: [[test3_before_f3:.+]]:
518; CHECK-NEXT: movl $3, %ecx
519; CHECK-NEXT: callq f
520; CHECK-NEXT: [[test3_after_f3:.+]]:
521; CHECK: .seh_proc [[test3_fault1:[^ ]+]]
522; CHECK: # %fault1
523; CHECK: .seh_endprologue
524; CHECK: [[test3_before_f2:.+]]:
525; CHECK-NEXT: movl $2, %ecx
526; CHECK-NEXT: callq f
527; CHECK-NEXT: [[test3_after_f2:.+]]:
528; CHECK: [[test3_end:.*func_end.*]]:
529}
530
531; Now check for EH table in xdata (following standard xdata)
532; CHECK-LABEL: .section .xdata
533; standard xdata comes here
534; CHECK:      .long 7{{$}}
535;                   ^ number of funclets
536; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
537;                   ^ offset from L_begin to start of 1st funclet
538; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
539;                   ^ offset from L_begin to start of 2nd funclet
540; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
541;                   ^ offset from L_begin to start of 3rd funclet
542; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
543;                   ^ offset from L_begin to start of 4th funclet
544; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
545;                   ^ offset from L_begin to start of 5th funclet
546; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
547;                   ^ offset from L_begin to start of 6th funclet
548; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
549;                   ^ offset from L_begin to start of 7th funclet
550; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
551;                   ^ offset from L_begin to end of last funclet
552; CHECK-NEXT: .long 10
553;                   ^ number of EH clauses
554; Clause 1: call f(1) is guarded by fault1
555; CHECK-NEXT: .long 4
556;                   ^ flags (4 => fault handler)
557; CHECK-NEXT: .long ([[test3_before_f1]]-[[test3_begin]])+1
558;                   ^ offset of start of clause
559; CHECK-NEXT: .long ([[test3_after_f1]]-[[test3_begin]])+1
560;                   ^ offset of end of clause
561; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
562;                   ^ offset of start of handler
563; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
564;                   ^ offset of end of handler
565; CHECK-NEXT: .long 0
566;                   ^ type token slot (null for fault)
567; Clause 3: call f(6) is guarded by catch1
568; CHECK-NEXT: .long 0
569;                   ^ flags (0 => catch handler)
570; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
571;                   ^ offset of start of clause
572; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
573;                   ^ offset of end of clause
574; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
575;                   ^ offset of start of handler
576; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
577;                   ^ offset of end of handler
578; CHECK-NEXT: .long 1
579;                   ^ type token of catch (from catchpad)
580; Clause 3: call f(6) is also guarded by catch2
581; CHECK-NEXT: .long 0
582;                   ^ flags (0 => catch handler)
583; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
584;                   ^ offset of start of clause
585; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
586;                   ^ offset of end of clause
587; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
588;                   ^ offset of start of handler
589; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
590;                   ^ offset of end of handler
591; CHECK-NEXT: .long 2
592;                   ^ type token of catch (from catchpad)
593; Clause 4: call f(7) is guarded by fault5
594;           This is a "duplicate" because the protected range (f(6)-f(7))
595;           is in funclet fault4 but fault5's immediate parent
596;           is fault1, not that funclet.
597; CHECK-NEXT: .long 12
598;                   ^ flags (4 => fault handler | 8 => duplicate)
599; CHECK-NEXT: .long ([[test3_before_f7]]-[[test3_begin]])+1
600;                   ^ offset of start of clause
601; CHECK-NEXT: .long ([[test3_after_f7]]-[[test3_begin]])+1
602;                   ^ offset of end of clause
603; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
604;                   ^ offset of start of handler
605; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
606;                   ^ offset of end of handler
607; CHECK-NEXT: .long 0
608;                   ^ type token slot (null for fault)
609; Clause 5: call f(4) is guarded by fault4
610; CHECK-NEXT: .long 4
611;                   ^ flags (4 => fault handler)
612; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
613;                   ^ offset of start of clause
614; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
615;                   ^ offset of end of clause
616; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
617;                   ^ offset of start of handler
618; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
619;                   ^ offset of end of handler
620; CHECK-NEXT: .long 0
621;                   ^ type token slot (null for fault)
622; Clause 6: call f(4) is also guarded by fault5
623;           This is a "duplicate" because the protected range (f(4))
624;           is in funclet fault3 but fault5's immediate parent
625;           is fault1, not that funclet.
626; CHECK-NEXT: .long 12
627;                   ^ flags (4 => fault handler)
628; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
629;                   ^ offset of start of clause
630; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
631;                   ^ offset of end of clause
632; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
633;                   ^ offset of start of handler
634; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
635;                   ^ offset of end of handler
636; CHECK-NEXT: .long 0
637;                   ^ type token slot (null for fault)
638; Clause 7: call f(3) is guarded by fault3
639; CHECK-NEXT: .long 4
640;                   ^ flags (4 => fault handler)
641; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
642;                   ^ offset of start of clause
643; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
644;                   ^ offset of end of clause
645; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
646;                   ^ offset of start of handler
647; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
648;                   ^ offset of end of handler
649; CHECK-NEXT: .long 0
650;                   ^ type token slot (null for fault)
651; Clause 8: call f(3) is guarded by fault5
652;           This is a "duplicate" because the protected range (f(3))
653;           is in funclet fault2 but fault5's immediate parent
654;           is fault1, not that funclet.
655; CHECK-NEXT: .long 12
656;                   ^ flags (4 => fault handler | 8 => duplicate)
657; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
658;                   ^ offset of start of clause
659; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
660;                   ^ offset of end of clause
661; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
662;                   ^ offset of start of handler
663; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
664;                   ^ offset of end of handler
665; CHECK-NEXT: .long 0
666;                   ^ type token slot (null for fault)
667; Clause 9: call f(2) is guarded by fault2
668; CHECK-NEXT: .long 4
669;                   ^ flags (4 => fault handler)
670; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
671;                   ^ offset of start of clause
672; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
673;                   ^ offset of end of clause
674; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
675;                   ^ offset of start of handler
676; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
677;                   ^ offset of end of handler
678; CHECK-NEXT: .long 0
679;                   ^ type token slot (null for fault)
680; Clause 10: call f(2) is guarded by fault5
681; CHECK-NEXT: .long 4
682;                   ^ flags (4 => fault handler)
683; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
684;                   ^ offset of start of clause
685; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
686;                   ^ offset of end of clause
687; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
688;                   ^ offset of start of handler
689; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
690;                   ^ offset of end of handler
691; CHECK-NEXT: .long 0
692;                   ^ type token slot (null for fault)
693