DW_TAG_formal_parameter always has DW_AT_decl_line == 1
It seems that Rust debuginfo always writes decl_line of 1 for formal parameters.
With this line.rs:
#![crate_type = "dylib"] // line 1
pub fn foo( // 2
x: i32, // 3
y: i32) // 4
-> i32 // 5
{ x + y } // 6
With:
$ rustc +nightly -Vv
rustc 1.22.0-nightly (185cc5f26 2017-10-02)
binary: rustc
commit-hash: 185cc5f26d2c8a794189b028b43f6a3b8fc586db
commit-date: 2017-10-02
host: x86_64-unknown-linux-gnu
release: 1.22.0-nightly
LLVM version: 4.0
I get this output:
$ rustc +nightly -g line.rs
$ dwgrep 'entry ?(@AT_name == "foo") child*' libline.so
[2f] subprogram
low_pc 0x42100
high_pc 66
frame_base 0..0xffffffffffffffff:0 reg6
linkage_name "_ZN4line3fooE"
name "foo"
decl_file "/tmp/line.rs"
decl_line 2
type [8c] base_type
external true
[4c] formal_parameter
location 0..0xffffffffffffffff:0 fbreg <-16>
name "x"
decl_file "/tmp/line.rs"
decl_line 1
type [8c] base_type
[5a] formal_parameter
location 0..0xffffffffffffffff:0 fbreg <-12>
name "y"
decl_file "/tmp/line.rs"
decl_line 1
type [8c] base_type
[68] lexical_block
ranges 0x4210e..0x4212a, 0x4213a..0x42142
[6d] variable
location 0..0xffffffffffffffff:0 fbreg <-8>
name "x"
decl_file "/tmp/line.rs"
decl_line 3
type [8c] base_type
[7b] variable
location 0..0xffffffffffffffff:0 fbreg <-4>
name "y"
decl_file "/tmp/line.rs"
decl_line 4
type [8c] base_type
So it describes the formal parameters of x and y both with line 1, but also describes local variables with the correct lines 3 and 4.
In LLVM IR it looks like this, with the wrong lines in !10 and !13:
$ rustc +nightly -g line.rs --emit=llvm-ir
$ sed -n '/DIComp/,$p' line.ll
!0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "clang LLVM (rustc version 1.22.0-nightly (185cc5f26 2017-10-02))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "line.rs", directory: "/tmp")
!2 = !{}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = distinct !DISubprogram(name: "foo", linkageName: "_ZN4line3fooE", scope: !5, file: !1, line: 2, type: !7, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, templateParams: !2, variables: !2)
!5 = !DINamespace(name: "line", scope: null, file: !6)
!6 = !DIFile(filename: "<unknown>", directory: "")
!7 = !DISubroutineType(types: !8)
!8 = !{!9, !9, !9}
!9 = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed)
!10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !1, line: 1, type: !9)
!11 = !DIExpression()
!12 = !DILocation(line: 1, scope: !4)
!13 = !DILocalVariable(name: "y", arg: 2, scope: !4, file: !1, line: 1, type: !9)
!14 = !DILocalVariable(name: "x", scope: !15, file: !1, line: 3, type: !9, align: 4)
!15 = distinct !DILexicalBlock(scope: !4, file: !1, line: 6)
!16 = !DILocation(line: 3, scope: !15)
!17 = !DILocalVariable(name: "y", scope: !15, file: !1, line: 4, type: !9, align: 4)
!18 = !DILocation(line: 4, scope: !15)
!19 = !DILocation(line: 6, scope: !15)
!20 = !DILocation(line: 6, scope: !4)
Historically, 1.11.0 looked more like I would expect, with the right line numbers on the formal parameters and no extra local variables.
$ rustc +1.11.0 -g line.rs --emit=llvm-ir && sed -n '/DIComp/,$p' line.ll !0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "rustc version 1.11.0 (9b21dcd6a 2016-08-15)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) !1 = !DIFile(filename: "./line.rs", directory: "/tmp") !2 = !{} !3 = !{!4} !4 = distinct !DISubprogram(name: "foo", linkageName: "_ZN4line3fooE", scope: !6, file: !5, line: 2, type: !7, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, templateParams: !2, variables: !2) !5 = !DIFile(filename: "/tmp/line.rs", directory: "/tmp") !6 = !DINamespace(name: "line", scope: null) !7 = !DISubroutineType(types: !8) !8 = !{!9, !9, !9} !9 = !DIBasicType(name: "i32", size: 32, align: 32, encoding: DW_ATE_signed) !10 = !{i32 2, !"Debug Info Version", i32 3} !11 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 3, type: !9) !12 = !DIExpression() !13 = !DILocation(line: 3, scope: !4) !14 = !DILocalVariable(name: "y", arg: 2, scope: !4, file: !5, line: 4, type: !9) !15 = !DILocation(line: 4, scope: !4) !16 = !DILocation(line: 6, scope: !17) !17 = distinct !DILexicalBlock(scope: !4, file: !5, line: 6) !18 = !DILocation(line: 6, scope: !4)In 1.12.0, it added the extra locals, and the formal parameters took on line 2 like the subprogram.
$ rustc +1.12.0 -g line.rs --emit=llvm-ir && sed -n '/DIComp/,$p' line.ll !0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "rustc version 1.12.0 (3191fbae9 2016-09-23)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) !1 = !DIFile(filename: "./line.rs", directory: "/tmp") !2 = !{} !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = distinct !DISubprogram(name: "foo", linkageName: "_ZN4line3fooE", scope: !6, file: !5, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, templateParams: !2, variables: !2) !5 = !DIFile(filename: "/tmp/line.rs", directory: "/tmp") !6 = !DINamespace(name: "line", scope: null, file: !7) !7 = !DIFile(filename: "<unknown>", directory: "") !8 = !DISubroutineType(types: !9) !9 = !{!10, !10, !10} !10 = !DIBasicType(name: "i32", size: 32, align: 32, encoding: DW_ATE_signed) !11 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 2, type: !10) !12 = !DIExpression() !13 = !DILocation(line: 2, scope: !4) !14 = !DILocalVariable(name: "y", arg: 2, scope: !4, file: !5, line: 2, type: !10) !15 = !DILocalVariable(name: "x", scope: !16, file: !5, line: 3, type: !10) !16 = distinct !DILexicalBlock(scope: !4, file: !5, line: 6) !17 = !DILocation(line: 3, scope: !16) !18 = !DILocalVariable(name: "y", scope: !16, file: !5, line: 4, type: !10) !19 = !DILocation(line: 4, scope: !16) !20 = !DILocation(line: 3, scope: !4) !21 = !DILocation(line: 4, scope: !4) !22 = !DILocation(line: 6, scope: !16) !23 = !DILocation(line: 2, scope: !16)Then 1.13.0 shows the line 1 behavior like the current nightly.
$ rustc +1.13.0 -g line.rs --emit=llvm-ir && sed -n '/DIComp/,$p' line.ll !0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "rustc version 1.13.0 (2c6933acc 2016-11-07)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) !1 = !DIFile(filename: "./line.rs", directory: "/tmp") !2 = !{} !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = distinct !DISubprogram(name: "foo", linkageName: "_ZN4line3fooE", scope: !6, file: !5, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, templateParams: !2, variables: !2) !5 = !DIFile(filename: "/tmp/line.rs", directory: "/tmp") !6 = !DINamespace(name: "line", scope: null, file: !7) !7 = !DIFile(filename: "<unknown>", directory: "") !8 = !DISubroutineType(types: !9) !9 = !{!10, !10, !10} !10 = !DIBasicType(name: "i32", size: 32, align: 32, encoding: DW_ATE_signed) !11 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !10) !12 = !DIExpression() !13 = !DILocation(line: 1, scope: !4) !14 = !DILocalVariable(name: "y", arg: 2, scope: !4, file: !5, line: 1, type: !10) !15 = !DILocalVariable(name: "x", scope: !16, file: !5, line: 3, type: !10) !16 = distinct !DILexicalBlock(scope: !4, file: !5, line: 6) !17 = !DILocation(line: 3, scope: !16) !18 = !DILocalVariable(name: "y", scope: !16, file: !5, line: 4, type: !10) !19 = !DILocation(line: 4, scope: !16) !20 = !DILocation(line: 3, scope: !4) !21 = !DILocation(line: 4, scope: !4) !22 = !DILocation(line: 6, scope: !16) !23 = !DILocation(line: 2, scope: !16)Josh Stone at 2017-10-03 21:30:37
With the latest nightly, it no longer has the extra local declarations (perhaps thanks to #44573), but the formal-parameter line numbers are still just 1.
$ rustc +nightly -Vv rustc 1.23.0-nightly (ee2286149 2017-11-07) binary: rustc commit-hash: ee2286149a5f0b148334841d4f067dc819dcca3b commit-date: 2017-11-07 host: x86_64-unknown-linux-gnu release: 1.23.0-nightly LLVM version: 4.0$ rustc +nightly -g line.rs --emit=llvm-ir $ sed -n '/DIComp/,$p' line.ll !0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "clang LLVM (rustc version 1.23.0-nightly (ee2286149 2017-11-07))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) !1 = !DIFile(filename: "line.rs", directory: "/tmp") !2 = !{} !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = distinct !DISubprogram(name: "foo", linkageName: "_ZN4line3fooE", scope: !5, file: !1, line: 2, type: !7, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, unit: !0, templateParams: !2, variables: !2) !5 = !DINamespace(name: "line", scope: null, file: !6) !6 = !DIFile(filename: "<unknown>", directory: "") !7 = !DISubroutineType(types: !8) !8 = !{!9, !9, !9} !9 = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !1, line: 1, type: !9) !11 = !DIExpression() !12 = !DILocation(line: 1, scope: !4) !13 = !DILocalVariable(name: "y", arg: 2, scope: !4, file: !1, line: 1, type: !9) !14 = !DILocation(line: 6, scope: !4)$ rustc +nightly -g line.rs $ dwgrep 'entry ?(@AT_name == "foo") child*' libline.so [2f] subprogram low_pc 0x42570 high_pc 54 frame_base 0..0xffffffffffffffff:0 reg6 linkage_name "_ZN4line3fooE" name "foo" decl_file "/tmp/line.rs" decl_line 2 type [6a] base_type external true [4c] formal_parameter location 0..0xffffffffffffffff:0 fbreg <-8> name "x" decl_file "/tmp/line.rs" decl_line 1 type [6a] base_type [5a] formal_parameter location 0..0xffffffffffffffff:0 fbreg <-4> name "y" decl_file "/tmp/line.rs" decl_line 1 type [6a] base_typeJosh Stone at 2017-11-08 01:16:19
This was fixed in Rust 1.40 (godbolt). wg-debugging would like to see a codegen test added to prevent regressions and then this can be closed.
Wesley Wiser at 2022-07-18 14:24:12