| // 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 |