llvm/test/CodeGen/WinEH/wineh-coreclr.ll
Joseph Tremoulet 3d6511f5ef [WinEH] Add CoreCLR EH table emission
Summary:
Emit the handler and clause locations immediately after the standard
xdata.
Clauses are emitted in the same order and format used to communiate them
to the CLR Execution Engine.
Add a lit test to verify correct table generation on a small but
interesting example function.

Reviewers: majnemer, andrew.w.kaylor, rnk

Subscribers: pgavlin, AndyAyers, llvm-commits

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@250219 91177308-0d34-0410-b5e6-96231b3b80d8
2015-10-13 20:18:27 +00:00

240 lines
8.4 KiB
LLVM

; RUN: llc -mtriple=x86_64-pc-windows-coreclr < %s | FileCheck %s
declare void @ProcessCLRException()
declare void @f(i32)
; 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: .seh_endprologue
; 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_endprologue
; 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_endprologue
; 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_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_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)