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)

Reply via email to