Skip to content

Commit 154ae32

Browse files
committed
Auto merge of rust-lang#114643 - dpaoliello:inlinedebuginfo, r=wesleywiser
Use the same DISubprogram for each instance of the same inlined function within a caller # Issue Details: The call to `panic` within a function like `Option::unwrap` is translated to LLVM as a `tail call` (as it will never return), when multiple calls to the same function like this is inlined LLVM will notice the common `tail call` block (i.e., loading the same panic string + location info and then calling `panic`) and merge them together. When merging these instructions together, LLVM will also attempt to merge the debug locations as well, but this fails (i.e., debug info is dropped) as Rust emits a new `DISubprogram` at each inline site thus LLVM doesn't recognize that these are actually the same function and so thinks that there isn't a common debug location. As an example of this when building for x86_64 Windows (note the lack of `.cv_loc` before the call to `panic`, thus it will be attributed to the same line at the `addq` instruction): ``` .cv_loc 0 1 23 0 # src\lib.rs:23:0 addq $40, %rsp retq leaq .Lalloc_f570dea0a53168780ce9a91e67646421(%rip), %rcx leaq .Lalloc_629ace53b7e5b76aaa810d549cc84ea3(%rip), %r8 movl $43, %edx callq _ZN4core9panicking5panic17h12e60b9063f6dee8E int3 ``` # Fix Details: Cache the `DISubprogram` emitted for each inlined function instance within a caller so that this can be reused if that instance is encountered again, this also requires caching the `DILexicalBlock` and `DIVariable` objects to avoid creating duplicates. After this change the above assembly now looks like: ``` .cv_loc 0 1 23 0 # src\lib.rs:23:0 addq $40, %rsp retq .cv_inline_site_id 5 within 0 inlined_at 1 0 0 .cv_inline_site_id 6 within 5 inlined_at 1 12 0 .cv_loc 6 2 935 0 # library\core\src\option.rs:935:0 leaq .Lalloc_5f55955de67e57c79064b537689facea(%rip), %rcx leaq .Lalloc_e741d4de8cb5801e1fd7a6c6795c1559(%rip), %r8 movl $43, %edx callq _ZN4core9panicking5panic17hde1558f32d5b1c04E int3 ```
2 parents 712d962 + 1097e09 commit 154ae32

File tree

7 files changed

+106
-51
lines changed

7 files changed

+106
-51
lines changed

compiler/rustc_codegen_gcc/src/debuginfo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
5555
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
5656
_llfn: RValue<'gcc>,
5757
_mir: &mir::Body<'tcx>,
58-
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
58+
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> {
5959
// TODO(antoyo)
6060
None
6161
}

compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs

+21-14
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub fn compute_mir_scopes<'ll, 'tcx>(
2020
cx: &CodegenCx<'ll, 'tcx>,
2121
instance: Instance<'tcx>,
2222
mir: &Body<'tcx>,
23-
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
23+
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
2424
) {
2525
// Find all scopes with variables defined in them.
2626
let variables = if cx.sess().opts.debuginfo == DebugInfo::Full {
@@ -51,7 +51,7 @@ fn make_mir_scope<'ll, 'tcx>(
5151
instance: Instance<'tcx>,
5252
mir: &Body<'tcx>,
5353
variables: &Option<BitSet<SourceScope>>,
54-
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
54+
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
5555
instantiated: &mut BitSet<SourceScope>,
5656
scope: SourceScope,
5757
) {
@@ -84,7 +84,6 @@ fn make_mir_scope<'ll, 'tcx>(
8484
}
8585

8686
let loc = cx.lookup_debug_loc(scope_data.span.lo());
87-
let file_metadata = file_metadata(cx, &loc.file);
8887

8988
let dbg_scope = match scope_data.inlined {
9089
Some((callee, _)) => {
@@ -95,18 +94,26 @@ fn make_mir_scope<'ll, 'tcx>(
9594
ty::ParamEnv::reveal_all(),
9695
ty::EarlyBinder::bind(callee),
9796
);
98-
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
99-
cx.dbg_scope_fn(callee, callee_fn_abi, None)
97+
debug_context.inlined_function_scopes.entry(callee).or_insert_with(|| {
98+
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
99+
cx.dbg_scope_fn(callee, callee_fn_abi, None)
100+
})
101+
}
102+
None => {
103+
let file_metadata = file_metadata(cx, &loc.file);
104+
debug_context
105+
.lexical_blocks
106+
.entry((parent_scope.dbg_scope, loc.line, loc.col, file_metadata))
107+
.or_insert_with(|| unsafe {
108+
llvm::LLVMRustDIBuilderCreateLexicalBlock(
109+
DIB(cx),
110+
parent_scope.dbg_scope,
111+
file_metadata,
112+
loc.line,
113+
loc.col,
114+
)
115+
})
100116
}
101-
None => unsafe {
102-
llvm::LLVMRustDIBuilderCreateLexicalBlock(
103-
DIB(cx),
104-
parent_scope.dbg_scope,
105-
file_metadata,
106-
loc.line,
107-
loc.col,
108-
)
109-
},
110117
};
111118

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

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+44-32
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_codegen_ssa::mir::debuginfo::VariableKind::*;
55
use self::metadata::{file_metadata, type_di_node};
66
use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
77
use self::namespace::mangled_name_of_instance;
8-
use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
8+
use self::utils::{create_DIArray, debug_context, is_node_local_to_unit, DIB};
99

1010
use crate::abi::FnAbi;
1111
use crate::builder::Builder;
@@ -67,6 +67,8 @@ pub struct CodegenUnitDebugContext<'ll, 'tcx> {
6767
type_map: metadata::TypeMap<'ll, 'tcx>,
6868
namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
6969
recursion_marker_type: OnceCell<&'ll DIType>,
70+
/// Maps a variable (name, scope, kind (argument or local), span) to its debug information.
71+
variables: RefCell<FxHashMap<(Symbol, &'ll DIScope, VariableKind, Span), &'ll DIVariable>>,
7072
}
7173

7274
impl Drop for CodegenUnitDebugContext<'_, '_> {
@@ -91,6 +93,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
9193
type_map: Default::default(),
9294
namespace_map: RefCell::new(Default::default()),
9395
recursion_marker_type: OnceCell::new(),
96+
variables: RefCell::new(Default::default()),
9497
}
9598
}
9699

@@ -292,7 +295,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
292295
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
293296
llfn: &'ll Value,
294297
mir: &mir::Body<'tcx>,
295-
) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> {
298+
) -> Option<FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>> {
296299
if self.sess().opts.debuginfo == DebugInfo::None {
297300
return None;
298301
}
@@ -304,8 +307,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
304307
file_start_pos: BytePos(0),
305308
file_end_pos: BytePos(0),
306309
};
307-
let mut fn_debug_context =
308-
FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
310+
let mut fn_debug_context = FunctionDebugContext {
311+
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
312+
inlined_function_scopes: Default::default(),
313+
lexical_blocks: Default::default(),
314+
};
309315

310316
// Fill in all the scopes, with the information from the MIR body.
311317
compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
@@ -606,33 +612,39 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
606612
variable_kind: VariableKind,
607613
span: Span,
608614
) -> &'ll DIVariable {
609-
let loc = self.lookup_debug_loc(span.lo());
610-
let file_metadata = file_metadata(self, &loc.file);
611-
612-
let type_metadata = type_di_node(self, variable_type);
613-
614-
let (argument_index, dwarf_tag) = match variable_kind {
615-
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
616-
LocalVariable => (0, DW_TAG_auto_variable),
617-
};
618-
let align = self.align_of(variable_type);
619-
620-
let name = variable_name.as_str();
621-
unsafe {
622-
llvm::LLVMRustDIBuilderCreateVariable(
623-
DIB(self),
624-
dwarf_tag,
625-
scope_metadata,
626-
name.as_ptr().cast(),
627-
name.len(),
628-
file_metadata,
629-
loc.line,
630-
type_metadata,
631-
true,
632-
DIFlags::FlagZero,
633-
argument_index,
634-
align.bytes() as u32,
635-
)
636-
}
615+
debug_context(self)
616+
.variables
617+
.borrow_mut()
618+
.entry((variable_name, scope_metadata, variable_kind, span))
619+
.or_insert_with(|| {
620+
let loc = self.lookup_debug_loc(span.lo());
621+
let file_metadata = file_metadata(self, &loc.file);
622+
623+
let type_metadata = type_di_node(self, variable_type);
624+
625+
let (argument_index, dwarf_tag) = match variable_kind {
626+
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
627+
LocalVariable => (0, DW_TAG_auto_variable),
628+
};
629+
let align = self.align_of(variable_type);
630+
631+
let name = variable_name.as_str();
632+
unsafe {
633+
llvm::LLVMRustDIBuilderCreateVariable(
634+
DIB(self),
635+
dwarf_tag,
636+
scope_metadata,
637+
name.as_ptr().cast(),
638+
name.len(),
639+
file_metadata,
640+
loc.line,
641+
type_metadata,
642+
true,
643+
DIFlags::FlagZero,
644+
argument_index,
645+
align.bytes() as u32,
646+
)
647+
}
648+
})
637649
}
638650
}

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::traits::*;
2+
use rustc_data_structures::fx::FxHashMap;
23
use rustc_index::IndexVec;
34
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
45
use rustc_middle::mir;
56
use rustc_middle::ty;
67
use rustc_middle::ty::layout::TyAndLayout;
78
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
9+
use rustc_middle::ty::Instance;
810
use rustc_middle::ty::Ty;
911
use rustc_session::config::DebugInfo;
1012
use rustc_span::symbol::{kw, Symbol};
@@ -17,11 +19,19 @@ use super::{FunctionCx, LocalRef};
1719

1820
use std::ops::Range;
1921

20-
pub struct FunctionDebugContext<S, L> {
22+
pub struct FunctionDebugContext<'tcx, S, L> {
23+
/// Maps from source code to the corresponding debug info scope.
2124
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
25+
26+
/// Maps from a given inlined function to its debug info declaration.
27+
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
28+
29+
/// Maps from a lexical block (parent scope, line, column, file) to its debug info declaration.
30+
/// This is particularily useful if the parent scope is an inlined function.
31+
pub lexical_blocks: FxHashMap<(S, u32, u32, S), S>,
2232
}
2333

24-
#[derive(Copy, Clone)]
34+
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
2535
pub enum VariableKind {
2636
ArgumentVariable(usize /*index*/),
2737
LocalVariable,

compiler/rustc_codegen_ssa/src/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
4545

4646
mir: &'tcx mir::Body<'tcx>,
4747

48-
debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>,
48+
debug_context: Option<FunctionDebugContext<'tcx, Bx::DIScope, Bx::DILocation>>,
4949

5050
llfn: Bx::Function,
5151

compiler/rustc_codegen_ssa/src/traits/debuginfo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
2626
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
2727
llfn: Self::Function,
2828
mir: &mir::Body<'tcx>,
29-
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>;
29+
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>>;
3030

3131
// FIXME(eddyb) find a common convention for all of the debuginfo-related
3232
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// compile-flags: -g -O
2+
3+
// Check that each inline call site for the same function uses the same "sub-program" so that LLVM
4+
// can correctly merge the debug info if it merges the inlined code (e.g., for merging of tail
5+
// calls to panic.
6+
7+
// CHECK: tail call void @_ZN4core9panicking5panic17h{{([0-9a-z]{16})}}E
8+
// CHECK-SAME: !dbg ![[#first_dbg:]]
9+
// CHECK: tail call void @_ZN4core9panicking5panic17h{{([0-9a-z]{16})}}E
10+
// CHECK-SAME: !dbg ![[#second_dbg:]]
11+
12+
// CHECK: ![[#func_dbg:]] = distinct !DISubprogram(name: "unwrap<i32>"
13+
// CHECK: ![[#first_dbg]] = !DILocation(line: [[#]]
14+
// CHECK-SAME: scope: ![[#func_dbg]], inlinedAt: ![[#]])
15+
// CHECK: ![[#second_dbg]] = !DILocation(line: [[#]]
16+
// CHECK-SAME: scope: ![[#func_dbg]], inlinedAt: ![[#]])
17+
18+
#![crate_type = "lib"]
19+
20+
#[no_mangle]
21+
extern "C" fn add_numbers(x: &Option<i32>, y: &Option<i32>) -> i32 {
22+
let x1 = x.unwrap();
23+
let y1 = y.unwrap();
24+
25+
x1 + y1
26+
}

0 commit comments

Comments
 (0)