Ping.
https://gcc.gnu.org/pipermail/gcc-patches/2026-October/735400.html
Thanks,
Kyrill

> On 20 Oct 2026, at 10:59, Kyrylo Tkachov <[email protected]> wrote:
> 
> From: Kyrylo Tkachov <[email protected]>
> 
> The early inliner charges an aggregate copy into the return value even
> when its source is a local variable that SRA can remove after inlining.
> For a value-type wrapper such as
> 
>  T operator* (const T &a, const T &b)
>  {
>    T t = a;
>    return t *= b;
>  }
> 
> the copy appears as
> 
>  <retval> = t;
> 
> Charging this copy can put the wrapper above early-inlining-insns and
> leave its operands and result in memory at each call.
> 
> Credit aggregate return copies from automatic local variables and
> parameters whose address is not taken.  Recognize both RESULT_DECL and
> an invisible-reference result.  Preserve the existing half credit for
> unmodified parameters.  Keep the cost of copies from globals, statics,
> pointers and address-taken locals.  Exclude volatile accesses before
> applying any elimination credit.
> 
> Add tests for early inlining, source restrictions, invisible-reference
> returns and volatile scalar and aggregate accesses.
> 
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
> 
> Cc: Martin Jambor <[email protected]>
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> 
> gcc/ChangeLog:
> 
> * ipa-fnsummary.cc (eliminated_by_inlining_prob): Do not credit
> volatile accesses.  Credit aggregate copies into the return value
> from automatic local variables and parameters whose address is not
> taken as eliminated by inlining.
> 
> gcc/testsuite/ChangeLog:
> 
> * g++.dg/ipa/inline-aggregate-retval.C: New test.
> * g++.dg/ipa/inline-aggregate-retval-2.C: Likewise.
> * gcc.dg/ipa/inline-aggregate-retval.c: Likewise.
> * gcc.dg/ipa/inline-aggregate-retval-2.c: Likewise.
> * gcc.dg/ipa/inline-aggregate-retval-3.c: Likewise.
> ---
> gcc/ipa-fnsummary.cc                          | 17 ++++-
> .../g++.dg/ipa/inline-aggregate-retval-2.C    | 21 ++++++
> .../g++.dg/ipa/inline-aggregate-retval.C      | 18 +++++
> .../gcc.dg/ipa/inline-aggregate-retval-2.c    | 75 +++++++++++++++++++
> .../gcc.dg/ipa/inline-aggregate-retval-3.c    | 26 +++++++
> .../gcc.dg/ipa/inline-aggregate-retval.c      | 27 +++++++
> 6 files changed, 183 insertions(+), 1 deletion(-)
> create mode 100644 gcc/testsuite/g++.dg/ipa/inline-aggregate-retval-2.C
> create mode 100644 gcc/testsuite/g++.dg/ipa/inline-aggregate-retval.C
> create mode 100644 gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-2.c
> create mode 100644 gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-3.c
> create mode 100644 gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval.c
> 
> diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc
> index 09f50846903..549504375d6 100644
> --- a/gcc/ipa-fnsummary.cc
> +++ b/gcc/ipa-fnsummary.cc
> @@ -1378,7 +1378,7 @@ eliminated_by_inlining_prob (ipa_func_body_info *fbi, 
> gimple *stmt)
>   enum gimple_code code = gimple_code (stmt);
>   enum tree_code rhs_code;
> 
> -  if (!optimize)
> +  if (!optimize || gimple_has_volatile_ops (stmt))
>     return 0;
> 
>   switch (code)
> @@ -1491,6 +1491,21 @@ eliminated_by_inlining_prob (ipa_func_body_info *fbi, 
> gimple *stmt)
>    rhs_free = true;
>  if (lhs_free && rhs_free)
>    return 1;
> +  /* Credit an aggregate copy into the return value from a local
> +     variable or parameter whose address is not taken as eliminated
> +     by inlining.  Field accesses in the caller can allow SRA to
> +     remove the copy.  */
> +  if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))
> +      && (TREE_CODE (inner_lhs) == RESULT_DECL
> +  || (TREE_CODE (inner_lhs) == MEM_REF
> +      && TREE_CODE (TREE_OPERAND (inner_lhs, 0)) == SSA_NAME
> +      && SSA_NAME_VAR (TREE_OPERAND (inner_lhs, 0))
> +      && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (inner_lhs, 0)))
> + == RESULT_DECL))
> +      && (VAR_P (inner_rhs) || TREE_CODE (inner_rhs) == PARM_DECL)
> +      && auto_var_in_fn_p (inner_rhs, current_function_decl)
> +      && !TREE_ADDRESSABLE (inner_rhs))
> +    return 2;
> }
>       return 0;
>     default:
> diff --git a/gcc/testsuite/g++.dg/ipa/inline-aggregate-retval-2.C 
> b/gcc/testsuite/g++.dg/ipa/inline-aggregate-retval-2.C
> new file mode 100644
> index 00000000000..1e991b1bd5b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ipa/inline-aggregate-retval-2.C
> @@ -0,0 +1,21 @@
> +// A user-provided destructor makes the result an invisible reference.
> +// The comma expression requires a copy from the local into that result.
> +// Early inlining removes the empty destructor and its use of the local's
> +// address.  Keep the copy visible by limiting total scalarization.
> +// { dg-do compile }
> +// { dg-options "-O2 --param sra-max-scalarization-size-Ospeed=1 
> -fdump-ipa-fnsummary-details -fno-partial-inlining -fno-ipa-cp" }
> +
> +struct V
> +{
> +  long d[8];
> +  ~V () {}
> +};
> +
> +V
> +from_local (const V &a)
> +{
> +  V t = a;
> +  return (t.d[0] += 1, t);
> +}
> +
> +// { dg-final { scan-ipa-dump {\*_[0-9]+\(D\) = t;\n[^\n]*\n\t\tWill be 
> eliminated by inlining} "fnsummary" } }
> diff --git a/gcc/testsuite/g++.dg/ipa/inline-aggregate-retval.C 
> b/gcc/testsuite/g++.dg/ipa/inline-aggregate-retval.C
> new file mode 100644
> index 00000000000..eb9e3f72ad4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ipa/inline-aggregate-retval.C
> @@ -0,0 +1,18 @@
> +// A by-value 'operator*' copies its local result into the return value.
> +// The copy is eliminated by inlining, so it does not count towards the early
> +// inliner growth and the operator fits in --param early-inlining-insns.
> +// { dg-do compile }
> +// { dg-options "-O3 --param early-inlining-insns=14 -fno-inline-functions 
> -fno-partial-inlining -fdump-tree-einline-optimized" }
> +
> +struct V {
> +  long d[8];
> +  V &operator*= (const V &o) { for (int i = 0; i < 8; i++) d[i] *= o.d[i]; 
> return *this; }
> +};
> +
> +static V operator* (const V &a, const V &b) { V t = a; return t *= b; }
> +
> +V ga, gb, gc, r;
> +
> +void use () { r = ga * gb * gc; }
> +
> +// { dg-final { scan-tree-dump-times {Inlining V operator\*\(const V&, const 
> V&\)/[0-9]+ into void use\(\)/[0-9]+} 2 "einline" } }
> diff --git a/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-2.c 
> b/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-2.c
> new file mode 100644
> index 00000000000..9d2b4a2b2be
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-2.c
> @@ -0,0 +1,75 @@
> +/* An aggregate copy into the return value is eliminated by inlining only
> +   when its source is a local variable or a parameter whose address is not
> +   taken.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-ipa-fnsummary-details -fno-early-inlining 
> -fno-partial-inlining -fno-ipa-cp" } */
> +
> +struct V { long d[8]; };
> +struct H { struct V *p; };
> +struct V glob;
> +void sink (struct V *);
> +
> +struct V
> +from_local (const struct V *a, const struct V *b)
> +{
> +  struct V loc = *a;
> +  for (int i = 0; i < 8; i++)
> +    loc.d[i] *= b->d[i];
> +  return loc;
> +}
> +
> +struct V
> +from_parm (struct V parm, const struct V *b)
> +{
> +  for (int i = 0; i < 8; i++)
> +    parm.d[i] *= b->d[i];
> +  return parm;
> +}
> +
> +struct V
> +from_unmodified_parm (struct V uparm)
> +{
> +  return uparm;
> +}
> +
> +struct V
> +from_escaped_local (const struct V *a)
> +{
> +  struct V esc = *a;
> +  sink (&esc);
> +  return esc;
> +}
> +
> +struct V
> +from_global (const struct V *b)
> +{
> +  for (int i = 0; i < 8; i++)
> +    glob.d[i] *= b->d[i];
> +  return glob;
> +}
> +
> +struct V
> +from_static (const struct V *b)
> +{
> +  static struct V stat;
> +  for (int i = 0; i < 8; i++)
> +    stat.d[i] += b->d[i];
> +  return stat;
> +}
> +
> +struct V
> +from_pointer (const struct H *h, const struct V *b)
> +{
> +  struct V *p = h->p;
> +  for (int i = 0; i < 8; i++)
> +    p->d[i] *= b->d[i];
> +  return *p;
> +}
> +
> +/* { dg-final { scan-ipa-dump {<retval> = loc;\n[^\n]*\n\t\tWill be 
> eliminated by inlining} "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump {<retval> = parm;\n[^\n]*\n\t\tWill be 
> eliminated by inlining} "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump {<retval> = uparm;\n[^\n]*\n\t\t50% will be 
> eliminated by inlining} "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump-not {<retval> = esc;\n[^\n]*\n\t\tWill be 
> eliminated by inlining} "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump-not {<retval> = glob;\n[^\n]*\n\t\tWill be 
> eliminated by inlining} "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump-not {<retval> = stat;\n[^\n]*\n\t\tWill be 
> eliminated by inlining} "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump-not {<retval> = \*p_[0-9]+;\n[^\n]*\n\t\tWill 
> be eliminated by inlining} "fnsummary" } } */
> diff --git a/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-3.c 
> b/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-3.c
> new file mode 100644
> index 00000000000..dc08d688e3d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval-3.c
> @@ -0,0 +1,26 @@
> +/* Volatile accesses remain after inlining.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-ipa-fnsummary-details -fno-early-inlining 
> -fno-partial-inlining -fno-ipa-cp" } */
> +
> +int
> +from_pointer (volatile int *p)
> +{
> +  return *p;
> +}
> +
> +int
> +from_parameter (volatile int p)
> +{
> +  return p;
> +}
> +
> +struct V { long d[8]; };
> +
> +struct V
> +from_aggregate_parameter (volatile struct V p)
> +{
> +  return p;
> +}
> +
> +/* { dg-final { scan-ipa-dump-times { =\{v\} 
> [^\n]*;\n[^\n]*\n\t\tAccounting} 3 "fnsummary" } } */
> +/* { dg-final { scan-ipa-dump-not { =\{v\} 
> [^\n]*;\n[^\n]*\n\t\t[^\n]*[Ww]ill be eliminated by inlining} "fnsummary" } } 
> */
> diff --git a/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval.c 
> b/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval.c
> new file mode 100644
> index 00000000000..8220a69241c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/inline-aggregate-retval.c
> @@ -0,0 +1,27 @@
> +/* The copy of the local result of 'mul' into its return value is eliminated
> +   by inlining, so it does not count towards the early inliner growth and
> +   'mul' fits in --param early-inlining-insns.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O3 --param early-inlining-insns=14 -fno-inline-functions 
> -fno-partial-inlining -fdump-tree-einline-optimized" } */
> +
> +struct V { long d[8]; };
> +
> +static struct V
> +mul (const struct V *a, const struct V *b)
> +{
> +  struct V t = *a;
> +  for (int i = 0; i < 8; i++)
> +    t.d[i] *= b->d[i];
> +  return t;
> +}
> +
> +struct V ga, gb, gc, r;
> +
> +void
> +use (void)
> +{
> +  struct V x = mul (&ga, &gb);
> +  r = mul (&x, &gc);
> +}
> +
> +/* { dg-final { scan-tree-dump-times {Inlining mul/[0-9]+ into use/[0-9]+} 2 
> "einline" } } */
> -- 
> 2.54.0 (Apple Git-157)
> 

Reply via email to