Skip to content

Commit 9f0ec68

Browse files
Auto merge of #142771 - dianqk:mir-stmt-debuginfo, r=<try>
Introduce debuginfo to statements in MIR The PR introduces support for debug information within dead statements. Currently, only the reference statement is supported, which is sufficient to fix #128081. I don't modify Stable MIR, as I don't think we need debug information when using it. This PR represents the debug information for the dead reference statement via `#dbg_value`. For example, `let _foo_b = &foo.b` becomes `#dbg_value(ptr %foo, !22, !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value), !26)`. You can see this here: https://rust.godbolt.org/z/d43js6adv. The general principle for handling debug information is to never provide less debug information than the optimized LLVM IR. The current rules for dropping debug information in this PR are: - If the LLVM IR cannot represent a reference address, it's replaced with poison or simply dropped. For example, see: https://rust.godbolt.org/z/shGqPec8W. I'm using poison in all such cases now. - All debuginfos is dropped when merging multiple successor BBs. An example is available here: https://rust.godbolt.org/z/TE1q3Wq6M. > I haven't drop debuginfos in `MatchBranchSimplification`, because LLVM also pick one branch for it. For [the perf result](#142771 (comment)): I expected this to introduce some regressions; however, the results mixed the effects of inlining. Looking at the doc profile, this is a clear optimization. One potential regression I'm investigating is `serde-1.0.219-debug-full`.
2 parents 3014e79 + 3a1ab06 commit 9f0ec68

File tree

69 files changed

+1993
-607
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1993
-607
lines changed

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11461146
let opt_assignment_rhs_span =
11471147
self.find_assignments(local).first().map(|&location| {
11481148
if let Some(mir::Statement {
1149-
source_info: _,
11501149
kind:
11511150
mir::StatementKind::Assign(box (
11521151
_,

compiler/rustc_codegen_gcc/src/debuginfo.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
2626
&mut self,
2727
_dbg_var: Self::DIVariable,
2828
_dbg_loc: Self::DILocation,
29-
_variable_alloca: Self::Value,
29+
is_declared: bool,
30+
val: Self::Value,
3031
_direct_offset: Size,
3132
_indirect_offsets: &[Size],
3233
_fragment: Option<Range<Size>>,
3334
) {
3435
// FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here.
3536
#[cfg(feature = "master")]
36-
_variable_alloca.set_location(_dbg_loc);
37+
if is_declared {
38+
val.set_location(_dbg_loc);
39+
}
3740
}
3841

3942
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {

compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ declare_constant!(DW_OP_plus_uconst: u64);
3535
/// Double-checked by a static assertion in `RustWrapper.cpp`.
3636
#[allow(non_upper_case_globals)]
3737
pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000;
38+
// It describes the actual value of a source variable which might not exist in registers or in memory.
39+
#[allow(non_upper_case_globals)]
40+
pub(crate) const DW_OP_stack_value: u64 = 0x9f;

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,23 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
160160
&mut self,
161161
dbg_var: &'ll DIVariable,
162162
dbg_loc: &'ll DILocation,
163-
variable_alloca: Self::Value,
163+
is_declared: bool,
164+
val: Self::Value,
164165
direct_offset: Size,
165166
indirect_offsets: &[Size],
166167
fragment: Option<Range<Size>>,
167168
) {
168-
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};
169+
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value};
169170

170171
// Convert the direct and indirect offsets and fragment byte range to address ops.
171172
let mut addr_ops = SmallVec::<[u64; 8]>::new();
172173

173174
if direct_offset.bytes() > 0 {
174175
addr_ops.push(DW_OP_plus_uconst);
175176
addr_ops.push(direct_offset.bytes() as u64);
177+
if !is_declared {
178+
addr_ops.push(DW_OP_stack_value);
179+
}
176180
}
177181
for &offset in indirect_offsets {
178182
addr_ops.push(DW_OP_deref);
@@ -189,17 +193,30 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
189193
addr_ops.push((fragment.end - fragment.start).bits() as u64);
190194
}
191195

192-
unsafe {
193-
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
194-
llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
195-
DIB(self.cx()),
196-
variable_alloca,
197-
dbg_var,
198-
addr_ops.as_ptr(),
199-
addr_ops.len() as c_uint,
200-
dbg_loc,
201-
self.llbb(),
202-
);
196+
if is_declared {
197+
unsafe {
198+
llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
199+
DIB(self.cx()),
200+
val,
201+
dbg_var,
202+
addr_ops.as_ptr(),
203+
addr_ops.len() as c_uint,
204+
dbg_loc,
205+
self.llbb(),
206+
);
207+
}
208+
} else {
209+
unsafe {
210+
llvm::LLVMRustDIBuilderInsertDbgValueAtEnd(
211+
DIB(self.cx()),
212+
val,
213+
dbg_var,
214+
addr_ops.as_ptr(),
215+
addr_ops.len() as c_uint,
216+
dbg_loc,
217+
self.llbb(),
218+
);
219+
}
203220
}
204221
}
205222

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,16 @@ unsafe extern "C" {
23082308
InsertAtEnd: &'a BasicBlock,
23092309
);
23102310

2311+
pub(crate) fn LLVMRustDIBuilderInsertDbgValueAtEnd<'a>(
2312+
Builder: &DIBuilder<'a>,
2313+
Val: &'a Value,
2314+
VarInfo: &'a DIVariable,
2315+
AddrOps: *const u64,
2316+
AddrOpsCount: c_uint,
2317+
DL: &'a DILocation,
2318+
InsertAtEnd: &'a BasicBlock,
2319+
);
2320+
23112321
pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>(
23122322
Builder: &DIBuilder<'a>,
23132323
Name: *const c_char,

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12521252
for statement in &data.statements {
12531253
self.codegen_statement(bx, statement);
12541254
}
1255+
self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos);
12551256

12561257
let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
12571258
if let MergingSucc::False = merging_succ {

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,52 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
253253
spill_slot
254254
}
255255

256+
pub(crate) fn debug_new_value_to_local(
257+
&self,
258+
bx: &mut Bx,
259+
local: mir::Local,
260+
base: PlaceValue<Bx::Value>,
261+
layout: TyAndLayout<'tcx>,
262+
projection: &[mir::PlaceElem<'tcx>],
263+
) {
264+
let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
265+
if !full_debug_info {
266+
return;
267+
}
268+
269+
let vars = match &self.per_local_var_debug_info {
270+
Some(per_local) => &per_local[local],
271+
None => return,
272+
};
273+
274+
for var in vars.iter().cloned() {
275+
self.debug_new_value_to_local_as_var(bx, base, layout, projection, var);
276+
}
277+
}
278+
279+
fn debug_new_value_to_local_as_var(
280+
&self,
281+
bx: &mut Bx,
282+
base: PlaceValue<Bx::Value>,
283+
layout: TyAndLayout<'tcx>,
284+
projection: &[mir::PlaceElem<'tcx>],
285+
var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
286+
) {
287+
let Some(dbg_var) = var.dbg_var else { return };
288+
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
289+
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
290+
calculate_debuginfo_offset(bx, projection, layout);
291+
bx.dbg_var_addr(
292+
dbg_var,
293+
dbg_loc,
294+
false,
295+
base.llval,
296+
direct_offset,
297+
&indirect_offsets,
298+
var.fragment,
299+
);
300+
}
301+
256302
/// Apply debuginfo and/or name, after creating the `alloca` for a local,
257303
/// or initializing the local with an operand (whichever applies).
258304
pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
@@ -421,6 +467,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
421467
bx.dbg_var_addr(
422468
dbg_var,
423469
dbg_loc,
470+
true,
424471
alloca.val.llval,
425472
Size::ZERO,
426473
&[Size::ZERO],
@@ -430,6 +477,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
430477
bx.dbg_var_addr(
431478
dbg_var,
432479
dbg_loc,
480+
true,
433481
base.val.llval,
434482
direct_offset,
435483
&indirect_offsets,
@@ -455,7 +503,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
455503
let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
456504
bx.clear_dbg_loc();
457505

458-
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment);
506+
bx.dbg_var_addr(dbg_var, dbg_loc, true, base.val.llval, Size::ZERO, &[], fragment);
459507
}
460508
}
461509
}

compiler/rustc_codegen_ssa/src/mir/statement.rs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
use rustc_middle::mir::{self, NonDivergingIntrinsic};
2-
use rustc_middle::span_bug;
1+
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
2+
use rustc_middle::{bug, span_bug};
33
use tracing::instrument;
44

55
use super::{FunctionCx, LocalRef};
6+
use crate::common::TypeKind;
7+
use crate::mir::operand::OperandValue;
8+
use crate::mir::place::PlaceRef;
69
use crate::traits::*;
710

811
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
912
#[instrument(level = "debug", skip(self, bx))]
1013
pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
14+
self.codegen_stmt_debuginfos(bx, &statement.debuginfos);
1115
self.set_debug_loc(bx, statement.source_info);
1216
match statement.kind {
1317
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
@@ -101,4 +105,66 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
101105
| mir::StatementKind::Nop => {}
102106
}
103107
}
108+
109+
pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) {
110+
match debuginfo {
111+
StmtDebugInfo::AssignRef(dest, place) => {
112+
let place_ref = match self.locals[place.local] {
113+
LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => {
114+
Some(place_ref)
115+
}
116+
LocalRef::Operand(operand_ref) => match operand_ref.val {
117+
OperandValue::Immediate(v) => {
118+
Some(PlaceRef::new_sized(v, operand_ref.layout))
119+
}
120+
OperandValue::Ref(_)
121+
| OperandValue::Pair(_, _)
122+
| OperandValue::ZeroSized => None,
123+
},
124+
LocalRef::PendingOperand => None,
125+
}
126+
.filter(|place_ref| {
127+
// Drop unsupported projections.
128+
// FIXME: Add a test case.
129+
place.projection.iter().all(|p| p.can_use_in_debuginfo()) &&
130+
// Only pointers can calculate addresses.
131+
bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer
132+
});
133+
let (val, layout, projection) =
134+
match (place_ref, place.is_indirect_first_projection()) {
135+
(Some(place_ref), false) => {
136+
(place_ref.val, place_ref.layout, place.projection.as_slice())
137+
}
138+
(Some(place_ref), true) => {
139+
let projected_ty =
140+
place_ref.layout.ty.builtin_deref(true).unwrap_or_else(|| {
141+
bug!("deref of non-pointer {:?}", place_ref)
142+
});
143+
let layout = bx.cx().layout_of(projected_ty);
144+
(place_ref.val, layout, &place.projection[1..])
145+
}
146+
_ => {
147+
// If the address cannot be computed, use poison to indicate that the value has been optimized out.
148+
let ty = self.monomorphize(self.mir.local_decls[*dest].ty);
149+
let layout = bx.cx().layout_of(ty);
150+
let to_backend_ty = bx.cx().immediate_backend_type(layout);
151+
let place_ref =
152+
PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
153+
(place_ref.val, layout, [].as_slice())
154+
}
155+
};
156+
self.debug_new_value_to_local(bx, *dest, val, layout, projection);
157+
}
158+
}
159+
}
160+
161+
pub(crate) fn codegen_stmt_debuginfos(
162+
&mut self,
163+
bx: &mut Bx,
164+
debuginfos: &[StmtDebugInfo<'tcx>],
165+
) {
166+
for debuginfo in debuginfos {
167+
self.codegen_stmt_debuginfo(bx, debuginfo);
168+
}
169+
}
104170
}

compiler/rustc_codegen_ssa/src/traits/debuginfo.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
7171
&mut self,
7272
dbg_var: Self::DIVariable,
7373
dbg_loc: Self::DILocation,
74-
variable_alloca: Self::Value,
74+
is_declared: bool,
75+
val: Self::Value,
7576
direct_offset: Size,
7677
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
7778
indirect_offsets: &[Size],

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ using namespace llvm::object;
5858
// This opcode is an LLVM detail that could hypothetically change (?), so
5959
// verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM.
6060
static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000);
61+
static_assert(dwarf::DW_OP_stack_value == 0x9f);
6162

6263
// LLVMAtomicOrdering is already an enum - don't create another
6364
// one.
@@ -1241,6 +1242,18 @@ LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V,
12411242
DebugLoc(cast<MDNode>(unwrap(DL))), unwrap(InsertAtEnd));
12421243
}
12431244

1245+
extern "C" void
1246+
LLVMRustDIBuilderInsertDbgValueAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V,
1247+
LLVMMetadataRef VarInfo, uint64_t *AddrOps,
1248+
unsigned AddrOpsCount, LLVMMetadataRef DL,
1249+
LLVMBasicBlockRef InsertAtEnd) {
1250+
unwrap(Builder)->insertDbgValueIntrinsic(
1251+
unwrap(V), unwrap<DILocalVariable>(VarInfo),
1252+
unwrap(Builder)->createExpression(
1253+
llvm::ArrayRef<uint64_t>(AddrOps, AddrOpsCount)),
1254+
DebugLoc(cast<MDNode>(unwrap(DL))), unwrap(InsertAtEnd));
1255+
}
1256+
12441257
extern "C" LLVMMetadataRef
12451258
LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name,
12461259
size_t NameLen, const uint64_t Value[2],

0 commit comments

Comments
 (0)