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) >
