Skip to content

Commit 8b6b15b

Browse files
committed
Auto merge of #142771 - dianqk:mir-stmt-debuginfo, r=cjgillot
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 doesn't drop debuginfos in `MatchBranchSimplification`, because LLVM also pick one branch for it.
2 parents dd09100 + c2a03ce commit 8b6b15b

File tree

74 files changed

+2798
-581
lines changed

Some content is hidden

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

74 files changed

+2798
-581
lines changed

compiler/rustc_codegen_gcc/src/debuginfo.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,24 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
2929
_variable_alloca: Self::Value,
3030
_direct_offset: Size,
3131
_indirect_offsets: &[Size],
32-
_fragment: Option<Range<Size>>,
32+
_fragment: &Option<Range<Size>>,
3333
) {
3434
// FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here.
3535
#[cfg(feature = "master")]
3636
_variable_alloca.set_location(_dbg_loc);
3737
}
3838

39+
fn dbg_var_value(
40+
&mut self,
41+
_dbg_var: Self::DIVariable,
42+
_dbg_loc: Self::DILocation,
43+
_value: Self::Value,
44+
_direct_offset: Size,
45+
_indirect_offsets: &[Size],
46+
_fragment: &Option<Range<Size>>,
47+
) {
48+
}
49+
3950
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
4051
// TODO(antoyo): insert reference to gdb debug scripts section global.
4152
}

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: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
156156
variable_alloca: Self::Value,
157157
direct_offset: Size,
158158
indirect_offsets: &[Size],
159-
fragment: Option<Range<Size>>,
159+
fragment: &Option<Range<Size>>,
160160
) {
161161
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};
162162

@@ -187,7 +187,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
187187
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
188188
};
189189
unsafe {
190-
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
191190
llvm::LLVMDIBuilderInsertDeclareRecordAtEnd(
192191
di_builder,
193192
variable_alloca,
@@ -199,6 +198,56 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
199198
};
200199
}
201200

201+
fn dbg_var_value(
202+
&mut self,
203+
dbg_var: &'ll DIVariable,
204+
dbg_loc: &'ll DILocation,
205+
value: Self::Value,
206+
direct_offset: Size,
207+
indirect_offsets: &[Size],
208+
fragment: &Option<Range<Size>>,
209+
) {
210+
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value};
211+
212+
// Convert the direct and indirect offsets and fragment byte range to address ops.
213+
let mut addr_ops = SmallVec::<[u64; 8]>::new();
214+
215+
if direct_offset.bytes() > 0 {
216+
addr_ops.push(DW_OP_plus_uconst);
217+
addr_ops.push(direct_offset.bytes() as u64);
218+
addr_ops.push(DW_OP_stack_value);
219+
}
220+
for &offset in indirect_offsets {
221+
addr_ops.push(DW_OP_deref);
222+
if offset.bytes() > 0 {
223+
addr_ops.push(DW_OP_plus_uconst);
224+
addr_ops.push(offset.bytes() as u64);
225+
}
226+
}
227+
if let Some(fragment) = fragment {
228+
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
229+
// offset and size, both of them in bits.
230+
addr_ops.push(DW_OP_LLVM_fragment);
231+
addr_ops.push(fragment.start.bits() as u64);
232+
addr_ops.push((fragment.end - fragment.start).bits() as u64);
233+
}
234+
235+
let di_builder = DIB(self.cx());
236+
let addr_expr = unsafe {
237+
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
238+
};
239+
unsafe {
240+
llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd(
241+
di_builder,
242+
value,
243+
dbg_var,
244+
addr_expr,
245+
dbg_loc,
246+
self.llbb(),
247+
);
248+
}
249+
}
250+
202251
fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) {
203252
unsafe {
204253
llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc);

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,15 @@ unsafe extern "C" {
19911991
Block: &'ll BasicBlock,
19921992
) -> &'ll DbgRecord;
19931993

1994+
pub(crate) fn LLVMDIBuilderInsertDbgValueRecordAtEnd<'ll>(
1995+
Builder: &DIBuilder<'ll>,
1996+
Val: &'ll Value,
1997+
VarInfo: &'ll Metadata,
1998+
Expr: &'ll Metadata,
1999+
DebugLoc: &'ll Metadata,
2000+
Block: &'ll BasicBlock,
2001+
) -> &'ll DbgRecord;
2002+
19942003
pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>(
19952004
Builder: &DIBuilder<'ll>,
19962005
Scope: &'ll Metadata,

compiler/rustc_codegen_ssa/src/mir/analyze.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,10 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
260260
PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(),
261261
}
262262
}
263+
264+
fn visit_statement_debuginfo(&mut self, _: &mir::StmtDebugInfo<'tcx>, _: Location) {
265+
// Debuginfo does not generate actual code.
266+
}
263267
}
264268

265269
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13201320
for statement in &data.statements {
13211321
self.codegen_statement(bx, statement);
13221322
}
1323+
self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos);
13231324

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

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
253253
spill_slot
254254
}
255255

256+
// Indicates that local is set to a new value. The `layout` and `projection` are used to
257+
// calculate the offset.
258+
pub(crate) fn debug_new_val_to_local(
259+
&self,
260+
bx: &mut Bx,
261+
local: mir::Local,
262+
base: PlaceRef<'tcx, Bx::Value>,
263+
projection: &[mir::PlaceElem<'tcx>],
264+
) {
265+
let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
266+
if !full_debug_info {
267+
return;
268+
}
269+
270+
let vars = match &self.per_local_var_debug_info {
271+
Some(per_local) => &per_local[local],
272+
None => return,
273+
};
274+
275+
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
276+
calculate_debuginfo_offset(bx, projection, base.layout);
277+
for var in vars.iter() {
278+
let Some(dbg_var) = var.dbg_var else {
279+
continue;
280+
};
281+
let Some(dbg_loc) = self.dbg_loc(var.source_info) else {
282+
continue;
283+
};
284+
bx.dbg_var_value(
285+
dbg_var,
286+
dbg_loc,
287+
base.val.llval,
288+
direct_offset,
289+
&indirect_offsets,
290+
&var.fragment,
291+
);
292+
}
293+
}
294+
295+
pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) {
296+
let ty = self.monomorphize(self.mir.local_decls[local].ty);
297+
let layout = bx.cx().layout_of(ty);
298+
let to_backend_ty = bx.cx().immediate_backend_type(layout);
299+
let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
300+
self.debug_new_val_to_local(bx, local, place_ref, &[]);
301+
}
302+
256303
/// Apply debuginfo and/or name, after creating the `alloca` for a local,
257304
/// or initializing the local with an operand (whichever applies).
258305
pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
@@ -424,7 +471,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
424471
alloca.val.llval,
425472
Size::ZERO,
426473
&[Size::ZERO],
427-
var.fragment,
474+
&var.fragment,
428475
);
429476
} else {
430477
bx.dbg_var_addr(
@@ -433,7 +480,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
433480
base.val.llval,
434481
direct_offset,
435482
&indirect_offsets,
436-
var.fragment,
483+
&var.fragment,
437484
);
438485
}
439486
}
@@ -455,7 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
455502
let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
456503
bx.clear_dbg_loc();
457504

458-
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment);
505+
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment);
459506
}
460507
}
461508
}

compiler/rustc_codegen_ssa/src/mir/operand.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,23 @@ pub enum OperandValue<V> {
7171
}
7272

7373
impl<V: CodegenObject> OperandValue<V> {
74+
/// Return the data pointer and optional metadata as backend values
75+
/// if this value can be treat as a pointer.
76+
pub(crate) fn try_pointer_parts(self) -> Option<(V, Option<V>)> {
77+
match self {
78+
OperandValue::Immediate(llptr) => Some((llptr, None)),
79+
OperandValue::Pair(llptr, llextra) => Some((llptr, Some(llextra))),
80+
OperandValue::Ref(_) | OperandValue::ZeroSized => None,
81+
}
82+
}
83+
7484
/// Treat this value as a pointer and return the data pointer and
7585
/// optional metadata as backend values.
7686
///
7787
/// If you're making a place, use [`Self::deref`] instead.
7888
pub(crate) fn pointer_parts(self) -> (V, Option<V>) {
79-
match self {
80-
OperandValue::Immediate(llptr) => (llptr, None),
81-
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
82-
_ => bug!("OperandValue cannot be a pointer: {self:?}"),
83-
}
89+
self.try_pointer_parts()
90+
.unwrap_or_else(|| bug!("OperandValue cannot be a pointer: {self:?}"))
8491
}
8592

8693
/// Treat this value as a pointer and return the place to which it points.

compiler/rustc_codegen_ssa/src/mir/statement.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_middle::mir::{self, NonDivergingIntrinsic};
1+
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
22
use rustc_middle::span_bug;
33
use tracing::instrument;
44

@@ -8,6 +8,7 @@ use crate::traits::*;
88
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
99
#[instrument(level = "debug", skip(self, bx))]
1010
pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
11+
self.codegen_stmt_debuginfos(bx, &statement.debuginfos);
1112
self.set_debug_loc(bx, statement.source_info);
1213
match statement.kind {
1314
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
@@ -101,4 +102,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
101102
| mir::StatementKind::Nop => {}
102103
}
103104
}
105+
106+
pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) {
107+
match debuginfo {
108+
StmtDebugInfo::AssignRef(dest, place) => {
109+
let local_ref = match self.locals[place.local] {
110+
// For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place.
111+
// The place is an indirect pointer, we can refer to it directly.
112+
LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())),
113+
// For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`.
114+
// The deref projection is no-op here.
115+
LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => {
116+
Some((operand_ref.deref(bx.cx()), &place.projection[1..]))
117+
}
118+
// For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`,
119+
// we cannot get the address.
120+
// N.B. `non_ssa_locals` returns that this is an SSA local.
121+
LocalRef::Operand(_) => None,
122+
LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None,
123+
}
124+
.filter(|(_, projection)| {
125+
// Drop unsupported projections.
126+
projection.iter().all(|p| p.can_use_in_debuginfo())
127+
});
128+
if let Some((base, projection)) = local_ref {
129+
self.debug_new_val_to_local(bx, *dest, base, projection);
130+
} else {
131+
// If the address cannot be calculated, use poison to indicate that the value has been optimized out.
132+
self.debug_poison_to_local(bx, *dest);
133+
}
134+
}
135+
StmtDebugInfo::InvalidAssign(local) => {
136+
self.debug_poison_to_local(bx, *local);
137+
}
138+
}
139+
}
140+
141+
pub(crate) fn codegen_stmt_debuginfos(
142+
&mut self,
143+
bx: &mut Bx,
144+
debuginfos: &[StmtDebugInfo<'tcx>],
145+
) {
146+
for debuginfo in debuginfos {
147+
self.codegen_stmt_debuginfo(bx, debuginfo);
148+
}
149+
}
104150
}

compiler/rustc_codegen_ssa/src/traits/debuginfo.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,19 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
7777
indirect_offsets: &[Size],
7878
// Byte range in the `dbg_var` covered by this fragment,
7979
// if this is a fragment of a composite `DIVariable`.
80-
fragment: Option<Range<Size>>,
80+
fragment: &Option<Range<Size>>,
81+
);
82+
fn dbg_var_value(
83+
&mut self,
84+
dbg_var: Self::DIVariable,
85+
dbg_loc: Self::DILocation,
86+
value: Self::Value,
87+
direct_offset: Size,
88+
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
89+
indirect_offsets: &[Size],
90+
// Byte range in the `dbg_var` covered by this fragment,
91+
// if this is a fragment of a composite `DIVariable`.
92+
fragment: &Option<Range<Size>>,
8193
);
8294
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
8395
fn clear_dbg_loc(&mut self);

0 commit comments

Comments
 (0)