https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126841

            Bug ID: 126841
           Summary: [LTO][IPA-ICF] -ffunction-sections emits distinct
                    lto_priv functions into the same section
           Product: gcc
           Version: 16.1.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: lto
          Assignee: unassigned at gcc dot gnu.org
          Reporter: luolongjuna at gmail dot com
  Target Milestone: ---

With full LTO and -ffunction-sections, GCC can emit two distinct local
functions, with separate bodies and different section-relative addresses,
into the same native .text section.

Environment:

  gcc version 16.1.1 20260515 (Red Hat 16.1.1-2) (GCC)
  Target: x86_64-redhat-linux

Reduced reproducer
==================

The reproducer contains three separate translation units. Each translation
unit defines an identically named file-local function and stores its address
in a different global callback variable.

a.c:

  static int same_fn(int x)
  {
          return x * 33 + 7;
  }

  int (*a_callback)(int) = same_fn;

b.c:

  static int same_fn(int x)
  {
          return x * 33 + 7;
  }

  int (*b_callback)(int) = same_fn;

c.c:

  static int same_fn(int x)
  {
          return x * 33 + 7;
  }

  int (*c_callback)(int) = same_fn;

Compile and link:

  gcc -O2 -flto -ffunction-sections -fPIC -c a.c -o a.o
  gcc -O2 -flto -ffunction-sections -fPIC -c b.c -o b.o
  gcc -O2 -flto -ffunction-sections -fPIC -c c.c -o c.o

  gcc -shared -O2 -flto \
      -ffunction-sections -save-temps=obj \
      a.o b.o c.o -o repro.so

Inspect the generated LTRANS assembly:

  grep -nE '\.section.*same_fn|same_fn\.lto_priv' \
      repro.so.ltrans0.ltrans.s

Observed result
===============

The relevant LTRANS output is:

  .section .text.same_fn.lto_priv.0,"ax",@progbits
  same_fn.lto_priv.0:

  .section .text.same_fn,"ax",@progbits
  same_fn.lto_priv.1:
  ...
  same_fn.lto_priv.2:

The assembly can be converted back to an ELF relocatable object for an
unambiguous section-index check:

  gcc -c repro.so.ltrans0.ltrans.s -o ltrans-native.o
  readelf -Ws ltrans-native.o | grep same_fn
  readelf -SW ltrans-native.o | grep text.same_fn

Relevant output:

     3: 0000000000000000    10 FUNC LOCAL DEFAULT 4 same_fn.lto_priv.0
     5: 0000000000000000    10 FUNC LOCAL DEFAULT 5 same_fn.lto_priv.1
     6: 0000000000000010    10 FUNC LOCAL DEFAULT 5 same_fn.lto_priv.2

  [4] .text.same_fn.lto_priv.0 PROGBITS ... size 0x0a
  [5] .text.same_fn            PROGBITS ... size 0x1a

Thus same_fn.lto_priv.1 and same_fn.lto_priv.2 are not two aliases for one
folded body. They are separate emitted function bodies at offsets 0x0 and
0x10 in the same .text.same_fn section.

Expected result
===============

Because -ffunction-sections is enabled and the functions remain distinct
emitted bodies, each body should be placed in a distinct native section, for
example:

  .text.same_fn.lto_priv.0
  .text.same_fn.lto_priv.1
  .text.same_fn.lto_priv.2

Control result
==============

Adding -fno-ipa-icf to each of the three compile commands and to the link
command produces the expected three separate sections:

  .text.same_fn.lto_priv.0
  .text.same_fn.lto_priv.1
  .text.same_fn.lto_priv.2

The positive reproducer was rebuilt 30 times in separate clean directories
and reproduced 30/30 times. All 30 generated LTRANS assembly files were
byte-identical. The -fno-ipa-icf control was also rebuilt 30 times and
produced separate sections 30/30 times, again with byte-identical LTRANS
assembly.

Real-world instance
===================

This was reduced from a Fedora Valkey 9.0.5 full-LTO build. The real source
contains three separate files--eval.c, functions.c, and scripting_engine.c--
that each define a file-local dictStrCaseHash function and store its address
in a different global dictType object.

In the emitted native object, dictStrCaseHash.lto_priv.1 and
dictStrCaseHash.lto_priv.2 are separate 41-byte functions at offsets 0x0 and
0x30, but both are placed in .text.dictStrCaseHash.

Possible cause
==============

An implicit function-section name may be retained across LTO symbol
privatization or IPA-ICF processing and not recomputed for every final
lto_priv function. This is only a hypothesis; I have not confirmed the exact
root cause.

Reply via email to