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)

Reply via email to