https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126348
Longjun Luo <luolongjuna at gmail dot com> changed:
What |Removed |Added
----------------------------------------------------------------------------
Summary|[LTO] Split clone and |[LTO] Clone and inline
|direct children refer to |instances refer to a
|abstract DIEs from |different input's abstract
|different CUs |origin than their children
Blocks| |47819
See Also| |https://gcc.gnu.org/bugzill
| |a/show_bug.cgi?id=128623
--- Comment #5 from Longjun Luo <luolongjuna at gmail dot com> ---
I have reduced three additional cases of the instance/child origin mismatch
reported here: nested compile-time inlining, prevailing-declaration
replacement, and mixed -g1/-g inputs.
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
A. Nested compile-time inlining
In both inputs, leaf is inlined into middle, which is inlined into its caller
during LGEN (recorded in -fdump-ipa-clones). With link order a.o b.o, stopping
at h.h:4 in main's inline copy gives:
With -flto: With -fno-lto:
#0 leaf (n=<optimized out>, n=<optimized out>) at h.h:4 #0 leaf (n=1)
at h.h:4
#1 middle (n=<optimized out>, n=<optimized out>) at h.h:9 #1 middle
(n=1) at h.h:9
#2 main (argc=1) at b.C:4 #2 main
(argc=1) at b.C:4
leaf: n listed twice; value = 4 and <optimized out> leaf: n = 1,
value = 4 (once each)
middle: n listed twice middle: n = 1
(once)
In late DWARF, both inline levels in main have their DW_AT_abstract_origin
redirected to a.C while their parameters and locals still point to b.C:
inlined middle -> middle in a.C
parameter n -> middle's n in b.C
variable result -> middle's result in b.C
inlined leaf -> leaf in a.C
parameter n -> leaf's n in b.C
variable value -> leaf's value in b.C
B. Prevailing-declaration replacement
In this three-TU testcase, b.C adds -fno-strict-aliasing, so its FUNCTION_DECL
is not unified with a.C's. During prevailing-declaration fixup, lto_fixup_state
replaces the function reference in the body's decl-state with a.C's
declaration. When the body is read, BLOCK_ABSTRACT_ORIGIN resolves through that
slot to a.C's declaration, while the parameter and local-variable origins
retain b.C's early DIE references. This produces the following mismatch on the
two inline instances of Buffer::printable_length from b.C:
inlined printable_length -> printable_length in a.C
parameter this -> this in b.C
variable location -> location in b.C
(Verified also in a local diagnostic build where unify_scc unconditionally
returns false while symbol resolution and prevailing-decl fixup remain active;
the -fno-lto build has no mismatch.)
C. Mixed -g1/-g inputs (lookup failure)
Here a.C is compiled with -g1 and b.C with -g. Stopping at h.h:6 in main's
inline copy of transform:
Link order a.o b.o: Link order b.o a.o:
(gdb) print x (gdb) print x
No symbol "x" in current $1 = 8
context.
With a.o first, the inline instance from b.C has its origin redirected to a.C's
-g1 subprogram DIE (which omits local/static variable DIEs), while its concrete
children still point to b.C:
inlined transform in main -> transform in a.C (-g1, no x DIE)
parameter n -> n in b.C (-g)
variable result -> result in b.C (-g)
Reversing the link order keeps this instance and its children on b.C's -g
descriptions and restores 'print x'. (In the a.o-first link, n is also shown as
<optimized out>; that is PR128623, where the -g1 object's optimization node
disables variable tracking for b.o's functions.)
Scope and related PRs
Together with the original split-clone case, these examples show SCC
unification or prevailing-declaration replacement redirecting an instance's
origin to another input, while its parameters and locals retain their original
early DIE references.
Related classes filed separately:
- PR128616: merged COMDAT declaration and prevailing body come from different
TUs (affects the root function itself, DECL_ORIGIN(fn) == fn, and copies
inlined from it in LTRANS).
- PR128617: mixed -g0/-g links where a -g0 body borrows another TU's early DIE
or emits an originless DW_TAG_inlined_subroutine (unlike Case C above, where a
-g instance is redirected to a -g1 DIE).
Patch v4 and review thread (following v3 in comment 4):
https://inbox.sourceware.org/gcc-patches/[email protected]/
Reproducers
Save each case in a separate directory.
=== Case A ===
--- h.h ---
inline int leaf (int n)
{
int value = n + 3;
asm volatile ("nop" : "+r" (value) : : "memory");
return value;
}
inline int middle (int n)
{
int result = leaf (n);
return result + 1;
}
__attribute__((noipa)) int local_value (int);
--- a.C ---
#include "h.h"
int local_value (int n) { return middle (n); }
--- b.C ---
#include "h.h"
int main (int argc, char **)
{
int a = middle (argc);
int b = local_value (argc + 1);
return a + b != 11;
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
mode=${1:--flto}
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" \
-fdump-ipa-clones -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" \
-fdump-ipa-clones -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -flto-partition=one \
-no-pie a.o b.o -o repro
"${GDB:-gdb}" -q -nx -batch -iex 'set debuginfod enabled off' \
-ex 'break h.h:4' -ex run -ex 'bt 3' \
-ex 'info args' -ex 'info locals' \
-ex up -ex 'info args' repro
Run with:
CXX=g++ GDB=gdb sh run.sh -flto
readelf --debug-dump=info --wide repro
CXX=g++ GDB=gdb sh run.sh -fno-lto
=== Case B ===
--- h.h ---
struct Location { const char *file; const char *function; int line; };
extern void fail (int, const char *, const Location &)
__attribute__((noreturn, cold, noipa));
extern void note (const char *, int) __attribute__((cold, noipa));
struct Buffer
{
int length;
__attribute__((used)) int printable_length () const
{
if (__builtin_expect (length < 1024, 1))
return length;
const Location location = { __FILE__, __func__, __LINE__ };
note (location.file, location.line);
note (location.function, length);
note (location.file, length + 1);
fail (3, "length < 1024", location);
}
};
__attribute__((noinline)) int one (const Buffer &);
--- a.C ---
#include "h.h"
__attribute__((noinline)) int one (const Buffer &b)
{
return b.printable_length ();
}
__attribute__((noinline)) int one_extra (const Buffer &b)
{
return b.printable_length ();
}
--- b.C ---
#include "h.h"
void fail (int, const char *, const Location &) { __builtin_trap (); }
void note (const char *, int) { asm volatile ("" ::: "memory"); }
__attribute__((noinline)) int two (const Buffer &b)
{
return b.printable_length ();
}
__attribute__((noinline)) int two_extra (const Buffer &b)
{
return b.printable_length ();
}
--- main.C ---
#include "h.h"
__attribute__((noinline)) int one_extra (const Buffer &);
__attribute__((noinline)) int two (const Buffer &);
__attribute__((noinline)) int two_extra (const Buffer &);
int main (int argc, char **)
{
Buffer buffer = { argc };
return one (buffer) + one_extra (buffer)
+ two (buffer) + two_extra (buffer);
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
mode=${1:--flto}
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" \
--param=max-inline-insns-auto=100 -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" \
--param=max-inline-insns-auto=100 \
-fno-strict-aliasing -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" \
--param=max-inline-insns-auto=100 -c main.C -o main.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" \
--param=max-inline-insns-auto=100 -flto-partition=one \
-no-pie a.o b.o main.o -o repro
readelf --debug-dump=info --wide repro > dwarf.txt
Run with:
CXX=g++ sh run.sh -flto
mv dwarf.txt dwarf-lto.txt
CXX=g++ sh run.sh -fno-lto
mv dwarf.txt dwarf-nonlto.txt
=== Case C ===
--- h.h ---
inline int transform (int n)
{
static volatile int x = 7;
int result = x + n;
x = result;
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 main ()
{
int a = transform (1);
int b = local_value (2);
return a + b != 18;
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -g1 -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -flto-partition=one \
-no-pie a.o b.o -o forward
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -flto-partition=one \
-no-pie b.o a.o -o reverse
"${GDB:-gdb}" -q -nx -batch -iex 'set debuginfod enabled off' \
-ex 'break h.h:6' -ex run -ex 'print x' forward || true
"${GDB:-gdb}" -q -nx -batch -iex 'set debuginfod enabled off' \
-ex 'break h.h:6' -ex run -ex 'print x' reverse
Run with:
CXX=g++ GDB=gdb sh run.sh
readelf --debug-dump=info --wide forward
readelf --debug-dump=info --wide reverse
Referenced Bugs:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47819
[Bug 47819] [meta-bug] LTO debug information issues