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: [[test1_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: [[test1_before_f1:.+]]:
39 ; CHECK-NEXT: movl $1, %ecx
41 ; CHECK-NEXT: [[test1_after_f1:.+]]:
43 to label %inner_try unwind label %finally
46 ; CHECK: [[test1_before_f2:.+]]:
47 ; CHECK-NEXT: movl $2, %ecx
49 ; CHECK-NEXT: [[test1_after_f2:.+]]:
51 to label %finally.clone unwind label %exn.dispatch
53 %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally
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
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
72 ; CHECK-NEXT: [[test1_after_f3:.+]]:
73 invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ]
74 to label %catch1.ret unwind label %finally
76 catchret from %catch.pad1 to label %finally.clone
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
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
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
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
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
123 cleanupret from %fault.pad unwind label %finally
125 catchret from %catch.pad2 to label %finally.clone
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
146 ; CHECK: [[test1_end:.*func_end.*]]:
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)
267 ; Test with a cleanup that has no cleanupret, and thus needs its unwind dest
268 ; inferred from an inner catchswitch
270 ; corresponds to C# along the lines of:
286 ; CHECK-LABEL: test2: # @test2
287 ; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:
288 define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
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
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
308 %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer
310 %catch.pad1 = catchpad within %catchswitch.inner [i32 1]
311 ; CHECK: .seh_proc [[test2_catch1:[^ ]+]]
312 catchret from %catch.pad1 to label %unreachable
314 %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller
316 %catch.pad2 = catchpad within %catchswitch.outer [i32 2]
317 ; CHECK: .seh_proc [[test2_catch2:[^ ]+]]
318 catchret from %catch.pad2 to label %exit
323 ; CHECK: [[test2_end:.*func_end.*]]:
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)
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
402 ; Corresponds to something like:
406 ; } fault { // fault1
411 ; } fault { // fault2
414 ; } fault { // fault3
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)
424 ; goto __unreachable;
426 ; } catch (type 2) { // marked "unwinds to caller" because we allow
427 ; // that if the unwind won't be taken (see
428 ; // SimplifyUnreachable & RemoveUnwindEdge)
436 ; } fault { // fault 5
441 ; CHECK-LABEL: test3: # @test3
442 ; CHECK-NEXT: [[test3_begin:.*func_begin.*]]:
443 define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
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
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
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
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
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
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
487 %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2
489 %catch.pad1 = catchpad within %catchswitch1 [i32 1]
490 ; CHECK: .seh_proc [[test3_catch1:[^ ]+]]
491 catchret from %catch.pad1 to label %unreachable
493 %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller
495 %catch.pad2 = catchpad within %catchswitch2 [i32 2]
496 ; CHECK: .seh_proc [[test3_catch2:[^ ]+]]
497 catchret from %catch.pad2 to label %unreachable
499 ; CHECK: .seh_proc [[test3_fault5:[^ ]+]]
500 %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef]
501 ; CHECK: .seh_endprologue
502 cleanupret from %fault.pad5 unwind to caller
507 ; CHECK: .seh_proc [[test3_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:[^ ]+]]
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:[^ ]+]]
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.*]]:
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)