• 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: [[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;
286define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
287entry:
288  invoke void @f(i32 1)
289    to label %exit unwind label %fault
290fault:
291  %fault.pad = cleanuppad within none [i32 undef]
292  invoke void @f(i32 2) ["funclet"(token %fault.pad)]
293    to label %unreachable unwind label %exn.dispatch.inner
294exn.dispatch.inner:
295  %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer
296catch1:
297  %catch.pad1 = catchpad within %catchswitch.inner [i32 1]
298  catchret from %catch.pad1 to label %unreachable
299exn.dispatch.outer:
300  %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller
301catch2:
302  %catch.pad2 = catchpad within %catchswitch.outer [i32 2]
303  catchret from %catch.pad2 to label %exit
304exit:
305  ret void
306unreachable:
307  unreachable
308}
309; CHECK-LABEL: test2:     # @test2
310; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:
311; CHECK: .seh_endprologue
312; CHECK: [[test2_before_f1:.+]]:
313; CHECK-NEXT: movl $1, %ecx
314; CHECK-NEXT: callq f
315; CHECK-NEXT: [[test2_after_f1:.+]]:
316; CHECK: .seh_proc [[test2_catch1:[^ ]+]]
317; CHECK: .seh_proc [[test2_catch2:[^ ]+]]
318; CHECK: .seh_proc [[test2_fault:[^ ]+]]
319; CHECK: .seh_endprologue
320; CHECK: [[test2_before_f2:.+]]:
321; CHECK-NEXT: movl $2, %ecx
322; CHECK-NEXT: callq f
323; CHECK-NEXT: [[test2_after_f2:.+]]:
324; CHECK: int3
325; CHECK: [[test2_end:.*func_end.*]]:
326
327
328; Now check for EH table in xdata (following standard xdata)
329; CHECK-LABEL: .section .xdata
330; standard xdata comes here
331; CHECK:      .long 3{{$}}
332;                   ^ number of funclets
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_fault]]-[[test2_begin]]
338;                   ^ offset from L_begin to start of 1st funclet
339; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
340;                   ^ offset from L_begin to end of last funclet
341; CHECK-NEXT: .long 4
342;                   ^ number of EH clauses
343; Clause 1: call f(1) is guarded by fault
344; CHECK-NEXT: .long 4
345;                   ^ flags (4 => fault handler)
346; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
347;                   ^ offset of start of clause
348; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
349;                   ^ offset of end of clause
350; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
351;                   ^ offset of start of handler
352; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
353;                   ^ offset of end of handler
354; CHECK-NEXT: .long 0
355;                   ^ type token slot (null for fault)
356; Clause 2: call f(1) is also guarded by catch2
357; CHECK-NEXT: .long 0
358;                   ^ flags (0 => catch handler)
359; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
360;                   ^ offset of start of clause
361; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
362;                   ^ offset of end of clause
363; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
364;                   ^ offset of start of handler
365; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
366;                   ^ offset of end of handler
367; CHECK-NEXT: .long 2
368;                   ^ type token of catch (from catchpad)
369; Clause 3: calls f(2) is guarded by catch1
370; CHECK-NEXT: .long 0
371;                   ^ flags (0 => catch handler)
372; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
373;                   ^ offset of start of clause
374; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
375;                   ^ offset of end of clause
376; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
377;                   ^ offset of start of handler
378; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
379;                   ^ offset of end of handler
380; CHECK-NEXT: .long 1
381;                   ^ type token of catch (from catchpad)
382; Clause 4: call f(2) is also guarded by catch2
383;           This is a "duplicate" because the protected range (f(2))
384;           is in funclet fault but catch2's immediate parent
385;           is the main function, not that funclet.
386; CHECK-NEXT: .long 8
387;                   ^ flags (0 => catch handler | 8 => duplicate)
388; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
389;                   ^ offset of start of clause
390; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
391;                   ^ offset of end of clause
392; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
393;                   ^ offset of start of handler
394; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
395;                   ^ offset of end of handler
396; CHECK-NEXT: .long 2
397;                   ^ type token of catch (from catchpad)
398
399; Test with several cleanups that need to infer their unwind dests from each
400; other, the inner one needing to make the inference from an invoke, ignoring
401; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some
402; internal invokes/catchswitches
403;
404; Corresponds to something like:
405; void test3() {
406;   try {
407;     f(1);
408;   } fault { // fault1
409;     try {
410;       try {
411;         f(2);
412;         __unreachable();
413;       } fault { // fault2
414;         try {
415;           f(3);
416;         } fault { // fault3
417;           try {
418;             f(4);
419;           } fault { // fault4
420;             f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but
421;                    didn't bother to specify nounwind)
422;             try {
423;               try {
424;                 f(6);
425;               } catch(type 1) {
426;                 goto __unreachable;
427;               }
428;             } catch (type 2) { // marked "unwinds to caller" because we allow
429;                                // that if the unwind won't be taken (see
430;                                // SimplifyUnreachable & RemoveUnwindEdge)
431;               goto _unreachable;
432;             }
433;             f(7);
434;             __unreachable();
435;           }
436;         }
437;       }
438;     } fault { // fault 5
439;     }
440;   }
441; }
442;
443; CHECK-LABEL: test3:     # @test3
444; CHECK-NEXT: [[test3_begin:.*func_begin.*]]:
445define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
446entry:
447; CHECK: .seh_endprologue
448; CHECK: [[test3_before_f1:.+]]:
449; CHECK-NEXT: movl $1, %ecx
450; CHECK-NEXT: callq f
451; CHECK-NEXT: [[test3_after_f1:.+]]:
452  invoke void @f(i32 1)
453    to label %exit unwind label %fault1
454fault1:
455 ; check lines below since this gets reordered to end-of-func
456  %fault.pad1 = cleanuppad within none [i32 undef]
457  invoke void @f(i32 2) ["funclet"(token %fault.pad1)]
458    to label %unreachable unwind label %fault2
459fault2:
460 ; check lines below since this gets reordered to end-of-func
461  %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef]
462  invoke void @f(i32 3) ["funclet"(token %fault.pad2)]
463    to label %unreachable unwind label %fault3
464fault3:
465 ; check lines below since this gets reordered to end-of-func
466  %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef]
467  invoke void @f(i32 4) ["funclet"(token %fault.pad3)]
468    to label %unreachable unwind label %fault4
469fault4:
470; CHECK: .seh_proc [[test3_fault4:[^ ]+]]
471  %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef]
472; CHECK: .seh_endprologue
473  call void @f(i32 5) ["funclet"(token %fault.pad4)]
474; CHECK: [[test3_before_f6:.+]]:
475; CHECK-NEXT: movl $6, %ecx
476; CHECK-NEXT: callq f
477; CHECK-NEXT: [[test3_after_f6:.+]]:
478  invoke void @f(i32 6) ["funclet"(token %fault.pad4)]
479    to label %fault4.cont unwind label %exn.dispatch1
480fault4.cont:
481; CHECK: # %fault4.cont
482; CHECK: [[test3_before_f7:.+]]:
483; CHECK-NEXT: movl $7, %ecx
484; CHECK-NEXT: callq f
485; CHECK-NEXT: [[test3_after_f7:.+]]:
486  invoke void @f(i32 7) ["funclet"(token %fault.pad4)]
487    to label %unreachable unwind label %fault5
488exn.dispatch1:
489  %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2
490catch1:
491  %catch.pad1 = catchpad within %catchswitch1 [i32 1]
492; CHECK: .seh_proc [[test3_catch1:[^ ]+]]
493  catchret from %catch.pad1 to label %unreachable
494exn.dispatch2:
495  %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller
496catch2:
497  %catch.pad2 = catchpad within %catchswitch2 [i32 2]
498; CHECK: .seh_proc [[test3_catch2:[^ ]+]]
499  catchret from %catch.pad2 to label %unreachable
500fault5:
501; CHECK: .seh_proc [[test3_fault5:[^ ]+]]
502  %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef]
503; CHECK: .seh_endprologue
504cleanupret from %fault.pad5 unwind to caller
505exit:
506  ret void
507unreachable:
508  unreachable
509; CHECK: .seh_proc [[test3_fault3:[^ ]+]]
510; CHECK: # %fault3
511; CHECK: .seh_endprologue
512; CHECK: [[test3_before_f4:.+]]:
513; CHECK-NEXT: movl $4, %ecx
514; CHECK-NEXT: callq f
515; CHECK-NEXT: [[test3_after_f4:.+]]:
516; CHECK: int3
517; CHECK: .seh_proc [[test3_fault2:[^ ]+]]
518; CHECK: # %fault2
519; CHECK: .seh_endprologue
520; CHECK: [[test3_before_f3:.+]]:
521; CHECK-NEXT: movl $3, %ecx
522; CHECK-NEXT: callq f
523; CHECK-NEXT: [[test3_after_f3:.+]]:
524; CHECK: int3
525; CHECK: .seh_proc [[test3_fault1:[^ ]+]]
526; CHECK: # %fault1
527; CHECK: .seh_endprologue
528; CHECK: [[test3_before_f2:.+]]:
529; CHECK-NEXT: movl $2, %ecx
530; CHECK-NEXT: callq f
531; CHECK-NEXT: [[test3_after_f2:.+]]:
532; CHECK: int3
533; CHECK: [[test3_end:.*func_end.*]]:
534}
535
536; Now check for EH table in xdata (following standard xdata)
537; CHECK-LABEL: .section .xdata
538; standard xdata comes here
539; CHECK:      .long 7{{$}}
540;                   ^ number of funclets
541; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
542;                   ^ offset from L_begin to start of 1st funclet
543; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
544;                   ^ offset from L_begin to start of 2nd funclet
545; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
546;                   ^ offset from L_begin to start of 3rd funclet
547; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
548;                   ^ offset from L_begin to start of 4th funclet
549; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
550;                   ^ offset from L_begin to start of 5th funclet
551; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
552;                   ^ offset from L_begin to start of 6th funclet
553; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
554;                   ^ offset from L_begin to start of 7th funclet
555; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
556;                   ^ offset from L_begin to end of last funclet
557; CHECK-NEXT: .long 10
558;                   ^ number of EH clauses
559; Clause 1: call f(1) is guarded by fault1
560; CHECK-NEXT: .long 4
561;                   ^ flags (4 => fault handler)
562; CHECK-NEXT: .long ([[test3_before_f1]]-[[test3_begin]])+1
563;                   ^ offset of start of clause
564; CHECK-NEXT: .long ([[test3_after_f1]]-[[test3_begin]])+1
565;                   ^ offset of end of clause
566; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
567;                   ^ offset of start of handler
568; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
569;                   ^ offset of end of handler
570; CHECK-NEXT: .long 0
571;                   ^ type token slot (null for fault)
572; Clause 3: call f(6) is guarded by catch1
573; CHECK-NEXT: .long 0
574;                   ^ flags (0 => catch handler)
575; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
576;                   ^ offset of start of clause
577; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
578;                   ^ offset of end of clause
579; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
580;                   ^ offset of start of handler
581; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
582;                   ^ offset of end of handler
583; CHECK-NEXT: .long 1
584;                   ^ type token of catch (from catchpad)
585; Clause 3: call f(6) is also guarded by catch2
586; CHECK-NEXT: .long 0
587;                   ^ flags (0 => catch handler)
588; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
589;                   ^ offset of start of clause
590; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
591;                   ^ offset of end of clause
592; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
593;                   ^ offset of start of handler
594; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
595;                   ^ offset of end of handler
596; CHECK-NEXT: .long 2
597;                   ^ type token of catch (from catchpad)
598; Clause 4: call f(7) is guarded by fault5
599;           This is a "duplicate" because the protected range (f(6)-f(7))
600;           is in funclet fault4 but fault5's immediate parent
601;           is fault1, not that funclet.
602; CHECK-NEXT: .long 12
603;                   ^ flags (4 => fault handler | 8 => duplicate)
604; CHECK-NEXT: .long ([[test3_before_f7]]-[[test3_begin]])+1
605;                   ^ offset of start of clause
606; CHECK-NEXT: .long ([[test3_after_f7]]-[[test3_begin]])+1
607;                   ^ offset of end of clause
608; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
609;                   ^ offset of start of handler
610; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
611;                   ^ offset of end of handler
612; CHECK-NEXT: .long 0
613;                   ^ type token slot (null for fault)
614; Clause 5: call f(4) is guarded by fault4
615; CHECK-NEXT: .long 4
616;                   ^ flags (4 => fault handler)
617; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
618;                   ^ offset of start of clause
619; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
620;                   ^ offset of end of clause
621; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
622;                   ^ offset of start of handler
623; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
624;                   ^ offset of end of handler
625; CHECK-NEXT: .long 0
626;                   ^ type token slot (null for fault)
627; Clause 6: call f(4) is also guarded by fault5
628;           This is a "duplicate" because the protected range (f(4))
629;           is in funclet fault3 but fault5's immediate parent
630;           is fault1, not that funclet.
631; CHECK-NEXT: .long 12
632;                   ^ flags (4 => fault handler)
633; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
634;                   ^ offset of start of clause
635; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
636;                   ^ offset of end of clause
637; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
638;                   ^ offset of start of handler
639; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
640;                   ^ offset of end of handler
641; CHECK-NEXT: .long 0
642;                   ^ type token slot (null for fault)
643; Clause 7: call f(3) is guarded by fault3
644; CHECK-NEXT: .long 4
645;                   ^ flags (4 => fault handler)
646; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
647;                   ^ offset of start of clause
648; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
649;                   ^ offset of end of clause
650; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
651;                   ^ offset of start of handler
652; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
653;                   ^ offset of end of handler
654; CHECK-NEXT: .long 0
655;                   ^ type token slot (null for fault)
656; Clause 8: call f(3) is guarded by fault5
657;           This is a "duplicate" because the protected range (f(3))
658;           is in funclet fault2 but fault5's immediate parent
659;           is fault1, not that funclet.
660; CHECK-NEXT: .long 12
661;                   ^ flags (4 => fault handler | 8 => duplicate)
662; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
663;                   ^ offset of start of clause
664; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
665;                   ^ offset of end of clause
666; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
667;                   ^ offset of start of handler
668; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
669;                   ^ offset of end of handler
670; CHECK-NEXT: .long 0
671;                   ^ type token slot (null for fault)
672; Clause 9: call f(2) is guarded by fault2
673; CHECK-NEXT: .long 4
674;                   ^ flags (4 => fault handler)
675; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
676;                   ^ offset of start of clause
677; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
678;                   ^ offset of end of clause
679; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
680;                   ^ offset of start of handler
681; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
682;                   ^ offset of end of handler
683; CHECK-NEXT: .long 0
684;                   ^ type token slot (null for fault)
685; Clause 10: call f(2) is guarded by fault5
686; CHECK-NEXT: .long 4
687;                   ^ flags (4 => fault handler)
688; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
689;                   ^ offset of start of clause
690; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
691;                   ^ offset of end of clause
692; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
693;                   ^ offset of start of handler
694; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
695;                   ^ offset of end of handler
696; CHECK-NEXT: .long 0
697;                   ^ type token slot (null for fault)
698