694 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			LLVM
		
	
	
	
			
		
		
	
	
			694 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			LLVM
		
	
	
	
; RUN: llc -mtriple=x86_64-pc-windows-coreclr -verify-machineinstrs < %s | FileCheck %s
 | 
						|
 | 
						|
declare void @ProcessCLRException()
 | 
						|
declare void @f(i32)
 | 
						|
declare void @g(i8 addrspace(1)*)
 | 
						|
declare i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token)
 | 
						|
 | 
						|
; Simplified IR for pseudo-C# like the following:
 | 
						|
; void test1() {
 | 
						|
;   try {
 | 
						|
;     f(1);
 | 
						|
;     try {
 | 
						|
;       f(2);
 | 
						|
;     } catch (type1) {
 | 
						|
;       f(3);
 | 
						|
;     } catch (type2) {
 | 
						|
;       f(4);
 | 
						|
;       try {
 | 
						|
;         f(5);
 | 
						|
;       } fault {
 | 
						|
;         f(6);
 | 
						|
;       }
 | 
						|
;     }
 | 
						|
;   } finally {
 | 
						|
;     f(7);
 | 
						|
;   }
 | 
						|
;   f(8);
 | 
						|
; }
 | 
						|
;
 | 
						|
; CHECK-LABEL: test1:     # @test1
 | 
						|
; CHECK-NEXT: [[test1_begin:.*func_begin.*]]:
 | 
						|
define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
 | 
						|
entry:
 | 
						|
; CHECK: # %entry
 | 
						|
; CHECK: leaq [[FPOffset:[0-9]+]](%rsp), %rbp
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK: movq %rsp, [[PSPSymOffset:[0-9]+]](%rsp)
 | 
						|
; CHECK: [[test1_before_f1:.+]]:
 | 
						|
; CHECK-NEXT: movl $1, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test1_after_f1:.+]]:
 | 
						|
  invoke void @f(i32 1)
 | 
						|
    to label %inner_try unwind label %finally
 | 
						|
inner_try:
 | 
						|
; CHECK: # %inner_try
 | 
						|
; CHECK: [[test1_before_f2:.+]]:
 | 
						|
; CHECK-NEXT: movl $2, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test1_after_f2:.+]]:
 | 
						|
  invoke void @f(i32 2)
 | 
						|
    to label %finally.clone unwind label %exn.dispatch
 | 
						|
exn.dispatch:
 | 
						|
  %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally
 | 
						|
catch1:
 | 
						|
  %catch.pad1 = catchpad within %catchswitch [i32 1]
 | 
						|
; CHECK: .seh_proc [[test1_catch1:[^ ]+]]
 | 
						|
; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
 | 
						|
;                        ^ all funclets use the same frame size
 | 
						|
; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
 | 
						|
;                              ^ establisher frame pointer passed in rcx
 | 
						|
; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
 | 
						|
; CHECK: leaq [[FPOffset]](%rcx), %rbp
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK: movq %rdx, %rcx
 | 
						|
;             ^ exception pointer passed in rdx
 | 
						|
; CHECK-NEXT: callq g
 | 
						|
  %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad1)
 | 
						|
  call void @g(i8 addrspace(1)* %exn1) [ "funclet"(token %catch.pad1) ]
 | 
						|
; CHECK: [[test1_before_f3:.+]]:
 | 
						|
; CHECK-NEXT: movl $3, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test1_after_f3:.+]]:
 | 
						|
  invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ]
 | 
						|
    to label %catch1.ret unwind label %finally
 | 
						|
catch1.ret:
 | 
						|
  catchret from %catch.pad1 to label %finally.clone
 | 
						|
catch2:
 | 
						|
  %catch.pad2 = catchpad within %catchswitch [i32 2]
 | 
						|
; CHECK: .seh_proc [[test1_catch2:[^ ]+]]
 | 
						|
; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
 | 
						|
;                        ^ all funclets use the same frame size
 | 
						|
; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
 | 
						|
;                              ^ establisher frame pointer passed in rcx
 | 
						|
; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
 | 
						|
; CHECK: leaq [[FPOffset]](%rcx), %rbp
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK: movq %rdx, %rcx
 | 
						|
;             ^ exception pointer passed in rdx
 | 
						|
; CHECK-NEXT: callq g
 | 
						|
  %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad2)
 | 
						|
  call void @g(i8 addrspace(1)* %exn2) [ "funclet"(token %catch.pad2) ]
 | 
						|
; CHECK: [[test1_before_f4:.+]]:
 | 
						|
; CHECK-NEXT: movl $4, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test1_after_f4:.+]]:
 | 
						|
  invoke void @f(i32 4) [ "funclet"(token %catch.pad2) ]
 | 
						|
    to label %try_in_catch unwind label %finally
 | 
						|
try_in_catch:
 | 
						|
; CHECK: # %try_in_catch
 | 
						|
; CHECK: [[test1_before_f5:.+]]:
 | 
						|
; CHECK-NEXT: movl $5, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test1_after_f5:.+]]:
 | 
						|
  invoke void @f(i32 5) [ "funclet"(token %catch.pad2) ]
 | 
						|
    to label %catch2.ret unwind label %fault
 | 
						|
fault:
 | 
						|
; CHECK: .seh_proc [[test1_fault:[^ ]+]]
 | 
						|
  %fault.pad = cleanuppad within %catch.pad2 [i32 undef]
 | 
						|
; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
 | 
						|
;                        ^ all funclets use the same frame size
 | 
						|
; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
 | 
						|
;                              ^ establisher frame pointer passed in rcx
 | 
						|
; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
 | 
						|
; CHECK: leaq [[FPOffset]](%rcx), %rbp
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK: [[test1_before_f6:.+]]:
 | 
						|
; CHECK-NEXT: movl $6, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test1_after_f6:.+]]:
 | 
						|
  invoke void @f(i32 6) [ "funclet"(token %fault.pad) ]
 | 
						|
    to label %fault.ret unwind label %finally
 | 
						|
fault.ret:
 | 
						|
  cleanupret from %fault.pad unwind label %finally
 | 
						|
catch2.ret:
 | 
						|
  catchret from %catch.pad2 to label %finally.clone
 | 
						|
finally.clone:
 | 
						|
  call void @f(i32 7)
 | 
						|
  br label %tail
 | 
						|
finally:
 | 
						|
; CHECK: .seh_proc [[test1_finally:[^ ]+]]
 | 
						|
  %finally.pad = cleanuppad within none []
 | 
						|
; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
 | 
						|
;                        ^ all funclets use the same frame size
 | 
						|
; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
 | 
						|
;                              ^ establisher frame pointer passed in rcx
 | 
						|
; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
 | 
						|
; CHECK: leaq [[FPOffset]](%rcx), %rbp
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK-NEXT: movl $7, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
  call void @f(i32 7) [ "funclet"(token %finally.pad) ]
 | 
						|
  cleanupret from %finally.pad unwind to caller
 | 
						|
tail:
 | 
						|
  call void @f(i32 8)
 | 
						|
  ret void
 | 
						|
; CHECK: [[test1_end:.*func_end.*]]:
 | 
						|
}
 | 
						|
 | 
						|
; Now check for EH table in xdata (following standard xdata)
 | 
						|
; CHECK-LABEL: .section .xdata
 | 
						|
; standard xdata comes here
 | 
						|
; CHECK:      .long 4{{$}}
 | 
						|
;                   ^ number of funclets
 | 
						|
; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]
 | 
						|
;                   ^ offset from L_begin to start of 1st funclet
 | 
						|
; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
 | 
						|
;                   ^ offset from L_begin to start of 2nd funclet
 | 
						|
; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
 | 
						|
;                   ^ offset from L_begin to start of 3rd funclet
 | 
						|
; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
 | 
						|
;                   ^ offset from L_begin to start of 4th funclet
 | 
						|
; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
 | 
						|
;                   ^ offset from L_begin to end of last funclet
 | 
						|
; CHECK-NEXT: .long 7
 | 
						|
;                   ^ number of EH clauses
 | 
						|
; Clause 1: call f(2) is guarded by catch1
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ flags (0 => catch handler)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 1
 | 
						|
;                   ^ type token of catch (from catchpad)
 | 
						|
; Clause 2: call f(2) is also guarded by catch2
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ flags (0 => catch handler)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 2
 | 
						|
;                   ^ type token of catch (from catchpad)
 | 
						|
; Clause 3: calls f(1) and f(2) are guarded by finally
 | 
						|
; CHECK-NEXT: .long 2
 | 
						|
;                   ^ flags (2 => finally handler)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f1]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ type token slot (null for finally)
 | 
						|
; Clause 4: call f(3) is guarded by finally
 | 
						|
;           This is a "duplicate" because the protected range (f(3))
 | 
						|
;           is in funclet catch1 but the finally's immediate parent
 | 
						|
;           is the main function, not that funclet.
 | 
						|
; CHECK-NEXT: .long 10
 | 
						|
;                   ^ flags (2 => finally handler | 8 => duplicate)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f3]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f3]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ type token slot (null for finally)
 | 
						|
; Clause 5: call f(5) is guarded by fault
 | 
						|
; CHECK-NEXT: .long 4
 | 
						|
;                   ^ flags (4 => fault handler)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f5]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ type token slot (null for fault)
 | 
						|
; Clause 6: calls f(4) and f(5) are guarded by finally
 | 
						|
;           This is a "duplicate" because the protected range (f(4)-f(5))
 | 
						|
;           is in funclet catch2 but the finally's immediate parent
 | 
						|
;           is the main function, not that funclet.
 | 
						|
; CHECK-NEXT: .long 10
 | 
						|
;                   ^ flags (2 => finally handler | 8 => duplicate)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f4]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ type token slot (null for finally)
 | 
						|
; Clause 7: call f(6) is guarded by finally
 | 
						|
;           This is a "duplicate" because the protected range (f(3))
 | 
						|
;           is in funclet catch1 but the finally's immediate parent
 | 
						|
;           is the main function, not that funclet.
 | 
						|
; CHECK-NEXT: .long 10
 | 
						|
;                   ^ flags (2 => finally handler | 8 => duplicate)
 | 
						|
; CHECK-NEXT: .long ([[test1_before_f6]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of start of clause
 | 
						|
; CHECK-NEXT: .long ([[test1_after_f6]]-[[test1_begin]])+1
 | 
						|
;                   ^ offset of end of clause
 | 
						|
; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
 | 
						|
;                   ^ offset of start of handler
 | 
						|
; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
 | 
						|
;                   ^ offset of end of handler
 | 
						|
; CHECK-NEXT: .long 0
 | 
						|
;                   ^ type token slot (null for finally)
 | 
						|
 | 
						|
; 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) {
 | 
						|
;   }
 | 
						|
; }
 | 
						|
;
 | 
						|
define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
 | 
						|
entry:
 | 
						|
  invoke void @f(i32 1)
 | 
						|
    to label %exit unwind label %fault
 | 
						|
fault:
 | 
						|
  %fault.pad = cleanuppad within none [i32 undef]
 | 
						|
  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]
 | 
						|
  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]
 | 
						|
  catchret from %catch.pad2 to label %exit
 | 
						|
exit:
 | 
						|
  ret void
 | 
						|
unreachable:
 | 
						|
  unreachable
 | 
						|
}
 | 
						|
; CHECK-LABEL: test2:     # @test2
 | 
						|
; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK: [[test2_before_f1:.+]]:
 | 
						|
; CHECK-NEXT: movl $1, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test2_after_f1:.+]]:
 | 
						|
; CHECK: .seh_proc [[test2_catch1:[^ ]+]]
 | 
						|
; CHECK: .seh_proc [[test2_catch2:[^ ]+]]
 | 
						|
; CHECK: .seh_proc [[test2_fault:[^ ]+]]
 | 
						|
; CHECK: .seh_endprologue
 | 
						|
; CHECK: [[test2_before_f2:.+]]:
 | 
						|
; CHECK-NEXT: movl $2, %ecx
 | 
						|
; CHECK-NEXT: callq f
 | 
						|
; CHECK-NEXT: [[test2_after_f2:.+]]:
 | 
						|
; 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_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_fault]]-[[test2_begin]]
 | 
						|
;                   ^ offset from L_begin to start of 1st 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_end]]-[[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_fault]]-[[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_fault]]-[[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)
 |