Skip to content

Commit 3139ff0

Browse files
committed
Auto merge of #128861 - khuey:mir-inlining-parameters-debuginfo, r=wesleywiser
Rework MIR inlining debuginfo so function parameters show up in debuggers. Line numbers of multiply-inlined functions were fixed in #114643 by using a single DISubprogram. That, however, triggered assertions because parameters weren't deduplicated. The "solution" to that in #115417 was to insert a DILexicalScope below the DISubprogram and parent all of the parameters to that scope. That fixed the assertion, but debuggers (including gdb and lldb) don't recognize variables that are not parented to the subprogram itself as parameters, even if they are emitted with DW_TAG_formal_parameter. Consider the program: ```rust use std::env; #[inline(always)] fn square(n: i32) -> i32 { n * n } #[inline(never)] fn square_no_inline(n: i32) -> i32 { n * n } fn main() { let x = square(env::vars().count() as i32); let y = square_no_inline(env::vars().count() as i32); println!("{x} == {y}"); } ``` When making a release build with debug=2 and rustc 1.82.0-nightly (8b38707 2024-08-07) ``` (gdb) r Starting program: /ephemeral/tmp/target/release/tmp [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, tmp::square () at src/main.rs:5 5 n * n (gdb) info args No arguments. (gdb) info locals n = 31 (gdb) c Continuing. Breakpoint 2, tmp::square_no_inline (n=31) at src/main.rs:10 10 n * n (gdb) info args n = 31 (gdb) info locals No locals. ``` This issue is particularly annoying because it removes arguments from stack traces. The DWARF for the inlined function looks like this: ``` < 2><0x00002132 GOFF=0x00002132> DW_TAG_subprogram DW_AT_linkage_name _ZN3tmp6square17hc507052ff3d2a488E DW_AT_name square DW_AT_decl_file 0x0000000f /ephemeral/tmp/src/main.rs DW_AT_decl_line 0x00000004 DW_AT_type 0x00001a56<.debug_info+0x00001a56> DW_AT_inline DW_INL_inlined < 3><0x00002142 GOFF=0x00002142> DW_TAG_lexical_block < 4><0x00002143 GOFF=0x00002143> DW_TAG_formal_parameter DW_AT_name n DW_AT_decl_file 0x0000000f /ephemeral/tmp/src/main.rs DW_AT_decl_line 0x00000004 DW_AT_type 0x00001a56<.debug_info+0x00001a56> < 4><0x0000214e GOFF=0x0000214e> DW_TAG_null < 3><0x0000214f GOFF=0x0000214f> DW_TAG_null ``` That DW_TAG_lexical_block inhibits every debugger I've tested from recognizing 'n' as a parameter. This patch removes the additional lexical scope. Parameters can be easily deduplicated by a tuple of their scope and the argument index, at the trivial cost of taking a Hash + Eq bound on DIScope.
2 parents 026e9ed + 1c5e3c9 commit 3139ff0

File tree

6 files changed

+32
-20
lines changed

6 files changed

+32
-20
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs

+10-12
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fn make_mir_scope<'ll, 'tcx>(
8888
let loc = cx.lookup_debug_loc(scope_data.span.lo());
8989
let file_metadata = file_metadata(cx, &loc.file);
9090

91-
let parent_dbg_scope = match scope_data.inlined {
91+
let dbg_scope = match scope_data.inlined {
9292
Some((callee, _)) => {
9393
// FIXME(eddyb) this would be `self.monomorphize(&callee)`
9494
// if this is moved to `rustc_codegen_ssa::mir::debuginfo`.
@@ -102,17 +102,15 @@ fn make_mir_scope<'ll, 'tcx>(
102102
cx.dbg_scope_fn(callee, callee_fn_abi, None)
103103
})
104104
}
105-
None => parent_scope.dbg_scope,
106-
};
107-
108-
let dbg_scope = unsafe {
109-
llvm::LLVMRustDIBuilderCreateLexicalBlock(
110-
DIB(cx),
111-
parent_dbg_scope,
112-
file_metadata,
113-
loc.line,
114-
loc.col,
115-
)
105+
None => unsafe {
106+
llvm::LLVMRustDIBuilderCreateLexicalBlock(
107+
DIB(cx),
108+
parent_scope.dbg_scope,
109+
file_metadata,
110+
loc.line,
111+
loc.col,
112+
)
113+
},
116114
};
117115

118116
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::hash_map::Entry;
12
use std::ops::Range;
23

34
use rustc_data_structures::fx::FxHashMap;
@@ -447,6 +448,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
447448
}
448449

449450
let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
451+
let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
450452
for var in &self.mir.var_debug_info {
451453
let dbg_scope_and_span = if full_debug_info {
452454
self.adjusted_span_and_dbg_scope(var.source_info)
@@ -491,7 +493,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
491493
VariableKind::LocalVariable
492494
};
493495

494-
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
496+
if let VariableKind::ArgumentVariable(arg_index) = var_kind {
497+
match params_seen.entry((dbg_scope, arg_index)) {
498+
Entry::Occupied(o) => o.get().clone(),
499+
Entry::Vacant(v) => v
500+
.insert(
501+
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span),
502+
)
503+
.clone(),
504+
}
505+
} else {
506+
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
507+
}
495508
});
496509

497510
let fragment = if let Some(ref fragment) = var.composite {

compiler/rustc_codegen_ssa/src/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
106106
locals: locals::Locals<'tcx, Bx::Value>,
107107

108108
/// All `VarDebugInfo` from the MIR body, partitioned by `Local`.
109-
/// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed.
109+
/// This is `None` if no variable debuginfo/names are needed.
110110
per_local_var_debug_info:
111111
Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>>,
112112

compiler/rustc_codegen_ssa/src/traits/backend.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::any::Any;
2+
use std::hash::Hash;
23

34
use rustc_ast::expand::allocator::AllocatorKind;
45
use rustc_data_structures::fx::FxIndexMap;
@@ -30,7 +31,7 @@ pub trait BackendTypes {
3031

3132
// FIXME(eddyb) find a common convention for all of the debuginfo-related
3233
// names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.).
33-
type DIScope: Copy;
34+
type DIScope: Copy + Hash + PartialEq + Eq;
3435
type DILocation: Copy;
3536
type DIVariable: Copy;
3637
}

tests/codegen/debuginfo-inline-callsite-location.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
// CHECK: tail call void @{{[A-Za-z0-9_]+4core6option13unwrap_failed}}
1010
// CHECK-SAME: !dbg ![[#second_dbg:]]
1111

12-
// CHECK-DAG: ![[#func_dbg:]] = distinct !DISubprogram(name: "unwrap<i32>"
13-
// CHECK-DAG: ![[#first_scope:]] = distinct !DILexicalBlock(scope: ![[#func_dbg]],
14-
// CHECK: ![[#second_scope:]] = distinct !DILexicalBlock(scope: ![[#func_dbg]],
12+
// CHECK-DAG: ![[#func_scope:]] = distinct !DISubprogram(name: "unwrap<i32>"
13+
// CHECK-DAG: ![[#]] = !DILocalVariable(name: "self",{{( arg: 1,)?}} scope: ![[#func_scope]]
1514
// CHECK: ![[#first_dbg]] = !DILocation(line: [[#]]
16-
// CHECK-SAME: scope: ![[#first_scope]], inlinedAt: ![[#]])
15+
// CHECK-SAME: scope: ![[#func_scope]], inlinedAt: ![[#]])
1716
// CHECK: ![[#second_dbg]] = !DILocation(line: [[#]]
18-
// CHECK-SAME: scope: ![[#second_scope]], inlinedAt: ![[#]])
17+
// CHECK-SAME: scope: ![[#func_scope]], inlinedAt: ![[#]])
1918

2019
#![crate_type = "lib"]
2120

tests/codegen/inline-function-args-debug-info.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub fn outer_function(x: usize, y: usize) -> usize {
1515
fn inner_function(aaaa: usize, bbbb: usize) -> usize {
1616
// CHECK: !DILocalVariable(name: "aaaa", arg: 1
1717
// CHECK-SAME: line: 15
18+
// CHECK-NOT: !DILexicalBlock(
1819
// CHECK: !DILocalVariable(name: "bbbb", arg: 2
1920
// CHECK-SAME: line: 15
2021
aaaa + bbbb

0 commit comments

Comments
 (0)