From f9273c43241c5b9554c298f049d4155b42776fa4 Mon Sep 17 00:00:00 2001 From: pbrook Date: Wed, 24 Mar 2004 17:20:16 +0000 Subject: [PATCH] * config.gcc : Add --with-abi= * config/arm/arm-protos.h (arm_get_frame_size, thumb_get_frame_size, thumb_far_jump_used): Remove prototypes. (arm_needs_doubleword_align): Add prototype. (thumb_compute_initial_elimination_offset): Ditto. * config/arm/arm.c (arm_get_frame_offsets): New function. (use_return_insn, output_return_instruction, arm_output_epilogue, arm_output_function_epilogue, arm_compute_initial_elimination_offset, arm_expand_prologue, thumb_expand_epilogue): Use it. (arm_abi, target_abi_name, all_arm_abis): New variables. (arm_override_options): Set them. Set structure padding for AAPCS. (arm_return_in_memory): Update ABI check. (arm_init_cumulative_args): Initialize can_split. (arm_needs_doubleword_align): New function. (arm_function_arg): Don't split args after pushing to stack. Handle doubleword/even reg alignment. (arm_va_arg): Handle all doubleword aligned args. (add_minpoolforward ref, dump_minpool, push_minpool_fix): Align based on ABI, not CPU. (arm_compute_save_reg0_reg12_mask): Fix comment. (thumb_get_frame_size, thumb_get_frame_size): Remove. (thumb_jump_far_used_p): Remove superfluous argument. Return save value for alignment. (thumb_unexpanded_epilogue, thumb_output_function_prologue): Change to match. (thumb_compute_initial_elimination_offset): New function. (thumb_expand_prologue): Use arm_get_frame_offsets. Remove unneccessary rounding. * config/arm/arm.h (target_abi_name): Declare. (ARM_DOUBLEWORD_ALIGN, DOUBLEWORD_ALIGNMENT, TARGET_IWMMXT_ABI, arm_abi_type, ARM_DEFAULT_ABI): Define. (ARM_FLAG_ATPCS): Remove. (TARGET_OPTIONS, OPTION_DEFAULT_SPECS): Add -mabi=. (BIGGEST_ALIGNMENT, PREFERRED_STACK_BOUNDARY, STACK_BOUNDARY): Use it. (ADJUST_FIELD_ALIGN, DATA_ALIGNMENT, LOCAL_ALIGNMENT, TYPE_NEEDS_IWMMXT_ALIGNMENT): Remove. (LIBCALL_VALUE, FUNCTION_VALUE_REGNO_P, FUNCTION_ARG_REGNO_P): Contitionalize on ABI, not CPU. (struct arm_stack_offsets): Define. (struct machine_function): Add stack_offsets. Remove frame_size. (FUNCTION_ARG_PARTIAL_NREGS): Don't split if previous args have been pushed. (FUNCTION_ARG_ADVANCE, FUNCTION_ARG_BOUNDARY): Handle general doubleword alignment. (THUMB_INITIAL_ELIMINATION_OFFSET, ARM_INITIAL_ELIMINATION_OFFSET): Remove. (INITIAL_ELIMINATION_OFFSET): Call functions directly. * config/arm/arm.md (align_8): Enable for all targets. * config/arm/netbsd-elf.h (TARGET_DEFAULT): Remove TARGET_ATPCS. (ARM_DEFAULT_ABI): Define. * doc/invoke.texi : Document -mabi=. Update documentation for -mstructure-size-boundary. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@79921 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 55 ++++ gcc/config.gcc | 17 +- gcc/config/arm/arm-protos.h | 6 +- gcc/config/arm/arm.c | 691 +++++++++++++++++++++++--------------------- gcc/config/arm/arm.h | 180 +++++------- gcc/config/arm/arm.md | 2 +- gcc/config/arm/netbsd-elf.h | 4 +- gcc/doc/invoke.texi | 24 +- 8 files changed, 534 insertions(+), 445 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6681f96d97f..f6c465078e0 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,58 @@ +2004-03-24 Paul Brook + + * config.gcc : Add --with-abi= + * config/arm/arm-protos.h (arm_get_frame_size, thumb_get_frame_size, + thumb_far_jump_used): Remove prototypes. + (arm_needs_doubleword_align): Add prototype. + (thumb_compute_initial_elimination_offset): Ditto. + * config/arm/arm.c (arm_get_frame_offsets): New function. + (use_return_insn, output_return_instruction, arm_output_epilogue, + arm_output_function_epilogue, arm_compute_initial_elimination_offset, + arm_expand_prologue, thumb_expand_epilogue): Use it. + (arm_abi, target_abi_name, all_arm_abis): New variables. + (arm_override_options): Set them. Set structure padding for AAPCS. + (arm_return_in_memory): Update ABI check. + (arm_init_cumulative_args): Initialize can_split. + (arm_needs_doubleword_align): New function. + (arm_function_arg): Don't split args after pushing to stack. Handle + doubleword/even reg alignment. + (arm_va_arg): Handle all doubleword aligned args. + (add_minpoolforward ref, dump_minpool, push_minpool_fix): Align based + on ABI, not CPU. + (arm_compute_save_reg0_reg12_mask): Fix comment. + (thumb_get_frame_size, thumb_get_frame_size): Remove. + (thumb_jump_far_used_p): Remove superfluous argument. Return save + value for alignment. + (thumb_unexpanded_epilogue, thumb_output_function_prologue): Change + to match. + (thumb_compute_initial_elimination_offset): New function. + (thumb_expand_prologue): Use arm_get_frame_offsets. Remove + unneccessary rounding. + * config/arm/arm.h (target_abi_name): Declare. + (ARM_DOUBLEWORD_ALIGN, DOUBLEWORD_ALIGNMENT, TARGET_IWMMXT_ABI, + arm_abi_type, ARM_DEFAULT_ABI): Define. + (ARM_FLAG_ATPCS): Remove. + (TARGET_OPTIONS, OPTION_DEFAULT_SPECS): Add -mabi=. + (BIGGEST_ALIGNMENT, PREFERRED_STACK_BOUNDARY, STACK_BOUNDARY): Use it. + (ADJUST_FIELD_ALIGN, DATA_ALIGNMENT, LOCAL_ALIGNMENT, + TYPE_NEEDS_IWMMXT_ALIGNMENT): Remove. + (LIBCALL_VALUE, FUNCTION_VALUE_REGNO_P, FUNCTION_ARG_REGNO_P): + Contitionalize on ABI, not CPU. + (struct arm_stack_offsets): Define. + (struct machine_function): Add stack_offsets. Remove frame_size. + (FUNCTION_ARG_PARTIAL_NREGS): Don't split if previous args have been + pushed. + (FUNCTION_ARG_ADVANCE, FUNCTION_ARG_BOUNDARY): Handle general + doubleword alignment. + (THUMB_INITIAL_ELIMINATION_OFFSET, + ARM_INITIAL_ELIMINATION_OFFSET): Remove. + (INITIAL_ELIMINATION_OFFSET): Call functions directly. + * config/arm/arm.md (align_8): Enable for all targets. + * config/arm/netbsd-elf.h (TARGET_DEFAULT): Remove TARGET_ATPCS. + (ARM_DEFAULT_ABI): Define. + * doc/invoke.texi : Document -mabi=. Update documentation for + -mstructure-size-boundary. + 2004-03-24 Nathanael Nerode * configure.ac: Check for -Wno-variadic-macros; don't use diff --git a/gcc/config.gcc b/gcc/config.gcc index 286d23a4dd5..8d7f0d04d20 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -2226,7 +2226,7 @@ fi ;; arm*-*-*) - supported_defaults="arch cpu float tune fpu" + supported_defaults="arch cpu float tune fpu abi" for which in cpu tune; do eval "val=\$with_$which" case "$val" in @@ -2280,13 +2280,24 @@ fi # OK ;; *) - echo "Unknown fpu used in --with-fpu=$fpu" 2>&1 + echo "Unknown fpu used in --with-fpu=$with_fpu" 2>&1 + exit 1 + ;; + esac + + case "$with_abi" in + "" \ + | apcs-gnu | atpcs | aapcs | iwmmxt ) + #OK + ;; + *) + echo "Unknown ABI used in --with-abi=$with_abi" exit 1 ;; esac if test "x$with_arch" != x && test "x$with_cpu" != x; then - echo "Warning: --with-arch overrides --with-cpu" 1>&2 + echo "Warning: --with-arch overrides --with-cpu=$with_cpu" 1>&2 fi ;; diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 47503fd58f5..986f1eca78e 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -31,12 +31,13 @@ extern void arm_finalize_pic (int); extern int arm_volatile_func (void); extern const char *arm_output_epilogue (rtx); extern void arm_expand_prologue (void); -extern HOST_WIDE_INT arm_get_frame_size (void); extern const char *arm_strip_name_encoding (const char *); extern void arm_asm_output_labelref (FILE *, const char *); extern unsigned long arm_current_func_type (void); extern unsigned int arm_compute_initial_elimination_offset (unsigned int, unsigned int); +extern HOST_WIDE_INT thumb_compute_initial_elimination_offset (unsigned int, + unsigned int); #ifdef TREE_CODE extern int arm_return_in_memory (tree); @@ -157,6 +158,7 @@ extern void arm_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree); extern rtx arm_va_arg (tree, tree); extern int arm_function_arg_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, tree, int); +extern bool arm_needs_doubleword_align (enum machine_mode, tree); #endif #if defined AOF_ASSEMBLER @@ -175,9 +177,7 @@ extern int arm_float_words_big_endian (void); /* Thumb functions. */ extern void arm_init_expanders (void); -extern int thumb_far_jump_used_p (int); extern const char *thumb_unexpanded_epilogue (void); -extern HOST_WIDE_INT thumb_get_frame_size (void); extern void thumb_expand_prologue (void); extern void thumb_expand_epilogue (void); #ifdef TREE_CODE diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 6358e4bff0f..a957a130f43 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -59,6 +59,7 @@ typedef struct minipool_fixup Mfix; const struct attribute_spec arm_attribute_table[]; /* Forward function declarations. */ +static arm_stack_offsets *arm_get_frame_offsets (void); static void arm_add_gc_roots (void); static int arm_gen_constant (enum rtx_code, enum machine_mode, HOST_WIDE_INT, rtx, rtx, int, int); @@ -67,6 +68,7 @@ static int arm_address_register_rtx_p (rtx, int); static int arm_legitimate_index_p (enum machine_mode, rtx, RTX_CODE, int); static int thumb_base_register_rtx_p (rtx, enum machine_mode, int); inline static int thumb_index_register_rtx_p (rtx, int); +static int thumb_far_jump_used_p (void); static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code); static rtx emit_multi_reg_push (int); static rtx emit_sfm (int, int); @@ -284,6 +286,9 @@ enum fputype arm_fpu_tune; /* Whether to use floating point hardware. */ enum float_abi_type arm_float_abi; +/* Which ABI to use. */ +enum arm_abi_type arm_abi; + /* What program mode is the cpu running in? 26-bit mode or 32-bit mode. */ enum prog_mode_type arm_prgmode; @@ -296,6 +301,9 @@ const char * target_fpe_name = NULL; /* Set by the -mfloat-abi=... option. */ const char * target_float_abi_name = NULL; +/* Set by the -mabi=... option. */ +const char * target_abi_name = NULL; + /* Used to parse -mstructure_size_boundary command line option. */ const char * structure_size_string = NULL; int arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY; @@ -510,6 +518,23 @@ static const struct float_abi all_float_abis[] = }; +struct abi_name +{ + const char *name; + enum arm_abi_type abi_type; +}; + + +/* Available values for -mabi=. */ + +static const struct abi_name arm_all_abis[] = +{ + {"apcs-gnu", ARM_ABI_APCS}, + {"atpcs", ARM_ABI_ATPCS}, + {"aapcs", ARM_ABI_AAPCS}, + {"iwmmxt", ARM_ABI_IWMMXT} +}; + /* Return the number of bits set in VALUE. */ static unsigned bit_count (unsigned long value) @@ -814,8 +839,27 @@ arm_override_options (void) arm_tune_xscale = (tune_flags & FL_XSCALE) != 0; arm_arch_iwmmxt = (insn_flags & FL_IWMMXT) != 0; - if (TARGET_IWMMXT && (! TARGET_ATPCS)) - target_flags |= ARM_FLAG_ATPCS; + if (target_abi_name) + { + for (i = 0; i < ARRAY_SIZE (arm_all_abis); i++) + { + if (streq (arm_all_abis[i].name, target_abi_name)) + { + arm_abi = arm_all_abis[i].abi_type; + break; + } + } + if (i == ARRAY_SIZE (arm_all_abis)) + error ("invalid ABI option: -mabi=%s", target_abi_name); + } + else + arm_abi = ARM_DEFAULT_ABI; + + if (TARGET_IWMMXT && !ARM_DOUBLEWORD_ALIGN) + error ("iwmmxt requires an AAPCS compatible ABI for proper operation"); + + if (TARGET_IWMMXT_ABI && !TARGET_IWMMXT) + error ("iwmmxt abi requires an iwmmxt capable cpu"); arm_fp_model = ARM_FP_MODEL_UNKNOWN; if (target_fpu_name == NULL && target_fpe_name != NULL) @@ -908,14 +952,20 @@ arm_override_options (void) arm_prgmode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26; + /* Override the default structure alignment for AAPCS ABI. */ + if (arm_abi == ARM_ABI_AAPCS) + arm_structure_size_boundary = 8; + if (structure_size_string != NULL) { int size = strtol (structure_size_string, NULL, 0); - - if (size == 8 || size == 32) + + if (size == 8 || size == 32 + || (ARM_DOUBLEWORD_ALIGN && size == 64)) arm_structure_size_boundary = size; else - warning ("structure size boundary can only be set to 8 or 32"); + warning ("structure size boundary can only be set to %s", + ARM_DOUBLEWORD_ALIGN ? "8, 32 or 64": "8 or 32"); } if (arm_pic_register_string != NULL) @@ -1109,6 +1159,7 @@ use_return_insn (int iscond, rtx sibling) unsigned int func_type; unsigned long saved_int_regs; unsigned HOST_WIDE_INT stack_adjust; + arm_stack_offsets *offsets; /* Never use a return instruction before reload has run. */ if (!reload_completed) @@ -1125,7 +1176,8 @@ use_return_insn (int iscond, rtx sibling) if (IS_INTERRUPT (func_type) && frame_pointer_needed) return 0; - stack_adjust = arm_get_frame_size () + current_function_outgoing_args_size; + offsets = arm_get_frame_offsets (); + stack_adjust = offsets->outgoing_args - offsets->saved_regs; /* As do variadic functions. */ if (current_function_pretend_args_size @@ -2018,9 +2070,9 @@ arm_return_in_memory (tree type) size = int_size_in_bytes (type); - if (TARGET_ATPCS) + if (arm_abi != ARM_ABI_APCS) { - /* ATPCS returns aggregate types in memory only if they are + /* ATPCS and later return aggregate types in memory only if they are larger than a word (or are variable size). */ return (size < 0 || size > UNITS_PER_WORD); } @@ -2143,6 +2195,7 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype, /* On the ARM, the offset starts at 0. */ pcum->nregs = ((fntype && aggregate_value_p (TREE_TYPE (fntype), fntype)) ? 1 : 0); pcum->iwmmxt_nregs = 0; + pcum->can_split = true; pcum->call_cookie = CALL_NORMAL; @@ -2178,6 +2231,19 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype, } } + +/* Return true if mode/type need doubleword alignment. */ +bool +arm_needs_doubleword_align (enum machine_mode mode, tree type) +{ + return (mode == DImode + || mode == DFmode + || VECTOR_MODE_SUPPORTED_P (mode) + || (mode == BLKmode + && TYPE_ALIGN (type) > PARM_BOUNDARY)); +} + + /* Determine where to put an argument to a function. Value is zero to push the argument on the stack, or a hard register in which to store the argument. @@ -2193,37 +2259,44 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype, rtx arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode, - tree type ATTRIBUTE_UNUSED, int named) + tree type, int named) { - if (TARGET_REALLY_IWMMXT) + int nregs; + + /* Varargs vectors are treated the same as long long. + named_count avoids having to change the way arm handles 'named' */ + if (TARGET_IWMMXT_ABI + && VECTOR_MODE_SUPPORTED_P (mode) + && pcum->named_count > pcum->nargs + 1) { - if (VECTOR_MODE_SUPPORTED_P (mode)) + if (pcum->iwmmxt_nregs <= 9) + return gen_rtx_REG (mode, pcum->iwmmxt_nregs + FIRST_IWMMXT_REGNUM); + else { - /* varargs vectors are treated the same as long long. - named_count avoids having to change the way arm handles 'named' */ - if (pcum->named_count <= pcum->nargs + 1) - { - if (pcum->nregs == 1) - pcum->nregs += 1; - if (pcum->nregs <= 2) - return gen_rtx_REG (mode, pcum->nregs); - else - return NULL_RTX; - } - else if (pcum->iwmmxt_nregs <= 9) - return gen_rtx_REG (mode, pcum->iwmmxt_nregs + FIRST_IWMMXT_REGNUM); - else - return NULL_RTX; + pcum->can_split = false; + return NULL_RTX; } - else if ((mode == DImode || mode == DFmode) && pcum->nregs & 1) - pcum->nregs += 1; } + /* Put doubleword aligned quantities in even register pairs. */ + if (pcum->nregs & 1 + && ARM_DOUBLEWORD_ALIGN + && arm_needs_doubleword_align (mode, type)) + pcum->nregs++; + if (mode == VOIDmode) /* Compute operand 2 of the call insn. */ return GEN_INT (pcum->call_cookie); - - if (!named || pcum->nregs >= NUM_ARG_REGS) + + /* Only allow splitting an arg between regs and memory if all preceeding + args were allocated to regs. For args passed by reference we only count + the reference pointer. */ + if (pcum->can_split) + nregs = 1; + else + nregs = ARM_NUM_REGS2 (mode, type); + + if (!named || pcum->nregs + nregs > NUM_ARG_REGS) return NULL_RTX; return gen_rtx_REG (mode, pcum->nregs); @@ -2245,6 +2318,8 @@ arm_function_arg_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, rtx arm_va_arg (tree valist, tree type) { + int align; + /* Variable sized types are passed by reference. */ if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) { @@ -2252,17 +2327,18 @@ arm_va_arg (tree valist, tree type) return gen_rtx_MEM (ptr_mode, force_reg (Pmode, addr)); } - if (FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), NULL) == IWMMXT_ALIGNMENT) + align = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type); + if (align > PARM_BOUNDARY) { - tree minus_eight; + tree mask; tree t; /* Maintain 64-bit alignment of the valist pointer by constructing: valist = ((valist + (8 - 1)) & -8). */ - minus_eight = build_int_2 (- (IWMMXT_ALIGNMENT / BITS_PER_UNIT), -1); - t = build_int_2 ((IWMMXT_ALIGNMENT / BITS_PER_UNIT) - 1, 0); + mask = build_int_2 (- (align / BITS_PER_UNIT), -1); + t = build_int_2 ((align / BITS_PER_UNIT) - 1, 0); t = build (PLUS_EXPR, TREE_TYPE (valist), valist, t); - t = build (BIT_AND_EXPR, TREE_TYPE (t), t, minus_eight); + t = build (BIT_AND_EXPR, TREE_TYPE (t), t, mask); t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); @@ -6846,7 +6922,7 @@ add_minipool_forward_ref (Mfix *fix) we have not already found an insertion point, then make sure that all such 8-byte aligned quantities are placed at the start of the pool. */ - if (TARGET_REALLY_IWMMXT + if (ARM_DOUBLEWORD_ALIGN && max_mp == NULL && fix->fix_size == 8 && mp->fix_size != 8) @@ -7030,7 +7106,8 @@ add_minipool_backward_ref (Mfix *fix) { /* For now, we do not allow the insertion of 8-byte alignment requiring nodes anywhere but at the start of the pool. */ - if (TARGET_REALLY_IWMMXT && fix->fix_size == 8 && mp->fix_size != 8) + if (ARM_DOUBLEWORD_ALIGN + && fix->fix_size == 8 && mp->fix_size != 8) return NULL; else min_mp = mp; @@ -7049,7 +7126,7 @@ add_minipool_backward_ref (Mfix *fix) we have not already found an insertion point, then make sure that all such 8-byte aligned quantities are placed at the start of the pool. */ - else if (TARGET_REALLY_IWMMXT + else if (ARM_DOUBLEWORD_ALIGN && min_mp == NULL && fix->fix_size == 8 && mp->fix_size < 8) @@ -7147,7 +7224,7 @@ dump_minipool (rtx scan) Mnode * nmp; int align64 = 0; - if (TARGET_REALLY_IWMMXT) + if (ARM_DOUBLEWORD_ALIGN) for (mp = minipool_vector_head; mp != NULL; mp = mp->next) if (mp->refcount > 0 && mp->fix_size == 8) { @@ -7389,11 +7466,11 @@ push_minipool_fix (rtx insn, HOST_WIDE_INT address, rtx *loc, if (fix->forwards == 0 && fix->backwards == 0) abort (); - /* With iWMMXt enabled, the pool is aligned to an 8-byte boundary. + /* With AAPCS/iWMMXt enabled, the pool is aligned to an 8-byte boundary. So there might be an empty word before the start of the pool. Hence we reduce the forward range by 4 to allow for this possibility. */ - if (TARGET_REALLY_IWMMXT && fix->fix_size == 8) + if (ARM_DOUBLEWORD_ALIGN && fix->fix_size == 8) fix->forwards -= 4; if (dump_file) @@ -8651,8 +8728,7 @@ output_ascii_pseudo_op (FILE *stream, const unsigned char *p, int len) } /* Compute the register sabe mask for registers 0 through 12 - inclusive. This code is used by both arm_compute_save_reg_mask - and arm_compute_initial_elimination_offset. */ + inclusive. This code is used by arm_compute_save_reg_mask. */ static unsigned long arm_compute_save_reg0_reg12_mask (void) { @@ -8798,6 +8874,7 @@ output_return_instruction (rtx operand, int really_return, int reverse) int reg; unsigned long live_regs_mask; unsigned long func_type; + arm_stack_offsets *offsets; func_type = arm_current_func_type (); @@ -8896,9 +8973,10 @@ output_return_instruction (rtx operand, int really_return, int reverse) points to the base of the saved core registers. */ if (live_regs_mask & (1 << SP_REGNUM)) { - unsigned HOST_WIDE_INT stack_adjust = - arm_get_frame_size () + current_function_outgoing_args_size; - + unsigned HOST_WIDE_INT stack_adjust; + + offsets = arm_get_frame_offsets (); + stack_adjust = offsets->outgoing_args - offsets->saved_regs; if (stack_adjust != 0 && stack_adjust != 4) abort (); @@ -9128,12 +9206,12 @@ arm_output_epilogue (rtx sibling) frame that is $fp + 4 for a non-variadic function. */ int floats_offset = 0; rtx operands[3]; - int frame_size = arm_get_frame_size (); FILE * f = asm_out_file; rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs; unsigned int lrm_count = 0; int really_return = (sibling == NULL); int start_reg; + arm_stack_offsets *offsets; /* If we have already generated the return instruction then it is futile to generate anything else. */ @@ -9164,14 +9242,13 @@ arm_output_epilogue (rtx sibling) be doing a return, so we can't tail-call. */ abort (); + offsets = arm_get_frame_offsets (); saved_regs_mask = arm_compute_save_reg_mask (); if (TARGET_IWMMXT) lrm_count = bit_count (saved_regs_mask); - /* XXX We should adjust floats_offset for any anonymous args, and then - re-adjust vfp_offset below to compensate. */ - + floats_offset = offsets->saved_args; /* Compute how far away the floats will be. */ for (reg = 0; reg <= LAST_ARM_REGNUM; reg++) if (saved_regs_mask & (1 << reg)) @@ -9180,7 +9257,7 @@ arm_output_epilogue (rtx sibling) if (frame_pointer_needed) { /* This variable is for the Virtual Frame Pointer, not VFP regs. */ - int vfp_offset = 4; + int vfp_offset = offsets->frame; if (arm_fpu_arch == FPUTYPE_FPA_EMU2) { @@ -9332,8 +9409,7 @@ arm_output_epilogue (rtx sibling) be reset correctly to the original value, should an interrupt occur. If the stack pointer already points at the right place, then omit the subtraction. */ - if (((frame_size + current_function_outgoing_args_size + floats_offset) - != 4 * (1 + (int) bit_count (saved_regs_mask))) + if (offsets->outgoing_args != (1 + (int) bit_count (saved_regs_mask)) || current_function_calls_alloca) asm_fprintf (f, "\tsub\t%r, %r, #%d\n", SP_REGNUM, FP_REGNUM, 4 * bit_count (saved_regs_mask)); @@ -9347,11 +9423,10 @@ arm_output_epilogue (rtx sibling) else { /* Restore stack pointer if necessary. */ - if (frame_size + current_function_outgoing_args_size != 0) + if (offsets->outgoing_args != offsets->saved_regs) { operands[0] = operands[1] = stack_pointer_rtx; - operands[2] = GEN_INT (frame_size - + current_function_outgoing_args_size); + operands[2] = GEN_INT (offsets->outgoing_args - offsets->saved_regs); output_add_immediate (operands); } @@ -9517,8 +9592,10 @@ arm_output_epilogue (rtx sibling) static void arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, - HOST_WIDE_INT frame_size) + HOST_WIDE_INT frame_size ATTRIBUTE_UNUSED) { + arm_stack_offsets *offsets; + if (TARGET_THUMB) { /* ??? Probably not safe to set this here, since it assumes that a @@ -9529,11 +9606,11 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, else { /* We need to take into account any stack-frame rounding. */ - frame_size = arm_get_frame_size (); + offsets = arm_get_frame_offsets (); if (use_return_insn (FALSE, NULL) && return_used_this_function - && (frame_size + current_function_outgoing_args_size) != 0 + && offsets->saved_regs != offsets->outgoing_args && !frame_pointer_needed) abort (); @@ -9763,82 +9840,173 @@ emit_sfm (int base_reg, int count) The sign of the number returned reflects the direction of stack growth, so the values are positive for all eliminations except - from the soft frame pointer to the hard frame pointer. */ -unsigned int -arm_compute_initial_elimination_offset (unsigned int from, unsigned int to) + from the soft frame pointer to the hard frame pointer. + + SFP may point just inside the local variables block to ensure correct + alignment. */ + + +/* Calculate stack offsets. These are used to calculate register elimination + offsets and in prologue/epilogue code. */ + +static arm_stack_offsets * +arm_get_frame_offsets (void) { - unsigned int local_vars = arm_get_frame_size (); - unsigned int outgoing_args = current_function_outgoing_args_size; - unsigned int stack_frame; - unsigned int call_saved_registers; + struct arm_stack_offsets *offsets; unsigned long func_type; + int leaf; + bool new_block; + int saved; + HOST_WIDE_INT frame_size; + + offsets = &cfun->machine->stack_offsets; - func_type = arm_current_func_type (); + /* We need to know if we are a leaf function. Unfortunately, it + is possible to be called after start_sequence has been called, + which causes get_insns to return the insns for the sequence, + not the function, which will cause leaf_function_p to return + the incorrect result. - /* Volatile functions never return, so there is - no need to save call saved registers. */ - call_saved_registers = 0; - if (! IS_VOLATILE (func_type)) + to know about leaf functions once reload has completed, and the + frame size cannot be changed after that time, so we can safely + use the cached value. */ + + if (reload_completed) + return offsets; + + /* Initialy this is the size of the local variables. It will translated + into an offset once we have determined the size of preceeding data. */ + frame_size = ROUND_UP_WORD (get_frame_size ()); + + leaf = leaf_function_p (); + + /* Space for variadic functions. */ + offsets->saved_args = current_function_pretend_args_size; + + offsets->frame = offsets->saved_args + (frame_pointer_needed ? 4 : 0); + + if (TARGET_ARM) { - unsigned int reg_mask; - unsigned int reg; - bool new_block; - - /* Make sure that we compute which registers will be saved - on the stack using the same algorithm that is used by - the prologue creation code. */ - reg_mask = arm_compute_save_reg_mask (); - - /* Now count the number of bits set in save_reg_mask. - If we have already counted the registers in the stack - frame, do not count them again. Non call-saved registers - might be saved in the call-save area of the stack, if - doing so will preserve the stack's alignment. Hence we - must count them here. For each set bit we need 4 bytes - of stack space. */ - if (frame_pointer_needed) - reg_mask &= 0x07ff; - call_saved_registers += 4 * bit_count (reg_mask); + unsigned int regno; - /* If the hard floating point registers are going to be - used then they must be saved on the stack as well. - Each register occupies 12 bytes of stack space. */ - for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++) - if (regs_ever_live[reg] && ! call_used_regs[reg]) - call_saved_registers += 12; + saved = bit_count (arm_compute_save_reg_mask ()) * 4; - /* Likewise VFP regs. */ - if (TARGET_HARD_FLOAT && TARGET_VFP) + /* We know that SP will be doubleword aligned on entry, and we must + preserve that condition at any subroutine call. We also require the + soft frame pointer to be doubleword aligned. */ + + if (TARGET_REALLY_IWMMXT) { - new_block = TRUE; - for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2) + /* Check for the call-saved iWMMXt registers. */ + for (regno = FIRST_IWMMXT_REGNUM; + regno <= LAST_IWMMXT_REGNUM; + regno++) + if (regs_ever_live [regno] && ! call_used_regs [regno]) + saved += 8; + } + + func_type = arm_current_func_type (); + if (! IS_VOLATILE (func_type)) + { + /* Space for saved FPA registers. */ + for (regno = FIRST_FPA_REGNUM; regno <= LAST_FPA_REGNUM; regno++) + if (regs_ever_live[regno] && ! call_used_regs[regno]) + saved += 12; + + /* Space for saved VFP registers. */ + if (TARGET_HARD_FLOAT && TARGET_VFP) { - if ((regs_ever_live[reg] && !call_used_regs[reg]) - || (regs_ever_live[reg + 1] && !call_used_regs[reg + 1])) + new_block = TRUE; + for (regno = FIRST_VFP_REGNUM; + regno < LAST_VFP_REGNUM; + regno += 2) { - if (new_block) + if ((regs_ever_live[regno] && !call_used_regs[regno]) + || (regs_ever_live[regno + 1] + && !call_used_regs[regno + 1])) { - call_saved_registers += 4; - new_block = FALSE; + if (new_block) + { + saved += 4; + new_block = FALSE; + } + saved += 8; } - call_saved_registers += 8; + else + new_block = TRUE; } - else - new_block = TRUE; } } + } + else /* TARGET_THUMB */ + { + int reg; + int count_regs; + + saved = 0; + count_regs = 0; + for (reg = 8; reg < 13; reg ++) + if (THUMB_REG_PUSHED_P (reg)) + count_regs ++; + if (count_regs) + saved += 4 * count_regs; + count_regs = 0; + for (reg = 0; reg <= LAST_LO_REGNUM; reg ++) + if (THUMB_REG_PUSHED_P (reg)) + count_regs ++; + if (count_regs || ! leaf_function_p () + || thumb_far_jump_used_p ()) + saved += 4 * (count_regs + 1); + if (TARGET_BACKTRACE) + { + if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0)) + saved += 20; + else + saved += 16; + } + } - if (TARGET_REALLY_IWMMXT) - /* Check for the call-saved iWMMXt registers. */ - for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++) - if (regs_ever_live[reg] && ! call_used_regs [reg]) - call_saved_registers += 8; + /* Saved registers include the stack frame. */ + offsets->saved_regs = offsets->saved_args + saved; + offsets->soft_frame = offsets->saved_regs; + /* A leaf function does not need any stack alignment if it has nothing + on the stack. */ + if (leaf && frame_size == 0) + { + offsets->outgoing_args = offsets->soft_frame; + return offsets; + } + + /* Ensure SFP has the correct alignment. */ + if (ARM_DOUBLEWORD_ALIGN + && (offsets->soft_frame & 7)) + offsets->soft_frame += 4; + + offsets->outgoing_args = offsets->soft_frame + frame_size + + current_function_outgoing_args_size; + + if (ARM_DOUBLEWORD_ALIGN) + { + /* Ensure SP remains doubleword aligned. */ + if (offsets->outgoing_args & 7) + offsets->outgoing_args += 4; + if (offsets->outgoing_args & 7) + abort (); } - /* The stack frame contains 4 registers - the old frame pointer, - the old stack pointer, the return address and PC of the start - of the function. */ - stack_frame = frame_pointer_needed ? 16 : 0; + return offsets; +} + + +/* Calculate the realative offsets for the different stack pointers. Positive + offsets are in the direction of stack growth. */ + +unsigned int +arm_compute_initial_elimination_offset (unsigned int from, unsigned int to) +{ + arm_stack_offsets *offsets; + + offsets = arm_get_frame_offsets (); /* OK, now we have enough information to compute the distances. There must be an entry in these switch tables for each pair @@ -9855,24 +10023,22 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to) case FRAME_POINTER_REGNUM: /* This is the reverse of the soft frame pointer to hard frame pointer elimination below. */ - if (call_saved_registers == 0 && stack_frame == 0) - return 0; - return (call_saved_registers + stack_frame - 4); + return offsets->soft_frame - offsets->saved_args; case ARM_HARD_FRAME_POINTER_REGNUM: /* If there is no stack frame then the hard frame pointer and the arg pointer coincide. */ - if (stack_frame == 0 && call_saved_registers != 0) + if (offsets->frame == offsets->saved_regs) return 0; - /* FIXME: Not sure about this. Maybe we should always return 0 ? */ - return (frame_pointer_needed - && current_function_needs_context - && ! cfun->machine->uses_anonymous_args) ? 4 : 0; + /* FIXME: Not sure about this. Maybe we should always return 0 ? */ + return (frame_pointer_needed + && current_function_needs_context + && ! cfun->machine->uses_anonymous_args) ? 4 : 0; case STACK_POINTER_REGNUM: /* If nothing has been pushed on the stack at all then this will return -4. This *is* correct! */ - return call_saved_registers + stack_frame + local_vars + outgoing_args - 4; + return offsets->outgoing_args - (offsets->saved_args + 4); default: abort (); @@ -9890,12 +10056,11 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to) stack frame. The soft frame pointer to the bottom entry in the stack frame. If there is no stack frame at all, then they are identical. */ - if (call_saved_registers == 0 && stack_frame == 0) - return 0; - return - (call_saved_registers + stack_frame - 4); + + return offsets->frame - offsets->soft_frame; case STACK_POINTER_REGNUM: - return local_vars + outgoing_args; + return offsets->outgoing_args - offsets->soft_frame; default: abort (); @@ -9912,107 +10077,6 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to) } } -/* Calculate the size of the stack frame, taking into account any - padding that is required to ensure stack-alignment. */ -HOST_WIDE_INT -arm_get_frame_size (void) -{ - int regno; - - int base_size = ROUND_UP_WORD (get_frame_size ()); - int entry_size = 0; - unsigned long func_type = arm_current_func_type (); - int leaf; - bool new_block; - - if (! TARGET_ARM) - abort(); - - if (! TARGET_ATPCS) - return base_size; - - /* We need to know if we are a leaf function. Unfortunately, it - is possible to be called after start_sequence has been called, - which causes get_insns to return the insns for the sequence, - not the function, which will cause leaf_function_p to return - the incorrect result. - - To work around this, we cache the computed frame size. This - works because we will only be calling RTL expanders that need - to know about leaf functions once reload has completed, and the - frame size cannot be changed after that time, so we can safely - use the cached value. */ - - if (reload_completed) - return cfun->machine->frame_size; - - leaf = leaf_function_p (); - - /* A leaf function does not need any stack alignment if it has nothing - on the stack. */ - if (leaf && base_size == 0) - { - cfun->machine->frame_size = 0; - return 0; - } - - /* We know that SP will be word aligned on entry, and we must - preserve that condition at any subroutine call. But those are - the only constraints. */ - - /* Space for variadic functions. */ - if (current_function_pretend_args_size) - entry_size += current_function_pretend_args_size; - - /* Space for saved registers. */ - entry_size += bit_count (arm_compute_save_reg_mask ()) * 4; - - if (! IS_VOLATILE (func_type)) - { - /* Space for saved FPA registers. */ - for (regno = FIRST_FPA_REGNUM; regno <= LAST_FPA_REGNUM; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - entry_size += 12; - - /* Space for saved VFP registers. */ - if (TARGET_HARD_FLOAT && TARGET_VFP) - { - new_block = TRUE; - for (regno = FIRST_VFP_REGNUM; regno < LAST_VFP_REGNUM; regno += 2) - { - if ((regs_ever_live[regno] && !call_used_regs[regno]) - || (regs_ever_live[regno + 1] && !call_used_regs[regno + 1])) - { - if (new_block) - { - entry_size += 4; - new_block = FALSE; - } - entry_size += 8; - } - else - new_block = TRUE; - } - } - } - - if (TARGET_REALLY_IWMMXT) - { - /* Check for the call-saved iWMMXt registers. */ - for (regno = FIRST_IWMMXT_REGNUM; regno <= LAST_IWMMXT_REGNUM; regno++) - if (regs_ever_live [regno] && ! call_used_regs [regno]) - entry_size += 8; - } - - if ((entry_size + base_size + current_function_outgoing_args_size) & 7) - base_size += 4; - if ((entry_size + base_size + current_function_outgoing_args_size) & 7) - abort (); - - cfun->machine->frame_size = base_size; - - return base_size; -} /* Generate the prologue instructions for entry into an ARM function. */ void @@ -10026,7 +10090,9 @@ arm_expand_prologue (void) unsigned long func_type; int fp_offset = 0; int saved_pretend_args = 0; + int saved_regs = 0; unsigned int args_to_push; + arm_stack_offsets *offsets; func_type = arm_current_func_type (); @@ -10172,6 +10238,7 @@ arm_expand_prologue (void) if (live_regs_mask) { insn = emit_multi_reg_push (live_regs_mask); + saved_regs += bit_count (live_regs_mask) * 4; RTX_FRAME_RELATED_P (insn) = 1; } @@ -10184,6 +10251,7 @@ arm_expand_prologue (void) insn = emit_insn (gen_rtx_SET (VOIDmode, insn, gen_rtx_REG (V2SImode, reg))); RTX_FRAME_RELATED_P (insn) = 1; + saved_regs += 8; } if (! IS_VOLATILE (func_type)) @@ -10202,6 +10270,7 @@ arm_expand_prologue (void) insn = emit_insn (gen_rtx_SET (VOIDmode, insn, gen_rtx_REG (XFmode, reg))); RTX_FRAME_RELATED_P (insn) = 1; + saved_regs += 12; } } else @@ -10225,6 +10294,7 @@ arm_expand_prologue (void) { insn = emit_sfm (reg + 1, start_reg - reg); RTX_FRAME_RELATED_P (insn) = 1; + saved_regs += (reg - start_reg) * 12; } start_reg = reg - 1; } @@ -10233,6 +10303,7 @@ arm_expand_prologue (void) if (start_reg != reg) { insn = emit_sfm (reg + 1, start_reg - reg); + saved_regs += (reg - start_reg) * 12; RTX_FRAME_RELATED_P (insn) = 1; } } @@ -10250,6 +10321,7 @@ arm_expand_prologue (void) insn = vfp_emit_fstmx (start_reg, (reg - start_reg) / 2); RTX_FRAME_RELATED_P (insn) = 1; + saved_regs += (start_reg - reg) * 4 + 4; } start_reg = reg + 2; } @@ -10259,6 +10331,7 @@ arm_expand_prologue (void) insn = vfp_emit_fstmx (start_reg, (reg - start_reg) / 2); RTX_FRAME_RELATED_P (insn) = 1; + saved_regs += (start_reg - reg) * 4 + 4; } } } @@ -10289,14 +10362,16 @@ arm_expand_prologue (void) } } - amount = GEN_INT (-(arm_get_frame_size () - + current_function_outgoing_args_size)); - - if (amount != const0_rtx) + offsets = arm_get_frame_offsets (); + if (offsets->outgoing_args != offsets->saved_args + saved_regs) { /* This add can produce multiple insns for a large constant, so we need to get tricky. */ rtx last = get_last_insn (); + + amount = GEN_INT (offsets->saved_args + saved_regs + - offsets->outgoing_args); + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, amount)); do @@ -12618,8 +12693,8 @@ thumb_shiftable_const (unsigned HOST_WIDE_INT val) /* Returns nonzero if the current function contains, or might contain a far jump. */ -int -thumb_far_jump_used_p (int in_prologue) +static int +thumb_far_jump_used_p (void) { rtx insn; @@ -12637,7 +12712,7 @@ thumb_far_jump_used_p (int in_prologue) /* If this function is not being called from the prologue/epilogue generation code then it must be being called from the INITIAL_ELIMINATION_OFFSET macro. */ - if (!in_prologue) + if (!(ARM_DOUBLEWORD_ALIGN || reload_completed)) { /* In this case we know that we are being asked about the elimination of the arg pointer register. If that register is not being used, @@ -12656,7 +12731,10 @@ thumb_far_jump_used_p (int in_prologue) A false negative will not result in bad code being generated, but it will result in a needless push and pop of the link register. We - hope that this does not occur too often. */ + hope that this does not occur too often. + + If we need doubleword stack alignment this could affect the other + elimination offsets so we can't risk getting it wrong. */ if (regs_ever_live [ARG_POINTER_REGNUM]) cfun->machine->arg_pointer_live = 1; else if (!cfun->machine->arg_pointer_live) @@ -12807,7 +12885,7 @@ thumb_unexpanded_epilogue (void) } had_to_push_lr = (live_regs_mask || !leaf_function - || thumb_far_jump_used_p (1)); + || thumb_far_jump_used_p ()); if (TARGET_BACKTRACE && ((live_regs_mask & 0xFF) == 0) @@ -12913,100 +12991,66 @@ arm_init_expanders (void) init_machine_status = arm_init_machine_status; } -HOST_WIDE_INT -thumb_get_frame_size (void) -{ - int regno; - int base_size = ROUND_UP_WORD (get_frame_size ()); - int count_regs = 0; - int entry_size = 0; - int leaf; - - if (! TARGET_THUMB) - abort (); +/* Like arm_compute_initial_elimination offset. Simpler because + THUMB_HARD_FRAME_POINTER isn't actually the ABI specified frame pointer. */ - if (! TARGET_ATPCS) - return base_size; - - /* We need to know if we are a leaf function. Unfortunately, it - is possible to be called after start_sequence has been called, - which causes get_insns to return the insns for the sequence, - not the function, which will cause leaf_function_p to return - the incorrect result. - - To work around this, we cache the computed frame size. This - works because we will only be calling RTL expanders that need - to know about leaf functions once reload has completed, and the - frame size cannot be changed after that time, so we can safely - use the cached value. */ - - if (reload_completed) - return cfun->machine->frame_size; - - leaf = leaf_function_p (); - - /* A leaf function does not need any stack alignment if it has nothing - on the stack. */ - if (leaf && base_size == 0) - { - cfun->machine->frame_size = 0; - return 0; - } - - /* We know that SP will be word aligned on entry, and we must - preserve that condition at any subroutine call. But those are - the only constraints. */ - - /* Space for variadic functions. */ - if (current_function_pretend_args_size) - entry_size += current_function_pretend_args_size; +HOST_WIDE_INT +thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to) +{ + arm_stack_offsets *offsets; - /* Space for pushed lo registers. */ - for (regno = 0; regno <= LAST_LO_REGNUM; regno++) - if (THUMB_REG_PUSHED_P (regno)) - count_regs++; + offsets = arm_get_frame_offsets (); - /* Space for backtrace structure. */ - if (TARGET_BACKTRACE) + switch (from) { - if (count_regs == 0 && regs_ever_live[LAST_ARG_REGNUM] != 0) - entry_size += 20; - else - entry_size += 16; - } + case ARG_POINTER_REGNUM: + switch (to) + { + case STACK_POINTER_REGNUM: + return offsets->outgoing_args - offsets->saved_args; - if (count_regs || !leaf || thumb_far_jump_used_p (1)) - count_regs++; /* LR */ + case FRAME_POINTER_REGNUM: + return offsets->soft_frame - offsets->saved_args; - entry_size += count_regs * 4; - count_regs = 0; + case THUMB_HARD_FRAME_POINTER_REGNUM: + case ARM_HARD_FRAME_POINTER_REGNUM: + return offsets->saved_regs - offsets->saved_args; - /* Space for pushed hi regs. */ - for (regno = 8; regno < 13; regno++) - if (THUMB_REG_PUSHED_P (regno)) - count_regs++; + default: + abort(); + } + break; - entry_size += count_regs * 4; + case FRAME_POINTER_REGNUM: + switch (to) + { + case STACK_POINTER_REGNUM: + return offsets->outgoing_args - offsets->soft_frame; - if ((entry_size + base_size + current_function_outgoing_args_size) & 7) - base_size += 4; - if ((entry_size + base_size + current_function_outgoing_args_size) & 7) - abort (); + case THUMB_HARD_FRAME_POINTER_REGNUM: + case ARM_HARD_FRAME_POINTER_REGNUM: + return offsets->saved_regs - offsets->soft_frame; - cfun->machine->frame_size = base_size; + default: + abort(); + } + break; - return base_size; + default: + abort (); + } } + /* Generate the rest of a function's prologue. */ void thumb_expand_prologue (void) { rtx insn, dwarf; - HOST_WIDE_INT amount = (thumb_get_frame_size () - + current_function_outgoing_args_size); + HOST_WIDE_INT amount; + arm_stack_offsets *offsets; unsigned long func_type; func_type = arm_current_func_type (); @@ -13021,16 +13065,18 @@ thumb_expand_prologue (void) return; } + offsets = arm_get_frame_offsets (); + if (frame_pointer_needed) { - insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx)); + insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, + stack_pointer_rtx)); RTX_FRAME_RELATED_P (insn) = 1; } + amount = offsets->outgoing_args - offsets->saved_regs; if (amount) { - amount = ROUND_UP_WORD (amount); - if (amount < 512) { insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, @@ -13129,20 +13175,21 @@ thumb_expand_prologue (void) void thumb_expand_epilogue (void) { - HOST_WIDE_INT amount = (thumb_get_frame_size () - + current_function_outgoing_args_size); + HOST_WIDE_INT amount; + arm_stack_offsets *offsets; int regno; /* Naked functions don't have prologues. */ if (IS_NAKED (arm_current_func_type ())) return; + offsets = arm_get_frame_offsets (); + amount = offsets->outgoing_args - offsets->saved_regs; + if (frame_pointer_needed) emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); else if (amount) { - amount = ROUND_UP_WORD (amount); - if (amount < 512) emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (amount))); @@ -13257,7 +13304,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) if (THUMB_REG_PUSHED_P (regno)) live_regs_mask |= 1 << regno; - if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1)) + if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p ()) live_regs_mask |= 1 << LR_REGNUM; if (TARGET_BACKTRACE) diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index 666ce746907..fa8b8f9e0a4 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -140,6 +140,8 @@ extern const char *target_fpu_name; extern const char *target_fpe_name; /* Whether to use floating point hardware. */ extern const char *target_float_abi_name; +/* Which ABI to use. */ +extern const char *target_abi_name; /* Define the information needed to generate branch insns. This is stored from the compare operation. */ extern GTY(()) rtx arm_compare_op0; @@ -433,11 +435,8 @@ extern GTY(()) rtx aof_pic_label; destination is non-Thumb aware. */ #define THUMB_FLAG_CALLER_SUPER_INTERWORKING (1 << 20) -/* Nonzero means to use ARM/Thumb Procedure Call Standard conventions. */ -#define ARM_FLAG_ATPCS (1 << 21) - /* Fix invalid Cirrus instruction combinations by inserting NOPs. */ -#define CIRRUS_FIX_INVALID_INSNS (1 << 22) +#define CIRRUS_FIX_INVALID_INSNS (1 << 21) #define TARGET_APCS_FRAME (target_flags & ARM_FLAG_APCS_FRAME) #define TARGET_POKE_FUNCTION_NAME (target_flags & ARM_FLAG_POKE) @@ -446,7 +445,6 @@ extern GTY(()) rtx aof_pic_label; #define TARGET_APCS_STACK (target_flags & ARM_FLAG_APCS_STACK) #define TARGET_APCS_FLOAT (target_flags & ARM_FLAG_APCS_FLOAT) #define TARGET_APCS_REENT (target_flags & ARM_FLAG_APCS_REENT) -#define TARGET_ATPCS (target_flags & ARM_FLAG_ATPCS) #define TARGET_MMU_TRAPS (target_flags & ARM_FLAG_MMU_TRAPS) #define TARGET_SOFT_FLOAT (arm_float_abi == ARM_FLOAT_ABI_SOFT) #define TARGET_SOFT_FLOAT_ABI (arm_float_abi != ARM_FLOAT_ABI_HARD) @@ -456,6 +454,7 @@ extern GTY(()) rtx aof_pic_label; #define TARGET_VFP (arm_fp_model == ARM_FP_MODEL_VFP) #define TARGET_IWMMXT (arm_arch_iwmmxt) #define TARGET_REALLY_IWMMXT (TARGET_IWMMXT && TARGET_ARM) +#define TARGET_IWMMXT_ABI (TARGET_ARM && arm_abi == ARM_ABI_IWMMXT) #define TARGET_BIG_END (target_flags & ARM_FLAG_BIG_END) #define TARGET_INTERWORK (target_flags & ARM_FLAG_INTERWORK) #define TARGET_LITTLE_WORDS (target_flags & ARM_FLAG_LITTLE_WORDS) @@ -574,7 +573,8 @@ extern GTY(()) rtx aof_pic_label; {"structure-size-boundary=", & structure_size_string, \ N_("Specify the minimum bit alignment of structures"), 0}, \ {"pic-register=", & arm_pic_register_string, \ - N_("Specify the register to be used for PIC addressing"), 0} \ + N_("Specify the register to be used for PIC addressing"), 0}, \ + {"abi=", &target_abi_name, N_("Specify an ABI"), 0} \ } /* Support for a compile-time default CPU, et cetera. The rules are: @@ -585,14 +585,16 @@ extern GTY(()) rtx aof_pic_label; by -march). --with-float is ignored if -mhard-float, -msoft-float or -mfloat-abi are specified. - --with-fpu is ignored if -mfpu is specified. */ + --with-fpu is ignored if -mfpu is specified. + --with-abi is ignored is -mabi is specified. */ #define OPTION_DEFAULT_SPECS \ {"arch", "%{!march=*:%{!mcpu=*:-march=%(VALUE)}}" }, \ {"cpu", "%{!march=*:%{!mcpu=*:-mcpu=%(VALUE)}}" }, \ {"tune", "%{!mcpu=*:%{!mtune=*:-mtune=%(VALUE)}}" }, \ {"float", \ "%{!msoft-float:%{!mhard-float:%{!mfloat-abi=*:-mfloat-abi=%(VALUE)}}}" }, \ - {"fpu", "%{!mfpu=*:-mfpu=%(VALUE)}"}, + {"fpu", "%{!mfpu=*:-mfpu=%(VALUE)}"}, \ + {"abi", "%{!mabi=*:-mabi=%(VALUE)}"}, struct arm_cpu_select { @@ -682,6 +684,21 @@ extern enum float_abi_type arm_float_abi; #define FPUTYPE_DEFAULT FPUTYPE_MAVERICK #endif +/* Which ABI to use. */ +enum arm_abi_type +{ + ARM_ABI_APCS, + ARM_ABI_ATPCS, + ARM_ABI_AAPCS, + ARM_ABI_IWMMXT +}; + +extern enum arm_abi_type arm_abi; + +#ifndef ARM_DEFAULT_ABI +#define ARM_DEFAULT_ABI ARM_ABI_APCS +#endif + /* Nonzero if this chip supports the ARM Architecture 3M extensions. */ extern int arm_arch3m; @@ -811,13 +828,17 @@ extern int arm_is_6_or_7; #define UNITS_PER_WORD 4 -#define PARM_BOUNDARY 32 +/* True if natural alignment is used for doubleword types. */ +#define ARM_DOUBLEWORD_ALIGN \ + (arm_abi == ARM_ABI_AAPCS || arm_abi == ARM_ABI_IWMMXT) +#define DOUBLEWORD_ALIGNMENT 64 -#define IWMMXT_ALIGNMENT 64 +#define PARM_BOUNDARY 32 -#define STACK_BOUNDARY 32 +#define STACK_BOUNDARY (ARM_DOUBLEWORD_ALIGN ? DOUBLEWORD_ALIGNMENT : 32) -#define PREFERRED_STACK_BOUNDARY (TARGET_ATPCS ? 64 : 32) +#define PREFERRED_STACK_BOUNDARY \ + (arm_abi == ARM_ABI_ATPCS ? 64 : STACK_BOUNDARY) #define FUNCTION_BOUNDARY 32 @@ -828,62 +849,30 @@ extern int arm_is_6_or_7; #define EMPTY_FIELD_BOUNDARY 32 -#define BIGGEST_ALIGNMENT (TARGET_REALLY_IWMMXT ? 64 : 32) - -#define TYPE_NEEDS_IWMMXT_ALIGNMENT(TYPE) \ - (TARGET_REALLY_IWMMXT \ - && ((TREE_CODE (TYPE) == VECTOR_TYPE) || (TYPE_MODE (TYPE) == DImode) || (TYPE_MODE (TYPE) == DFmode))) +#define BIGGEST_ALIGNMENT (ARM_DOUBLEWORD_ALIGN ? DOUBLEWORD_ALIGNMENT : 32) /* XXX Blah -- this macro is used directly by libobjc. Since it supports no vector modes, cut out the complexity and fall back on BIGGEST_FIELD_ALIGNMENT. */ #ifdef IN_TARGET_LIBS #define BIGGEST_FIELD_ALIGNMENT 64 -#else -/* An expression for the alignment of a structure field FIELD if the - alignment computed in the usual way is COMPUTED. GCC uses this - value instead of the value in `BIGGEST_ALIGNMENT' or - `BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */ -#define ADJUST_FIELD_ALIGN(FIELD, COMPUTED) \ - (TYPE_NEEDS_IWMMXT_ALIGNMENT (TREE_TYPE (FIELD)) \ - ? IWMMXT_ALIGNMENT \ - : (COMPUTED)) #endif -/* If defined, a C expression to compute the alignment for a static variable. - TYPE is the data type, and ALIGN is the alignment that the object - would ordinarily have. The value of this macro is used instead of that - alignment to align the object. - - If this macro is not defined, then ALIGN is used. */ -#define DATA_ALIGNMENT(TYPE, ALIGN) \ - (TYPE_NEEDS_IWMMXT_ALIGNMENT (TYPE) ? IWMMXT_ALIGNMENT : ALIGN) - -/* If defined, a C expression to compute the alignment for a - variables in the local store. TYPE is the data type, and - BASIC-ALIGN is the alignment that the object would ordinarily - have. The value of this macro is used instead of that alignment - to align the object. - - If this macro is not defined, then BASIC-ALIGN is used. */ -#define LOCAL_ALIGNMENT(TYPE, ALIGN) \ - (TYPE_NEEDS_IWMMXT_ALIGNMENT (TYPE) ? IWMMXT_ALIGNMENT : ALIGN) - /* Make strings word-aligned so strcpy from constants will be faster. */ #define CONSTANT_ALIGNMENT_FACTOR (TARGET_THUMB || ! arm_tune_xscale ? 1 : 2) #define CONSTANT_ALIGNMENT(EXP, ALIGN) \ - ((TARGET_REALLY_IWMMXT && TREE_CODE (EXP) == VECTOR_TYPE) ? IWMMXT_ALIGNMENT : \ - (TREE_CODE (EXP) == STRING_CST \ - && (ALIGN) < BITS_PER_WORD * CONSTANT_ALIGNMENT_FACTOR) \ - ? BITS_PER_WORD * CONSTANT_ALIGNMENT_FACTOR : (ALIGN)) + ((TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < BITS_PER_WORD * CONSTANT_ALIGNMENT_FACTOR) \ + ? BITS_PER_WORD * CONSTANT_ALIGNMENT_FACTOR : (ALIGN)) /* Setting STRUCTURE_SIZE_BOUNDARY to 32 produces more efficient code, but the value set in previous versions of this toolchain was 8, which produces more compact structures. The command line option -mstructure_size_boundary= can be used to change this value. For compatibility with the ARM SDK however the value should be left at 32. ARM SDT Reference Manual (ARM DUI - 0020D) page 2-20 says "Structures are aligned on word boundaries". */ + 0020D) page 2-20 says "Structures are aligned on word boundaries". + The AAPCS specifies a value of 8. */ #define STRUCTURE_SIZE_BOUNDARY arm_structure_size_boundary extern int arm_structure_size_boundary; @@ -1728,7 +1717,7 @@ enum reg_class : TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK \ && GET_MODE_CLASS (MODE) == MODE_FLOAT \ ? gen_rtx_REG (MODE, FIRST_CIRRUS_FP_REGNUM) \ - : TARGET_REALLY_IWMMXT && VECTOR_MODE_SUPPORTED_P (MODE) \ + : TARGET_IWMMXT_ABI && VECTOR_MODE_SUPPORTED_P (MODE) \ ? gen_rtx_REG (MODE, FIRST_IWMMXT_REGNUM) \ : gen_rtx_REG (MODE, ARG_REGISTER (1))) @@ -1746,7 +1735,7 @@ enum reg_class ((REGNO) == ARG_REGISTER (1) \ || (TARGET_ARM && ((REGNO) == FIRST_CIRRUS_FP_REGNUM) \ && TARGET_HARD_FLOAT && TARGET_MAVERICK) \ - || (TARGET_ARM && ((REGNO) == FIRST_IWMMXT_REGNUM) && TARGET_IWMMXT) \ + || ((REGNO) == FIRST_IWMMXT_REGNUM && TARGET_IWMMXT_ABI) \ || (TARGET_ARM && ((REGNO) == FIRST_FPA_REGNUM) \ && TARGET_HARD_FLOAT && TARGET_FPA)) @@ -1800,6 +1789,22 @@ enum reg_class #define IS_NAKED(t) (t & ARM_FT_NAKED) #define IS_NESTED(t) (t & ARM_FT_NESTED) + +/* Structure used to hold the function stack frame layout. Offsets are + relative to the stack pointer on function entry. Positive offsets are + in the direction of stack growth. + Only soft_frame is used in thumb mode. */ + +typedef struct arm_stack_offsets GTY(()) +{ + int saved_args; /* ARG_POINTER_REGNUM. */ + int frame; /* ARM_HARD_FRAME_POINTER_REGNUM. */ + int saved_regs; + int soft_frame; /* FRAME_POINTER_REGNUM. */ + int outgoing_args; /* STACK_POINTER_REGNUM. */ +} +arm_stack_offsets; + /* A C structure for machine-specific, per-function data. This is added to the cfun structure. */ typedef struct machine_function GTY(()) @@ -1813,7 +1818,7 @@ typedef struct machine_function GTY(()) /* Records if the save of LR has been eliminated. */ int lr_save_eliminated; /* The size of the stack frame. Only valid after reload. */ - int frame_size; + arm_stack_offsets stack_offsets; /* Records the type of the current function. */ unsigned long func_type; /* Record if the function has a variable argument list. */ @@ -1837,6 +1842,7 @@ typedef struct int nargs; /* One of CALL_NORMAL, CALL_LONG or CALL_SHORT. */ int call_cookie; + int can_split; } CUMULATIVE_ARGS; /* Define where to put the arguments to a function. @@ -1866,7 +1872,8 @@ typedef struct #define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ (VECTOR_MODE_SUPPORTED_P (MODE) ? 0 : \ NUM_ARG_REGS > (CUM).nregs \ - && (NUM_ARG_REGS < ((CUM).nregs + ARM_NUM_REGS2 (MODE, TYPE))) \ + && (NUM_ARG_REGS < ((CUM).nregs + ARM_NUM_REGS2 (MODE, TYPE)) \ + && (CUM).can_split) \ ? NUM_ARG_REGS - (CUM).nregs : 0) /* A C expression that indicates when an argument must be passed by @@ -1889,26 +1896,26 @@ typedef struct (TYPE is null for libcalls where that information may not be available.) */ #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ (CUM).nargs += 1; \ - if (VECTOR_MODE_SUPPORTED_P (MODE)) \ - if ((CUM).named_count <= (CUM).nargs) \ - (CUM).nregs += 2; \ - else \ - (CUM).iwmmxt_nregs += 1; \ + if (VECTOR_MODE_SUPPORTED_P (MODE) \ + && (CUM).named_count > (CUM).nargs) \ + (CUM).iwmmxt_nregs += 1; \ else \ - (CUM).nregs += ARM_NUM_REGS2 (MODE, TYPE) + (CUM).nregs += ARM_NUM_REGS2 (MODE, TYPE) /* If defined, a C expression that gives the alignment boundary, in bits, of an argument with the specified mode and type. If it is not defined, `PARM_BOUNDARY' is used for all arguments. */ #define FUNCTION_ARG_BOUNDARY(MODE,TYPE) \ - (TARGET_REALLY_IWMMXT && (VALID_IWMMXT_REG_MODE (MODE) || ((MODE) == DFmode)) \ - ? IWMMXT_ALIGNMENT : PARM_BOUNDARY) + ((ARM_DOUBLEWORD_ALIGN && arm_needs_doubleword_align (MODE, TYPE)) \ + ? DOUBLEWORD_ALIGNMENT \ + : PARM_BOUNDARY ) /* 1 if N is a possible register number for function argument passing. On the ARM, r0-r3 are used to pass args. */ #define FUNCTION_ARG_REGNO_P(REGNO) \ (IN_RANGE ((REGNO), 0, 3) \ - || (TARGET_REALLY_IWMMXT && IN_RANGE ((REGNO), FIRST_IWMMXT_REGNUM, FIRST_IWMMXT_REGNUM + 9))) + || (TARGET_IWMMXT_ABI \ + && IN_RANGE ((REGNO), FIRST_IWMMXT_REGNUM, FIRST_IWMMXT_REGNUM + 9))) /* Implement `va_arg'. */ #define EXPAND_BUILTIN_VA_ARG(valist, type) \ @@ -2030,53 +2037,12 @@ typedef struct /* Define the offset between two registers, one to be eliminated, and the other its replacement, at the start of a routine. */ -#define ARM_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ - do \ - { \ - (OFFSET) = arm_compute_initial_elimination_offset (FROM, TO); \ - } \ - while (0) - -/* Note: This macro must match the code in thumb_function_prologue(). */ -#define THUMB_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ -{ \ - (OFFSET) = 0; \ - if ((FROM) == ARG_POINTER_REGNUM) \ - { \ - int count_regs = 0; \ - int regno; \ - for (regno = 8; regno < 13; regno ++) \ - if (THUMB_REG_PUSHED_P (regno)) \ - count_regs ++; \ - if (count_regs) \ - (OFFSET) += 4 * count_regs; \ - count_regs = 0; \ - for (regno = 0; regno <= LAST_LO_REGNUM; regno ++) \ - if (THUMB_REG_PUSHED_P (regno)) \ - count_regs ++; \ - if (count_regs || ! leaf_function_p () || thumb_far_jump_used_p (0))\ - (OFFSET) += 4 * (count_regs + 1); \ - if (TARGET_BACKTRACE) \ - { \ - if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0)) \ - (OFFSET) += 20; \ - else \ - (OFFSET) += 16; \ - } \ - } \ - if ((TO) == STACK_POINTER_REGNUM) \ - { \ - (OFFSET) += current_function_outgoing_args_size; \ - (OFFSET) += thumb_get_frame_size (); \ - } \ -} - #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ if (TARGET_ARM) \ - ARM_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET); \ + (OFFSET) = arm_compute_initial_elimination_offset (FROM, TO); \ else \ - THUMB_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET) - + (OFFSET) = thumb_compute_initial_elimination_offset (FROM, TO) + /* Special case handling of the location of arguments passed on the stack. */ #define DEBUGGER_ARG_OFFSET(value, addr) value ? value : arm_debugger_arg_offset (value, addr) diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index 253fe11aeab..0a5fbd29947 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -10102,7 +10102,7 @@ (define_insn "align_8" [(unspec_volatile [(const_int 0)] VUNSPEC_ALIGN8)] - "TARGET_REALLY_IWMMXT" + "TARGET_EITHER" "* assemble_align (64); return \"\"; diff --git a/gcc/config/arm/netbsd-elf.h b/gcc/config/arm/netbsd-elf.h index c626c9e1299..76bda04f24d 100644 --- a/gcc/config/arm/netbsd-elf.h +++ b/gcc/config/arm/netbsd-elf.h @@ -38,10 +38,12 @@ (ARM_FLAG_APCS_32 \ | ARM_FLAG_SOFT_FLOAT \ | ARM_FLAG_APCS_FRAME \ - | ARM_FLAG_ATPCS \ | ARM_FLAG_MMU_TRAPS \ | TARGET_ENDIAN_DEFAULT) +#undef ARM_DEFAULT_ABI +#define ARM_DEFAULT_ABI ARM_ABI_ATPCS + #define TARGET_OS_CPP_BUILTINS() \ do \ { \ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 8c6e4400376..1e6b36bdb40 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -368,6 +368,7 @@ in the following sections. @emph{ARM Options} @gccoptlist{-mapcs-frame -mno-apcs-frame @gol +-mabi=@var{name} @gol -mapcs-26 -mapcs-32 @gol -mapcs-stack-check -mno-apcs-stack-check @gol -mapcs-float -mno-apcs-float @gol @@ -6413,6 +6414,11 @@ These @samp{-m} options are defined for Advanced RISC Machines (ARM) architectures: @table @gcctabopt +@item -mabi=@var{name} +@opindex mabi +Generate code for the specified ABI. Permissible values are: @samp{apcs-gnu}, +@samp{atpcs}, @samp{aapcs} and @samp{iwmmxt}. + @item -mapcs-frame @opindex mapcs-frame Generate a stack frame that is compliant with the ARM Procedure Call @@ -6635,14 +6641,16 @@ floating point values. @item -mstructure-size-boundary=@var{n} @opindex mstructure-size-boundary The size of all structures and unions will be rounded up to a multiple -of the number of bits set by this option. Permissible values are 8 and -32. The default value varies for different toolchains. For the COFF -targeted toolchain the default value is 8. Specifying the larger number -can produce faster, more efficient code, but can also increase the size -of the program. The two values are potentially incompatible. Code -compiled with one value cannot necessarily expect to work with code or -libraries compiled with the other value, if they exchange information -using structures or unions. +of the number of bits set by this option. Permissible values are 8, 32 +and 64. The default value varies for different toolchains. For the COFF +targeted toolchain the default value is 8. A value of 64 is only allowed +if the underlying ABI supports it. + +Specifying the larger number can produce faster, more efficient code, but +can also increase the size of the program. Different values are potentially +incompatible. Code compiled with one value cannot necessarily expect to +work with code or libraries compiled with annother value, if they exchange +information using structures or unions. @item -mabort-on-noreturn @opindex mabort-on-noreturn -- 2.11.4.GIT