Skip to content

Commit 7490ccd

Browse files
committed
Auto merge of #119471 - cjgillot:mir-composite-deref, r=<try>
[perf only] Reimplement references debuginfo as projection. This PR is not meant to be merged: it completely breaks debuginfo, as LLVM does not support implicit pointers in debuginfo yet.
2 parents e78913b + 36fae9e commit 7490ccd

File tree

32 files changed

+2149
-1044
lines changed

32 files changed

+2149
-1044
lines changed

‎compiler/rustc_codegen_ssa/src/mir/debuginfo.rs‎

Lines changed: 85 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxHashMap;
33
use rustc_index::IndexVec;
44
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
55
use rustc_middle::mir;
6-
use rustc_middle::ty;
76
use rustc_middle::ty::layout::TyAndLayout;
87
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
98
use rustc_middle::ty::Instance;
@@ -17,8 +16,6 @@ use super::operand::{OperandRef, OperandValue};
1716
use super::place::PlaceRef;
1817
use super::{FunctionCx, LocalRef};
1918

20-
use std::ops::Range;
21-
2219
pub struct FunctionDebugContext<'tcx, S, L> {
2320
/// Maps from source code to the corresponding debug info scope.
2421
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
@@ -36,17 +33,17 @@ pub enum VariableKind {
3633
#[derive(Clone)]
3734
pub struct PerLocalVarDebugInfo<'tcx, D> {
3835
pub name: Symbol,
36+
pub ty: Ty<'tcx>,
3937
pub source_info: mir::SourceInfo,
4038

4139
/// `DIVariable` returned by `create_dbg_var`.
4240
pub dbg_var: Option<D>,
4341

44-
/// Byte range in the `dbg_var` covered by this fragment,
45-
/// if this is a fragment of a composite `VarDebugInfo`.
46-
pub fragment: Option<Range<Size>>,
47-
4842
/// `.place.projection` from `mir::VarDebugInfo`.
49-
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
43+
pub projection: &'tcx [mir::PlaceElem<'tcx>],
44+
45+
/// Projection from fragment debuginfo.
46+
pub fragment: &'tcx [mir::PlaceElem<'tcx>],
5047
}
5148

5249
#[derive(Clone, Copy, Debug)]
@@ -149,6 +146,8 @@ struct DebugInfoOffset<T> {
149146
indirect_offsets: Vec<Size>,
150147
/// The final location debuginfo should point to.
151148
result: T,
149+
/// Whether the final location is a fragment of a larger contiguous projection.
150+
fragment: bool,
152151
}
153152

154153
fn calculate_debuginfo_offset<
@@ -165,17 +164,21 @@ fn calculate_debuginfo_offset<
165164
// FIXME(eddyb) use smallvec here.
166165
let mut indirect_offsets = vec![];
167166
let mut place = base;
167+
let mut fragment = false;
168168

169169
for elem in projection {
170+
let layout = place.layout();
170171
match *elem {
171172
mir::ProjectionElem::Deref => {
172173
indirect_offsets.push(Size::ZERO);
173174
place = place.deref(bx);
175+
fragment = false;
174176
}
175177
mir::ProjectionElem::Field(field, _) => {
176178
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
177179
*offset += place.layout().fields.offset(field.index());
178180
place = place.project_field(bx, field);
181+
fragment |= place.layout().size != layout.size;
179182
}
180183
mir::ProjectionElem::Downcast(_, variant) => {
181184
place = place.downcast(bx, variant);
@@ -191,16 +194,17 @@ fn calculate_debuginfo_offset<
191194
};
192195
*offset += stride * index;
193196
place = place.project_constant_index(bx, index);
197+
fragment |= place.layout().size != layout.size;
194198
}
195199
_ => {
196200
// Sanity check for `can_use_in_debuginfo`.
197201
debug_assert!(!elem.can_use_in_debuginfo());
198-
bug!("unsupported var debuginfo projection `{:?}`", projection)
202+
bug!("unsupported var debuginfo place `{:?}`", projection)
199203
}
200204
}
201205
}
202206

203-
DebugInfoOffset { direct_offset, indirect_offsets, result: place }
207+
DebugInfoOffset { direct_offset, indirect_offsets, result: place, fragment }
204208
}
205209

206210
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
@@ -290,14 +294,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
290294
} else {
291295
let name = kw::Empty;
292296
let decl = &self.mir.local_decls[local];
297+
let arg_ty = self.monomorphize(decl.ty);
298+
293299
let dbg_var = if full_debug_info {
294300
self.adjusted_span_and_dbg_scope(decl.source_info).map(
295301
|(dbg_scope, _, span)| {
296302
// FIXME(eddyb) is this `+ 1` needed at all?
297303
let kind = VariableKind::ArgumentVariable(arg_index + 1);
298304

299-
let arg_ty = self.monomorphize(decl.ty);
300-
301305
self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
302306
},
303307
)
@@ -307,10 +311,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
307311

308312
Some(PerLocalVarDebugInfo {
309313
name,
314+
ty: arg_ty,
310315
source_info: decl.source_info,
311316
dbg_var,
312-
fragment: None,
313-
projection: ty::List::empty(),
317+
fragment: &[],
318+
projection: &[],
314319
})
315320
}
316321
} else {
@@ -392,8 +397,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
392397
let Some(dbg_var) = var.dbg_var else { return };
393398
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
394399

395-
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
400+
let DebugInfoOffset { mut direct_offset, indirect_offsets, result: _, fragment: _ } =
396401
calculate_debuginfo_offset(bx, var.projection, base.layout);
402+
let mut indirect_offsets = &indirect_offsets[..];
397403

398404
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
399405
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
@@ -410,8 +416,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
410416
&& (direct_offset != Size::ZERO || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
411417

412418
if should_create_individual_allocas {
413-
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
414-
calculate_debuginfo_offset(bx, var.projection, base);
419+
let DebugInfoOffset {
420+
direct_offset: _,
421+
indirect_offsets: _,
422+
fragment: _,
423+
result: place,
424+
} = calculate_debuginfo_offset(bx, var.projection, base);
415425

416426
// Create a variable which will be a pointer to the actual value
417427
let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty);
@@ -423,24 +433,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
423433
bx.store(place.llval, alloca.llval, alloca.align);
424434

425435
// Point the debug info to `*alloca` for the current variable
426-
bx.dbg_var_addr(
427-
dbg_var,
428-
dbg_loc,
429-
alloca.llval,
430-
Size::ZERO,
431-
&[Size::ZERO],
432-
var.fragment,
433-
);
436+
direct_offset = Size::ZERO;
437+
indirect_offsets = &[Size::ZERO];
438+
}
439+
440+
self.debug_introduce_place(
441+
bx,
442+
dbg_var,
443+
dbg_loc,
444+
base.llval,
445+
direct_offset,
446+
indirect_offsets,
447+
var.ty,
448+
var.fragment,
449+
);
450+
}
451+
452+
fn debug_introduce_place(
453+
&self,
454+
bx: &mut Bx,
455+
dbg_var: Bx::DIVariable,
456+
dbg_loc: Bx::DILocation,
457+
base: Bx::Value,
458+
direct_offset: Size,
459+
indirect_offsets: &[Size],
460+
ty: Ty<'tcx>,
461+
fragment: &[mir::PlaceElem<'tcx>],
462+
) {
463+
let DebugInfoOffset {
464+
direct_offset: fragment_offset,
465+
indirect_offsets: fragment_indirect,
466+
result: fragment_layout,
467+
fragment,
468+
} = calculate_debuginfo_offset(bx, fragment, bx.layout_of(ty));
469+
470+
let fragment = if fragment_layout.size == Size::ZERO {
471+
return;
472+
} else if fragment {
473+
Some(fragment_offset..fragment_offset + fragment_layout.size)
434474
} else {
435-
bx.dbg_var_addr(
436-
dbg_var,
437-
dbg_loc,
438-
base.llval,
439-
direct_offset,
440-
&indirect_offsets,
441-
var.fragment,
442-
);
475+
None
476+
};
477+
478+
if !fragment_indirect.is_empty() {
479+
return;
443480
}
481+
482+
bx.dbg_var_addr(dbg_var, dbg_loc, base, direct_offset, indirect_offsets, fragment);
444483
}
445484

446485
pub fn debug_introduce_locals(&self, bx: &mut Bx) {
@@ -512,32 +551,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
512551
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
513552
});
514553

515-
let fragment = if let Some(ref fragment) = var.composite {
516-
let var_layout = self.cx.layout_of(var_ty);
517-
518-
let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
519-
calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
520-
debug_assert!(indirect_offsets.is_empty());
521-
522-
if fragment_layout.size == Size::ZERO {
523-
// Fragment is a ZST, so does not represent anything. Avoid generating anything
524-
// as this may conflict with a fragment that covers the entire variable.
525-
continue;
526-
} else if fragment_layout.size == var_layout.size {
527-
// Fragment covers entire variable, so as far as
528-
// DWARF is concerned, it's not really a fragment.
529-
None
530-
} else {
531-
Some(direct_offset..direct_offset + fragment_layout.size)
532-
}
533-
} else {
534-
None
535-
};
554+
let fragment =
555+
if let Some(ref fragment) = var.composite { &fragment.projection[..] } else { &[] };
536556

537557
match var.value {
538558
mir::VarDebugInfoContents::Place(place) => {
539559
per_local[place.local].push(PerLocalVarDebugInfo {
540560
name: var.name,
561+
ty: var_ty,
541562
source_info: var.source_info,
542563
dbg_var,
543564
fragment,
@@ -553,7 +574,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
553574
let base =
554575
Self::spill_operand_to_stack(operand, Some(var.name.to_string()), bx);
555576

556-
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], fragment);
577+
self.debug_introduce_place(
578+
bx,
579+
dbg_var,
580+
dbg_loc,
581+
base.llval,
582+
Size::ZERO,
583+
&[],
584+
var_ty,
585+
fragment,
586+
);
557587
}
558588
}
559589
}

‎compiler/rustc_const_eval/src/transform/validate.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
796796
format!("invalid empty projection in debuginfo for {:?}", debuginfo.name),
797797
);
798798
}
799-
if projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
799+
if !projection.iter().all(|p| matches!(p, PlaceElem::Field(..) | PlaceElem::Deref)) {
800800
self.fail(
801801
START_BLOCK.start_location(),
802802
format!(

‎compiler/rustc_middle/src/mir/visit.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,13 @@ macro_rules! make_mir_visitor {
858858
if let Some(box VarDebugInfoFragment { ref $($mutability)? ty, ref $($mutability)? projection }) = composite {
859859
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
860860
for elem in projection {
861-
let ProjectionElem::Field(_, ty) = elem else { bug!() };
862-
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
861+
match elem {
862+
ProjectionElem::Deref => {}
863+
ProjectionElem::Field(_, ty) => {
864+
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location))
865+
}
866+
_ => bug!("unexpected projection in debuginfo: {elem:?}"),
867+
}
863868
}
864869
}
865870
match value {

‎compiler/rustc_mir_transform/src/ref_prop.rs‎

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,20 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
8989
debug!(?replacer.allowed_replacements);
9090
debug!(?replacer.storage_to_remove);
9191

92-
replacer.visit_body_preserves_cfg(body);
92+
replacer.local_decls = Some(&body.local_decls);
93+
for debuginfo in body.var_debug_info.iter_mut() {
94+
replacer.visit_var_debug_info(debuginfo);
95+
}
96+
for (bb, bbdata) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
97+
replacer.visit_basic_block_data(bb, bbdata);
98+
}
9399

94100
if replacer.any_replacement {
95101
crate::simplify::remove_unused_definitions(body);
102+
true
103+
} else {
104+
false
96105
}
97-
98-
replacer.any_replacement
99106
}
100107

101108
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -113,7 +120,7 @@ fn compute_replacement<'tcx>(
113120
tcx: TyCtxt<'tcx>,
114121
body: &Body<'tcx>,
115122
ssa: &SsaLocals,
116-
) -> Replacer<'tcx> {
123+
) -> Replacer<'tcx, 'tcx> {
117124
let always_live_locals = always_storage_live_locals(body);
118125

119126
// Compute which locals have a single `StorageLive` statement ever.
@@ -268,7 +275,9 @@ fn compute_replacement<'tcx>(
268275
targets,
269276
storage_to_remove,
270277
allowed_replacements,
278+
fully_replacable_locals,
271279
any_replacement: false,
280+
local_decls: None,
272281
};
273282

274283
struct ReplacementFinder<'a, 'tcx, F> {
@@ -342,15 +351,17 @@ fn fully_replacable_locals(ssa: &SsaLocals) -> BitSet<Local> {
342351
}
343352

344353
/// Utility to help performing subtitution of `*pattern` by `target`.
345-
struct Replacer<'tcx> {
354+
struct Replacer<'a, 'tcx> {
346355
tcx: TyCtxt<'tcx>,
347356
targets: IndexVec<Local, Value<'tcx>>,
348357
storage_to_remove: BitSet<Local>,
349358
allowed_replacements: FxHashSet<(Local, Location)>,
350359
any_replacement: bool,
360+
fully_replacable_locals: BitSet<Local>,
361+
local_decls: Option<&'a LocalDecls<'tcx>>,
351362
}
352363

353-
impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
364+
impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
354365
fn tcx(&self) -> TyCtxt<'tcx> {
355366
self.tcx
356367
}
@@ -367,6 +378,19 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
367378
if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() {
368379
*place = Place::from(target.local).project_deeper(rest, self.tcx);
369380
self.any_replacement = true;
381+
} else if self.fully_replacable_locals.contains(place.local) {
382+
debuginfo
383+
.composite
384+
.get_or_insert_with(|| {
385+
Box::new(VarDebugInfoFragment {
386+
ty: self.local_decls.unwrap()[place.local].ty,
387+
projection: Vec::new(),
388+
})
389+
})
390+
.projection
391+
.push(PlaceElem::Deref);
392+
*place = target;
393+
self.any_replacement = true;
370394
} else {
371395
break;
372396
}

0 commit comments

Comments
 (0)