On Wed, 22 Jul 2026, Longjun Luo wrote: > An IPA split clone can have no early DIE of its own. In that case > dwarf2out_die_ref_for_decl fails to stream an external DIE reference for > the clone. During LTO merging, the function origin can then resolve to > the prevailing TU while direct parameters and local variables retain > origins from the physical input TU. This leaves an out-of-line split-clone > DW_TAG_subprogram and its direct children referring to different input TUs.
That DW_TAG_subprogram still refers to the early die via abstract origin though? The issue is we do need a late DIE to annotate with location info. > When lookup_decl_die fails for a compiler-generated FUNCTION_DECL, use the > early DIE of its ultimate abstract origin. The existing external-DIE > streaming machinery then preserves the clone's input TU provenance. Limit > the fallback to artificial functions because the C front end also uses > DECL_ABSTRACT_ORIGIN for nested redeclarations. I think putting the fix into dwarf2out_die_ref_for_decl is disrupting this primitive. Is it possible to put the fallback to a more specific place? Thanks, Richard. > Tested on x86_64-pc-linux-gnu. The added test fails before the change > and passes afterwards. > > PR debug/126348 > > gcc/ChangeLog: > > * dwarf2out.cc (dwarf2out_die_ref_for_decl): Fall back to the > ultimate abstract origin's early DIE for artificial function clones > without an early DIE. > > gcc/testsuite/ChangeLog: > > * g++.dg/lto/pr126348.h: New test. > * g++.dg/lto/pr126348_0.C: Likewise. > * g++.dg/lto/pr126348_1.C: Likewise. > > Signed-off-by: Longjun Luo <[email protected]> > --- > gcc/dwarf2out.cc | 18 +++++++++++--- > gcc/testsuite/g++.dg/lto/pr126348.h | 28 +++++++++++++++++++++ > gcc/testsuite/g++.dg/lto/pr126348_0.C | 23 +++++++++++++++++ > gcc/testsuite/g++.dg/lto/pr126348_1.C | 36 +++++++++++++++++++++++++++ > 4 files changed, 102 insertions(+), 3 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/lto/pr126348.h > create mode 100644 gcc/testsuite/g++.dg/lto/pr126348_0.C > create mode 100644 gcc/testsuite/g++.dg/lto/pr126348_1.C > > diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc > index 0b974d63c80..a603d0d9420 100644 > --- a/gcc/dwarf2out.cc > +++ b/gcc/dwarf2out.cc > @@ -6034,8 +6034,8 @@ equate_block_to_die (tree block, dw_die_ref die) > > > /* For DECL which might have early dwarf output query a SYMBOL + OFFSET > - style reference. Return true if we found one referring to a DIE for > - DECL, otherwise return false. */ > + style reference. Return true if a suitable DIE reference is found, > + otherwise return false. */ > > static bool > dwarf2out_die_ref_for_decl (tree decl, const char **sym, > @@ -6060,7 +6060,19 @@ dwarf2out_die_ref_for_decl (tree decl, const char > **sym, > if (TREE_CODE (decl) == BLOCK) > die = lookup_block_die (decl); > else > - die = lookup_decl_die (decl); > + { > + die = lookup_decl_die (decl); > + if (!die > + && TREE_CODE (decl) == FUNCTION_DECL > + && DECL_ARTIFICIAL (decl)) > + { > + /* An LTO clone can have no early DIE of its own. The early DIE of > + its abstract origin retains the input TU provenance. */ > + tree origin = decl_ultimate_origin (decl); > + if (origin && origin != decl) > + die = lookup_decl_die (origin); > + } > + } > if (!die) > return false; > > diff --git a/gcc/testsuite/g++.dg/lto/pr126348.h > b/gcc/testsuite/g++.dg/lto/pr126348.h > new file mode 100644 > index 00000000000..eac6698064a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lto/pr126348.h > @@ -0,0 +1,28 @@ > +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)); > + > +static const char source_file[] = __BASE_FILE__; > + > +struct Buffer > +{ > + int length; > + > + int printable_length () const > + { > + if (__builtin_expect (length < 1024, 1)) > + return length; > + const Location location = { source_file, __func__, __LINE__ }; > + note (location.file, location.line); > + note (location.function, length); > + note (location.file, length + 1); > + fail (3, "length < 1024", location); > + } > +}; > diff --git a/gcc/testsuite/g++.dg/lto/pr126348_0.C > b/gcc/testsuite/g++.dg/lto/pr126348_0.C > new file mode 100644 > index 00000000000..b532dda0f47 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lto/pr126348_0.C > @@ -0,0 +1,23 @@ > +/* PR debug/126348 */ > +/* Verify that an out-of-line split function and its direct children retain > + the same input TU provenance. */ > +/* { dg-lto-do link } */ > +/* { dg-skip-if "No DWARF debug support" { hppa*-*-hpux* } } */ > +/* { dg-skip-if "AIX DWARF5" { powerpc-ibm-aix* } } */ > +/* { dg-lto-options { { -O2 -g -gdwarf-5 -dA -save-temps -flto > -flto-partition=one -fno-ipa-icf } } } */ > +/* { dg-final { scan-lto-assembler > "DW_TAG_subprogram\\)\n\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin\n(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_formal_parameter\\)\n\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin\n(?:\[^\n\]*\n){1,4}\[^\n\]*DW_TAG_variable\\)\n\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin" > } } */ > +/* { dg-final { scan-lto-assembler > "DW_TAG_subprogram\\)\n\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin\n(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_formal_parameter\\)\n\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin\n(?:\[^\n\]*\n){1,4}\[^\n\]*DW_TAG_variable\\)\n\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin" > } } */ > + > +#include "pr126348.h" > + > +__attribute__ ((noinline)) int > +one (const Buffer &buffer) > +{ > + return buffer.printable_length (); > +} > + > +__attribute__ ((noinline)) int > +one_extra (const Buffer &buffer) > +{ > + return buffer.printable_length (); > +} > diff --git a/gcc/testsuite/g++.dg/lto/pr126348_1.C > b/gcc/testsuite/g++.dg/lto/pr126348_1.C > new file mode 100644 > index 00000000000..f4aeafa6435 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lto/pr126348_1.C > @@ -0,0 +1,36 @@ > +#include "pr126348.h" > + > +__attribute__ ((noinline)) int one (const Buffer &); > +__attribute__ ((noinline)) int one_extra (const Buffer &); > + > +void > +fail (int, const char *, const Location &) > +{ > + __builtin_trap (); > +} > + > +void > +note (const char *, int) > +{ > + asm volatile ("" ::: "memory"); > +} > + > +__attribute__ ((noinline)) int > +two (const Buffer &buffer) > +{ > + return buffer.printable_length (); > +} > + > +__attribute__ ((noinline)) int > +two_extra (const Buffer &buffer) > +{ > + return buffer.printable_length (); > +} > + > +int > +main (int argc, char **) > +{ > + Buffer buffer = { argc }; > + return one (buffer) + one_extra (buffer) > + + two (buffer) + two_extra (buffer); > +} > -- 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)
