We currently handle arguments that are split between the stack and registers by storing the registers to the stack and then treating the argument as if it was entirely passed on the stack. Allow targets to override this behavior and instead treat the argument as if it was passed entirely in registers.
gcc/ChangeLog: * doc/tm.texi: Add TARGET_ARG_EXTENDED_ON_STACK. * doc/tm.texi.in: Likewise. * function.cc (struct assign_parm_data_one): Add extended. (assign_parm_find_entry_rtl): Set and use extended. (assign_parm_is_stack_parm): Use extended. (assign_parm_adjust_entry_rtl): Likewise. (assign_parm_setup_reg): Likewise. (assign_parm_setup_stack): Likewise. * target.def: Add TARGET_ARG_EXTENDED_ON_STACK. * targhooks.cc (hook_int_CUMULATIVE_ARGS_arg_info_1): New hook. * targhooks.h: Likewise. --- gcc/doc/tm.texi | 9 +++++++++ gcc/doc/tm.texi.in | 2 ++ gcc/function.cc | 39 ++++++++++++++++++++++++++++++++------- gcc/target.def | 11 +++++++++++ gcc/targhooks.cc | 7 +++++++ gcc/targhooks.h | 2 ++ 6 files changed, 63 insertions(+), 7 deletions(-) diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 5e305643b3a..356522d53c3 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -4320,6 +4320,15 @@ register to be used by the caller for this argument; likewise @code{TARGET_FUNCTION_INCOMING_ARG}, for the called function. @end deftypefn +@deftypefn {Target Hook} int TARGET_ARG_EXTENDED_ON_STACK (cumulative_args_t @var{cum}, const function_arg_info @var{&arg}) +When arguments are split between the registers and the stack, it +is usually profitable to construct the argument in place on the stack +and then load it into a register. These sequences may cause issues for +some systems, for example by generating misaligned accesses. This target +hook determines if arguments should be construct in place on the stack, or +the whole value should be reconstructed. +@end deftypefn + @deftypefn {Target Hook} bool TARGET_PASS_BY_REFERENCE (cumulative_args_t @var{cum}, const function_arg_info @var{&arg}) This target hook should return @code{true} if argument @var{arg} at the position indicated by @var{cum} should be passed by reference. This diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index eccc4d88493..925d5efd835 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -3338,6 +3338,8 @@ the stack. @hook TARGET_ARG_PARTIAL_BYTES +@hook TARGET_ARG_EXTENDED_ON_STACK + @hook TARGET_PASS_BY_REFERENCE @hook TARGET_CALLEE_COPIES diff --git a/gcc/function.cc b/gcc/function.cc index 48167b0c207..7aa389f77bf 100644 --- a/gcc/function.cc +++ b/gcc/function.cc @@ -2297,6 +2297,7 @@ struct assign_parm_data_one machine_mode passed_mode; struct locate_and_pad_arg_data locate; int partial; + int extended; }; /* A subroutine of assign_parms. Initialize ALL. */ @@ -2566,18 +2567,23 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all, if (entry_parm) { - int partial; + int partial, extended; partial = targetm.calls.arg_partial_bytes (all->args_so_far, data->arg); data->partial = partial; + extended = targetm.calls.arg_extended_on_stack (all->args_so_far, data->arg); + data->extended = extended; + /* The caller might already have allocated stack space for the register parameters. */ - if (partial != 0 && all->reg_parm_stack_space == 0) + if (partial != 0 && all->reg_parm_stack_space == 0 && extended) { /* Part of this argument is passed in registers and part is passed on the stack. Ask the prologue code to extend - the stack part so that we can recreate the full value. + the stack part so that we can recreate the full value, unless for + some reason the backend doesn't like that in which case we'll + synthesize the argument via subword moves later. PRETEND_BYTES is the size of the registers we need to store. CURRENT_FUNCTION_PRETEND_ARGS_SIZE is the amount of extra @@ -2631,8 +2637,9 @@ assign_parm_is_stack_parm (struct assign_parm_data_all *all, /* Trivially true if we've no incoming register. */ if (data->entry_parm == NULL) ; - /* Also true if we're partially in registers and partially not, - since we've arranged to drop the entire argument on the stack. */ + /* Also true if we're partially in registers and partially not, as we'll + either extend the argument in-place on the stack or fix it up later + depending on what the target wants. */ else if (data->partial != 0) ; /* Also true if the target says that it's passed in both registers @@ -2749,7 +2756,7 @@ assign_parm_adjust_entry_rtl (struct assign_parm_data_one *data) In the special case of a DImode or DFmode that is split, we could put it together in a pseudoreg directly, but for now that's not worth bothering with. */ - if (data->partial != 0) + if (data->partial != 0 && data->extended) { /* Handle calls that pass values in multiple non-contiguous locations. The Irix 6 ABI has examples of this. */ @@ -3337,6 +3344,15 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm, unsignedp, parmreg, promoted_nominal_mode, VOIDmode, false, NULL); } + else if (data->partial && !data->extended) + { + emit_move_insn (operand_subword (parmreg, 0, 1, promoted_nominal_mode), + operand_subword (data->entry_parm, 0, 1, + promoted_nominal_mode)); + emit_move_insn (operand_subword (parmreg, 1, 1, promoted_nominal_mode), + operand_subword (data->stack_parm, 0, 1, + promoted_nominal_mode)); + } else emit_move_insn (parmreg, validated_mem); @@ -3501,8 +3517,9 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm, if (data->entry_parm != data->stack_parm) { rtx src, dest; + rtx orig_stack_parm = data->stack_parm; - if (data->stack_parm == 0) + if (data->stack_parm == 0 || (data->partial && !data->extended)) { int align = STACK_SLOT_ALIGNMENT (data->arg.type, GET_MODE (data->entry_parm), @@ -3528,6 +3545,14 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm, if (TYPE_EMPTY_P (data->arg.type)) /* Empty types don't really need to be copied. */; + else if (data->partial && !data->extended) + { + rtx src2 = validize_mem (copy_rtx (orig_stack_parm)); + emit_move_insn (operand_subword (dest, 0, 1, GET_MODE (dest)), + operand_subword (src, 0, 1, GET_MODE (src))); + emit_move_insn (operand_subword (dest, 1, 1, GET_MODE (dest)), + operand_subword (src2, 0, 1, GET_MODE (src2))); + } else if (MEM_P (src)) { /* Use a block move to handle potentially misaligned entry_parm. */ diff --git a/gcc/target.def b/gcc/target.def index 38903eb567a..0864ac7246d 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -5165,6 +5165,17 @@ register to be used by the caller for this argument; likewise\n\ int, (cumulative_args_t cum, const function_arg_info &arg), hook_int_CUMULATIVE_ARGS_arg_info_0) +DEFHOOK +(arg_extended_on_stack, + "When arguments are split between the registers and the stack, it\n\ +is usually profitable to construct the argument in place on the stack\n\ +and then load it into a register. These sequences may cause issues for\n\ +some systems, for example by generating misaligned accesses. This target\n\ +hook determines if arguments should be construct in place on the stack, or\n\ +the whole value should be reconstructed.", + int, (cumulative_args_t cum, const function_arg_info &arg), + hook_int_CUMULATIVE_ARGS_arg_info_1) + /* Update the state in CA to advance past an argument in the argument list. The values MODE, TYPE, and NAMED describe that argument. */ diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc index c79458e374e..faf1906e084 100644 --- a/gcc/targhooks.cc +++ b/gcc/targhooks.cc @@ -801,6 +801,13 @@ hook_int_CUMULATIVE_ARGS_arg_info_0 (cumulative_args_t, return 0; } +int +hook_int_CUMULATIVE_ARGS_arg_info_1 (cumulative_args_t, + const function_arg_info &) +{ + return 1; +} + void hook_void_CUMULATIVE_ARGS (cumulative_args_t) { diff --git a/gcc/targhooks.h b/gcc/targhooks.h index f16b58798c2..ce0871cb7c2 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -144,6 +144,8 @@ extern bool hook_bool_CUMULATIVE_ARGS_arg_info_true (cumulative_args_t, const function_arg_info &); extern int hook_int_CUMULATIVE_ARGS_arg_info_0 (cumulative_args_t, const function_arg_info &); +extern int hook_int_CUMULATIVE_ARGS_arg_info_1 + (cumulative_args_t, const function_arg_info &); extern void hook_void_CUMULATIVE_ARGS (cumulative_args_t); extern void hook_void_CUMULATIVE_ARGS_tree (cumulative_args_t, tree); extern void hook_void_CUMULATIVE_ARGS_rtx_tree (cumulative_args_t, rtx, tree); -- 2.39.5 (Apple Git-154)