Skip to content

Commit 79146ba

Browse files
committed
Auto merge of #102570 - cjgillot:deagg-debuginfo, r=oli-obk
Perform simple scalar replacement of aggregates (SROA) MIR opt This is a re-open of #85796 I copied the debuginfo implementation (first commit) from `@eddyb's` own SROA PR. This pass replaces plain field accesses by simple locals when possible. To be eligible, the replaced locals: - must not be enums or unions; - must not be used whole; - must not have their address taken. The storage and deinit statements are duplicated on each created local. cc `@tmiasko` who reviewed the former version of this PR.
2 parents a00f8ba + a18de70 commit 79146ba

29 files changed

+1228
-57
lines changed

compiler/rustc_codegen_gcc/src/debuginfo.rs

+49-8
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,25 @@ use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
44
use rustc_middle::mir;
55
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
66
use rustc_span::{SourceFile, Span, Symbol};
7-
use rustc_target::abi::Size;
87
use rustc_target::abi::call::FnAbi;
8+
use rustc_target::abi::Size;
9+
use std::ops::Range;
910

1011
use crate::builder::Builder;
1112
use crate::context::CodegenCx;
1213

1314
impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
1415
// FIXME(eddyb) find a common convention for all of the debuginfo-related
1516
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
16-
fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) {
17+
fn dbg_var_addr(
18+
&mut self,
19+
_dbg_var: Self::DIVariable,
20+
_scope_metadata: Self::DIScope,
21+
_variable_alloca: Self::Value,
22+
_direct_offset: Size,
23+
_indirect_offsets: &[Size],
24+
_fragment: Option<Range<Size>>,
25+
) {
1726
unimplemented!();
1827
}
1928

@@ -31,32 +40,64 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
3140
}
3241

3342
impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
34-
fn create_vtable_debuginfo(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
43+
fn create_vtable_debuginfo(
44+
&self,
45+
_ty: Ty<'tcx>,
46+
_trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
47+
_vtable: Self::Value,
48+
) {
3549
// TODO(antoyo)
3650
}
3751

38-
fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
52+
fn create_function_debug_context(
53+
&self,
54+
_instance: Instance<'tcx>,
55+
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
56+
_llfn: RValue<'gcc>,
57+
_mir: &mir::Body<'tcx>,
58+
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
3959
// TODO(antoyo)
4060
None
4161
}
4262

43-
fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope {
63+
fn extend_scope_to_file(
64+
&self,
65+
_scope_metadata: Self::DIScope,
66+
_file: &SourceFile,
67+
) -> Self::DIScope {
4468
unimplemented!();
4569
}
4670

4771
fn debuginfo_finalize(&self) {
4872
// TODO(antoyo)
4973
}
5074

51-
fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable {
75+
fn create_dbg_var(
76+
&self,
77+
_variable_name: Symbol,
78+
_variable_type: Ty<'tcx>,
79+
_scope_metadata: Self::DIScope,
80+
_variable_kind: VariableKind,
81+
_span: Span,
82+
) -> Self::DIVariable {
5283
unimplemented!();
5384
}
5485

55-
fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option<RValue<'gcc>>) -> Self::DIScope {
86+
fn dbg_scope_fn(
87+
&self,
88+
_instance: Instance<'tcx>,
89+
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
90+
_maybe_definition_llfn: Option<RValue<'gcc>>,
91+
) -> Self::DIScope {
5692
unimplemented!();
5793
}
5894

59-
fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option<Self::DILocation>, _span: Span) -> Self::DILocation {
95+
fn dbg_loc(
96+
&self,
97+
_scope: Self::DIScope,
98+
_inlined_at: Option<Self::DILocation>,
99+
_span: Span,
100+
) -> Self::DILocation {
60101
unimplemented!();
61102
}
62103
}

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use smallvec::SmallVec;
3939
use std::cell::OnceCell;
4040
use std::cell::RefCell;
4141
use std::iter;
42+
use std::ops::Range;
4243

4344
mod create_scope_map;
4445
pub mod gdb;
@@ -163,12 +164,14 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
163164
variable_alloca: Self::Value,
164165
direct_offset: Size,
165166
indirect_offsets: &[Size],
167+
fragment: Option<Range<Size>>,
166168
) {
167-
// Convert the direct and indirect offsets to address ops.
169+
// Convert the direct and indirect offsets and fragment byte range to address ops.
168170
// FIXME(eddyb) use `const`s instead of getting the values via FFI,
169171
// the values should match the ones in the DWARF standard anyway.
170172
let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
171173
let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
174+
let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() };
172175
let mut addr_ops = SmallVec::<[u64; 8]>::new();
173176

174177
if direct_offset.bytes() > 0 {
@@ -182,6 +185,13 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
182185
addr_ops.push(offset.bytes() as u64);
183186
}
184187
}
188+
if let Some(fragment) = fragment {
189+
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
190+
// offset and size, both of them in bits.
191+
addr_ops.push(op_llvm_fragment());
192+
addr_ops.push(fragment.start.bits() as u64);
193+
addr_ops.push((fragment.end - fragment.start).bits() as u64);
194+
}
185195

186196
unsafe {
187197
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,7 @@ extern "C" {
22102210
) -> &'a DILocation;
22112211
pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
22122212
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
2213+
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;
22132214

22142215
#[allow(improper_ctypes)]
22152216
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+64-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use super::operand::{OperandRef, OperandValue};
1414
use super::place::PlaceRef;
1515
use super::{FunctionCx, LocalRef};
1616

17+
use std::ops::Range;
18+
1719
pub struct FunctionDebugContext<S, L> {
1820
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
1921
}
@@ -25,14 +27,18 @@ pub enum VariableKind {
2527
}
2628

2729
/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
28-
#[derive(Copy, Clone)]
30+
#[derive(Clone)]
2931
pub struct PerLocalVarDebugInfo<'tcx, D> {
3032
pub name: Symbol,
3133
pub source_info: mir::SourceInfo,
3234

3335
/// `DIVariable` returned by `create_dbg_var`.
3436
pub dbg_var: Option<D>,
3537

38+
/// Byte range in the `dbg_var` covered by this fragment,
39+
/// if this is a fragment of a composite `VarDebugInfo`.
40+
pub fragment: Option<Range<Size>>,
41+
3642
/// `.place.projection` from `mir::VarDebugInfo`.
3743
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
3844
}
@@ -145,7 +151,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
145151
Some(per_local) => &per_local[local],
146152
None => return,
147153
};
148-
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
154+
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
149155
let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
150156

151157
let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
@@ -187,6 +193,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
187193
name,
188194
source_info: decl.source_info,
189195
dbg_var,
196+
fragment: None,
190197
projection: ty::List::empty(),
191198
})
192199
}
@@ -199,7 +206,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
199206
let name = if bx.sess().fewer_names() {
200207
None
201208
} else {
202-
Some(match whole_local_var.or(fallback_var) {
209+
Some(match whole_local_var.or(fallback_var.clone()) {
203210
Some(var) if var.name != kw::Empty => var.name.to_string(),
204211
_ => format!("{:?}", local),
205212
})
@@ -249,7 +256,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
249256
LocalRef::UnsizedPlace(_) => return,
250257
};
251258

252-
let vars = vars.iter().copied().chain(fallback_var);
259+
let vars = vars.iter().cloned().chain(fallback_var);
253260

254261
for var in vars {
255262
let Some(dbg_var) = var.dbg_var else { continue };
@@ -312,9 +319,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
312319
bx.store(place.llval, alloca.llval, alloca.align);
313320

314321
// Point the debug info to `*alloca` for the current variable
315-
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
322+
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
316323
} else {
317-
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
324+
bx.dbg_var_addr(
325+
dbg_var,
326+
dbg_loc,
327+
base.llval,
328+
direct_offset,
329+
&indirect_offsets,
330+
None,
331+
);
318332
}
319333
}
320334
}
@@ -382,6 +396,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
382396
let ty = self.monomorphize(c.ty());
383397
(ty, VariableKind::LocalVariable)
384398
}
399+
mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
400+
let ty = self.monomorphize(ty);
401+
(ty, VariableKind::LocalVariable)
402+
}
385403
};
386404

387405
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
@@ -393,6 +411,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
393411
name: var.name,
394412
source_info: var.source_info,
395413
dbg_var,
414+
fragment: None,
396415
projection: place.projection,
397416
});
398417
}
@@ -407,10 +426,48 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
407426
bx,
408427
);
409428

410-
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);
429+
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
411430
}
412431
}
413432
}
433+
mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
434+
let var_ty = self.monomorphize(ty);
435+
let var_layout = self.cx.layout_of(var_ty);
436+
for fragment in fragments {
437+
let mut fragment_start = Size::ZERO;
438+
let mut fragment_layout = var_layout;
439+
440+
for elem in &fragment.projection {
441+
match *elem {
442+
mir::ProjectionElem::Field(field, _) => {
443+
let i = field.index();
444+
fragment_start += fragment_layout.fields.offset(i);
445+
fragment_layout = fragment_layout.field(self.cx, i);
446+
}
447+
_ => span_bug!(
448+
var.source_info.span,
449+
"unsupported fragment projection `{:?}`",
450+
elem,
451+
),
452+
}
453+
}
454+
455+
let place = fragment.contents;
456+
per_local[place.local].push(PerLocalVarDebugInfo {
457+
name: var.name,
458+
source_info: var.source_info,
459+
dbg_var,
460+
fragment: if fragment_layout.size == var_layout.size {
461+
// Fragment covers entire variable, so as far as
462+
// DWARF is concerned, it's not really a fragment.
463+
None
464+
} else {
465+
Some(fragment_start..fragment_start + fragment_layout.size)
466+
},
467+
projection: place.projection,
468+
});
469+
}
470+
}
414471
}
415472
}
416473
Some(per_local)

compiler/rustc_codegen_ssa/src/traits/debuginfo.rs

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use rustc_span::{SourceFile, Span, Symbol};
66
use rustc_target::abi::call::FnAbi;
77
use rustc_target::abi::Size;
88

9+
use std::ops::Range;
10+
911
pub trait DebugInfoMethods<'tcx>: BackendTypes {
1012
fn create_vtable_debuginfo(
1113
&self,
@@ -72,6 +74,9 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
7274
direct_offset: Size,
7375
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
7476
indirect_offsets: &[Size],
77+
// Byte range in the `dbg_var` covered by this fragment,
78+
// if this is a fragment of a composite `DIVariable`.
79+
fragment: Option<Range<Size>>,
7580
);
7681
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
7782
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,10 @@ extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() {
11111111
return dwarf::DW_OP_plus_uconst;
11121112
}
11131113

1114+
extern "C" int64_t LLVMRustDIBuilderCreateOpLLVMFragment() {
1115+
return dwarf::DW_OP_LLVM_fragment;
1116+
}
1117+
11141118
extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
11151119
RawRustStringOstream OS(Str);
11161120
unwrap<llvm::Type>(Ty)->print(OS);

compiler/rustc_middle/src/mir/mod.rs

+53
Original file line numberDiff line numberDiff line change
@@ -1071,14 +1071,67 @@ pub enum VarDebugInfoContents<'tcx> {
10711071
/// based on a `Local`, not a `Static`, and contains no indexing.
10721072
Place(Place<'tcx>),
10731073
Const(Constant<'tcx>),
1074+
/// The user variable's data is split across several fragments,
1075+
/// each described by a `VarDebugInfoFragment`.
1076+
/// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
1077+
/// and LLVM's `DW_OP_LLVM_fragment` for more details on
1078+
/// the underlying debuginfo feature this relies on.
1079+
Composite {
1080+
/// Type of the original user variable.
1081+
ty: Ty<'tcx>,
1082+
/// All the parts of the original user variable, which ended
1083+
/// up in disjoint places, due to optimizations.
1084+
fragments: Vec<VarDebugInfoFragment<'tcx>>,
1085+
},
10741086
}
10751087

10761088
impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
10771089
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
10781090
match self {
10791091
VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
10801092
VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
1093+
VarDebugInfoContents::Composite { ty, fragments } => {
1094+
write!(fmt, "{:?}{{ ", ty)?;
1095+
for f in fragments.iter() {
1096+
write!(fmt, "{:?}, ", f)?;
1097+
}
1098+
write!(fmt, "}}")
1099+
}
1100+
}
1101+
}
1102+
}
1103+
1104+
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
1105+
pub struct VarDebugInfoFragment<'tcx> {
1106+
/// Where in the composite user variable this fragment is,
1107+
/// represented as a "projection" into the composite variable.
1108+
/// At lower levels, this corresponds to a byte/bit range.
1109+
// NOTE(eddyb) there's an unenforced invariant that this contains
1110+
// only `Field`s, and not into `enum` variants or `union`s.
1111+
// FIXME(eddyb) support this for `enum`s by either using DWARF's
1112+
// more advanced control-flow features (unsupported by LLVM?)
1113+
// to match on the discriminant, or by using custom type debuginfo
1114+
// with non-overlapping variants for the composite variable.
1115+
pub projection: Vec<PlaceElem<'tcx>>,
1116+
1117+
/// Where the data for this fragment can be found.
1118+
// NOTE(eddyb) There's an unenforced invariant that this `Place` is
1119+
// contains no indexing (with a non-constant index).
1120+
pub contents: Place<'tcx>,
1121+
}
1122+
1123+
impl Debug for VarDebugInfoFragment<'_> {
1124+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1125+
for elem in self.projection.iter() {
1126+
match elem {
1127+
ProjectionElem::Field(field, _) => {
1128+
write!(fmt, ".{:?}", field.index())?;
1129+
}
1130+
_ => bug!("unsupported fragment projection `{:?}`", elem),
1131+
}
10811132
}
1133+
1134+
write!(fmt, " => {:?}", self.contents)
10821135
}
10831136
}
10841137

0 commit comments

Comments
 (0)