newlib-cygwin/newlib/libc/machine/arm/arm_asm.h

505 lines
15 KiB
C

/*
* Copyright (c) 2009 ARM Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the company may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARM_ASM__H
#define ARM_ASM__H
#include "arm-acle-compat.h"
#if __ARM_ARCH >= 7 && defined (__ARM_ARCH_ISA_ARM)
# define _ISA_ARM_7
#endif
#if __ARM_ARCH >= 6 && defined (__ARM_ARCH_ISA_ARM)
# define _ISA_ARM_6
#endif
#if __ARM_ARCH >= 5
# define _ISA_ARM_5
#endif
#if __ARM_ARCH >= 4 && __ARM_ARCH_ISA_THUMB >= 1
# define _ISA_ARM_4T
#endif
#if __ARM_ARCH >= 4 && __ARM_ARCH_ISA_THUMB == 0
# define _ISA_ARM_4
#endif
#if __ARM_ARCH_ISA_THUMB >= 2
# define _ISA_THUMB_2
#endif
#if __ARM_ARCH_ISA_THUMB >= 1
# define _ISA_THUMB_1
#endif
/* Check whether leaf function PAC signing has been requested in the
-mbranch-protect compile-time option. */
#define LEAF_PROTECT_BIT 2
#ifdef __ARM_FEATURE_PAC_DEFAULT
# define HAVE_PAC_LEAF \
((__ARM_FEATURE_PAC_DEFAULT & (1 << LEAF_PROTECT_BIT)) && 1)
#else
# define HAVE_PAC_LEAF 0
#endif
/* Provide default parameters for PAC-code handling in leaf-functions. */
#if HAVE_PAC_LEAF
# ifndef PAC_LEAF_PUSH_IP
# define PAC_LEAF_PUSH_IP 1
# endif
#else /* !HAVE_PAC_LEAF */
# undef PAC_LEAF_PUSH_IP
# define PAC_LEAF_PUSH_IP 0
#endif /* HAVE_PAC_LEAF */
#define STACK_ALIGN_ENFORCE 0
#ifdef __ASSEMBLER__
/******************************************************************************
* Implementation of the prologue and epilogue assembler macros and their
* associated helper functions.
*
* These functions add support for the following:
*
* - M-profile branch target identification (BTI) landing-pads when compiled
* with `-mbranch-protection=bti'.
* - PAC-signing and verification instructions, depending on hardware support
* and whether the PAC-signing of leaf functions has been requested via the
* `-mbranch-protection=pac-ret+leaf' compiler argument.
* - 8-byte stack alignment preservation at function entry, defaulting to the
* value of STACK_ALIGN_ENFORCE.
*
* Notes:
* - Prologue stack alignment is implemented by detecting a push with an odd
* number of registers and prepending a dummy register to the list.
* - If alignment is attempted on a list containing r0, compilation will result
* in an error.
* - If alignment is attempted in a list containing r1, r0 will be prepended to
* the register list and r0 will be restored prior to function return. for
* functions with non-void return types, this will result in the corruption of
* the result register.
* - Stack alignment is enforced via the following helper macro call-chain:
*
* {prologue|epilogue} ->_align8 -> _preprocess_reglist ->
* _preprocess_reglist1 -> {_prologue|_epilogue}
*
* - Debug CFI directives are automatically added to prologues and epilogues,
* assisted by `cfisavelist' and `cfirestorelist', respectively.
*
* Arguments:
* prologue
* --------
* - first - If `last' specified, this serves as start of general-purpose
* register (GPR) range to push onto stack, otherwise represents
* single GPR to push onto stack. If omitted, no GPRs pushed
* onto stack at prologue.
* - last - If given, specifies inclusive upper-bound of GPR range.
* - push_ip - Determines whether IP register is to be pushed to stack at
* prologue. When pac-signing is requested, this holds the
* the pac-key. Either 1 or 0 to push or not push, respectively.
* Default behavior: Set to value of PAC_LEAF_PUSH_IP macro.
* - push_lr - Determines whether to push lr to the stack on function entry.
* Either 1 or 0 to push or not push, respectively.
* - align8 - Whether to enforce alignment. Either 1 or 0, with 1 requesting
* alignment.
*
* epilogue
* --------
* The epilogue should be called passing the same arguments as those passed to
* the prologue to ensure the stack is not corrupted on function return.
*
* Usage examples:
*
* prologue push_ip=1 -> push {ip}
* epilogue push_ip=1, align8=1 -> pop {r2, ip}
* prologue push_ip=1, push_lr=1 -> push {ip, lr}
* epilogue 1 -> pop {r1}
* prologue 1, align8=1 -> push {r0, r1}
* epilogue 1, push_ip=1 -> pop {r1, ip}
* prologue 1, 4 -> push {r1-r4}
* epilogue 1, 4 push_ip=1 -> pop {r1-r4, ip}
*
******************************************************************************/
/* Emit .cfi_restore directives for a consecutive sequence of registers. */
.macro cfirestorelist first, last
.cfi_restore \last
.if \last-\first
cfirestorelist \first, \last-1
.endif
.endm
/* Emit .cfi_offset directives for a consecutive sequence of registers. */
.macro cfisavelist first, last, index=1
.cfi_offset \last, -4*(\index)
.if \last-\first
cfisavelist \first, \last-1, \index+1
.endif
.endm
.macro _prologue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
.if \push_ip & 1 != \push_ip
.error "push_ip may be either 0 or 1"
.endif
.if \push_lr & 1 != \push_lr
.error "push_lr may be either 0 or 1"
.endif
.if \first != -1
.if \last == -1
/* Upper-bound not provided: Set upper = lower. */
_prologue \first, \first, \push_ip, \push_lr
.exitm
.endif
.endif
#if HAVE_PAC_LEAF
#if __ARM_FEATURE_BTI_DEFAULT
pacbti ip, lr, sp
#else
pac ip, lr, sp
#endif /* __ARM_FEATURE_BTI_DEFAULT */
.cfi_register 143, 12
#else
#if __ARM_FEATURE_BTI_DEFAULT
bti
#endif /* __ARM_FEATURE_BTI_DEFAULT */
#endif /* HAVE_PAC_LEAF */
.if \first != -1
.if \last != \first
.if \last >= 13
.error "SP cannot be in the save list"
.endif
.if \push_ip
.if \push_lr
/* Case 1: push register range, ip and lr registers. */
push {r\first-r\last, ip, lr}
.cfi_adjust_cfa_offset ((\last-\first)+3)*4
.cfi_offset 14, -4
.cfi_offset 143, -8
cfisavelist \first, \last, 3
.else // !\push_lr
/* Case 2: push register range and ip register. */
push {r\first-r\last, ip}
.cfi_adjust_cfa_offset ((\last-\first)+2)*4
.cfi_offset 143, -4
cfisavelist \first, \last, 2
.endif
.else // !\push_ip
.if \push_lr
/* Case 3: push register range and lr register. */
push {r\first-r\last, lr}
.cfi_adjust_cfa_offset ((\last-\first)+2)*4
.cfi_offset 14, -4
cfisavelist \first, \last, 2
.else // !\push_lr
/* Case 4: push register range. */
push {r\first-r\last}
.cfi_adjust_cfa_offset ((\last-\first)+1)*4
cfisavelist \first, \last, 1
.endif
.endif
.else // \last == \first
.if \push_ip
.if \push_lr
/* Case 5: push single GP register plus ip and lr registers. */
push {r\first, ip, lr}
.cfi_adjust_cfa_offset 12
.cfi_offset 14, -4
.cfi_offset 143, -8
cfisavelist \first, \first, 3
.else // !\push_lr
/* Case 6: push single GP register plus ip register. */
push {r\first, ip}
.cfi_adjust_cfa_offset 8
.cfi_offset 143, -4
cfisavelist \first, \first, 2
.endif
.else // !\push_ip
.if \push_lr
/* Case 7: push single GP register plus lr register. */
push {r\first, lr}
.cfi_adjust_cfa_offset 8
.cfi_offset 14, -4
cfisavelist \first, \first, 2
.else // !\push_lr
/* Case 8: push single GP register. */
push {r\first}
.cfi_adjust_cfa_offset 4
cfisavelist \first, \first, 1
.endif
.endif
.endif
.else // \first == -1
.if \push_ip
.if \push_lr
/* Case 9: push ip and lr registers. */
push {ip, lr}
.cfi_adjust_cfa_offset 8
.cfi_offset 14, -4
.cfi_offset 143, -8
.else // !\push_lr
/* Case 10: push ip register. */
push {ip}
.cfi_adjust_cfa_offset 4
.cfi_offset 143, -4
.endif
.else // !\push_ip
.if \push_lr
/* Case 11: push lr register. */
push {lr}
.cfi_adjust_cfa_offset 4
.cfi_offset 14, -4
.endif
.endif
.endif
.endm
.macro _epilogue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
.if \push_ip & 1 != \push_ip
.error "push_ip may be either 0 or 1"
.endif
.if \push_lr & 1 != \push_lr
.error "push_lr may be either 0 or 1"
.endif
.if \first != -1
.if \last == -1
/* Upper-bound not provided: Set upper = lower. */
_epilogue \first, \first, \push_ip, \push_lr
.exitm
.endif
.if \last != \first
.if \last >= 13
.error "SP cannot be in the save list"
.endif
.if \push_ip
.if \push_lr
/* Case 1: pop register range, ip and lr registers. */
pop {r\first-r\last, ip, lr}
.cfi_restore 14
.cfi_register 143, 12
cfirestorelist \first, \last
.else // !\push_lr
/* Case 2: pop register range and ip register. */
pop {r\first-r\last, ip}
.cfi_register 143, 12
cfirestorelist \first, \last
.endif
.else // !\push_ip
.if \push_lr
/* Case 3: pop register range and lr register. */
pop {r\first-r\last, lr}
.cfi_restore 14
cfirestorelist \first, \last
.else // !\push_lr
/* Case 4: pop register range. */
pop {r\first-r\last}
cfirestorelist \first, \last
.endif
.endif
.else // \last == \first
.if \push_ip
.if \push_lr
/* Case 5: pop single GP register plus ip and lr registers. */
pop {r\first, ip, lr}
.cfi_restore 14
.cfi_register 143, 12
cfirestorelist \first, \first
.else // !\push_lr
/* Case 6: pop single GP register plus ip register. */
pop {r\first, ip}
.cfi_register 143, 12
cfirestorelist \first, \first
.endif
.else // !\push_ip
.if \push_lr
/* Case 7: pop single GP register plus lr register. */
pop {r\first, lr}
.cfi_restore 14
cfirestorelist \first, \first
.else // !\push_lr
/* Case 8: pop single GP register. */
pop {r\first}
cfirestorelist \first, \first
.endif
.endif
.endif
.else // \first == -1
.if \push_ip
.if \push_lr
/* Case 9: pop ip and lr registers. */
pop {ip, lr}
.cfi_restore 14
.cfi_register 143, 12
.else // !\push_lr
/* Case 10: pop ip register. */
pop {ip}
.cfi_register 143, 12
.endif
.else // !\push_ip
.if \push_lr
/* Case 11: pop lr register. */
pop {lr}
.cfi_restore 14
.endif
.endif
.endif
#if HAVE_PAC_LEAF
aut ip, lr, sp
#endif /* HAVE_PAC_LEAF */
bx lr
.endm
# clean up expressions in 'last'
.macro _preprocess_reglist1 first:req, last:req, push_ip:req, push_lr:req, reglist_op:req
.if \last == 0
\reglist_op \first, 0, \push_ip, \push_lr
.elseif \last == 1
\reglist_op \first, 1, \push_ip, \push_lr
.elseif \last == 2
\reglist_op \first, 2, \push_ip, \push_lr
.elseif \last == 3
\reglist_op \first, 3, \push_ip, \push_lr
.elseif \last == 4
\reglist_op \first, 4, \push_ip, \push_lr
.elseif \last == 5
\reglist_op \first, 5, \push_ip, \push_lr
.elseif \last == 6
\reglist_op \first, 6, \push_ip, \push_lr
.elseif \last == 7
\reglist_op \first, 7, \push_ip, \push_lr
.elseif \last == 8
\reglist_op \first, 8, \push_ip, \push_lr
.elseif \last == 9
\reglist_op \first, 9, \push_ip, \push_lr
.elseif \last == 10
\reglist_op \first, 10, \push_ip, \push_lr
.elseif \last == 11
\reglist_op \first, 11, \push_ip, \push_lr
.else
.error "last (\last) out of range"
.endif
.endm
# clean up expressions in 'first'
.macro _preprocess_reglist first:req, last, push_ip=0, push_lr=0, reglist_op:req
.ifb \last
_preprocess_reglist \first \first \push_ip \push_lr
.else
.if \first > \last
.error "last (\last) must be at least as great as first (\first)"
.endif
.if \first == 0
_preprocess_reglist1 0, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 1
_preprocess_reglist1 1, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 2
_preprocess_reglist1 2, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 3
_preprocess_reglist1 3, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 4
_preprocess_reglist1 4, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 5
_preprocess_reglist1 5, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 6
_preprocess_reglist1 6, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 7
_preprocess_reglist1 7, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 8
_preprocess_reglist1 8, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 9
_preprocess_reglist1 9, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 10
_preprocess_reglist1 10, \last, \push_ip, \push_lr, \reglist_op
.elseif \first == 11
_preprocess_reglist1 11, \last, \push_ip, \push_lr, \reglist_op
.else
.error "first (\first) out of range"
.endif
.endif
.endm
.macro _align8 first, last, push_ip=0, push_lr=0, reglist_op=_prologue
.ifb \first
.ifnb \last
.error "can't have last (\last) without specifying first"
.else // \last not blank
.if ((\push_ip + \push_lr) % 2) == 0
\reglist_op first=-1, last=-1, push_ip=\push_ip, push_lr=\push_lr
.exitm
.else // ((\push_ip + \push_lr) % 2) odd
_align8 2, 2, \push_ip, \push_lr, \reglist_op
.exitm
.endif // ((\push_ip + \push_lr) % 2) == 0
.endif // .ifnb \last
.endif // .ifb \first
.ifb \last
_align8 \first, \first, \push_ip, \push_lr, \reglist_op
.else
.if \push_ip & 1 <> \push_ip
.error "push_ip may be 0 or 1"
.endif
.if \push_lr & 1 <> \push_lr
.error "push_lr may be 0 or 1"
.endif
.ifeq (\last - \first + \push_ip + \push_lr) % 2
.if \first == 0
.error "Alignment required and first register is r0"
.exitm
.endif
_preprocess_reglist \first-1, \last, \push_ip, \push_lr, \reglist_op
.else
_preprocess_reglist \first \last, \push_ip, \push_lr, \reglist_op
.endif
.endif
.endm
.macro prologue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
.if \align8
_align8 \first, \last, \push_ip, \push_lr, _prologue
.else
_prologue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
.endif
.endm
.macro epilogue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
.if \align8
_align8 \first, \last, \push_ip, \push_lr, reglist_op=_epilogue
.else
_epilogue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
.endif
.endm
#endif /* __ASSEMBLER__ */
#endif /* ARM_ASM__H */