diff options
Diffstat (limited to 'gcc-4.9/libgcc/config/c6x/pr-support.c')
-rw-r--r-- | gcc-4.9/libgcc/config/c6x/pr-support.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/gcc-4.9/libgcc/config/c6x/pr-support.c b/gcc-4.9/libgcc/config/c6x/pr-support.c new file mode 100644 index 000000000..99f44d397 --- /dev/null +++ b/gcc-4.9/libgcc/config/c6x/pr-support.c @@ -0,0 +1,535 @@ +/* C6X ABI compliant unwinding routines + Copyright (C) 2011-2014 Free Software Foundation, Inc. + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3, or (at your option) any + later version. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include "unwind.h" + +/* We add a prototype for abort here to avoid creating a dependency on + target headers. */ +extern void abort (void); + +typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ + +/* Misc constants. */ +#define R_A0 0 +#define R_A1 1 +#define R_A2 2 +#define R_A3 3 +#define R_A4 4 +#define R_A5 5 +#define R_A6 6 +#define R_A7 7 +#define R_A8 8 +#define R_A9 9 +#define R_A10 10 +#define R_A11 11 +#define R_A12 12 +#define R_A13 13 +#define R_A14 14 +#define R_A15 15 +#define R_B0 16 +#define R_B1 17 +#define R_B2 18 +#define R_B3 19 +#define R_B4 20 +#define R_B5 21 +#define R_B6 22 +#define R_B7 23 +#define R_B8 24 +#define R_B9 25 +#define R_B10 26 +#define R_B11 27 +#define R_B12 28 +#define R_B13 29 +#define R_B14 30 +#define R_B15 31 + +#define R_SP R_B15 +#define R_PC 33 + +#define uint32_highbit (((_uw) 1) << 31) + +void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); + +/* Unwind descriptors. */ + +typedef struct +{ + _uw16 length; + _uw16 offset; +} EHT16; + +typedef struct +{ + _uw length; + _uw offset; +} EHT32; + +/* Calculate the address encoded by a 31-bit self-relative offset at address + P. Copy of routine in unwind-arm.c. */ + +static inline _uw +selfrel_offset31 (const _uw *p) +{ + _uw offset; + + offset = *p; + /* Sign extend to 32 bits. */ + if (offset & (1 << 30)) + offset |= 1u << 31; + + return offset + (_uw) p; +} + + +/* Personality routine helper functions. */ + +#define CODE_FINISH (0xe7) + +/* Return the next byte of unwinding information, or CODE_FINISH if there is + no data remaining. */ +static inline _uw8 +next_unwind_byte (__gnu_unwind_state * uws) +{ + _uw8 b; + + if (uws->bytes_left == 0) + { + /* Load another word */ + if (uws->words_left == 0) + return CODE_FINISH; /* Nothing left. */ + uws->words_left--; + uws->data = *(uws->next++); + uws->bytes_left = 3; + } + else + uws->bytes_left--; + + /* Extract the most significant byte. */ + b = (uws->data >> 24) & 0xff; + uws->data <<= 8; + return b; +} + +static void +unwind_restore_pair (_Unwind_Context * context, int reg, _uw *ptr) +{ +#ifdef _BIG_ENDIAN + _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr + 1); + _Unwind_VRS_Set (context, _UVRSC_CORE, reg + 1, _UVRSD_UINT32, ptr); +#else + _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, ptr); + _Unwind_VRS_Set (context, _UVRSC_CORE, reg + 1, _UVRSD_UINT32, ptr + 1); +#endif +} + +static const int +unwind_frame_regs[13] = +{ + R_A15, R_B15, R_B14, R_B13, R_B12, R_B11, R_B10, R_B3, + R_A14, R_A13, R_A12, R_A11, R_A10 +}; + +static void +pop_compact_frame (_Unwind_Context * context, _uw mask, _uw *ptr, int inc_sp) +{ + int size; + _uw test; + int i, regno, nregs; + + size = 0; + nregs = __builtin_popcount (mask); + for (i = 0; i < 13; i++) + { + test = 1 << i; + if ((mask & test) == 0) + continue; + + regno = unwind_frame_regs[12 - i]; + + if (i < 12 && nregs > 2 + && (mask & (test << 1)) != 0 + && unwind_frame_regs[11 - i] == regno + 1 + && (regno & 1) == 0) + { + i++; + nregs--; + } + + nregs--; + size += 2; + } + + if (!inc_sp) + ptr -= size; + + /* SP points just past the end of the stack. */ + ptr += 2; + nregs = __builtin_popcount (mask); + for (i = 0; i < 13; i++) + { + test = 1 << i; + if ((mask & test) == 0) + continue; + + regno = unwind_frame_regs[12 - i]; + + if (i < 12 && nregs > 2 + && (mask & (test << 1)) != 0 + && unwind_frame_regs[11 - i] == regno + 1 + && (regno & 1) == 0) + { + /* Register pair. */ + unwind_restore_pair (context, regno, ptr); + i++; + nregs--; + } + else + { + /* Single register with padding. */ + _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ptr); + } + + nregs--; + ptr += 2; + } + + ptr -= 2; + if ((mask & (1 << 11)) == 0) + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); +} + +static void +pop_frame (_Unwind_Context * context, _uw mask, _uw *ptr, int inc_sp) +{ + int i; + int regno; + int nregs; + + nregs = __builtin_popcount (mask); + + if (!inc_sp) + ptr -= nregs; + else if (nregs & 1) + ptr++; + + ptr++; + for (i = 0; i < 13; i++) + { + if ((mask & (1 << i)) == 0) + continue; + regno = unwind_frame_regs[12 - i]; + if (i < 12 && unwind_frame_regs[11 - i] == (regno + 1) + && (mask & (1 << (i + 1))) != 0 + && (((_uw)ptr) & 4) == 0 + && (regno & 1) == 0) + { + unwind_restore_pair (context, regno, ptr); + i++; + ptr += 2; + } + else + { + _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, + ptr); + ptr++; + } + } + + ptr--; + if ((mask & (1 << 11)) == 0) + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); +} + +/* Unwind a 24-bit encoded frame. */ +_Unwind_Reason_Code +__gnu_unwind_24bit (_Unwind_Context * context, _uw data, int compact) +{ + _uw offset; + _uw mask; + _uw *ptr; + _uw tmp; + int ret_reg = unwind_frame_regs[data & 0xf]; + + if (ret_reg != R_B3) + { + _Unwind_VRS_Get (context, _UVRSC_CORE, unwind_frame_regs[data & 0xf], + _UVRSD_UINT32, &tmp); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, &tmp); + } + + mask = (data >> 4) & 0x1fff; + + offset = (data >> 17) & 0x7f; + if (offset == 0x7f) + _Unwind_VRS_Get (context, _UVRSC_CORE, R_A15, _UVRSD_UINT32, &ptr); + else + { + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); + ptr += offset * 2; + } + + + if (compact) + pop_compact_frame (context, mask, ptr, offset != 0x7f); + else + pop_frame (context, mask, ptr, offset != 0x7f); + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, &tmp); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, &tmp); + + return _URC_OK; +} + +static void +unwind_pop_rts (_Unwind_Context * context) +{ + _uw *ptr; + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); +#ifdef _BIG_ENDIAN + _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ptr + 1); +#else + _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ptr + 2); +#endif + ptr += 3; + unwind_restore_pair (context, R_A10, ptr); + ptr += 2; + unwind_restore_pair (context, R_B10, ptr); + ptr += 2; + unwind_restore_pair (context, R_A12, ptr); + ptr += 2; + unwind_restore_pair (context, R_B12, ptr); + ptr += 2; + unwind_restore_pair (context, R_A14, ptr); + ptr += 2; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_B14, _UVRSD_UINT32, ptr); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); + /* PC will be set by implicit RETURN opcode. */ +} + +/* Execute the unwinding instructions described by UWS. */ +_Unwind_Reason_Code +__gnu_unwind_execute (_Unwind_Context * context, __gnu_unwind_state * uws) +{ + _uw op; + int inc_sp; + _uw reg; + _uw *ptr; + + inc_sp = 1; + for (;;) + { + op = next_unwind_byte (uws); + if (op == CODE_FINISH) + { + /* Drop out of the loop. */ + break; + } + if ((op & 0xc0) == 0) + { + /* sp += (imm6 << 3) + 8. */ + _uw offset; + + offset = ((op & 0x3f) << 3) + 8; + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + reg += offset; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + + if (op == 0xd2) + { + /* vsp = vsp + 0x204 + (uleb128 << 2). */ + int shift; + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + op = next_unwind_byte (uws); + shift = 3; + while (op & 0x80) + { + reg += ((op & 0x7f) << shift); + shift += 7; + op = next_unwind_byte (uws); + } + reg += ((op & 0x7f) << shift) + 0x408; + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + + if ((op & 0xe0) == 0x80) + { + /* POP bitmask */ + _uw mask = ((op & 0x1f) << 8) | next_unwind_byte (uws); + + if (mask == 0) + { + /* CANTUNWIND */ + return _URC_FAILURE; + } + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); + pop_frame (context, mask, ptr, inc_sp); + continue; + } + + if ((op & 0xe0) == 0xa0) + { + /* POP bitmask (compact) */ + _uw mask = ((op & 0x1f) << 8) | next_unwind_byte (uws); + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); + pop_compact_frame (context, mask, ptr, inc_sp); + continue; + } + + if ((op & 0xf0) == 0xc0) + { + /* POP registers */ + int nregs = op & 0xf; + + _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &ptr); + while (nregs > 0) + { + op = next_unwind_byte (uws); + if ((op >> 4) != 0xf) + { + reg = unwind_frame_regs[op >> 4]; + _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, + ptr); + nregs--; + } + ptr--; + if ((op & 0xf) != 0xf) + { + reg = unwind_frame_regs[op & 0xf]; + _Unwind_VRS_Set (context, _UVRSC_CORE, reg, _UVRSD_UINT32, + ptr); + nregs--; + } + ptr--; + } + + continue; + } + + if (op == 0xd0) + { + /* MV FP, SP */ + inc_sp = 0; + _Unwind_VRS_Get (context, _UVRSC_CORE, R_A15, _UVRSD_UINT32, ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); + continue; + } + + if (op == 0xd1) + { + /* __cx6abi_pop_rts */ + unwind_pop_rts (context); + break; + } + + if ((op & 0xf0) == 0xe0) + { + /* B3 = reg. RETURN case alreadh handled above. */ + int regno = unwind_frame_regs[op & 0xf]; + + _Unwind_VRS_Get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ®); + continue; + } + + /* Reserved. */ + return _URC_FAILURE; + } + + /* Implicit RETURN. */ + _Unwind_VRS_Get (context, _UVRSC_CORE, R_B3, _UVRSD_UINT32, ®); + _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, ®); + return _URC_OK; +} + + +/* Execute the unwinding instructions associated with a frame. UCBP and + CONTEXT are the current exception object and virtual CPU state + respectively. */ + +_Unwind_Reason_Code +__gnu_unwind_frame (_Unwind_Control_Block * ucbp, _Unwind_Context * context) +{ + _uw *ptr; + __gnu_unwind_state uws; + + ptr = (_uw *) ucbp->pr_cache.ehtp; + /* Skip over the personality routine address. */ + ptr++; + /* Setup the unwinder state. */ + uws.data = (*ptr) << 8; + uws.next = ptr + 1; + uws.bytes_left = 3; + uws.words_left = ((*ptr) >> 24) & 0xff; + + return __gnu_unwind_execute (context, &uws); +} + +/* Data segment base pointer corresponding to the function catching + the exception. */ + +_Unwind_Ptr +_Unwind_GetDataRelBase (_Unwind_Context *context) +{ + return _Unwind_GetGR (context, R_B14); +} + +/* This should never be used. */ + +_Unwind_Ptr +_Unwind_GetTextRelBase (_Unwind_Context *context __attribute__ ((unused))) +{ + abort (); +} + +/* Only used by gcc personality routines, so can rely on a value they hid + there earlier. */ +_Unwind_Ptr +_Unwind_GetRegionStart (_Unwind_Context *context) +{ + _Unwind_Control_Block *ucbp; + + ucbp = (_Unwind_Control_Block *) _Unwind_GetGR (context, UNWIND_POINTER_REG); + return (_Unwind_Ptr) ucbp->pr_cache.fnstart; +} + +void * +_Unwind_GetLanguageSpecificData (_Unwind_Context *context) +{ + _Unwind_Control_Block *ucbp; + _uw *ptr; + + ucbp = (_Unwind_Control_Block *) _Unwind_GetGR (context, UNWIND_POINTER_REG); + ptr = (_uw *) ucbp->pr_cache.ehtp; + /* Skip the personality routine address. */ + ptr++; + /* Skip the unwind opcodes. */ + ptr += (((*ptr) >> 24) & 0xff) + 1; + + return ptr; +} |