On Fri, 21 Aug 2026, Longjun Luo wrote: > Since r167795, cfgexpand has resolved a function's section before RTL > expansion. r168624 added resolve_unique_section to the direct thunk > emission path, when thunks were still emitted directly as assembly. When > r202592 added a GIMPLE thunk path, the call was retained in both paths. The > direct assembler path still needs it, but the GIMPLE path later reaches > cfgexpand, so its call is redundant. > > It can also be too early. IPA ICF can turn equivalent functions whose > addresses matter into GIMPLE wrappers during WPA, before LTO privatizes their > assembler names. The early call records a section based on the old name, > and that stale section is streamed into LTRANS. IPA strub has the same > problem in the GIMPLE wrapper code added in r14-6201. > > Remove the early calls for GIMPLE wrappers and let cfgexpand resolve their > sections from the final assembler names. Keep the call for directly emitted > assembler thunks; during LTO output it is reached only after privatization. > Explicit and deliberately inherited section names remain unchanged. This > fixes the stale name at its source instead of discarding an already-resolved > section only when LTO privatizes a symbol. > > Bootstrapped and regression-tested on x86_64-pc-linux-gnu (C and C++ only), > with no new failures. Both new tests fail without the change and pass with > it. The complete gcc.dg/lto and g++.dg/lto suites, the strub tests, and > g++.dg/other/pr47218* have no unexpected results.
This is OK for trunk if you don't hear otherwise from Honza until the end of this week. Thanks, Richard. > PR lto/126841 > > gcc/ChangeLog: > > * doc/invoke.texi (-ffunction-sections): Document implicit function > section names under LTO. > * ipa-strub.cc (pass_ipa_strub::execute): Let cfgexpand resolve the > GIMPLE wrapper section. > * symtab-thunks.cc (expand_thunk): Likewise for generic GIMPLE thunks. > Clarify the direct-assembler case. > > gcc/testsuite/ChangeLog: > > * gcc.dg/lto/pr126841-strub_0.c: New test. > * gcc.dg/lto/pr126841-strub_1.c: New test. > * gcc.dg/lto/pr126841_0.c: New test. > * gcc.dg/lto/pr126841_1.c: New test. > * gcc.dg/lto/pr126841_2.c: New test. > * gcc.dg/lto/pr126841_3.c: New test. > > Signed-off-by: Longjun Luo <[email protected]> > --- > Changes in v2: > - Fix the premature section resolution at the GIMPLE-wrapper construction > sites instead of changing LTO streaming. > - Cover the same issue in IPA strub with an independent regression test. > - Narrow the documentation to implicit function sections and retain the > exception for deliberate co-location. > > I tried the suggested lto_output_node approach. It needs exceptions for > thunks and former thunks that deliberately inherit a section. Aliases are > another issue because they have no body of their own to pass through > cfgexpand. Removing the premature calls is narrower: these GIMPLE wrappers > already pass through cfgexpand, while directly emitted assembler thunks > retain their output-time call. This forgoes the possible reduction in > streaming size, but avoids changing established section relationships. > > For functions that are not deliberately co-located, keeping one section per > function matches the documented -ffunction-sections behavior. Here the final > lto_priv names already provide the disambiguation discussed in comment #6; > resolving from the pre-privatization name instead silently puts distinct > functions in one section. > > I also compared explicit-section ICF and strub cases before and after the > change; their LTRANS assembly is unchanged. The updated manual also builds > successfully with makeinfo. > > gcc/doc/invoke.texi | 6 ++++++ > gcc/ipa-strub.cc | 4 ---- > gcc/symtab-thunks.cc | 7 ++----- > gcc/testsuite/gcc.dg/lto/pr126841-strub_0.c | 21 ++++++++++++++++++++ > gcc/testsuite/gcc.dg/lto/pr126841-strub_1.c | 7 +++++++ > gcc/testsuite/gcc.dg/lto/pr126841_0.c | 22 +++++++++++++++++++++ > gcc/testsuite/gcc.dg/lto/pr126841_1.c | 7 +++++++ > gcc/testsuite/gcc.dg/lto/pr126841_2.c | 7 +++++++ > gcc/testsuite/gcc.dg/lto/pr126841_3.c | 5 +++++ > 9 files changed, 77 insertions(+), 9 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/lto/pr126841-strub_0.c > create mode 100644 gcc/testsuite/gcc.dg/lto/pr126841-strub_1.c > create mode 100644 gcc/testsuite/gcc.dg/lto/pr126841_0.c > create mode 100644 gcc/testsuite/gcc.dg/lto/pr126841_1.c > create mode 100644 gcc/testsuite/gcc.dg/lto/pr126841_2.c > create mode 100644 gcc/testsuite/gcc.dg/lto/pr126841_3.c > > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index 01925ecf30d..ed341e88f48 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -16927,6 +16927,12 @@ file if the target supports arbitrary sections. The > name of the > function or the name of the data item determines the section's name > in the output file. > > +With link-time optimization, an implicitly generated function section name is > +derived from the final assembler name unless the compiler deliberately places > +related functions, such as a thunk and its target, in the same section. The > +final assembler name may include a compiler-generated suffix that > distinguishes > +identically named internal-linkage items from different translation units. > + > Use these options on systems where the linker can perform optimizations to > improve locality of reference in the instruction space. Most systems using > the > ELF object format have linkers with such optimizations. On AIX, the linker > diff --git a/gcc/ipa-strub.cc b/gcc/ipa-strub.cc > index b0156fe46f6..118c4a08082 100644 > --- a/gcc/ipa-strub.cc > +++ b/gcc/ipa-strub.cc > @@ -3124,10 +3124,6 @@ pass_ipa_strub::execute (function *) > > current_function_decl = thunk_fndecl; > > - /* Ensure thunks are emitted in their correct sections. */ > - resolve_unique_section (thunk_fndecl, 0, > - flag_function_sections); > - > bitmap_obstack_initialize (NULL); > > /* Build the return declaration for the function. */ > diff --git a/gcc/symtab-thunks.cc b/gcc/symtab-thunks.cc > index fb4ff87c988..e0a8d5e9357 100644 > --- a/gcc/symtab-thunks.cc > +++ b/gcc/symtab-thunks.cc > @@ -363,7 +363,8 @@ expand_thunk (cgraph_node *node, bool output_asm_thunks, > > current_function_decl = thunk_fndecl; > > - /* Ensure thunks are emitted in their correct sections. */ > + /* This thunk is emitted directly and bypasses cfgexpand, so resolve > + its section here. */ > resolve_unique_section (thunk_fndecl, 0, > flag_function_sections); > > @@ -432,10 +433,6 @@ expand_thunk (cgraph_node *node, bool output_asm_thunks, > > current_function_decl = thunk_fndecl; > > - /* Ensure thunks are emitted in their correct sections. */ > - resolve_unique_section (thunk_fndecl, 0, > - flag_function_sections); > - > bitmap_obstack_initialize (NULL); > > if (info->virtual_offset_p) > diff --git a/gcc/testsuite/gcc.dg/lto/pr126841-strub_0.c > b/gcc/testsuite/gcc.dg/lto/pr126841-strub_0.c > new file mode 100644 > index 00000000000..bd8e628ade5 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/lto/pr126841-strub_0.c > @@ -0,0 +1,21 @@ > +/* { dg-lto-do link } */ > +/* { dg-require-effective-target elf } */ > +/* { dg-require-effective-target named_sections } */ > +/* { dg-require-effective-target fpic } */ > +/* { dg-require-effective-target shared } */ > +/* { dg-require-effective-target strub } */ > +/* { dg-require-linker-plugin "" } */ > +/* { dg-lto-options { { -O2 -flto -fPIC -shared -ffunction-sections > -save-temps -fstrub=all -fno-ipa-icf } } } */ > + > +static int > +same_fn (int x) > +{ > + return x * 33 + 7; > +} > + > +int (*a_callback) (int) = same_fn; > + > +/* The strub wrappers must use sections derived from their privatized > + assembler names. */ > +/* { dg-final { scan-lto-assembler {\.section[ > \t]+\.text\.same_fn\.lto_priv\.0[, \t"]} } } */ > +/* { dg-final { scan-lto-assembler {\.section[ > \t]+\.text\.same_fn\.lto_priv\.1[, \t"]} } } */ > diff --git a/gcc/testsuite/gcc.dg/lto/pr126841-strub_1.c > b/gcc/testsuite/gcc.dg/lto/pr126841-strub_1.c > new file mode 100644 > index 00000000000..39356acba71 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/lto/pr126841-strub_1.c > @@ -0,0 +1,7 @@ > +static int > +same_fn (int x) > +{ > + return x * 33 + 7; > +} > + > +int (*b_callback) (int) = same_fn; > diff --git a/gcc/testsuite/gcc.dg/lto/pr126841_0.c > b/gcc/testsuite/gcc.dg/lto/pr126841_0.c > new file mode 100644 > index 00000000000..a0222d3336f > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/lto/pr126841_0.c > @@ -0,0 +1,22 @@ > +/* { dg-lto-do link } */ > +/* { dg-require-effective-target elf } */ > +/* { dg-require-effective-target named_sections } */ > +/* { dg-require-effective-target fpic } */ > +/* { dg-require-effective-target shared } */ > +/* { dg-require-linker-plugin "" } */ > +/* { dg-lto-options { { -O2 -flto -fPIC -shared -ffunction-sections > -save-temps } } } */ > + > +static int > +same_fn (int x) > +{ > + return x * 33 + 7; > +} > + > +int (*a_callback) (int) = same_fn; > + > +/* The exported function keeps the unsuffixed section, and each privatized > + function must have its own section. */ > +/* { dg-final { scan-lto-assembler {\.section[ \t]+\.text\.same_fn[, \t"]} } > } */ > +/* { dg-final { scan-lto-assembler {\.section[ > \t]+\.text\.same_fn\.lto_priv\.0[, \t"]} } } */ > +/* { dg-final { scan-lto-assembler {\.section[ > \t]+\.text\.same_fn\.lto_priv\.1[, \t"]} } } */ > +/* { dg-final { scan-lto-assembler {\.section[ > \t]+\.text\.same_fn\.lto_priv\.2[, \t"]} } } */ > diff --git a/gcc/testsuite/gcc.dg/lto/pr126841_1.c > b/gcc/testsuite/gcc.dg/lto/pr126841_1.c > new file mode 100644 > index 00000000000..39356acba71 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/lto/pr126841_1.c > @@ -0,0 +1,7 @@ > +static int > +same_fn (int x) > +{ > + return x * 33 + 7; > +} > + > +int (*b_callback) (int) = same_fn; > diff --git a/gcc/testsuite/gcc.dg/lto/pr126841_2.c > b/gcc/testsuite/gcc.dg/lto/pr126841_2.c > new file mode 100644 > index 00000000000..369eb1836a6 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/lto/pr126841_2.c > @@ -0,0 +1,7 @@ > +static int > +same_fn (int x) > +{ > + return x * 33 + 7; > +} > + > +int (*c_callback) (int) = same_fn; > diff --git a/gcc/testsuite/gcc.dg/lto/pr126841_3.c > b/gcc/testsuite/gcc.dg/lto/pr126841_3.c > new file mode 100644 > index 00000000000..567eb29cda0 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/lto/pr126841_3.c > @@ -0,0 +1,5 @@ > +int > +same_fn (int x) > +{ > + return x - 1; > +} > -- Richard Biener <[email protected]> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)
