+
+; Test with a cleanup that has no cleanupret, and thus needs its unwind dest
+; inferred from an inner catchswitch
+;
+; corresponds to C# along the lines of:
+; void test2() {
+; try {
+; try {
+; f(1);
+; } fault {
+; try {
+; f(2);
+; } catch(type1) {
+; }
+; __unreachable();
+; }
+; } catch(type2) {
+; }
+; }
+;
+; CHECK-LABEL: test2: # @test2
+; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:
+define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
+entry:
+; CHECK: .seh_endprologue
+; CHECK: [[test2_before_f1:.+]]:
+; CHECK-NEXT: movl $1, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test2_after_f1:.+]]:
+ invoke void @f(i32 1)
+ to label %exit unwind label %fault
+fault:
+; CHECK: .seh_proc [[test2_fault:[^ ]+]]
+ %fault.pad = cleanuppad within none [i32 undef]
+; CHECK: .seh_endprologue
+; CHECK: [[test2_before_f2:.+]]:
+; CHECK-NEXT: movl $2, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test2_after_f2:.+]]:
+ invoke void @f(i32 2) ["funclet"(token %fault.pad)]
+ to label %unreachable unwind label %exn.dispatch.inner
+exn.dispatch.inner:
+ %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer
+catch1:
+ %catch.pad1 = catchpad within %catchswitch.inner [i32 1]
+; CHECK: .seh_proc [[test2_catch1:[^ ]+]]
+ catchret from %catch.pad1 to label %unreachable
+exn.dispatch.outer:
+ %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller
+catch2:
+ %catch.pad2 = catchpad within %catchswitch.outer [i32 2]
+; CHECK: .seh_proc [[test2_catch2:[^ ]+]]
+ catchret from %catch.pad2 to label %exit
+exit:
+ ret void
+unreachable:
+ unreachable
+; CHECK: [[test2_end:.*func_end.*]]:
+}
+
+; Now check for EH table in xdata (following standard xdata)
+; CHECK-LABEL: .section .xdata
+; standard xdata comes here
+; CHECK: .long 3{{$}}
+; ^ number of funclets
+; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
+; ^ offset from L_begin to start of 1st funclet
+; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
+; ^ offset from L_begin to start of 2nd funclet
+; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
+; ^ offset from L_begin to start of 3rd funclet
+; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
+; ^ offset from L_begin to end of last funclet
+; CHECK-NEXT: .long 4
+; ^ number of EH clauses
+; Clause 1: call f(1) is guarded by fault
+; CHECK-NEXT: .long 4
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 2: call f(1) is also guarded by catch2
+; CHECK-NEXT: .long 0
+; ^ flags (0 => catch handler)
+; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 2
+; ^ type token of catch (from catchpad)
+; Clause 3: calls f(2) is guarded by catch1
+; CHECK-NEXT: .long 0
+; ^ flags (0 => catch handler)
+; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 1
+; ^ type token of catch (from catchpad)
+; Clause 4: call f(2) is also guarded by catch2
+; This is a "duplicate" because the protected range (f(2))
+; is in funclet fault but catch2's immediate parent
+; is the main function, not that funclet.
+; CHECK-NEXT: .long 8
+; ^ flags (0 => catch handler | 8 => duplicate)
+; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 2
+; ^ type token of catch (from catchpad)
+
+; Test with several cleanups that need to infer their unwind dests from each
+; other, the inner one needing to make the inference from an invoke, ignoring
+; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some
+; internal invokes/catchswitches
+;
+; Corresponds to something like:
+; void test3() {
+; try {
+; f(1);
+; } fault { // fault1
+; try {
+; try {
+; f(2);
+; __unreachable();
+; } fault { // fault2
+; try {
+; f(3);
+; } fault { // fault3
+; try {
+; f(4);
+; } fault { // fault4
+; f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but
+; didn't bother to specify nounwind)
+; try {
+; try {
+; f(6);
+; } catch(type 1) {
+; goto __unreachable;
+; }
+; } catch (type 2) { // marked "unwinds to caller" because we allow
+; // that if the unwind won't be taken (see
+; // SimplifyUnreachable & RemoveUnwindEdge)
+; goto _unreachable;
+; }
+; f(7);
+; __unreachable();
+; }
+; }
+; }
+; } fault { // fault 5
+; }
+; }
+; }
+;
+; CHECK-LABEL: test3: # @test3
+; CHECK-NEXT: [[test3_begin:.*func_begin.*]]:
+define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
+entry:
+; CHECK: .seh_endprologue
+; CHECK: [[test3_before_f1:.+]]:
+; CHECK-NEXT: movl $1, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test3_after_f1:.+]]:
+ invoke void @f(i32 1)
+ to label %exit unwind label %fault1
+fault1:
+ ; check lines below since this gets reordered to end-of-func
+ %fault.pad1 = cleanuppad within none [i32 undef]
+ invoke void @f(i32 2) ["funclet"(token %fault.pad1)]
+ to label %unreachable unwind label %fault2
+fault2:
+ ; check lines below since this gets reordered to end-of-func
+ %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef]
+ invoke void @f(i32 3) ["funclet"(token %fault.pad2)]
+ to label %unreachable unwind label %fault3
+fault3:
+ ; check lines below since this gets reordered to end-of-func
+ %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef]
+ invoke void @f(i32 4) ["funclet"(token %fault.pad3)]
+ to label %unreachable unwind label %fault4
+fault4:
+; CHECK: .seh_proc [[test3_fault4:[^ ]+]]
+ %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef]
+; CHECK: .seh_endprologue
+ call void @f(i32 5) ["funclet"(token %fault.pad4)]
+; CHECK: [[test3_before_f6:.+]]:
+; CHECK-NEXT: movl $6, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test3_after_f6:.+]]:
+ invoke void @f(i32 6) ["funclet"(token %fault.pad4)]
+ to label %fault4.cont unwind label %exn.dispatch1
+fault4.cont:
+; CHECK: # %fault4.cont
+; CHECK: [[test3_before_f7:.+]]:
+; CHECK-NEXT: movl $7, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test3_after_f7:.+]]:
+ invoke void @f(i32 7) ["funclet"(token %fault.pad4)]
+ to label %unreachable unwind label %fault5
+exn.dispatch1:
+ %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2
+catch1:
+ %catch.pad1 = catchpad within %catchswitch1 [i32 1]
+; CHECK: .seh_proc [[test3_catch1:[^ ]+]]
+ catchret from %catch.pad1 to label %unreachable
+exn.dispatch2:
+ %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller
+catch2:
+ %catch.pad2 = catchpad within %catchswitch2 [i32 2]
+; CHECK: .seh_proc [[test3_catch2:[^ ]+]]
+ catchret from %catch.pad2 to label %unreachable
+fault5:
+; CHECK: .seh_proc [[test3_fault5:[^ ]+]]
+ %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef]
+; CHECK: .seh_endprologue
+cleanupret from %fault.pad5 unwind to caller
+exit:
+ ret void
+unreachable:
+ unreachable
+; CHECK: .seh_proc [[test3_fault3:[^ ]+]]
+; CHECK: # %fault3
+; CHECK: .seh_endprologue
+; CHECK: [[test3_before_f4:.+]]:
+; CHECK-NEXT: movl $4, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test3_after_f4:.+]]:
+; CHECK: .seh_proc [[test3_fault2:[^ ]+]]
+; CHECK: # %fault2
+; CHECK: .seh_endprologue
+; CHECK: [[test3_before_f3:.+]]:
+; CHECK-NEXT: movl $3, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test3_after_f3:.+]]:
+; CHECK: .seh_proc [[test3_fault1:[^ ]+]]
+; CHECK: # %fault1
+; CHECK: .seh_endprologue
+; CHECK: [[test3_before_f2:.+]]:
+; CHECK-NEXT: movl $2, %ecx
+; CHECK-NEXT: callq f
+; CHECK-NEXT: [[test3_after_f2:.+]]:
+; CHECK: [[test3_end:.*func_end.*]]:
+}
+
+; Now check for EH table in xdata (following standard xdata)
+; CHECK-LABEL: .section .xdata
+; standard xdata comes here
+; CHECK: .long 7{{$}}
+; ^ number of funclets
+; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
+; ^ offset from L_begin to start of 1st funclet
+; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
+; ^ offset from L_begin to start of 2nd funclet
+; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
+; ^ offset from L_begin to start of 3rd funclet
+; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
+; ^ offset from L_begin to start of 4th funclet
+; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
+; ^ offset from L_begin to start of 5th funclet
+; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
+; ^ offset from L_begin to start of 6th funclet
+; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
+; ^ offset from L_begin to start of 7th funclet
+; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
+; ^ offset from L_begin to end of last funclet
+; CHECK-NEXT: .long 10
+; ^ number of EH clauses
+; Clause 1: call f(1) is guarded by fault1
+; CHECK-NEXT: .long 4
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test3_before_f1]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f1]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 3: call f(6) is guarded by catch1
+; CHECK-NEXT: .long 0
+; ^ flags (0 => catch handler)
+; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 1
+; ^ type token of catch (from catchpad)
+; Clause 3: call f(6) is also guarded by catch2
+; CHECK-NEXT: .long 0
+; ^ flags (0 => catch handler)
+; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 2
+; ^ type token of catch (from catchpad)
+; Clause 4: call f(7) is guarded by fault5
+; This is a "duplicate" because the protected range (f(6)-f(7))
+; is in funclet fault4 but fault5's immediate parent
+; is fault1, not that funclet.
+; CHECK-NEXT: .long 12
+; ^ flags (4 => fault handler | 8 => duplicate)
+; CHECK-NEXT: .long ([[test3_before_f7]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f7]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 5: call f(4) is guarded by fault4
+; CHECK-NEXT: .long 4
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 6: call f(4) is also guarded by fault5
+; This is a "duplicate" because the protected range (f(4))
+; is in funclet fault3 but fault5's immediate parent
+; is fault1, not that funclet.
+; CHECK-NEXT: .long 12
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 7: call f(3) is guarded by fault3
+; CHECK-NEXT: .long 4
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 8: call f(3) is guarded by fault5
+; This is a "duplicate" because the protected range (f(3))
+; is in funclet fault2 but fault5's immediate parent
+; is fault1, not that funclet.
+; CHECK-NEXT: .long 12
+; ^ flags (4 => fault handler | 8 => duplicate)
+; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 9: call f(2) is guarded by fault2
+; CHECK-NEXT: .long 4
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)
+; Clause 10: call f(2) is guarded by fault5
+; CHECK-NEXT: .long 4
+; ^ flags (4 => fault handler)
+; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
+; ^ offset of start of clause
+; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
+; ^ offset of end of clause
+; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
+; ^ offset of start of handler
+; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
+; ^ offset of end of handler
+; CHECK-NEXT: .long 0
+; ^ type token slot (null for fault)