https://gcc.gnu.org/g:543617c93595e0cb45ff7ee52c635ca30a2fe361
commit r17-4380-g543617c93595e0cb45ff7ee52c635ca30a2fe361 Author: Longjun Luo <[email protected]> Date: Thu Sep 17 03:02:05 2026 +0000 middle-end: Defer section resolution for GIMPLE wrappers [PR126841] 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. 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]> Diff: --- 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(-) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 3311ff946198..498a3608151e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -16946,6 +16946,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 b0156fe46f66..118c4a080824 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 fb4ff87c9883..e0a8d5e93573 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 000000000000..bd8e628ade50 --- /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 000000000000..39356acba71a --- /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 000000000000..a0222d3336f4 --- /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 000000000000..39356acba71a --- /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 000000000000..369eb1836a6c --- /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 000000000000..567eb29cda0e --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/pr126841_3.c @@ -0,0 +1,5 @@ +int +same_fn (int x) +{ + return x - 1; +}
