Hi, This patch checks an assignment to see if the "from" is about parameter, and if the parameter may passing through registers, then use the register mode to move sub-blocks for the assignment.
Bootstraped and regtested on ppc{,le} and x86_64. Is this ok for trunk? BR, Jeff (Jiufu) gcc/ChangeLog: * expr.cc (move_sub_blocks): New function. (expand_assignment): Call move_sub_blocks for assigning from parameter. --- gcc/expr.cc | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/gcc/expr.cc b/gcc/expr.cc index d9407432ea5..201fee6fd9a 100644 --- a/gcc/expr.cc +++ b/gcc/expr.cc @@ -5559,6 +5559,57 @@ mem_ref_refers_to_non_mem_p (tree ref) return non_mem_decl_p (base); } +/* Sub routine of expand_assignment, invoked when assigning from a + parameter or assigning to a return val on struct type which may + be passed through registers. The mode of register is used to + move the content for the assignment. + + This routine generates code for expression FROM which is BLKmode, + and move the generated content to TO_RTX by su-blocks in SUB_MODE. */ + +static void +move_sub_blocks (rtx to_rtx, tree from, machine_mode sub_mode, bool nontemporal) +{ + HOST_WIDE_INT size, sub_size; + int len; + + gcc_assert (MEM_P (to_rtx)); + + size = MEM_SIZE (to_rtx).to_constant (); + sub_size = GET_MODE_SIZE (sub_mode).to_constant (); + len = size / sub_size; + + /* As there are limit registers for passing parameters or return + value according target ABI. It would be not profitable to move + through sub-modes, if the size does not follow registers. */ + int heurisitic_num = 8; + if (!size || (size % sub_size) != 0 || len < 2 || len > heurisitic_num) + { + push_temp_slots (); + rtx result = store_expr (from, to_rtx, 0, nontemporal, false); + preserve_temp_slots (result); + pop_temp_slots (); + return; + } + + push_temp_slots (); + + rtx from_rtx; + from_rtx = expand_expr (from, NULL_RTX, GET_MODE (to_rtx), EXPAND_NORMAL); + for (int i = 0; i < len; i++) + { + rtx temp = gen_reg_rtx (sub_mode); + rtx src = adjust_address (from_rtx, sub_mode, sub_size * i); + rtx dest = adjust_address (to_rtx, sub_mode, sub_size * i); + emit_move_insn (temp, src); + emit_move_insn (dest, temp); + } + + preserve_temp_slots (to_rtx); + pop_temp_slots (); + return; +} + /* Expand an assignment that stores the value of FROM into TO. If NONTEMPORAL is true, try generating a nontemporal store. */ @@ -6045,6 +6096,25 @@ expand_assignment (tree to, tree from, bool nontemporal) return; } + /* If it is assigning from a struct param which may be passed via registers, + It would be better to use the register's mode to move sub-blocks for the + assignment. */ + if (TREE_CODE (from) == PARM_DECL && mode == BLKmode + && DECL_INCOMING_RTL (from) + && (GET_CODE (DECL_INCOMING_RTL (from)) == PARALLEL + || REG_P (DECL_INCOMING_RTL (from)))) + { + rtx parm = DECL_INCOMING_RTL (from); + machine_mode sub_mode; + if (REG_P (parm)) + sub_mode = word_mode; + else + sub_mode = GET_MODE (XEXP (XVECEXP (parm, 0, 0), 0)); + + move_sub_blocks (to_rtx, from, sub_mode, nontemporal); + return; + } + /* Compute FROM and store the value in the rtx we got. */ push_temp_slots (); -- 2.17.1