Skip to content

Incorrect line number in debuginfo when duplicate callsites are merged together at -O1 #65667

Closed
@wesleywiser

Description

@wesleywiser

When the following IR is compiled with llc -O1 -trap-unreachable, an incorrect line number is generated in the debuginfo for the call to panic_test2::do_panic:

IR
source_filename = "panic_test2.6f3b49f66efc8fc7-cgu.0"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@alloc_14c43fe6be9850e9c6ac099b83b2e4e2 = private unnamed_addr constant <{ [7 x i8] }> <{ [7 x i8] c"unknown" }>, align 1
@alloc_1a2b9f3efbe1a8edd339fa75af2334ed = private unnamed_addr constant <{ [5 x i8] }> <{ [5 x i8] c"hello" }>, align 1

; Function Attrs: inlinehint noreturn nounwind nonlazybind
define fastcc void @bad_debug_info(ptr noalias nocapture noundef readonly align 8 dereferenceable(16) %_1) unnamed_addr #4 !dbg !85 {
start:
  %_11 = alloca { ptr, i64 }, align 8
  %_7 = alloca { ptr, i64 }, align 8
  %0 = getelementptr inbounds { ptr, i64 }, ptr %_1, i64 0, i32 1, !dbg !87
  %_13.1 = load i64, ptr %0, align 8, !dbg !87, !noundef !14
; call panic_test2::condition
  %1 = tail call fastcc ptr @_ZN11panic_test29condition17haa518b986e55a3c9E(i64 noundef %_13.1) #9, !dbg !89
  %.not = icmp eq ptr %1, null, !dbg !90
  br i1 %.not, label %bb3, label %bb2, !dbg !90

bb2:                                              ; preds = %start
  call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %_7), !dbg !91
  store ptr %1, ptr %_7, align 8, !dbg !91
  %2 = getelementptr inbounds { ptr, i64 }, ptr %_7, i64 0, i32 1, !dbg !91
  store i64 5, ptr %2, align 8, !dbg !91
; call panic_test2::do_panic
  call fastcc void @_ZN11panic_test28do_panic17h2c0ead66774c0583E(ptr noalias noundef nonnull readonly align 8 dereferenceable(16) %_7) #10, !dbg !92
  unreachable, !dbg !92

bb3:                                              ; preds = %start
  call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %_11), !dbg !93
  store ptr @alloc_14c43fe6be9850e9c6ac099b83b2e4e2, ptr %_11, align 8, !dbg !93
  %3 = getelementptr inbounds { ptr, i64 }, ptr %_11, i64 0, i32 1, !dbg !93
  store i64 7, ptr %3, align 8, !dbg !93
; call panic_test2::do_panic
  call fastcc void @_ZN11panic_test28do_panic17h2c0ead66774c0583E(ptr noalias noundef nonnull readonly align 8 dereferenceable(16) %_11) #10, !dbg !94
  unreachable, !dbg !94
}

; panic_test2::condition
; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind nonlazybind willreturn memory(none)
define internal fastcc ptr @_ZN11panic_test29condition17haa518b986e55a3c9E(i64 noundef %s.1) unnamed_addr #6 {
start:
  %0 = icmp eq i64 %s.1, 0
  %alloc_1a2b9f3efbe1a8edd339fa75af2334ed. = select i1 %0, ptr @alloc_1a2b9f3efbe1a8edd339fa75af2334ed, ptr null
  ret ptr %alloc_1a2b9f3efbe1a8edd339fa75af2334ed.
}

; panic_test2::do_panic
; Function Attrs: noreturn nounwind nonlazybind
define internal fastcc void @_ZN11panic_test28do_panic17h2c0ead66774c0583E(ptr noalias noundef readonly align 8 dereferenceable(16) %h) unnamed_addr #5 {
start:
  unreachable
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #8

attributes #1 = { nounwind nonlazybind "probe-stack"="inline-asm" "target-cpu"="x86-64" }
attributes #5 = { noreturn nounwind nonlazybind "probe-stack"="inline-asm" "target-cpu"="x86-64" }
attributes #6 = { mustprogress nofree noinline norecurse nosync nounwind nonlazybind willreturn memory(none) "probe-stack"="inline-asm" "target-cpu"="x86-64" }
attributes #8 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #9 = { nounwind }
attributes #10 = { noreturn nounwind }

!llvm.module.flags = !{!3, !4}
!llvm.ident = !{!5}
!llvm.dbg.cu = !{!6}

!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{!"rustc version 1.73.0-beta.4 (9f37cd4f7 2023-09-01)"}
!6 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !7, producer: "clang LLVM (rustc version 1.73.0-beta.4 (9f37cd4f7 2023-09-01))", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false)
!7 = !DIFile(filename: "src/main.rs/@/panic_test2.6f3b49f66efc8fc7-cgu.0", directory: "/tmp/panic_test")
!13 = !DISubroutineType(types: !14)
!14 = !{}
!80 = !DIFile(filename: "src/main.rs", directory: "/tmp/panic_test", checksumkind: CSK_MD5, checksum: "7cd0751a66038391ee52be036e31f480")
!81 = !DINamespace(name: "panic_test2", scope: null)
!85 = distinct !DISubprogram(name: "bad_debug_info", linkageName: "bad_debug_info", scope: !86, file: !80, line: 14, type: !13, scopeLine: 14, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !6, templateParams: !14)
!86 = !DINamespace(name: "never_returns", scope: !81)
!87 = !DILocation(line: 15, column: 36, scope: !88)
!88 = distinct !DILexicalBlock(scope: !85, file: !80, line: 15, column: 39)
!89 = !DILocation(line: 15, column: 26, scope: !88)
!90 = !DILocation(line: 15, column: 16, scope: !88)
!91 = !DILocation(line: 16, column: 23, scope: !88)
!92 = !DILocation(line: 16, column: 13, scope: !88)
!93 = !DILocation(line: 18, column: 23, scope: !85)
!94 = !DILocation(line: 18, column: 13, scope: !85)

godbolt

Notice that in the source IR, both calls to do_panic have the correct line numbers:

...
bb2:                                              ; preds = %start
...
; call panic_test2::do_panic
  call fastcc void @_ZN11panic_test28do_panic17h2c0ead66774c0583E(ptr noalias noundef nonnull readonly align 8 dereferenceable(16) %_7) #10, !dbg !92
...

bb3:                                              ; preds = %start
...
; call panic_test2::do_panic
  call fastcc void @_ZN11panic_test28do_panic17h2c0ead66774c0583E(ptr noalias noundef nonnull readonly align 8 dereferenceable(16) %_11) #10, !dbg !94
...
!85 = distinct !DISubprogram(name: "bad_debug_info", linkageName: "bad_debug_info", scope: !86, file: !80, line: 14, type: !13, scopeLine: 14, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !6, templateParams: !14)
!88 = distinct !DILexicalBlock(scope: !85, file: !80, line: 15, column: 39)
!92 = !DILocation(line: 16, column: 13, scope: !88)
!94 = !DILocation(line: 18, column: 13, scope: !85)

However, when lowering to machine code, these line numbers are erased and replaced with line 0:

...
        .loc    1 0 23 is_stmt 0                # src/main.rs:0:23
        leaq    8(%rsp), %rdi
        callq   panic_test2::do_panic::h2c0ead66774c0583
        ud2
...

Looking at the optimization passes in godbolt, it seems that the "Control Flow Optimizer (branch-folder)" pass is responsible for merging the calls together and removing the correct line numbers.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions