Hi!
Thanks so much for your review!

Richard Biener <rguent...@suse.de> writes:

> On Fri, 11 Nov 2022, Jiufu Guo wrote:
>
>> Hi,
>> 
>> When assigning a struct parameter to another variable, or loading a
>> memory block to a struct var (especially for return value),
>> Now, "block move" would be used during expand the assignment. And
>> the "block move" may use a type/mode different from the mode which
>> is accessing the var. e.g. on ppc64le, V2DI would be used to move
>> the block of 16bytes.
>> 
>> And then, this "block move" would prevent optimization passes from
>> leaping/crossing over the assignment. PR65421 reflects this issue.
>> 
>> As the example code in PR65421.
>> 
>> typedef struct { double a[4]; } A;
>> A foo (const A *a) { return *a; }
>> 
>> On ppc64le, the below instructions are used for the "block move":
>>   7: r122:V2DI=[r121:DI]
>>   8: r124:V2DI=[r121:DI+r123:DI]
>>   9: [r112:DI]=r122:V2DI
>>   10: [r112:DI+0x10]=r124:V2DI
>> 
>> For this issue, a few comments/suggestions are mentioned via RFC:
>> https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604646.html
>> I drafted a patch which is updating the behavior of block_move for
>> struct type. This patch is simple to work with, a few ideas in the
>> comments are not put into this patch. I would submit this
>> patch first.
>> 
>> The idea is trying to use sub-modes(scalar) for the "block move".
>> And the sub-modes would align with the access patterns of the
>> struct members and usages on parameter/return value.
>> The major benefits of this change would be raising more 
>> opportunities for other optimization passes(cse/dse/xprop).
>> 
>> The suitable mode would be target specified and relates to ABI,
>> this patch introduces a target hook. And in this patch, the hook
>> is implemented on rs6000.
>> 
>> In this patch, the hook would be just using heuristic modes for all
>> struct block moving. And the hook would not check if the "block move"
>> is about parameters or return value or other uses.
>> 
>> For the rs6000 implementation of this hook, it is able to use
>> DF/DI/TD/.. modes for the struct block movement. The sub-modes
>> would be the same as the mode when the struct type is on parameter or
>> return value.
>> 
>> Bootstrapped and regtested on ppc64/ppc64le. 
>> Is this ok for trunk?
>> 
>> 
>> BR,
>> Jeff(Jiufu)
>> 
>> 
>> gcc/ChangeLog:
>> 
>>      * config/rs6000/rs6000.cc (TARGET_BLOCK_MOVE_FOR_STRUCT): Define.
>>      (submode_for_struct_block_move): New function.  Called from
>>      rs600_block_move_for_struct.
>>      (rs600_block_move_for_struct): New function.
>>      * doc/tm.texi: Regenerate.
>>      * doc/tm.texi.in (TARGET_BLOCK_MOVE_FOR_STRUCT): New.
>>      * expr.cc (store_expr): Call block_move_for_struct.
>>      * target.def (block_move_for_struct): New hook.
>>      * targhooks.cc (default_block_move_for_struct): New function.
>>      * targhooks.h (default_block_move_for_struct): New Prototype.
>> 
>> ---
>>  gcc/config/rs6000/rs6000.cc | 44 +++++++++++++++++++++++++++++++++++++
>>  gcc/doc/tm.texi             |  6 +++++
>>  gcc/doc/tm.texi.in          |  2 ++
>>  gcc/expr.cc                 | 14 +++++++++---
>>  gcc/target.def              | 10 +++++++++
>>  gcc/targhooks.cc            |  7 ++++++
>>  gcc/targhooks.h             |  1 +
>>  7 files changed, 81 insertions(+), 3 deletions(-)
>> 
>> diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
>> index a85d7630b41..e14cecba0ef 100644
>> --- a/gcc/config/rs6000/rs6000.cc
>> +++ b/gcc/config/rs6000/rs6000.cc
>> @@ -1758,6 +1758,9 @@ static const struct attribute_spec 
>> rs6000_attribute_table[] =
>>  #undef TARGET_NEED_IPA_FN_TARGET_INFO
>>  #define TARGET_NEED_IPA_FN_TARGET_INFO rs6000_need_ipa_fn_target_info
>>  
>> +#undef TARGET_BLOCK_MOVE_FOR_STRUCT
>> +#define TARGET_BLOCK_MOVE_FOR_STRUCT rs600_block_move_for_struct
>> +
>>  #undef TARGET_UPDATE_IPA_FN_TARGET_INFO
>>  #define TARGET_UPDATE_IPA_FN_TARGET_INFO rs6000_update_ipa_fn_target_info
>>  
>> @@ -23672,6 +23675,47 @@ rs6000_function_value (const_tree valtype,
>>    return gen_rtx_REG (mode, regno);
>>  }
>>  
>> +/* Subroutine of rs600_block_move_for_struct, to get the internal mode which
>> +   would be used to move the struct.  */
>> +static machine_mode
>> +submode_for_struct_block_move (tree type)
>> +{
>> +  gcc_assert (TREE_CODE (type) == RECORD_TYPE);
>> +
>> +  /* The sub mode may not be the field's type of the struct.
>> +     It would be fine to use the mode as if the type is used as a function
>> +     parameter or return value.  For example: DF for "{double a[4];}", and
>> +     DI for "{doubel a[3]; long l;}".
>> +     Here, using the mode as if it is function return type.  */
>> +  rtx val = rs6000_function_value (type, NULL, 0);
>> +  return (GET_CODE (val) == PARALLEL) ? GET_MODE (XEXP (XVECEXP (val, 0, 
>> 0), 0))
>> +                                  : word_mode;
>> +}
>> +
>> +/* Implement the TARGET_BLOCK_MOVE_FOR_STRUCT hook.  */
>> +static void
>> +rs600_block_move_for_struct (rtx x, rtx y, tree exp, HOST_WIDE_INT method)
>> +{
>> +  machine_mode mode = submode_for_struct_block_move (TREE_TYPE (exp));
>> +  int mode_size = GET_MODE_SIZE (mode);
>> +  int size = UINTVAL (expr_size (exp));
>> +  if (size < mode_size || (size % mode_size) != 0 || size > 64)
>> +    {
>> +      default_block_move_for_struct (x, y, exp, method);
>> +      return;
>> +    }
>> +
>> +  int len = size / mode_size;
>> +  for (int i = 0; i < len; i++)
>> +    {
>> +      rtx temp = gen_reg_rtx (mode);
>> +      rtx src = adjust_address (y, mode, mode_size * i);
>> +      rtx dest = adjust_address (x, mode, mode_size * i);
>> +      emit_move_insn (temp, src);
>> +      emit_move_insn (dest, temp);
>> +    }
>> +}
>> +
>>  /* Define how to find the value returned by a library function
>>     assuming the value has mode MODE.  */
>>  rtx
>> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
>> index 8572313b308..c8a1c1f30cf 100644
>> --- a/gcc/doc/tm.texi
>> +++ b/gcc/doc/tm.texi
>> @@ -1380,6 +1380,12 @@ retain the field's mode.
>>  Normally, this is not needed.
>>  @end deftypefn
>>  
>> +@deftypefn {Target Hook} void TARGET_BLOCK_MOVE_FOR_STRUCT (rtx @var{x}, 
>> rtx @var{y}, tree @var{exp}, HOST_WIDE_INT @var{method})
>> +Move from @var{y} to @var{x}, where @var{y} is as @var{exp} in structure
>> +type. @var{method} is the method if this function invokes block_move.
>> +The default definition invokes block_move.
>> +@end deftypefn
>> +
>>  @defmac ROUND_TYPE_ALIGN (@var{type}, @var{computed}, @var{specified})
>>  Define this macro as an expression for the alignment of a type (given
>>  by @var{type} as a tree node) if the alignment computed in the usual
>> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
>> index 986e8f0da09..f0f525a2008 100644
>> --- a/gcc/doc/tm.texi.in
>> +++ b/gcc/doc/tm.texi.in
>> @@ -1226,6 +1226,8 @@ to aligning a bit-field within the structure.
>>  
>>  @hook TARGET_MEMBER_TYPE_FORCES_BLK
>>  
>> +@hook TARGET_BLOCK_MOVE_FOR_STRUCT
>> +
>>  @defmac ROUND_TYPE_ALIGN (@var{type}, @var{computed}, @var{specified})
>>  Define this macro as an expression for the alignment of a type (given
>>  by @var{type} as a tree node) if the alignment computed in the usual
>> diff --git a/gcc/expr.cc b/gcc/expr.cc
>> index c6917fbf7bd..734dc07a76b 100644
>> --- a/gcc/expr.cc
>> +++ b/gcc/expr.cc
>> @@ -6504,9 +6504,17 @@ store_expr (tree exp, rtx target, int call_param_p,
>>      emit_group_store (target, temp, TREE_TYPE (exp),
>>                        int_size_in_bytes (TREE_TYPE (exp)));
>>        else if (GET_MODE (temp) == BLKmode)
>> -    emit_block_move (target, temp, expr_size (exp),
>> -                     (call_param_p
>> -                      ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
>> +    {
>> +      if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
>> +        targetm.block_move_for_struct (target, temp, exp,
>> +                                       call_param_p ? BLOCK_OP_CALL_PARM
>> +                                                    : BLOCK_OP_NORMAL);
>
> I think it's only sensible to do something about the call_param_p case,
> all other cases are GIGO.  And I don't see a good reason to add a new
> target hook since function parameter passing info is readily
> available.
This patch enhances the normal assignments on struct variables (
expand_assignment-->store_expr, where the call_param_p is 0). Because
the "struct block move/copy" is generated for this case too.

For example, the struct block_move is generated.
# .MEM_3 = VDEF <.MEM_1(D)>
D.3956 = *a_2(D);
For this stmt, "D.3956" is a struct variable, and "a_2(D)" points to a
block of the struct type.

If we only care about parameter/arg/retval(s), we would not need a
hook, as you said, since parm/retval contains the information about
the target/ABI.  And for more as you suggested in the previous emails,
we could do SRA-like analyze to check if an assignment relates to
parm/retval.

The current patch is simple relatively, it uses 'scalar-modes' to move
blocks for all struct-type assignments.  I feel this would be enough,
and I'm wondering if we need to check parm/retval.

>
> The question is whether this is best handled up in the call chain where
> the parameter setup is done or below here (or in emit_block_move).
As above discussed, this patch touches 'block move' for struct type,
including copy from 'parameter stack block' to a variable block.
Oh, assigning from 'parameter regs' to 'stack block' is not changed
here, it is done through "assign_parms-...->emit_group_xx".

If any misunderstanding, or any comments/concerns, please point them
out.  Thanks a lot!

BR,
Jeff (Jiufu)

>
> Richard.
>
>> +      else
>> +        emit_block_move (target, temp, expr_size (exp),
>> +                         (call_param_p ? BLOCK_OP_CALL_PARM
>> +                                       : BLOCK_OP_NORMAL));
>> +    }
>> +
>>        /* If we emit a nontemporal store, there is nothing else to do.  */
>>        else if (nontemporal && emit_storent_insn (target, temp))
>>      ;
>> diff --git a/gcc/target.def b/gcc/target.def
>> index 25f94c19fa7..e141f72a8a3 100644
>> --- a/gcc/target.def
>> +++ b/gcc/target.def
>> @@ -5584,6 +5584,16 @@ Normally, this is not needed.",
>>   bool, (const_tree field, machine_mode mode),
>>   default_member_type_forces_blk)
>>  
>> +
>> +/* Move block for structure type.  */
>> +DEFHOOK
>> +(block_move_for_struct,
>> + "Move from @var{y} to @var{x}, where @var{y} is as @var{exp} in 
>> structure\n\
>> +type. @var{method} is the method if this function invokes block_move.\n\
>> +The default definition invokes block_move.",
>> + void, (rtx x, rtx y, tree exp, HOST_WIDE_INT method),
>> + default_block_move_for_struct)
>> +
>>  /* See tree-ssa-math-opts.cc:divmod_candidate_p for conditions
>>     that gate the divod transform.  */
>>  DEFHOOK
>> diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
>> index 12a58456b39..2b96c348419 100644
>> --- a/gcc/targhooks.cc
>> +++ b/gcc/targhooks.cc
>> @@ -2330,6 +2330,13 @@ default_member_type_forces_blk (const_tree, 
>> machine_mode)
>>    return false;
>>  }
>>  
>> +/* Default version of block_move_for_struct.  */
>> +void
>> +default_block_move_for_struct (rtx x, rtx y, tree exp, HOST_WIDE_INT method)
>> +{
>> +  emit_block_move (x, y, expr_size (exp), (enum block_op_methods)method);
>> +}
>> +
>>  /* Default version of canonicalize_comparison.  */
>>  
>>  void
>> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
>> index a6a423c1abb..c284a35ee28 100644
>> --- a/gcc/targhooks.h
>> +++ b/gcc/targhooks.h
>> @@ -264,6 +264,7 @@ extern void default_asm_output_ident_directive (const 
>> char*);
>>  
>>  extern scalar_int_mode default_cstore_mode (enum insn_code);
>>  extern bool default_member_type_forces_blk (const_tree, machine_mode);
>> +extern void default_block_move_for_struct (rtx, rtx, tree, HOST_WIDE_INT);
>>  extern void default_atomic_assign_expand_fenv (tree *, tree *, tree *);
>>  extern tree build_va_arg_indirect_ref (tree);
>>  extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq 
>> *);
>> 

Reply via email to