On Mon, 17 Aug 2026, Longjun Luo wrote:
> IPA ICF can create address-preserving wrappers during WPA. Expanding
> such a wrapper resolves its implicit function-section name before LTO
> privatizes the symbol. The symbol is subsequently renamed with an
> lto_priv suffix, but the section keeps the pre-privatization name.
>
> Clear implicit section names on the renamed symbol and its aliases so
> that LTRANS regenerates them from the final assembler name. Leave
> explicit user-specified section names unchanged.
>
> Tested on x86_64-pc-linux-gnu. The new test fails without the change and
> passes with it. The complete gcc.dg/lto and g++.dg/lto test suites have no
> unexpected results.
>
> PR lto/126841
>
> gcc/ChangeLog:
>
> * doc/invoke.texi (-ffunction-sections): Document implicitly generated
> section names under LTO.
>
> gcc/lto/ChangeLog:
>
> * lto-partition.cc (clear_implicit_section): New.
> (privatize_symbol_name_1): Clear implicit section names after
> renaming.
>
> gcc/testsuite/ChangeLog:
>
> * 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]>
> ---
> The change is intentionally kept in privatize_symbol_name_1 rather than
> symbol_table::change_decl_assembler_name. LTO privatization is the path
> covered by the reproducer and regression test; other assembler-name changes
> have not been audited.
>
> gcc/doc/invoke.texi | 5 +++++
> gcc/lto/lto-partition.cc | 12 ++++++++++++
> 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 +++++
> 6 files changed, 58 insertions(+)
> 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 2e53a8c8a7e..e66cb895ae5 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -16927,6 +16927,11 @@ 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 section name is derived
> +from the final assembler name, which may include a compiler-generated suffix
> +to distinguish identically named internal-linkage items from different
> +translation units.
> +
So this doesn't match the implementation which re-computes the section
only when a symbol is privatized? To match the above it would work
if we'd simply not stream the section name / implicit_section flag
in lto_output_node (for implicit_section section names, that is)?
Saving on streaming size sounds like a good thing as well.
The question is of course what meets user expectation best here.
Richard.
> 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/lto/lto-partition.cc b/gcc/lto/lto-partition.cc
> index 5033ae3f97d..facb5d25907 100644
> --- a/gcc/lto/lto-partition.cc
> +++ b/gcc/lto/lto-partition.cc
> @@ -1797,6 +1797,17 @@ validize_symbol_for_target (symtab_node *node)
> /* Maps symbol names to unique lto clone counters. */
> static hash_map<const char *, unsigned> *lto_clone_numbers;
>
> +/* Clear compiler-generated section names after changing an assembler name,
> + so they are regenerated from the final name. */
> +
> +static bool
> +clear_implicit_section (symtab_node *node, void *)
> +{
> + if (node->implicit_section)
> + node->set_section_for_node (NULL);
> + return false;
> +}
> +
> /* Helper for privatize_symbol_name. Mangle NODE symbol name
> represented by DECL. */
>
> @@ -1813,6 +1824,7 @@ privatize_symbol_name_1 (symtab_node *node, tree decl)
> symtab->change_decl_assembler_name (decl,
> clone_function_name (
> name, "lto_priv", clone_number));
> + node->call_for_symbol_and_aliases (clear_implicit_section, NULL, true);
> clone_number++;
>
> if (node->lto_file_data)
> 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)