Skip to content

Commit bc88895

Browse files
committed
Auto merge of rust-lang#111440 - cjgillot:refprop-debuginfo, r=oli-obk
Allow MIR debuginfo to point to a variable's address MIR optimizations currently do not to operate on borrowed locals. When enabling rust-lang#106285, many borrows will be left as-is because they are used in debuginfo. This pass allows to replace this pattern directly in MIR debuginfo: ```rust a => _1 _1 = &raw? mut? _2 ``` becomes ```rust a => &_2 // No statement to borrow _2. ``` This pass is implemented as a drive-by in ReferencePropagation MIR pass. This transformation allows following following MIR opts to treat _2 as an unborrowed local, and optimize it as such, even in builds with debuginfo. In codegen, when encountering `a => &..&_2`, we create a list of allocas: ```llvm store ptr %_2.dbg.spill, ptr %a.ref0.dbg.spill store ptr %a.ref0.dbg.spill, ptr %a.ref1.dbg.spill ... call void `@llvm.dbg.declare(metadata` ptr %a.ref{n}.dbg.spill, /* ... */) ``` Caveat: this transformation looses the exact type, we do not differentiate `a` as a immutable, mutable reference or a raw pointer. Everything is declared to `*mut` to codegen. I'm not convinced this is a blocker.
2 parents ad6ab11 + 8fb888d commit bc88895

26 files changed

+1536
-652
lines changed

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+101-49
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
88
use rustc_session::config::DebugInfo;
99
use rustc_span::symbol::{kw, Symbol};
1010
use rustc_span::{BytePos, Span};
11-
use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx};
11+
use rustc_target::abi::{Abi, FieldIdx, FieldsShape, Size, VariantIdx};
1212

1313
use super::operand::{OperandRef, OperandValue};
1414
use super::place::PlaceRef;
@@ -41,6 +41,9 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
4141

4242
/// `.place.projection` from `mir::VarDebugInfo`.
4343
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
44+
45+
/// `references` from `mir::VarDebugInfo`.
46+
pub references: u8,
4447
}
4548

4649
#[derive(Clone, Copy, Debug)]
@@ -80,6 +83,7 @@ trait DebugInfoOffsetLocation<'tcx, Bx> {
8083
fn deref(&self, bx: &mut Bx) -> Self;
8184
fn layout(&self) -> TyAndLayout<'tcx>;
8285
fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self;
86+
fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self;
8387
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
8488
}
8589

@@ -98,6 +102,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
98102
PlaceRef::project_field(*self, bx, field.index())
99103
}
100104

105+
fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self {
106+
let lloffset = bx.cx().const_usize(offset);
107+
self.project_index(bx, lloffset)
108+
}
109+
101110
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
102111
self.project_downcast(bx, variant)
103112
}
@@ -120,6 +129,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
120129
self.field(bx.cx(), field.index())
121130
}
122131

132+
fn project_constant_index(&self, bx: &mut Bx, index: u64) -> Self {
133+
self.field(bx.cx(), index as usize)
134+
}
135+
123136
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
124137
self.for_variant(bx.cx(), variant)
125138
}
@@ -165,6 +178,18 @@ fn calculate_debuginfo_offset<
165178
mir::ProjectionElem::Downcast(_, variant) => {
166179
place = place.downcast(bx, variant);
167180
}
181+
mir::ProjectionElem::ConstantIndex {
182+
offset: index,
183+
min_length: _,
184+
from_end: false,
185+
} => {
186+
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
187+
let FieldsShape::Array { stride, count: _ } = place.layout().fields else {
188+
span_bug!(var.source_info.span, "ConstantIndex on non-array type {:?}", place.layout())
189+
};
190+
*offset += stride * index;
191+
place = place.project_constant_index(bx, index);
192+
}
168193
_ => {
169194
// Sanity check for `can_use_in_debuginfo`.
170195
debug_assert!(!elem.can_use_in_debuginfo());
@@ -293,6 +318,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
293318
dbg_var,
294319
fragment: None,
295320
projection: ty::List::empty(),
321+
references: 0,
296322
})
297323
}
298324
} else {
@@ -358,55 +384,74 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
358384
let vars = vars.iter().cloned().chain(fallback_var);
359385

360386
for var in vars {
361-
let Some(dbg_var) = var.dbg_var else { continue };
362-
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
363-
364-
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
365-
calculate_debuginfo_offset(bx, local, &var, base.layout);
366-
367-
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
368-
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
369-
// not DWARF and LLVM doesn't support translating the resulting
370-
// [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
371-
// Creating extra allocas on the stack makes the resulting debug info simple enough
372-
// that LLVM can generate correct CodeView records and thus the values appear in the
373-
// debugger. (#83709)
374-
let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
375-
&& self.mir.local_kind(local) == mir::LocalKind::Arg
376-
// LLVM can handle simple things but anything more complex than just a direct
377-
// offset or one indirect offset of 0 is too complex for it to generate CV records
378-
// correctly.
379-
&& (direct_offset != Size::ZERO
380-
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
381-
382-
if should_create_individual_allocas {
383-
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
384-
calculate_debuginfo_offset(bx, local, &var, base);
385-
386-
// Create a variable which will be a pointer to the actual value
387-
let ptr_ty = bx
388-
.tcx()
389-
.mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty });
390-
let ptr_layout = bx.layout_of(ptr_ty);
391-
let alloca = PlaceRef::alloca(bx, ptr_layout);
392-
bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill"));
393-
394-
// Write the pointer to the variable
395-
bx.store(place.llval, alloca.llval, alloca.align);
396-
397-
// Point the debug info to `*alloca` for the current variable
398-
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
399-
} else {
400-
bx.dbg_var_addr(
401-
dbg_var,
402-
dbg_loc,
403-
base.llval,
404-
direct_offset,
405-
&indirect_offsets,
406-
None,
407-
);
387+
self.debug_introduce_local_as_var(bx, local, base, var);
388+
}
389+
}
390+
391+
fn debug_introduce_local_as_var(
392+
&self,
393+
bx: &mut Bx,
394+
local: mir::Local,
395+
mut base: PlaceRef<'tcx, Bx::Value>,
396+
var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
397+
) {
398+
let Some(dbg_var) = var.dbg_var else { return };
399+
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
400+
401+
let DebugInfoOffset { mut direct_offset, indirect_offsets, result: _ } =
402+
calculate_debuginfo_offset(bx, local, &var, base.layout);
403+
let mut indirect_offsets = &indirect_offsets[..];
404+
405+
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
406+
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
407+
// not DWARF and LLVM doesn't support translating the resulting
408+
// [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
409+
// Creating extra allocas on the stack makes the resulting debug info simple enough
410+
// that LLVM can generate correct CodeView records and thus the values appear in the
411+
// debugger. (#83709)
412+
let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
413+
&& self.mir.local_kind(local) == mir::LocalKind::Arg
414+
// LLVM can handle simple things but anything more complex than just a direct
415+
// offset or one indirect offset of 0 is too complex for it to generate CV records
416+
// correctly.
417+
&& (direct_offset != Size::ZERO || !matches!(indirect_offsets, [Size::ZERO] | []));
418+
419+
let create_alloca = |bx: &mut Bx, place: PlaceRef<'tcx, Bx::Value>, refcount| {
420+
// Create a variable which will be a pointer to the actual value
421+
let ptr_ty = bx
422+
.tcx()
423+
.mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty });
424+
let ptr_layout = bx.layout_of(ptr_ty);
425+
let alloca = PlaceRef::alloca(bx, ptr_layout);
426+
bx.set_var_name(alloca.llval, &format!("{}.ref{}.dbg.spill", var.name, refcount));
427+
428+
// Write the pointer to the variable
429+
bx.store(place.llval, alloca.llval, alloca.align);
430+
431+
// Point the debug info to `*alloca` for the current variable
432+
alloca
433+
};
434+
435+
if var.references > 0 {
436+
base = calculate_debuginfo_offset(bx, local, &var, base).result;
437+
438+
// Point the debug info to `&...&base == alloca` for the current variable
439+
for refcount in 0..var.references {
440+
base = create_alloca(bx, base, refcount);
408441
}
442+
443+
direct_offset = Size::ZERO;
444+
indirect_offsets = &[];
445+
} else if should_create_individual_allocas {
446+
let place = calculate_debuginfo_offset(bx, local, &var, base).result;
447+
448+
// Point the debug info to `*alloca` for the current variable
449+
base = create_alloca(bx, place, 0);
450+
direct_offset = Size::ZERO;
451+
indirect_offsets = &[Size::ZERO];
409452
}
453+
454+
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, indirect_offsets, None);
410455
}
411456

412457
pub fn debug_introduce_locals(&self, bx: &mut Bx) {
@@ -439,7 +484,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
439484
};
440485

441486
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
442-
let (var_ty, var_kind) = match var.value {
487+
let (mut var_ty, var_kind) = match var.value {
443488
mir::VarDebugInfoContents::Place(place) => {
444489
let var_ty = self.monomorphized_place_ty(place.as_ref());
445490
let var_kind = if let Some(arg_index) = var.argument_index
@@ -476,6 +521,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
476521
}
477522
};
478523

524+
for _ in 0..var.references {
525+
var_ty =
526+
bx.tcx().mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty });
527+
}
528+
479529
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
480530
});
481531

@@ -487,6 +537,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
487537
dbg_var,
488538
fragment: None,
489539
projection: place.projection,
540+
references: var.references,
490541
});
491542
}
492543
mir::VarDebugInfoContents::Const(c) => {
@@ -540,6 +591,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
540591
Some(fragment_start..fragment_start + fragment_layout.size)
541592
},
542593
projection: place.projection,
594+
references: var.references,
543595
});
544596
}
545597
}

compiler/rustc_const_eval/src/transform/validate.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
448448
};
449449
match debuginfo.value {
450450
VarDebugInfoContents::Const(_) => {}
451-
VarDebugInfoContents::Place(place) => check_place(place),
451+
VarDebugInfoContents::Place(place) => {
452+
check_place(place);
453+
if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) {
454+
self.fail(
455+
START_BLOCK.start_location(),
456+
format!("debuginfo {:?}, has both ref and deref", debuginfo),
457+
);
458+
}
459+
}
452460
VarDebugInfoContents::Composite { ty, ref fragments } => {
453461
for f in fragments {
454462
check_place(f.contents);

compiler/rustc_middle/src/mir/mod.rs

+31-14
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,10 @@ pub struct VarDebugInfo<'tcx> {
11111111
/// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the
11121112
/// argument number in the original function before it was inlined.
11131113
pub argument_index: Option<u16>,
1114+
1115+
/// The data represents `name` dereferenced `references` times,
1116+
/// and not the direct value.
1117+
pub references: u8,
11141118
}
11151119

11161120
///////////////////////////////////////////////////////////////////////////
@@ -1550,8 +1554,11 @@ impl<V, T> ProjectionElem<V, T> {
15501554
/// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
15511555
pub fn can_use_in_debuginfo(&self) -> bool {
15521556
match self {
1553-
Self::Deref | Self::Downcast(_, _) | Self::Field(_, _) => true,
1554-
Self::ConstantIndex { .. }
1557+
Self::ConstantIndex { from_end: false, .. }
1558+
| Self::Deref
1559+
| Self::Downcast(_, _)
1560+
| Self::Field(_, _) => true,
1561+
Self::ConstantIndex { from_end: true, .. }
15551562
| Self::Index(_)
15561563
| Self::OpaqueCast(_)
15571564
| Self::Subslice { .. } => false,
@@ -1639,18 +1646,7 @@ impl<'tcx> Place<'tcx> {
16391646
return self;
16401647
}
16411648

1642-
let mut v: Vec<PlaceElem<'tcx>>;
1643-
1644-
let new_projections = if self.projection.is_empty() {
1645-
more_projections
1646-
} else {
1647-
v = Vec::with_capacity(self.projection.len() + more_projections.len());
1648-
v.extend(self.projection);
1649-
v.extend(more_projections);
1650-
&v
1651-
};
1652-
1653-
Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
1649+
self.as_ref().project_deeper(more_projections, tcx)
16541650
}
16551651
}
16561652

@@ -1721,6 +1717,27 @@ impl<'tcx> PlaceRef<'tcx> {
17211717
(base, *proj)
17221718
})
17231719
}
1720+
1721+
/// Generates a new place by appending `more_projections` to the existing ones
1722+
/// and interning the result.
1723+
pub fn project_deeper(
1724+
self,
1725+
more_projections: &[PlaceElem<'tcx>],
1726+
tcx: TyCtxt<'tcx>,
1727+
) -> Place<'tcx> {
1728+
let mut v: Vec<PlaceElem<'tcx>>;
1729+
1730+
let new_projections = if self.projection.is_empty() {
1731+
more_projections
1732+
} else {
1733+
v = Vec::with_capacity(self.projection.len() + more_projections.len());
1734+
v.extend(self.projection);
1735+
v.extend(more_projections);
1736+
&v
1737+
};
1738+
1739+
Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
1740+
}
17241741
}
17251742

17261743
impl Debug for Place<'_> {

compiler/rustc_middle/src/mir/pretty.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,13 @@ fn write_scope_tree(
551551
}
552552

553553
let indented_debug_info = format!(
554-
"{0:1$}debug {2} => {3:?};",
555-
INDENT, indent, var_debug_info.name, var_debug_info.value,
554+
"{0:1$}debug {2} => {3:&<4$}{5:?};",
555+
INDENT,
556+
indent,
557+
var_debug_info.name,
558+
"",
559+
var_debug_info.references as usize,
560+
var_debug_info.value,
556561
);
557562

558563
writeln!(

compiler/rustc_middle/src/mir/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ macro_rules! make_mir_visitor {
842842
source_info,
843843
value,
844844
argument_index: _,
845+
references: _,
845846
} = var_debug_info;
846847

847848
self.visit_source_info(source_info);

compiler/rustc_middle/src/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ CloneLiftImpls! {
204204
(),
205205
bool,
206206
usize,
207+
u8,
207208
u16,
208209
u32,
209210
u64,

compiler/rustc_mir_build/src/build/matches/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2241,6 +2241,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22412241
self.var_debug_info.push(VarDebugInfo {
22422242
name,
22432243
source_info: debug_source_info,
2244+
references: 0,
22442245
value: VarDebugInfoContents::Place(for_arm_body.into()),
22452246
argument_index: None,
22462247
});
@@ -2260,6 +2261,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22602261
self.var_debug_info.push(VarDebugInfo {
22612262
name,
22622263
source_info: debug_source_info,
2264+
references: 0,
22632265
value: VarDebugInfoContents::Place(ref_for_guard.into()),
22642266
argument_index: None,
22652267
});

compiler/rustc_mir_build/src/build/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
798798
};
799799
self.var_debug_info.push(VarDebugInfo {
800800
name,
801+
references: 0,
801802
source_info: SourceInfo::outermost(captured_place.var_ident.span),
802803
value: VarDebugInfoContents::Place(use_place),
803804
argument_index: None,
@@ -828,6 +829,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
828829
self.var_debug_info.push(VarDebugInfo {
829830
name,
830831
source_info,
832+
references: 0,
831833
value: VarDebugInfoContents::Place(arg_local.into()),
832834
argument_index: Some(argument_index as u16 + 1),
833835
});

0 commit comments

Comments
 (0)