Skip to content

Commit 52640f2

Browse files
committed
[mir-opt] Allow debuginfo to be generated for a constant or a Place
Prior to this commit, debuginfo was always generated by mapping a name to a Place. This has the side-effect that `SimplifyLocals` cannot remove locals that are only used for debuginfo because their other uses have been const-propagated. To allow these locals to be removed, we now allow debuginfo to point to a constant value. The `ConstProp` pass detects when debuginfo points to a local with a known constant value and replaces it with the value. This allows the later `SimplifyLocals` pass to remove the local.
1 parent 31530e5 commit 52640f2

22 files changed

+410
-134
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1416,10 +1416,11 @@ fn generator_layout_and_saved_local_names(
14161416

14171417
let state_arg = mir::Local::new(1);
14181418
for var in &body.var_debug_info {
1419-
if var.place.local != state_arg {
1419+
let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue };
1420+
if place.local != state_arg {
14201421
continue;
14211422
}
1422-
match var.place.projection[..] {
1423+
match place.projection[..] {
14231424
[
14241425
// Deref of the `Pin<&mut Self>` state argument.
14251426
mir::ProjectionElem::Field(..),

compiler/rustc_codegen_ssa/src/mir/constant.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use super::FunctionCx;
1111

1212
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
1313
pub fn eval_mir_constant_to_operand(
14-
&mut self,
14+
&self,
1515
bx: &mut Bx,
1616
constant: &mir::Constant<'tcx>,
1717
) -> Result<OperandRef<'tcx, Bx::Value>, ErrorHandled> {
@@ -21,7 +21,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2121
}
2222

2323
pub fn eval_mir_constant(
24-
&mut self,
24+
&self,
2525
constant: &mir::Constant<'tcx>,
2626
) -> Result<ConstValue<'tcx>, ErrorHandled> {
2727
match self.monomorphize(&constant.literal).val {

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+67-33
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_span::symbol::{kw, Symbol};
99
use rustc_span::{BytePos, Span};
1010
use rustc_target::abi::{LayoutOf, Size};
1111

12-
use super::operand::OperandValue;
12+
use super::operand::{OperandRef, OperandValue};
1313
use super::place::PlaceRef;
1414
use super::{FunctionCx, LocalRef};
1515

@@ -111,6 +111,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
111111
}
112112
}
113113

114+
fn spill_operand_to_stack(
115+
operand: &OperandRef<'tcx, Bx::Value>,
116+
name: Option<String>,
117+
bx: &mut Bx,
118+
) -> PlaceRef<'tcx, Bx::Value> {
119+
// "Spill" the value onto the stack, for debuginfo,
120+
// without forcing non-debuginfo uses of the local
121+
// to also load from the stack every single time.
122+
// FIXME(#68817) use `llvm.dbg.value` instead,
123+
// at least for the cases which LLVM handles correctly.
124+
let spill_slot = PlaceRef::alloca(bx, operand.layout);
125+
if let Some(name) = name {
126+
bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
127+
}
128+
operand.val.store(bx, spill_slot);
129+
spill_slot
130+
}
131+
114132
/// Apply debuginfo and/or name, after creating the `alloca` for a local,
115133
/// or initializing the local with an operand (whichever applies).
116134
pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
@@ -225,17 +243,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
225243
return;
226244
}
227245

228-
// "Spill" the value onto the stack, for debuginfo,
229-
// without forcing non-debuginfo uses of the local
230-
// to also load from the stack every single time.
231-
// FIXME(#68817) use `llvm.dbg.value` instead,
232-
// at least for the cases which LLVM handles correctly.
233-
let spill_slot = PlaceRef::alloca(bx, operand.layout);
234-
if let Some(name) = name {
235-
bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
236-
}
237-
operand.val.store(bx, spill_slot);
238-
spill_slot
246+
Self::spill_operand_to_stack(operand, name, bx)
239247
}
240248

241249
LocalRef::Place(place) => *place,
@@ -310,6 +318,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
310318
/// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
311319
pub fn compute_per_local_var_debug_info(
312320
&self,
321+
bx: &mut Bx,
313322
) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
314323
let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
315324

@@ -324,22 +333,30 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
324333
} else {
325334
(None, var.source_info.span)
326335
};
336+
let (var_ty, var_kind) = match var.value {
337+
mir::VarDebugInfoContents::Place(place) => {
338+
let var_ty = self.monomorphized_place_ty(place.as_ref());
339+
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
340+
&& place.projection.is_empty()
341+
&& var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
342+
{
343+
let arg_index = place.local.index() - 1;
344+
345+
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
346+
// offset in closures to account for the hidden environment?
347+
// Also, is this `+ 1` needed at all?
348+
VariableKind::ArgumentVariable(arg_index + 1)
349+
} else {
350+
VariableKind::LocalVariable
351+
};
352+
(var_ty, var_kind)
353+
}
354+
mir::VarDebugInfoContents::Const(c) => {
355+
let ty = self.monomorphize(&c.literal.ty);
356+
(ty, VariableKind::LocalVariable)
357+
}
358+
};
327359
let dbg_var = scope.map(|scope| {
328-
let place = var.place;
329-
let var_ty = self.monomorphized_place_ty(place.as_ref());
330-
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
331-
&& place.projection.is_empty()
332-
&& var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
333-
{
334-
let arg_index = place.local.index() - 1;
335-
336-
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
337-
// offset in closures to account for the hidden environment?
338-
// Also, is this `+ 1` needed at all?
339-
VariableKind::ArgumentVariable(arg_index + 1)
340-
} else {
341-
VariableKind::LocalVariable
342-
};
343360
self.cx.create_dbg_var(
344361
self.debug_context.as_ref().unwrap(),
345362
var.name,
@@ -350,12 +367,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
350367
)
351368
});
352369

353-
per_local[var.place.local].push(PerLocalVarDebugInfo {
354-
name: var.name,
355-
source_info: var.source_info,
356-
dbg_var,
357-
projection: var.place.projection,
358-
});
370+
match var.value {
371+
mir::VarDebugInfoContents::Place(place) => {
372+
per_local[place.local].push(PerLocalVarDebugInfo {
373+
name: var.name,
374+
source_info: var.source_info,
375+
dbg_var,
376+
projection: place.projection,
377+
});
378+
}
379+
mir::VarDebugInfoContents::Const(c) => {
380+
if let (Some(scope), Some(dbg_var)) = (scope, dbg_var) {
381+
if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
382+
let base = Self::spill_operand_to_stack(
383+
&operand,
384+
Some(var.name.to_string()),
385+
bx,
386+
);
387+
388+
bx.dbg_var_addr(dbg_var, scope, base.llval, Size::ZERO, &[], span);
389+
}
390+
}
391+
}
392+
}
359393
}
360394
Some(per_local)
361395
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
190190
caller_location: None,
191191
};
192192

193-
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info();
193+
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx);
194194

195195
for const_ in &mir.required_consts {
196196
if let Err(err) = fx.eval_mir_constant(const_) {

compiler/rustc_middle/src/mir/mod.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,23 @@ impl<'tcx> LocalDecl<'tcx> {
10741074
}
10751075
}
10761076

1077+
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
1078+
pub enum VarDebugInfoContents<'tcx> {
1079+
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
1080+
/// based on a `Local`, not a `Static`, and contains no indexing.
1081+
Place(Place<'tcx>),
1082+
Const(Constant<'tcx>),
1083+
}
1084+
1085+
impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
1086+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1087+
match self {
1088+
VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
1089+
VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
1090+
}
1091+
}
1092+
}
1093+
10771094
/// Debug information pertaining to a user variable.
10781095
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
10791096
pub struct VarDebugInfo<'tcx> {
@@ -1085,9 +1102,7 @@ pub struct VarDebugInfo<'tcx> {
10851102
pub source_info: SourceInfo,
10861103

10871104
/// Where the data for this user variable is to be found.
1088-
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
1089-
/// based on a `Local`, not a `Static`, and contains no indexing.
1090-
pub place: Place<'tcx>,
1105+
pub value: VarDebugInfoContents<'tcx>,
10911106
}
10921107

10931108
///////////////////////////////////////////////////////////////////////////

compiler/rustc_middle/src/mir/visit.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -796,16 +796,20 @@ macro_rules! make_mir_visitor {
796796
let VarDebugInfo {
797797
name: _,
798798
source_info,
799-
place,
799+
value,
800800
} = var_debug_info;
801801

802802
self.visit_source_info(source_info);
803803
let location = START_BLOCK.start_location();
804-
self.visit_place(
805-
place,
806-
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
807-
location,
808-
);
804+
match value {
805+
VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
806+
VarDebugInfoContents::Place(place) =>
807+
self.visit_place(
808+
place,
809+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
810+
location
811+
),
812+
}
809813
}
810814

811815
fn super_source_scope(&mut self,

compiler/rustc_mir/src/borrow_check/mod.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_index::vec::IndexVec;
1111
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
1212
use rustc_middle::mir::{
1313
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
14-
PlaceRef,
14+
PlaceRef, VarDebugInfoContents,
1515
};
1616
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
1717
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
@@ -133,19 +133,21 @@ fn do_mir_borrowck<'a, 'tcx>(
133133

134134
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
135135
for var_debug_info in &input_body.var_debug_info {
136-
if let Some(local) = var_debug_info.place.as_local() {
137-
if let Some(prev_name) = local_names[local] {
138-
if var_debug_info.name != prev_name {
139-
span_bug!(
140-
var_debug_info.source_info.span,
141-
"local {:?} has many names (`{}` vs `{}`)",
142-
local,
143-
prev_name,
144-
var_debug_info.name
145-
);
136+
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
137+
if let Some(local) = place.as_local() {
138+
if let Some(prev_name) = local_names[local] {
139+
if var_debug_info.name != prev_name {
140+
span_bug!(
141+
var_debug_info.source_info.span,
142+
"local {:?} has many names (`{}` vs `{}`)",
143+
local,
144+
prev_name,
145+
var_debug_info.name
146+
);
147+
}
146148
}
149+
local_names[local] = Some(var_debug_info.name);
147150
}
148-
local_names[local] = Some(var_debug_info.name);
149151
}
150152
}
151153

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! Finds locals which are assigned once to a const and unused except for debuginfo and converts
2+
//! their debuginfo to use the const directly, allowing the local to be removed.
3+
4+
use rustc_middle::{
5+
mir::{
6+
visit::{PlaceContext, Visitor},
7+
Body, Constant, Local, Location, Operand, Rvalue, StatementKind, VarDebugInfoContents,
8+
},
9+
ty::TyCtxt,
10+
};
11+
12+
use crate::transform::MirPass;
13+
use rustc_index::{bit_set::BitSet, vec::IndexVec};
14+
15+
pub struct ConstDebugInfo;
16+
17+
impl<'tcx> MirPass<'tcx> for ConstDebugInfo {
18+
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
19+
trace!("running ConstDebugInfo on {:?}", body.source);
20+
21+
for (local, constant) in find_optimization_oportunities(body) {
22+
for debuginfo in &mut body.var_debug_info {
23+
if let VarDebugInfoContents::Place(p) = debuginfo.value {
24+
if p.local == local && p.projection.is_empty() {
25+
trace!(
26+
"changing debug info for {:?} from place {:?} to constant {:?}",
27+
debuginfo.name,
28+
p,
29+
constant
30+
);
31+
debuginfo.value = VarDebugInfoContents::Const(constant);
32+
}
33+
}
34+
}
35+
}
36+
}
37+
}
38+
39+
struct LocalUseVisitor {
40+
local_mutating_uses: IndexVec<Local, u8>,
41+
local_assignment_locations: IndexVec<Local, Option<Location>>,
42+
}
43+
44+
fn find_optimization_oportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Constant<'tcx>)> {
45+
let mut visitor = LocalUseVisitor {
46+
local_mutating_uses: IndexVec::from_elem(0, &body.local_decls),
47+
local_assignment_locations: IndexVec::from_elem(None, &body.local_decls),
48+
};
49+
50+
visitor.visit_body(body);
51+
52+
let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len());
53+
for debuginfo in &body.var_debug_info {
54+
if let VarDebugInfoContents::Place(p) = debuginfo.value {
55+
if let Some(l) = p.as_local() {
56+
locals_to_debuginfo.insert(l);
57+
}
58+
}
59+
}
60+
61+
let mut eligable_locals = Vec::new();
62+
for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) {
63+
if mutating_uses != 1 || !locals_to_debuginfo.contains(local) {
64+
continue;
65+
}
66+
67+
if let Some(location) = visitor.local_assignment_locations[local] {
68+
let bb = &body[location.block];
69+
70+
// The value is assigned as the result of a call, not a constant
71+
if bb.statements.len() == location.statement_index {
72+
continue;
73+
}
74+
75+
if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) =
76+
&bb.statements[location.statement_index].kind
77+
{
78+
if let Some(local) = p.as_local() {
79+
eligable_locals.push((local, *c));
80+
}
81+
}
82+
}
83+
}
84+
85+
eligable_locals
86+
}
87+
88+
impl<'tcx> Visitor<'tcx> for LocalUseVisitor {
89+
fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
90+
if context.is_mutating_use() {
91+
self.local_mutating_uses[*local] += 1;
92+
93+
if context.is_place_assignment() {
94+
self.local_assignment_locations[*local] = Some(location);
95+
}
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)