
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
106 lines
4.5 KiB
LLVM
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)
|