// This test checks that the epilogue is packed where possible.
// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s -o %t.o
// RUN: llvm-readobj -u %t.o | FileCheck %s
// CHECK: UnwindInformation [
// CHECK-NEXT: RuntimeFunction {
// CHECK-NEXT: Function: func
// CHECK-NEXT: ExceptionRecord: .xdata
// CHECK-NEXT: ExceptionData {
// CHECK-NEXT: FunctionLength:
// CHECK-NEXT: Version:
// CHECK-NEXT: ExceptionData:
// CHECK-NEXT: EpiloguePacked: Yes
// CHECK-NEXT: EpilogueOffset: 2
// CHECK-NEXT: ByteCodeLength:
// CHECK-NEXT: Prologue [
// CHECK-NEXT: 0xdc04 ; str d8, [sp, // CHECK-NEXT: 0xe1 ; mov fp, sp
// CHECK-NEXT: 0x42 ; stp x29, x30, [sp, // CHECK-NEXT: 0x85 ; stp x29, x30, [sp, // CHECK-NEXT: 0xe6 ; save next
// CHECK-NEXT: 0x24 ; stp x19, x20, [sp, // CHECK-NEXT: 0xc842 ; stp x20, x21, [sp, // CHECK-NEXT: 0x03 ; sub sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0xe1 ; mov sp, fp
// CHECK-NEXT: 0x42 ; ldp x29, x30, [sp, // CHECK-NEXT: 0x85 ; ldp x29, x30, [sp], // CHECK-NEXT: 0xe6 ; restore next
// CHECK-NEXT: 0x24 ; ldp x19, x20, [sp], // CHECK-NEXT: 0xc842 ; ldp x20, x21, [sp, // CHECK-NEXT: 0x03 ; add sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK: RuntimeFunction {
// CHECK-NEXT: Function: packed2
// CHECK-NEXT: ExceptionRecord:
// CHECK-NEXT: ExceptionData {
// CHECK: ExceptionData:
// CHECK-NEXT: EpiloguePacked: Yes
// CHECK-NEXT: EpilogueOffset: 0
// CHECK-NEXT: ByteCodeLength: 4
// CHECK-NEXT: Prologue [
// CHECK-NEXT: 0x83 ; stp x29, x30, [sp, // CHECK-NEXT: 0x03 ; sub sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
// CHECK: RuntimeFunction {
// CHECK-NEXT: Function: nonpacked1
// CHECK-NEXT: ExceptionRecord:
// CHECK-NEXT: ExceptionData {
// CHECK: ExceptionData:
// CHECK-NEXT: EpiloguePacked: No
// CHECK: RuntimeFunction {
// CHECK-NEXT: Function: nonshared2
// CHECK-NEXT: ExceptionRecord:
// CHECK-NEXT: ExceptionData {
// CHECK: ExceptionData:
// CHECK-NEXT: EpiloguePacked: Yes
// CHECK-NEXT: EpilogueOffset: 3
// CHECK-NEXT: ByteCodeLength: 8
// CHECK-NEXT: Prologue [
// CHECK-NEXT: 0x02 ; sub sp, // CHECK-NEXT: 0x03 ; sub sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0x01 ; add sp, // CHECK-NEXT: 0x03 ; add sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
// CHECK: RuntimeFunction {
// CHECK-NEXT: Function: nonshared3
// CHECK-NEXT: ExceptionRecord:
// CHECK-NEXT: ExceptionData {
// CHECK: ExceptionData:
// CHECK-NEXT: EpiloguePacked: Yes
// CHECK-NEXT: EpilogueOffset: 3
// CHECK-NEXT: ByteCodeLength: 8
// CHECK-NEXT: Prologue [
// CHECK-NEXT: 0x02 ; sub sp, // CHECK-NEXT: 0x03 ; sub sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0xe1 ; mov sp, fp
// CHECK-NEXT: 0x02 ; add sp, // CHECK-NEXT: 0x03 ; add sp, // CHECK-NEXT: 0xe4 ; end
// CHECK-NEXT: ]
.text
.globl func
.seh_proc func
func:
sub sp, sp, .seh_stackalloc 48
// Check that canonical opcode forms (r19r20_x, fplr, fplr_x, save_next,
// set_fp) are treated as a match even if one (in prologue or epilogue)
// was simplified from the more generic opcodes.
stp x20, x21, [sp, .seh_save_regp x20, 16
stp x19, x20, [sp, .seh_save_r19r20_x 32
stp x21, x22, [sp, .seh_save_regp x21, 16
stp x29, x30, [sp, .seh_save_regp_x x29, 48
stp x29, x30, [sp, .seh_save_regp x29, 16
add x29, sp, .seh_add_fp 0
str d8, [sp, .seh_save_freg d8, 32
.seh_endprologue
nop
.seh_startepilogue
mov sp, x29
.seh_set_fp
ldp x29, x30, [sp, .seh_save_fplr 16
ldp x29, x30, [sp, .seh_save_fplr_x 48
ldp x21, x22, [sp, .seh_save_next
ldp x19, x20, [sp], .seh_save_regp_x x19, 32
ldp x20, x21, [sp, .seh_save_regp x20, 16
add sp, sp, .seh_stackalloc 48
.seh_endepilogue
ret
.seh_endproc
// Test a perfectly matching epilog with no offset.
.seh_proc packed2
packed2:
sub sp, sp, .seh_stackalloc 48
stp x29, lr, [sp, .seh_save_fplr_x 32
.seh_endprologue
nop
.seh_startepilogue
ldp x29, lr, [sp], .seh_save_fplr_x 32
add sp, sp, .seh_stackalloc 48
.seh_endepilogue
ret
.seh_endproc
.seh_proc nonpacked1
nonpacked1:
sub sp, sp, .seh_stackalloc 48
.seh_endprologue
nop
.seh_startepilogue
add sp, sp, .seh_stackalloc 48
.seh_endepilogue
// This epilogue isn't packed with the prologue, as it doesn't align with
// the end of the function (one extra nop before the ret).
nop
ret
.seh_endproc
.seh_proc nonshared2
nonshared2:
sub sp, sp, .seh_stackalloc 48
sub sp, sp, .seh_stackalloc 32
.seh_endprologue
nop
.seh_startepilogue
// Not shared; the epilogue mismatches at the second opcode.
add sp, sp, .seh_stackalloc 16
add sp, sp, .seh_stackalloc 48
.seh_endepilogue
ret
.seh_endproc
.seh_proc nonshared3
nonshared3:
sub sp, sp, .seh_stackalloc 48
sub sp, sp, .seh_stackalloc 32
.seh_endprologue
nop
.seh_startepilogue
// Not shared; the epilogue is longer than the prologue.
mov sp, x29
.seh_set_fp
add sp, sp, .seh_stackalloc 32
add sp, sp, .seh_stackalloc 48
.seh_endepilogue
ret
.seh_endproc