llvm-project/llvm/test/DebugInfo/X86/merge_inlined_loc.ll
David Stenberg 12a7aea6b0 [DebugInfo] Merge partially matching chains of inlined locations
For example, if you have a chain of inlined funtions like this:

   1 #include <stdlib.h>
   2 int g1 = 4, g2 = 6;
   3
   4 static inline void bar(int q) {
   5   if (q > 5)
   6     abort();
   7 }
   8
   9 static inline void foo(int q) {
  10   bar(q);
  11 }
  12
  13 int main() {
  14   foo(g1);
  15   foo(g2);
  16   return 0;
  17 }

with optimizations you could end up with a single abort call for the two
inlined instances of foo(). When merging the locations for those inlined
instances you would previously end up with a 0:0 location in main().
Leaving out that inlined chain from the location for the abort call
could make troubleshooting difficult in some cases.

This patch changes DILocation::getMergedLocation() to try to handle such
cases. The function is rewritten to first find a common starting point
for the two locations (same subprogram and inlined-at location), and
then in reverse traverses the inlined-at chain looking for matches in
each subprogram. For each subprogram, the merge function will find the
nearest common scope for the two locations, and matching line and
column (or set them to 0 if not matching).

In the example above, you will for the abort call get a location in
bar() at 6:5, inlined in foo() at 10:3, inlined in main() at 0:0 (since
the two inlined functions are on different lines, but in the same
scope).

I have not seen anything in the DWARF standard that would disallow
inlining a non-zero location at 0:0 in the inlined-at function, and both
LLDB and GDB seem to accept these locations (with D142552 needed for
LLDB to handle cases where the file, line and column number are all 0).
One incompatibility with GDB is that it seems to ignore 0-line locations
in some cases, but I am not aware of any specific issue that this patch
produces related to that.

With x86-64 LLDB (trunk) you previously got:

  frame #0: 0x00007ffff7a44930 libc.so.6`abort
  frame #1: 0x00005555555546ec a.out`main at merge.c:0

and will now get:

  frame #0: 0x[...] libc.so.6`abort
  frame #1: 0x[...] a.out`main [inlined] bar(q=<unavailable>) at merge.c:6:5
  frame #2: 0x[...] a.out`main [inlined] foo(q=<unavailable>) at merge.c:10:3
  frame #3: 0x[...] a.out`main at merge.c:0

and with x86-64 GDB (11.1) you will get:

  (gdb) bt
  #0  0x00007ffff7a44930 in abort () from /lib64/libc.so.6
  #1  0x00005555555546ec in bar (q=<optimized out>) at merge.c:6
  #2  foo (q=<optimized out>) at merge.c:10
  #3  0x00005555555546ec in main ()

Reviewed By: aprantl, dblaikie

Differential Revision: https://reviews.llvm.org/D142556
2023-03-06 14:23:29 +01:00

106 lines
4.5 KiB
LLVM

; RUN: llc %s -mtriple=x86_64-unknown-unknown -o - | FileCheck %s
; Generated with "clang -g -c -emit-llvm -S -O3"
; This will test several features of merging debug locations. Importantly,
; locations with the same source line but different scopes should be merged to
; a line zero location at the nearest common scope and inlining. The location
; of the single call to "common" (the two calls are collapsed together by
; BranchFolding) should be attributed to line 2 inside the wrapper inlined
; scope within wrapper2 at line 0 inlined within f1 at line 13.
; void common();
; inline void wrapper() { common(); }
; extern bool b;
; void sink();
; inline void wrapper2() {
; if (b) {
; sink();
; wrapper();
; } else
; wrapper();
; }
; void f1() { wrapper2(); }
; Ensure there are two inlined_subroutine (for wrapper and wrapper2).
; CHECK: .loc 1 2 25 epilogue_begin
; CHECK-NEXT: popq %rax
; CHECK-NEXT: .cfi_def_cfa_offset 8
; CHECK-NEXT: jmp _Z6commonv
; CHECK-NEXT: [[LABEL:.*]]:
; CHECK: .section .debug_info
; CHECK: DW_TAG_subprogram
; CHECK: DW_TAG_subprogram
; CHECK-NOT: {{DW_TAG\|End Of Children}}
; CHECK: DW_TAG_inlined_subroutine
; CHECK-NOT: {{DW_TAG\|End Of Children}}
; CHECK: [[LABEL]]-{{.*}} DW_AT_high_pc
; CHECK: .byte 13 # DW_AT_call_line
; CHECK: DW_TAG_inlined_subroutine
; CHECK: .byte 0 # DW_AT_call_line
; CHECK-NOT: DW_TAG
@b = external dso_local local_unnamed_addr global i8, align 1
; Function Attrs: uwtable
define dso_local void @_Z2f1v() local_unnamed_addr !dbg !7 {
entry:
%0 = load i8, ptr @b, align 1, !dbg !10, !tbaa !14, !range !18
%tobool.i = icmp eq i8 %0, 0, !dbg !10
br i1 %tobool.i, label %if.else.i, label %if.then.i, !dbg !19
if.then.i: ; preds = %entry
tail call void @_Z4sinkv(), !dbg !20
tail call void @_Z6commonv(), !dbg !22
br label %_Z8wrapper2v.exit, !dbg !25
if.else.i: ; preds = %entry
tail call void @_Z6commonv(), !dbg !26
br label %_Z8wrapper2v.exit
_Z8wrapper2v.exit: ; preds = %if.then.i, %if.else.i
ret void, !dbg !28
}
declare dso_local void @_Z4sinkv() local_unnamed_addr
declare dso_local void @_Z6commonv() local_unnamed_addr
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 8.0.0 (trunk 340559) (llvm/trunk 340572)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "merge_loc.cpp", directory: "/usr/local/google/home/blaikie/dev/scratch")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 8.0.0 (trunk 340559) (llvm/trunk 340572)"}
!7 = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 12, type: !8, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !2)
!8 = !DISubroutineType(types: !9)
!9 = !{null}
!10 = !DILocation(line: 6, column: 7, scope: !11, inlinedAt: !13)
!11 = distinct !DILexicalBlock(scope: !12, file: !1, line: 6, column: 7)
!12 = distinct !DISubprogram(name: "wrapper2", linkageName: "_Z8wrapper2v", scope: !1, file: !1, line: 5, type: !8, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !2)
!13 = distinct !DILocation(line: 13, column: 3, scope: !7)
!14 = !{!15, !15, i64 0}
!15 = !{!"bool", !16, i64 0}
!16 = !{!"omnipotent char", !17, i64 0}
!17 = !{!"Simple C++ TBAA"}
!18 = !{i8 0, i8 2}
!19 = !DILocation(line: 6, column: 7, scope: !12, inlinedAt: !13)
!20 = !DILocation(line: 7, column: 5, scope: !21, inlinedAt: !13)
!21 = distinct !DILexicalBlock(scope: !11, file: !1, line: 6, column: 10)
!22 = !DILocation(line: 2, column: 25, scope: !23, inlinedAt: !24)
!23 = distinct !DISubprogram(name: "wrapper", linkageName: "_Z7wrapperv", scope: !1, file: !1, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !2)
!24 = distinct !DILocation(line: 8, column: 5, scope: !21, inlinedAt: !13)
!25 = !DILocation(line: 9, column: 3, scope: !21, inlinedAt: !13)
!26 = !DILocation(line: 2, column: 25, scope: !23, inlinedAt: !27)
!27 = distinct !DILocation(line: 10, column: 5, scope: !11, inlinedAt: !13)
!28 = !DILocation(line: 14, column: 1, scope: !7)