llvm/test/CodeGen/WinEH/wineh-coreclr.ll
Joseph Tremoulet a736fb5421 [WinEH] Find root frame correctly in CLR funclets
Summary:
The value that the CoreCLR personality passes to a funclet for the
establisher frame may be the root function's frame or may be the parent
funclet's (mostly empty) frame in the case of nested funclets.  Each
funclet stores a pointer to the root frame in its own (mostly empty)
frame, as does the root function itself.  All frames allocate this slot at
the same offset, measured from the post-prolog stack pointer, so that the
same sequence can accept any ancestor as an establisher frame parameter
value, and so that a single offset can be reported to the GC, which also
looks at this slot.

This change allocate the slot when processing function entry, and records
its frame index on the WinEHFuncInfo object, then inserts the code to
set/copy it during prolog emission.


Reviewers: majnemer, AndyAyers, pgavlin, rnk

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D14614

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252983 91177308-0d34-0410-b5e6-96231b3b80d8
2015-11-13 00:39:23 +00:00

278 lines
10 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: [[L_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: [[L_before_f1:.+]]:
; CHECK-NEXT: movl $1, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f1:.+]]:
invoke void @f(i32 1)
to label %inner_try unwind label %finally.pad
inner_try:
; CHECK: # %inner_try
; CHECK: [[L_before_f2:.+]]:
; CHECK-NEXT: movl $2, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f2:.+]]:
invoke void @f(i32 2)
to label %finally.clone unwind label %catch1.pad
catch1.pad:
; CHECK: .seh_proc [[L_catch1:[^ ]+]]
%catch1 = catchpad [i32 1]
to label %catch1.body unwind label %catch2.pad
catch1.body:
; 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 %catch1)
call void @g(i8 addrspace(1)* %exn1)
; CHECK: [[L_before_f3:.+]]:
; CHECK-NEXT: movl $3, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f3:.+]]:
invoke void @f(i32 3)
to label %catch1.ret unwind label %catch.end
catch1.ret:
catchret %catch1 to label %finally.clone
catch2.pad:
; CHECK: .seh_proc [[L_catch2:[^ ]+]]
%catch2 = catchpad [i32 2]
to label %catch2.body unwind label %catch.end
catch2.body:
; 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 %catch2)
call void @g(i8 addrspace(1)* %exn2)
; CHECK: [[L_before_f4:.+]]:
; CHECK-NEXT: movl $4, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f4:.+]]:
invoke void @f(i32 4)
to label %try_in_catch unwind label %catch.end
try_in_catch:
; CHECK: # %try_in_catch
; CHECK: [[L_before_f5:.+]]:
; CHECK-NEXT: movl $5, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f5:.+]]:
invoke void @f(i32 5)
to label %catch2.ret unwind label %fault.pad
fault.pad:
; CHECK: .seh_proc [[L_fault:[^ ]+]]
%fault = cleanuppad [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: [[L_before_f6:.+]]:
; CHECK-NEXT: movl $6, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f6:.+]]:
invoke void @f(i32 6)
to label %fault.ret unwind label %fault.end
fault.ret:
cleanupret %fault unwind label %catch.end
fault.end:
cleanupendpad %fault unwind label %catch.end
catch2.ret:
catchret %catch2 to label %finally.clone
catch.end:
catchendpad unwind label %finally.pad
finally.clone:
call void @f(i32 7)
br label %tail
finally.pad:
; CHECK: .seh_proc [[L_finally:[^ ]+]]
%finally = cleanuppad []
; 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: [[L_before_f7:.+]]:
; CHECK-NEXT: movl $7, %ecx
; CHECK-NEXT: callq f
; CHECK-NEXT: [[L_after_f7:.+]]:
invoke void @f(i32 7)
to label %finally.ret unwind label %finally.end
finally.ret:
cleanupret %finally unwind to caller
finally.end:
cleanupendpad %finally unwind to caller
tail:
call void @f(i32 8)
ret void
; CHECK: [[L_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 [[L_catch1]]-[[L_begin]]
; ^ offset from L_begin to start of 1st funclet
; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
; ^ offset from L_begin to start of 2nd funclet
; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
; ^ offset from L_begin to start of 3rd funclet
; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
; ^ offset from L_begin to start of 4th funclet
; CHECK-NEXT: .long [[L_end]]-[[L_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 ([[L_before_f2]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_catch2]]-[[L_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 ([[L_before_f2]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_fault]]-[[L_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 ([[L_before_f1]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_end]]-[[L_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 ([[L_before_f3]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_end]]-[[L_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 ([[L_before_f5]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_fault]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_finally]]-[[L_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 ([[L_before_f4]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_end]]-[[L_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 ([[L_before_f6]]-[[L_begin]])+1
; ^ offset of start of clause
; CHECK-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1
; ^ offset of end of clause
; CHECK-NEXT: .long [[L_finally]]-[[L_begin]]
; ^ offset of start of handler
; CHECK-NEXT: .long [[L_end]]-[[L_begin]]
; ^ offset of end of handler
; CHECK-NEXT: .long 0
; ^ type token slot (null for finally)