Skip to content

Commit e99a89c

Browse files
committed
Auto merge of #73210 - wesleywiser:consts_in_debuginfo, r=oli-obk
[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.
2 parents e261649 + 0b18ed8 commit e99a89c

File tree

16 files changed

+398
-79
lines changed

16 files changed

+398
-79
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

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

14101410
let state_arg = mir::Local::new(1);
14111411
for var in &body.var_debug_info {
1412-
if var.place.local != state_arg {
1412+
let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue };
1413+
if place.local != state_arg {
14131414
continue;
14141415
}
1415-
match var.place.projection[..] {
1416+
match place.projection[..] {
14161417
[
14171418
// Deref of the `Pin<&mut Self>` state argument.
14181419
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

+73-32
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_span::symbol::{kw, Symbol};
88
use rustc_span::{BytePos, Span};
99
use rustc_target::abi::{LayoutOf, Size};
1010

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

@@ -116,6 +116,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
116116
span
117117
}
118118

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

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

242250
LocalRef::Place(place) => *place,
@@ -308,6 +316,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
308316
/// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
309317
pub fn compute_per_local_var_debug_info(
310318
&self,
319+
bx: &mut Bx,
311320
) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
312321
let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
313322

@@ -322,31 +331,63 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
322331
} else {
323332
None
324333
};
334+
325335
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
326-
let place = var.place;
327-
let var_ty = self.monomorphized_place_ty(place.as_ref());
328-
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
329-
&& place.projection.is_empty()
330-
&& var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
331-
{
332-
let arg_index = place.local.index() - 1;
333-
334-
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
335-
// offset in closures to account for the hidden environment?
336-
// Also, is this `+ 1` needed at all?
337-
VariableKind::ArgumentVariable(arg_index + 1)
338-
} else {
339-
VariableKind::LocalVariable
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+
}
340358
};
359+
341360
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
342361
});
343362

344-
per_local[var.place.local].push(PerLocalVarDebugInfo {
345-
name: var.name,
346-
source_info: var.source_info,
347-
dbg_var,
348-
projection: var.place.projection,
349-
});
363+
match var.value {
364+
mir::VarDebugInfoContents::Place(place) => {
365+
per_local[place.local].push(PerLocalVarDebugInfo {
366+
name: var.name,
367+
source_info: var.source_info,
368+
dbg_var,
369+
projection: place.projection,
370+
});
371+
}
372+
mir::VarDebugInfoContents::Const(c) => {
373+
if let Some(dbg_var) = dbg_var {
374+
let dbg_loc = match self.dbg_loc(var.source_info) {
375+
Some(dbg_loc) => dbg_loc,
376+
None => continue,
377+
};
378+
379+
if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
380+
let base = Self::spill_operand_to_stack(
381+
&operand,
382+
Some(var.name.to_string()),
383+
bx,
384+
);
385+
386+
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);
387+
}
388+
}
389+
}
390+
}
350391
}
351392
Some(per_local)
352393
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
186186
caller_location: None,
187187
};
188188

189-
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info();
189+
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx);
190190

191191
for const_ in &mir.required_consts {
192192
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
@@ -1060,6 +1060,23 @@ impl<'tcx> LocalDecl<'tcx> {
10601060
}
10611061
}
10621062

1063+
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
1064+
pub enum VarDebugInfoContents<'tcx> {
1065+
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
1066+
/// based on a `Local`, not a `Static`, and contains no indexing.
1067+
Place(Place<'tcx>),
1068+
Const(Constant<'tcx>),
1069+
}
1070+
1071+
impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
1072+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1073+
match self {
1074+
VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
1075+
VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
1076+
}
1077+
}
1078+
}
1079+
10631080
/// Debug information pertaining to a user variable.
10641081
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
10651082
pub struct VarDebugInfo<'tcx> {
@@ -1071,9 +1088,7 @@ pub struct VarDebugInfo<'tcx> {
10711088
pub source_info: SourceInfo,
10721089

10731090
/// Where the data for this user variable is to be found.
1074-
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
1075-
/// based on a `Local`, not a `Static`, and contains no indexing.
1076-
pub place: Place<'tcx>,
1091+
pub value: VarDebugInfoContents<'tcx>,
10771092
}
10781093

10791094
///////////////////////////////////////////////////////////////////////////

compiler/rustc_middle/src/mir/visit.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -829,16 +829,20 @@ macro_rules! make_mir_visitor {
829829
let VarDebugInfo {
830830
name: _,
831831
source_info,
832-
place,
832+
value,
833833
} = var_debug_info;
834834

835835
self.visit_source_info(source_info);
836836
let location = START_BLOCK.start_location();
837-
self.visit_place(
838-
place,
839-
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
840-
location,
841-
);
837+
match value {
838+
VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
839+
VarDebugInfoContents::Place(place) =>
840+
self.visit_place(
841+
place,
842+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
843+
location
844+
),
845+
}
842846
}
843847

844848
fn super_source_scope(&mut self,

compiler/rustc_mir/src/borrow_check/mod.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
1212
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
1313
use rustc_middle::mir::{
1414
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
15-
PlaceRef,
15+
PlaceRef, VarDebugInfoContents,
1616
};
1717
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
1818
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
@@ -135,19 +135,21 @@ fn do_mir_borrowck<'a, 'tcx>(
135135

136136
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
137137
for var_debug_info in &input_body.var_debug_info {
138-
if let Some(local) = var_debug_info.place.as_local() {
139-
if let Some(prev_name) = local_names[local] {
140-
if var_debug_info.name != prev_name {
141-
span_bug!(
142-
var_debug_info.source_info.span,
143-
"local {:?} has many names (`{}` vs `{}`)",
144-
local,
145-
prev_name,
146-
var_debug_info.name
147-
);
138+
if let VarDebugInfoContents::Place(place) = var_debug_info.value {
139+
if let Some(local) = place.as_local() {
140+
if let Some(prev_name) = local_names[local] {
141+
if var_debug_info.name != prev_name {
142+
span_bug!(
143+
var_debug_info.source_info.span,
144+
"local {:?} has many names (`{}` vs `{}`)",
145+
local,
146+
prev_name,
147+
var_debug_info.name
148+
);
149+
}
148150
}
151+
local_names[local] = Some(var_debug_info.name);
149152
}
150-
local_names[local] = Some(var_debug_info.name);
151153
}
152154
}
153155

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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+
if !tcx.sess.opts.debugging_opts.unsound_mir_opts {
20+
return;
21+
}
22+
23+
trace!("running ConstDebugInfo on {:?}", body.source);
24+
25+
for (local, constant) in find_optimization_oportunities(body) {
26+
for debuginfo in &mut body.var_debug_info {
27+
if let VarDebugInfoContents::Place(p) = debuginfo.value {
28+
if p.local == local && p.projection.is_empty() {
29+
trace!(
30+
"changing debug info for {:?} from place {:?} to constant {:?}",
31+
debuginfo.name,
32+
p,
33+
constant
34+
);
35+
debuginfo.value = VarDebugInfoContents::Const(constant);
36+
}
37+
}
38+
}
39+
}
40+
}
41+
}
42+
43+
struct LocalUseVisitor {
44+
local_mutating_uses: IndexVec<Local, u8>,
45+
local_assignment_locations: IndexVec<Local, Option<Location>>,
46+
}
47+
48+
fn find_optimization_oportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Constant<'tcx>)> {
49+
let mut visitor = LocalUseVisitor {
50+
local_mutating_uses: IndexVec::from_elem(0, &body.local_decls),
51+
local_assignment_locations: IndexVec::from_elem(None, &body.local_decls),
52+
};
53+
54+
visitor.visit_body(body);
55+
56+
let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len());
57+
for debuginfo in &body.var_debug_info {
58+
if let VarDebugInfoContents::Place(p) = debuginfo.value {
59+
if let Some(l) = p.as_local() {
60+
locals_to_debuginfo.insert(l);
61+
}
62+
}
63+
}
64+
65+
let mut eligable_locals = Vec::new();
66+
for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) {
67+
if mutating_uses != 1 || !locals_to_debuginfo.contains(local) {
68+
continue;
69+
}
70+
71+
if let Some(location) = visitor.local_assignment_locations[local] {
72+
let bb = &body[location.block];
73+
74+
// The value is assigned as the result of a call, not a constant
75+
if bb.statements.len() == location.statement_index {
76+
continue;
77+
}
78+
79+
if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) =
80+
&bb.statements[location.statement_index].kind
81+
{
82+
if let Some(local) = p.as_local() {
83+
eligable_locals.push((local, *c));
84+
}
85+
}
86+
}
87+
}
88+
89+
eligable_locals
90+
}
91+
92+
impl<'tcx> Visitor<'tcx> for LocalUseVisitor {
93+
fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
94+
if context.is_mutating_use() {
95+
self.local_mutating_uses[*local] = self.local_mutating_uses[*local].saturating_add(1);
96+
97+
if context.is_place_assignment() {
98+
self.local_assignment_locations[*local] = Some(location);
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)