https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128616
Bug ID: 128616
Summary: [LTO] COMDAT function's early DIE and prevailing body
come from different TUs
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: debug
Assignee: unassigned at gcc dot gnu.org
Reporter: luolongjuna at gmail dot com
Target Milestone: ---
In the testcase below, the out-of-line transform refers to the early function
DIE in a.C's CU, although its body is selected from b.C. Its parameter and
local variable refer to b.C's early DIEs, and GDB displays both twice.
Reproduced on x86_64 GNU/Linux with GCC 17.0.0 (g:bd921def14a4), GDB 17.2 and
binutils 2.46.1, and with Ubuntu GCC 13.3.0.
GCC 17 build configuration:
--enable-languages=c,c++ --disable-bootstrap --disable-multilib
--disable-nls --disable-werror --enable-checking=yes
--without-isl --with-system-zlib
With LTO and link order a.o b.o, 'info args' and 'info locals' at the asm in
the out-of-line transform show:
n = <optimized out>
n = <optimized out>
result = 6
result = <optimized out>
Without LTO:
n = 3
result = 6
The concrete parameter DIE has a valid location; its corresponding GDB symbol
evaluates to n=3. In GDB 17.2, 'info args' looks each argument up by name and
finds the inherited parameter, which has no location.
The final LTO DWARF has these DW_AT_abstract_origin references:
concrete transform -> transform in a.C's CU
parameter n -> n in b.C's CU
variable result -> result in b.C's CU
The parameter and variable origins are children of b.C's function DIE rather
than of a.C's function DIE.
Both inputs inline transform during LGEN; only b.o emits an out-of-line copy
because b.C takes its address, and repro.res records b.o as the prevailing
definition of _Z9transformi. Because both inputs inlined a call to transform,
DECL_POSSIBLY_INLINED matches on both declarations.
Reading a.o first makes WPA's SCC merging retain a.C's declaration and early
DIE reference, while the body selected from b.o keeps b.C's parameter and
local-variable origins. Linking b.o before a.o, or replacing transform(1) in
main with the constant 4 so DECL_POSSIBLY_INLINED differs and the declarations
do not merge, makes the out-of-line instance and its children refer to b.C's
DIEs. GDB then shows n=3 and result=6 once.
In the original testcase, in either link order, one compile-time inline
instance also has a mismatch (transform(1) in main with a.o first, or the call
in local_value with b.o first). Those instances already exist before WPA
merging and belong to PR126348. The mismatch here is on the selected COMDAT
function itself, where DECL_ORIGIN(fn) == fn.
The same mismatch propagates to copies inlined from that selected body during
LTRANS. In an -O2 -g -flto build of 12 GCC source files (base g:543617c93595)
with the PR126348 v4 patch applied, 11 link-time inline instances of this class
remained (10 of set_quality and one of gimple_call_set_lhs); see
https://gcc.gnu.org/pipermail/gcc-patches/2026-October/734019.html. The
testcase below demonstrates the out-of-line form.
Reproducer
--- h.h ---
inline int transform (int n)
{
int result = n + 3;
asm volatile ("nop" : "+r" (result) : : "memory");
return result;
}
__attribute__((noipa)) int local_value (int);
--- a.C ---
#include "h.h"
int local_value (int n) { return transform (n); }
--- b.C ---
#include "h.h"
int (*volatile pointer) (int) = transform;
int main ()
{
int a = transform (1); // Keep: sets DECL_POSSIBLY_INLINED in b.C.
int b = local_value (2);
int c = pointer (3);
return a + b + c != 15;
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
mode=${1:--flto}
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -flto-partition=one \
-save-temps -no-pie a.o b.o -o repro
"${GDB:-gdb}" -q -nx -batch -iex 'set debuginfod enabled off' \
-ex 'break *_Z9transformi' -ex run \
-ex 'advance h.h:4' \
-ex 'info args' -ex 'info locals' repro
Run with:
CXX=g++ GDB=gdb sh run.sh -flto
CXX=g++ GDB=gdb sh run.sh -fno-lto