Compiler projects using llvm
// 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, #32]
// CHECK-NEXT:         0xe1                ; mov fp, sp
// CHECK-NEXT:         0x42                ; stp x29, x30, [sp, #16]
// CHECK-NEXT:         0x85                ; stp x29, x30, [sp, #-48]!
// CHECK-NEXT:         0xe6                ; save next
// CHECK-NEXT:         0x24                ; stp x19, x20, [sp, #-32]!
// CHECK-NEXT:         0xc842              ; stp x20, x21, [sp, #16]
// CHECK-NEXT:         0x03                ; sub sp, #48
// CHECK-NEXT:         0xe4                ; end
// CHECK-NEXT:       ]
// CHECK-NEXT:       Epilogue [
// CHECK-NEXT:         0xe1                ; mov sp, fp
// CHECK-NEXT:         0x42                ; ldp x29, x30, [sp, #16]
// CHECK-NEXT:         0x85                ; ldp x29, x30, [sp], #48
// CHECK-NEXT:         0xe6                ; restore next
// CHECK-NEXT:         0x24                ; ldp x19, x20, [sp], #32
// CHECK-NEXT:         0xc842              ; ldp x20, x21, [sp, #16]
// CHECK-NEXT:         0x03                ; add sp, #48
// 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, #-32]!
// CHECK-NEXT:         0x03                ; sub sp, #48
// 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, #32
// CHECK-NEXT:         0x03                ; sub sp, #48
// CHECK-NEXT:         0xe4                ; end
// CHECK-NEXT:       ]
// CHECK-NEXT:       Epilogue [
// CHECK-NEXT:         0x01                ; add sp, #16
// CHECK-NEXT:         0x03                ; add sp, #48
// 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, #32
// CHECK-NEXT:         0x03                ; sub sp, #48
// CHECK-NEXT:         0xe4                ; end
// CHECK-NEXT:       ]
// CHECK-NEXT:       Epilogue [
// CHECK-NEXT:         0xe1                ; mov sp, fp
// CHECK-NEXT:         0x02                ; add sp, #32
// CHECK-NEXT:         0x03                ; add sp, #48
// CHECK-NEXT:         0xe4                ; end
// CHECK-NEXT:       ]

    .text
    .globl func
    .seh_proc func
func:
    sub sp, sp, #48
    .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, #16]
    .seh_save_regp x20, 16
    stp x19, x20, [sp, #-32]!
    .seh_save_r19r20_x 32
    stp x21, x22, [sp, #16]
    .seh_save_regp x21, 16
    stp x29, x30, [sp, #-48]!
    .seh_save_regp_x x29, 48
    stp x29, x30, [sp, #16]
    .seh_save_regp x29, 16
    add x29, sp, #0
    .seh_add_fp 0
    str d8, [sp, #32]
    .seh_save_freg d8, 32
    .seh_endprologue

    nop

    .seh_startepilogue
    mov sp, x29
    .seh_set_fp
    ldp x29, x30, [sp, #16]
    .seh_save_fplr 16
    ldp x29, x30, [sp, #-48]!
    .seh_save_fplr_x 48
    ldp x21, x22, [sp, #16]
    .seh_save_next
    ldp x19, x20, [sp], #32
    .seh_save_regp_x x19, 32
    ldp x20, x21, [sp, #16]
    .seh_save_regp x20, 16
    add sp, sp, #48
    .seh_stackalloc 48
    .seh_endepilogue
    ret
    .seh_endproc


    // Test a perfectly matching epilog with no offset.
    .seh_proc packed2
packed2:
    sub sp, sp, #48
    .seh_stackalloc 48
    stp x29, lr, [sp, #-32]!
    .seh_save_fplr_x 32
    .seh_endprologue
    nop
    .seh_startepilogue
    ldp x29, lr, [sp], #32
    .seh_save_fplr_x 32
    add sp, sp, #48
    .seh_stackalloc 48
    .seh_endepilogue
    ret
    .seh_endproc


    .seh_proc nonpacked1
nonpacked1:
    sub sp, sp, #48
    .seh_stackalloc 48
    .seh_endprologue

    nop
    .seh_startepilogue
    add sp, sp, #48
    .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, #48
    .seh_stackalloc 48
    sub sp, sp, #32
    .seh_stackalloc 32
    .seh_endprologue

    nop
    .seh_startepilogue
    // Not shared; the epilogue mismatches at the second opcode.
    add sp, sp, #16
    .seh_stackalloc 16
    add sp, sp, #48
    .seh_stackalloc 48
    .seh_endepilogue
    ret
    .seh_endproc

    .seh_proc nonshared3
nonshared3:
    sub sp, sp, #48
    .seh_stackalloc 48
    sub sp, sp, #32
    .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, #32
    .seh_stackalloc 32
    add sp, sp, #48
    .seh_stackalloc 48
    .seh_endepilogue
    ret
    .seh_endproc