diff options
Diffstat (limited to 'gcc/config/arm')
-rw-r--r-- | gcc/config/arm/arm-protos.h | 2 | ||||
-rw-r--r-- | gcc/config/arm/arm.c | 297 | ||||
-rw-r--r-- | gcc/config/arm/arm.h | 12 | ||||
-rw-r--r-- | gcc/config/arm/bpabi.h | 3 | ||||
-rw-r--r-- | gcc/config/arm/elf.h | 2 | ||||
-rw-r--r-- | gcc/config/arm/lib1funcs.asm | 1 | ||||
-rw-r--r-- | gcc/config/arm/libgcc-bpabi.ver | 14 | ||||
-rw-r--r-- | gcc/config/arm/t-bpabi | 8 | ||||
-rw-r--r-- | gcc/config/arm/t-symbian | 5 |
9 files changed, 343 insertions, 1 deletions
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 352a289bd75..b15fe10ce43 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -39,6 +39,8 @@ extern HOST_WIDE_INT arm_compute_initial_elimination_offset (unsigned int, extern HOST_WIDE_INT thumb_compute_initial_elimination_offset (unsigned int, unsigned int); extern unsigned int arm_dbx_register_number (unsigned int); +extern void arm_output_fn_unwind (FILE *, bool); + #ifdef TREE_CODE extern int arm_return_in_memory (tree); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 5348c79ba49..4e17d3a7fe3 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -171,6 +171,10 @@ static bool arm_default_short_enums (void); static bool arm_align_anon_bitfield (void); static bool arm_return_in_msb (tree); static bool arm_must_pass_in_stack (enum machine_mode, tree); +#ifdef TARGET_UNWIND_INFO +static void arm_unwind_emit (FILE *, rtx); +static bool arm_output_ttype (rtx); +#endif static tree arm_cxx_guard_type (void); static bool arm_cxx_guard_mask_bit (void); @@ -337,6 +341,18 @@ static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode); #undef TARGET_MUST_PASS_IN_STACK #define TARGET_MUST_PASS_IN_STACK arm_must_pass_in_stack +#ifdef TARGET_UNWIND_INFO +#undef TARGET_UNWIND_EMIT +#define TARGET_UNWIND_EMIT arm_unwind_emit + +/* EABI unwinding tables use a different format for the typeinfo tables. */ +#undef TARGET_ASM_TTYPE +#define TARGET_ASM_TTYPE arm_output_ttype + +#undef TARGET_ARM_EABI_UNWINDER +#define TARGET_ARM_EABI_UNWINDER true +#endif /* TARGET_UNWIND_INFO */ + struct gcc_target targetm = TARGET_INITIALIZER; /* Obstack for minipool constant handling. */ @@ -12417,6 +12433,21 @@ thumb_pushpop (FILE *f, unsigned long mask, int push, int *cfa_offset, return; } + if (ARM_EABI_UNWIND_TABLES && push) + { + fprintf (f, "\t.save\t{"); + for (regno = 0; regno < 15; regno++) + { + if (real_regs & (1 << regno)) + { + if (real_regs & ((1 << regno) -1)) + fprintf (f, ", "); + asm_fprintf (f, "%r", regno); + } + } + fprintf (f, "}\n"); + } + fprintf (f, "\t%s\t{", push ? "push" : "pop"); /* Look at the low registers first. */ @@ -13370,6 +13401,11 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) if (current_function_pretend_args_size) { + /* Output unwind directive for the stack adjustment. */ + if (ARM_EABI_UNWIND_TABLES) + fprintf (f, "\t.pad #%d\n", + current_function_pretend_args_size); + if (cfun->machine->uses_anonymous_args) { int num_pushes; @@ -13435,6 +13471,9 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) work_register = thumb_find_work_register (live_regs_mask); + if (ARM_EABI_UNWIND_TABLES) + asm_fprintf (f, "\t.pad #16\n"); + asm_fprintf (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n", SP_REGNUM, SP_REGNUM); @@ -14662,3 +14701,261 @@ arm_dbx_register_number (unsigned int regno) gcc_unreachable (); } + +#ifdef TARGET_UNWIND_INFO +/* Emit unwind directives for a store-multiple instruction. This should + only ever be generated by the function prologue code, so we expect it + to have a particular form. */ + +static void +arm_unwind_emit_stm (FILE * asm_out_file, rtx p) +{ + int i; + HOST_WIDE_INT offset; + HOST_WIDE_INT nregs; + int reg_size; + unsigned reg; + unsigned lastreg; + rtx e; + + /* First insn will adjust the stack pointer. */ + e = XVECEXP (p, 0, 0); + if (GET_CODE (e) != SET + || GET_CODE (XEXP (e, 0)) != REG + || REGNO (XEXP (e, 0)) != SP_REGNUM + || GET_CODE (XEXP (e, 1)) != PLUS) + abort (); + + offset = -INTVAL (XEXP (XEXP (e, 1), 1)); + nregs = XVECLEN (p, 0) - 1; + + reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1)); + if (reg < 16) + { + /* The function prologue may also push pc, but not annotate it as it is + never restored. We turn this into an stack pointer adjustment. */ + if (nregs * 4 == offset - 4) + { + fprintf (asm_out_file, "\t.pad #4\n"); + offset -= 4; + } + reg_size = 4; + } + else if (IS_VFP_REGNUM (reg)) + { + /* FPA register saves use an additional word. */ + offset -= 4; + reg_size = 8; + } + else if (reg >= FIRST_FPA_REGNUM && reg <= LAST_FPA_REGNUM) + { + /* FPA registers are done differently. */ + asm_fprintf (asm_out_file, "\t.save %r, %d\n", reg, nregs); + return; + } + else + /* Unknown register type. */ + abort (); + + /* If the stack increment doesn't match the size of the saved registers, + something has gone horribly wrong. */ + if (offset != nregs * reg_size) + abort (); + + fprintf (asm_out_file, "\t.save {"); + + offset = 0; + lastreg = 0; + /* The remaining insns will describe the stores. */ + for (i = 1; i <= nregs; i++) + { + /* Expect (set (mem <addr>) (reg)). + Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)). */ + e = XVECEXP (p, 0, i); + if (GET_CODE (e) != SET + || GET_CODE (XEXP (e, 0)) != MEM + || GET_CODE (XEXP (e, 1)) != REG) + abort (); + + reg = REGNO (XEXP (e, 1)); + if (reg < lastreg) + abort (); + + if (i != 1) + fprintf (asm_out_file, ", "); + /* We can't use %r for vfp because we need to use the + double precision register names. */ + if (IS_VFP_REGNUM (reg)) + asm_fprintf (asm_out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2); + else + asm_fprintf (asm_out_file, "%r", reg); + +#ifdef ENABLE_CHECKING + /* Check that the addresses are consecutive. */ + e = XEXP (XEXP (e, 0), 0); + if (GET_CODE (e) == PLUS) + { + offset += reg_size; + if (GET_CODE (XEXP (e, 0)) != REG + || REGNO (XEXP (e, 0)) != SP_REGNUM + || GET_CODE (XEXP (e, 1)) != CONST_INT + || offset != INTVAL (XEXP (e, 1))) + abort (); + } + else if (i != 1 + || GET_CODE (e) != REG + || REGNO (e) != SP_REGNUM) + abort (); +#endif + } + fprintf (asm_out_file, "}\n"); +} + +/* Emit unwind directives for a SET. */ + +static void +arm_unwind_emit_set (FILE * asm_out_file, rtx p) +{ + rtx e0; + rtx e1; + + e0 = XEXP (p, 0); + e1 = XEXP (p, 1); + switch (GET_CODE (e0)) + { + case MEM: + /* Pushing a single register. */ + if (GET_CODE (XEXP (e0, 0)) != PRE_DEC + || GET_CODE (XEXP (XEXP (e0, 0), 0)) != REG + || REGNO (XEXP (XEXP (e0, 0), 0)) != SP_REGNUM) + abort (); + + asm_fprintf (asm_out_file, "\t.save "); + if (IS_VFP_REGNUM (REGNO (e1))) + asm_fprintf(asm_out_file, "{d%d}\n", + (REGNO (e1) - FIRST_VFP_REGNUM) / 2); + else + asm_fprintf(asm_out_file, "{%r}\n", REGNO (e1)); + break; + + case REG: + if (REGNO (e0) == SP_REGNUM) + { + /* A stack increment. */ + if (GET_CODE (e1) != PLUS + || GET_CODE (XEXP (e1, 0)) != REG + || REGNO (XEXP (e1, 0)) != SP_REGNUM + || GET_CODE (XEXP (e1, 1)) != CONST_INT) + abort (); + + asm_fprintf (asm_out_file, "\t.pad #%d\n", + -INTVAL (XEXP (e1, 1))); + } + else if (REGNO (e0) == HARD_FRAME_POINTER_REGNUM) + { + HOST_WIDE_INT offset; + unsigned reg; + + if (GET_CODE (e1) == PLUS) + { + if (GET_CODE (XEXP (e1, 0)) != REG + || GET_CODE (XEXP (e1, 1)) != CONST_INT) + abort (); + reg = REGNO (XEXP (e1, 0)); + offset = INTVAL (XEXP (e1, 1)); + asm_fprintf (asm_out_file, "\t.setfp %r, %r, #%d\n", + HARD_FRAME_POINTER_REGNUM, reg, + INTVAL (XEXP (e1, 1))); + } + else if (GET_CODE (e1) == REG) + { + reg = REGNO (e1); + asm_fprintf (asm_out_file, "\t.setfp %r, %r\n", + HARD_FRAME_POINTER_REGNUM, reg); + } + else + abort (); + } + else if (GET_CODE (e1) == REG && REGNO (e1) == SP_REGNUM) + { + /* Move from sp to reg. */ + asm_fprintf (asm_out_file, "\t.movsp %r\n", REGNO (e0)); + } + else + abort (); + break; + + default: + abort (); + } +} + + +/* Emit unwind directives for the given insn. */ + +static void +arm_unwind_emit (FILE * asm_out_file, rtx insn) +{ + rtx pat; + + if (!ARM_EABI_UNWIND_TABLES) + return; + + if (GET_CODE (insn) == NOTE || !RTX_FRAME_RELATED_P (insn)) + return; + + pat = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); + if (pat) + pat = XEXP (pat, 0); + else + pat = PATTERN (insn); + + switch (GET_CODE (pat)) + { + case SET: + arm_unwind_emit_set (asm_out_file, pat); + break; + + case SEQUENCE: + /* Store multiple. */ + arm_unwind_emit_stm (asm_out_file, pat); + break; + + default: + abort(); + } +} + + +/* Output a reference from a function exception table to the type_info + object X. The EABI specifies that the symbol should be relocated by + an R_ARM_TARGET2 relocation. */ + +static bool +arm_output_ttype (rtx x) +{ + fputs ("\t.word\t", asm_out_file); + output_addr_const (asm_out_file, x); + /* Use special relocations for symbol references. */ + if (GET_CODE (x) != CONST_INT) + fputs ("(TARGET2)", asm_out_file); + fputc ('\n', asm_out_file); + + return TRUE; +} +#endif /* TARGET_UNWIND_INFO */ + + +/* Output unwind directives for the start/end of a function. */ + +void +arm_output_fn_unwind (FILE * f, bool prologue) +{ + if (!ARM_EABI_UNWIND_TABLES) + return; + + if (prologue) + fputs ("\t.fnstart\n", f); + else + fputs ("\t.fnend\n", f); +} diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index 65b4bad144b..327393c7e3d 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -770,8 +770,11 @@ extern int arm_structure_size_boundary; #define FIRST_HI_REGNUM 8 #define LAST_HI_REGNUM 11 +#ifndef TARGET_UNWIND_INFO /* We use sjlj exceptions for backwards compatibility. */ #define MUST_USE_SJLJ_EXCEPTIONS 1 +#endif + /* We can generate DWARF2 Unwind info, even though we don't use it. */ #define DWARF2_UNWIND_INFO 1 @@ -1977,6 +1980,15 @@ typedef struct if (!TARGET_LONG_CALLS || ! DECL_SECTION_NAME (DECL)) \ arm_encode_call_attribute (DECL, SHORT_CALL_FLAG_CHAR) +#define ARM_OUTPUT_FN_UNWIND(F, PROLOGUE) arm_output_fn_unwind (F, PROLOGUE) + +#ifdef TARGET_UNWIND_INFO +#define ARM_EABI_UNWIND_TABLES \ + ((!USING_SJLJ_EXCEPTIONS && flag_exceptions) || flag_unwind_tables) +#else +#define ARM_EABI_UNWIND_TABLES 0 +#endif + /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its validity for a certain class. We have two alternate definitions for each of them. diff --git a/gcc/config/arm/bpabi.h b/gcc/config/arm/bpabi.h index 981c58c4902..7fb7d6a265b 100644 --- a/gcc/config/arm/bpabi.h +++ b/gcc/config/arm/bpabi.h @@ -26,6 +26,9 @@ /* Assume that AAPCS ABIs should adhere to the full BPABI. */ #define TARGET_BPABI (TARGET_AAPCS_BASED) +/* BPABI targets use EABI frame unwinding tables. */ +#define TARGET_UNWIND_INFO 1 + /* Section 4.1 of the AAPCS requires the use of VFP format. */ #define FPUTYPE_DEFAULT FPUTYPE_VFP diff --git a/gcc/config/arm/elf.h b/gcc/config/arm/elf.h index a5600210092..ae3d5338d8f 100644 --- a/gcc/config/arm/elf.h +++ b/gcc/config/arm/elf.h @@ -77,6 +77,7 @@ ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function"); \ ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ ASM_OUTPUT_LABEL(FILE, NAME); \ + ARM_OUTPUT_FN_UNWIND (FILE, TRUE); \ } \ while (0) @@ -85,6 +86,7 @@ #define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \ do \ { \ + ARM_OUTPUT_FN_UNWIND (FILE, FALSE); \ ARM_DECLARE_FUNCTION_SIZE (FILE, FNAME, DECL); \ if (!flag_inhibit_size_directive) \ ASM_OUTPUT_MEASURED_SIZE (FILE, FNAME); \ diff --git a/gcc/config/arm/lib1funcs.asm b/gcc/config/arm/lib1funcs.asm index 519b41e0515..ebf31eb8cc5 100644 --- a/gcc/config/arm/lib1funcs.asm +++ b/gcc/config/arm/lib1funcs.asm @@ -1309,4 +1309,5 @@ LSYM(Lchange_\register): #include "ieee754-df.S" #include "ieee754-sf.S" #include "bpabi.S" +#include "libunwind.S" #endif /* __symbian__ */ diff --git a/gcc/config/arm/libgcc-bpabi.ver b/gcc/config/arm/libgcc-bpabi.ver index 788e3ad2b1f..35966bbfc72 100644 --- a/gcc/config/arm/libgcc-bpabi.ver +++ b/gcc/config/arm/libgcc-bpabi.ver @@ -60,4 +60,18 @@ GCC_3.5 { __aeabi_ulcmp __aeabi_ul2d __aeabi_ul2f + + # Exception-Handling + # \S 7.5 + _Unwind_Complete + _Unwind_VRS_Get + _Unwind_VRS_Set + _Unwind_VRS_Pop + # \S 9.2 + __aeabi_unwind_cpp_pr0 + __aeabi_unwind_cpp_pr1 + __aeabi_unwind_cpp_pr2 + # The libstdc++ exception-handling personality routine uses this + # GNU-specific entry point. + __gnu_unwind_frame } diff --git a/gcc/config/arm/t-bpabi b/gcc/config/arm/t-bpabi index 74445ddc92e..74c0ae138a3 100644 --- a/gcc/config/arm/t-bpabi +++ b/gcc/config/arm/t-bpabi @@ -1,9 +1,15 @@ # Add the bpabi.S functions. -LIB1ASMFUNCS += _aeabi_lcmp _aeabi_ulcmp _aeabi_ldivmod _aeabi_uldivmod +LIB1ASMFUNCS += _aeabi_lcmp _aeabi_ulcmp _aeabi_ldivmod _aeabi_uldivmod \ + _unwind # Add the BPABI C functions. LIB2FUNCS_EXTRA = $(srcdir)/config/arm/bpabi.c +UNWIND_H = $(srcdir)/config/arm/unwind-arm.h +LIB2ADDEH = $(srcdir)/config/arm/unwind-arm.c \ + $(srcdir)/config/arm/pr-support.c $(srcdir)/unwind-c.c +LIB2ADDEHDEP = $(UNWIND_H) + # Add the BPABI names. SHLIB_MAPFILES += $(srcdir)/config/arm/libgcc-bpabi.ver diff --git a/gcc/config/arm/t-symbian b/gcc/config/arm/t-symbian index 34a58ce9f11..8f72b3ea89d 100644 --- a/gcc/config/arm/t-symbian +++ b/gcc/config/arm/t-symbian @@ -12,6 +12,11 @@ LIB1ASMFUNCS += \ _truncdfsf2 _negsf2 _addsubsf3 _muldivsf3 _cmpsf2 _unordsf2 \ _fixsfsi _fixunssfsi +# Include the gcc personality routine +UNWIND_H = $(srcdir)/config/arm/unwind-arm.h +LIB2ADDEH = $(srcdir)/unwind-c.c +LIB2ADDEHDEP = $(UNWIND_H) + # Create a multilib for processors with VFP floating-point, and a # multilib for those without -- using the soft-float ABI in both # cases. Symbian OS object should be compiled with interworking |