Skip to content

LTO produces bogus call-site debug info #106422

Open
@pogo59

Description

@pogo59

While poking around looking at something else, I tripped over this. A
set of files compiled without LTO has perfectly reasonable debug info,
in particular "main" shows a call-site for calling "foo" and one for
"printf". The info for "foo" shows a call-site for calling
"A::Method1". All totally good.

But when built with LTO, we're able to inline "foo" into "main", and
something gets messed up. "main" shows a DW_TAG_inlined_subroutine for
"foo" followed by 3 call-sites like so:

0x000000a2:     DW_TAG_call_site
                  DW_AT_call_origin	(0x000000b5)
                  DW_AT_call_return_pc	(0x00000000000016fb)

0x000000a8:     DW_TAG_call_site
                  DW_AT_call_origin	(0x000000b6 "printf")
                  DW_AT_call_return_pc	(0x000000000000170b)

0x000000ae:     DW_TAG_call_site
                  DW_AT_call_origin	(0x000000b5)
                  DW_AT_call_return_pc	(0x0000000000001715)

0x000000b4:     NULL

0x000000b5:   DW_TAG_subprogram

0x000000b6:   DW_TAG_subprogram
                DW_AT_name	("printf")
                DW_AT_decl_file	("/usr/include/stdio.h")
                DW_AT_decl_line	(356)
                DW_AT_type	(0x0000005a "int")
                DW_AT_declaration	(true)
                DW_AT_external	(true)

Two call-sites pointing to a DW_TAG_subprogram that is... empty??

The call-site for "printf" shows that we are perfectly capable of
emitting a call-site that points to a declaration DIE. My guess is
that LTO is throwing something away that we don't want thrown away,
but because it's gone we end up with the bizarre attribute-free
subprogram DIE.

Because it's LTO, the repro requires multiple files.

// ----- 8< -----
// header.h
struct A {
  int Method1(int);
  int Method2(int);
};
int foo();
// ----- 8< -----
// header.cpp
#include "header.h"

__attribute__((noinline))
int A::Method1(int x) {
  return 5 + x;
}

int A::Method2(int x) {
  return 6 + x;
}

extern A a;

int foo() {
  return a.Method1(2);
}
// ----- 8< -----
// main.cpp
#include "header.h"
#include <stdio.h>

A a;

int main() {
  printf("%d\n", foo());
  return a.Method1(3) + a.Method2(4);
}
// ----- 8< -----
# Define CLANG and LLD to point to your build, then execute:
$CLANG -I. -O2 -g -fuse-ld=$LLD main.cpp header.cpp  -o normal.elf
$CLANG -I. -O2 -g -fuse-ld=$LLD main.cpp header.cpp  -o lto.elf -flto
# and dump the debug info for the two files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    LTOLink time optimization (regular/full LTO or ThinLTO)debuginfowrong-debug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions