// This test checks that the SEH directives emit the correct unwind data.
// RUN: llvm-mc -triple thumbv7-pc-win32 -filetype=obj %s | llvm-readobj -S -r -u - | FileCheck %s
// Check that the output assembler directives also can be parsed, and
// that they produce equivalent output:
// RUN: llvm-mc -triple thumbv7-pc-win32 -filetype=asm %s | llvm-mc -triple thumbv7-pc-win32 -filetype=obj - | llvm-readobj -S -r -u - | FileCheck %s
// CHECK: Sections [
// CHECK: Section {
// CHECK: Name: .text
// CHECK: RelocationCount: 1
// CHECK: Characteristics [
// CHECK-NEXT: ALIGN_4BYTES
// CHECK-NEXT: CNT_CODE
// CHECK-NEXT: MEM_16BIT
// CHECK-NEXT: MEM_EXECUTE
// CHECK-NEXT: MEM_PURGEABLE
// CHECK-NEXT: MEM_READ
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK: Section {
// CHECK: Name: .xdata
// CHECK: RawDataSize: 100
// CHECK: RelocationCount: 1
// CHECK: Characteristics [
// CHECK-NEXT: ALIGN_4BYTES
// CHECK-NEXT: CNT_INITIALIZED_DATA
// CHECK-NEXT: MEM_READ
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK: Section {
// CHECK: Name: .pdata
// CHECK: RelocationCount: 10
// CHECK: Characteristics [
// CHECK-NEXT: ALIGN_4BYTES
// CHECK-NEXT: CNT_INITIALIZED_DATA
// CHECK-NEXT: MEM_READ
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: Relocations [
// CHECK-NEXT: Section (1) .text {
// CHECK-NEXT: 0x5C IMAGE_REL_ARM_BRANCH24T tailcall
// CHECK-NEXT: }
// CHECK-NEXT: Section (4) .xdata {
// CHECK-NEXT: 0x34 IMAGE_REL_ARM_ADDR32NB __C_specific_handler
// CHECK-NEXT: }
// CHECK-NEXT: Section (5) .pdata {
// CHECK-NEXT: 0x0 IMAGE_REL_ARM_ADDR32NB .text
// CHECK-NEXT: 0x4 IMAGE_REL_ARM_ADDR32NB .xdata
// CHECK-NEXT: 0x8 IMAGE_REL_ARM_ADDR32NB .text
// CHECK-NEXT: 0xC IMAGE_REL_ARM_ADDR32NB .xdata
// CHECK-NEXT: 0x10 IMAGE_REL_ARM_ADDR32NB .text
// CHECK-NEXT: 0x14 IMAGE_REL_ARM_ADDR32NB .xdata
// CHECK-NEXT: 0x18 IMAGE_REL_ARM_ADDR32NB .text
// CHECK-NEXT: 0x1C IMAGE_REL_ARM_ADDR32NB .xdata
// CHECK-NEXT: 0x20 IMAGE_REL_ARM_ADDR32NB .text
// CHECK-NEXT: 0x24 IMAGE_REL_ARM_ADDR32NB .xdata
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: UnwindInformation [
// CHECK-NEXT: RuntimeFunction {
// CHECK-NEXT: Function: func
// CHECK-NEXT: ExceptionRecord: .xdata
// CHECK-NEXT: ExceptionData {
// CHECK-NEXT: FunctionLength: 86
// CHECK: EpiloguePacked: Yes
// CHECK: Fragment: No
// CHECK: EpilogueOffset: 31
// CHECK: Prologue [
// CHECK-NEXT: 0xed 0xf8 ; push {r3-r7, lr}
// CHECK-NEXT: 0xf6 0x27 ; vpush {d18-d23}
// CHECK-NEXT: 0xf5 0x7e ; vpush {d7-d14}
// CHECK-NEXT: 0xfb ; nop
// CHECK-NEXT: 0xce ; mov r14, sp
// CHECK-NEXT: 0xe3 ; vpush {d8-d11}
// CHECK-NEXT: 0xe6 ; vpush {d8-d14}
// CHECK-NEXT: 0xed 0xf8 ; push {r3-r7, lr}
// CHECK-NEXT: 0xbd 0x50 ; push.w {r4, r6, r8, r10-r12, lr}
// CHECK-NEXT: 0xd7 ; push {r4-r7, lr}
// CHECK-NEXT: 0xdd ; push.w {r4-r9, lr}
// CHECK-NEXT: 0xfa 0x01 0x00 0x00 ; sub.w sp, sp, #(65536 * 4)
// CHECK-NEXT: 0xfc ; nop.w
// CHECK-NEXT: 0xfc ; nop.w
// CHECK-NEXT: 0xf9 0x04 0x00 ; sub.w sp, sp, #(1024 * 4)
// CHECK-NEXT: 0xe8 0x80 ; sub.w sp, #(128 * 4)
// CHECK-NEXT: 0xe8 0x80 ; sub.w sp, #(128 * 4)
// CHECK-NEXT: 0x06 ; sub sp, #(6 * 4)
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0xfc ; nop.w
// CHECK-NEXT: 0xf7 0x00 0x80 ; add sp, sp, #(128 * 4)
// CHECK-NEXT: 0xfc ; nop.w
// CHECK-NEXT: 0xfc ; nop.w
// CHECK-NEXT: 0xf8 0x01 0x00 0x00 ; add sp, sp, #(65536 * 4)
// CHECK-NEXT: 0x06 ; add sp, #(6 * 4)
// CHECK-NEXT: 0xef 0x04 ; ldr.w lr, [sp], #16
// CHECK-NEXT: 0xfd ; bx <reg>
// CHECK-NEXT: ]
// CHECK-NEXT: ExceptionHandler [
// CHECK-NEXT: Routine: __C_specific_handler
// CHECK-NEXT: Parameter: 0x0
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: RuntimeFunction {
// CHECK-NEXT: Function: func2
// CHECK: Prologue [
// CHECK-NEXT: 0xd3 ; push {r4-r7}
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0xd2 ; pop {r4-r6}
// CHECK-NEXT: 0xfe ; b.w <target>
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: RuntimeFunction {
// CHECK-NEXT: Function: func3
// CHECK: FunctionLength: 8
// CHECK: EpilogueOffset: 2
// CHECK: Prologue [
// CHECK-NEXT: 0xd5 ; push {r4-r5, lr}
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0xd6 ; pop {r4-r6, pc}
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: RuntimeFunction {
// CHECK-NEXT: Function: fragment
// CHECK: FunctionLength: 6
// CHECK: Fragment: Yes
// CHECK: Prologue [
// CHECK-NEXT: 0xcb ; mov r11, sp
// CHECK-NEXT: 0x10 ; sub sp, #(16 * 4)
// CHECK-NEXT: 0xd5 ; push {r4-r5, lr}
// CHECK-NEXT: ]
// CHECK-NEXT: Epilogue [
// CHECK-NEXT: 0x10 ; add sp, #(16 * 4)
// CHECK-NEXT: 0xd5 ; pop {r4-r5, pc}
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: RuntimeFunction {
// CHECK-NEXT: Function: condepilog
// CHECK: FunctionLength: 8
// CHECK: Prologue [
// CHECK-NEXT: 0xd5 ; push {r4-r5, lr}
// CHECK-NEXT: ]
// CHECK-NEXT: EpilogueScopes [
// CHECK-NEXT: EpilogueScope {
// CHECK-NEXT: StartOffset: 3
// CHECK-NEXT: Condition: 10
// CHECK-NEXT: EpilogueStartIndex: 0
// CHECK-NEXT: Opcodes [
// CHECK-NEXT: 0xd5 ; pop {r4-r5, pc}
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ]
.text
.syntax unified
.globl func
.def func
.scl 2
.type 32
.endef
.seh_proc func
func:
sub sp, sp, #24
.seh_stackalloc 24
sub sp, sp, #512
.seh_stackalloc_w 512
sub sp, sp, #512
.seh_stackalloc_w 512
sub sp, sp, #4096
.seh_stackalloc_w 4096
movw r7, #0
.seh_nop_w
movt r7, #0x4 // 0x40000
.seh_nop_w
sub sp, sp, r7
.seh_stackalloc_w 0x40000
push {r4-r8,lr}
.seh_save_regs_w {r4-r9,lr}
push {r4-r7,lr}
.seh_save_regs {r4-r7,lr}
push {r4,r6,r8,r10,r11,r12,lr}
.seh_save_regs_w {r4,r6,r8,r10,r11,r12,lr}
push {r3-r7,lr}
.seh_save_regs {r3-r7,lr}
vpush {d8-d14}
.seh_save_fregs {d8-d14}
vpush {q4-q5}
.seh_save_fregs {q4-q5}
mov lr, sp
.seh_save_sp lr
nop
.seh_nop
vpush {d7-d14}
.seh_save_fregs {d7-d14}
vpush {d18-d23}
.seh_save_fregs {d18-d23}
push {r3-r7,lr}
.seh_custom 0xed, 0xf8
.seh_endprologue
nop
.seh_startepilogue
mov r7, #512
.seh_nop_w
add sp, sp, r7
.seh_stackalloc 512
movw r7, #0
.seh_nop_w
movt r7, #0x4 // 0x40000
.seh_nop_w
add sp, sp, r7
.seh_stackalloc 0x40000
add sp, sp, #24
.seh_stackalloc 24
ldr lr, [sp], #16
.seh_save_lr 16
bx lr
.seh_nop
.seh_endepilogue
.seh_handler __C_specific_handler, %except
.seh_handlerdata
.long 0
.text
.seh_endproc
.seh_proc func2
func2:
push {r4-r7}
.seh_save_regs {r4-r7}
.seh_endprologue
nop
.seh_startepilogue
pop {r4-r6}
.seh_save_regs {r4-r6}
b.w tailcall
.seh_nop_w
.seh_endepilogue
.seh_endproc
.seh_proc func3
func3:
push {r4-r5,lr}
.seh_save_regs {r4-r5,lr}
.seh_endprologue
nop
// The p2align causes the length of the function to be unknown.
.p2align 1
nop
.seh_startepilogue
pop {r4-r6,pc}
.seh_save_regs {r4-r6,pc}
.seh_endepilogue
.seh_endproc
.seh_proc fragment
fragment:
// Prologue opcodes without matching instructions
.seh_save_regs {r4-r5,lr}
.seh_stackalloc 64
.seh_save_sp r11
.seh_endprologue_fragment
nop
.seh_startepilogue
add sp, sp, #64
.seh_stackalloc 64
pop {r4-r5,pc}
.seh_save_regs {r4-r5,pc}
.seh_endepilogue
.seh_endproc
.seh_proc condepilog
condepilog:
push {r4-r5,lr}
.seh_save_regs {r4-r5,lr}
.seh_endprologue
nop
it ge
.seh_startepilogue_cond ge
popge {r4-r5,pc}
.seh_save_regs {r4-r5,pc}
.seh_endepilogue
.seh_endproc
// Function with no .seh directives; no pdata/xdata entries are
// generated.
.globl smallFunc
.def smallFunc
.scl 2
.type 32
.endef
.seh_proc smallFunc
smallFunc:
bx lr
.seh_endproc
// Function with no .seh directives, but with .seh_handlerdata.
// No xdata/pdata entries are generated, but the custom handler data
// (the .long after .seh_handlerdata) is left orphaned in the xdata
// section.
.globl handlerFunc
.def handlerFunc
.scl 2
.type 32
.endef
.seh_proc handlerFunc
handlerFunc:
bx lr
.seh_handler __C_specific_handler, %except
.seh_handlerdata
.long 0
.text
.seh_endproc