Skip to content

Perform simple scalar replacement of aggregates (SROA) MIR opt #102570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 49 additions & 8 deletions compiler/rustc_codegen_gcc/src/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
use rustc_middle::mir;
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
use rustc_span::{SourceFile, Span, Symbol};
use rustc_target::abi::Size;
use rustc_target::abi::call::FnAbi;
use rustc_target::abi::Size;
use std::ops::Range;

use crate::builder::Builder;
use crate::context::CodegenCx;

impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
// FIXME(eddyb) find a common convention for all of the debuginfo-related
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) {
fn dbg_var_addr(
&mut self,
_dbg_var: Self::DIVariable,
_scope_metadata: Self::DIScope,
_variable_alloca: Self::Value,
_direct_offset: Size,
_indirect_offsets: &[Size],
_fragment: Option<Range<Size>>,
) {
unimplemented!();
}

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

impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn create_vtable_debuginfo(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
fn create_vtable_debuginfo(
&self,
_ty: Ty<'tcx>,
_trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
_vtable: Self::Value,
) {
// TODO(antoyo)
}

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>> {
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>> {
// TODO(antoyo)
None
}

fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope {
fn extend_scope_to_file(
&self,
_scope_metadata: Self::DIScope,
_file: &SourceFile,
) -> Self::DIScope {
unimplemented!();
}

fn debuginfo_finalize(&self) {
// TODO(antoyo)
}

fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable {
fn create_dbg_var(
&self,
_variable_name: Symbol,
_variable_type: Ty<'tcx>,
_scope_metadata: Self::DIScope,
_variable_kind: VariableKind,
_span: Span,
) -> Self::DIVariable {
unimplemented!();
}

fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option<RValue<'gcc>>) -> Self::DIScope {
fn dbg_scope_fn(
&self,
_instance: Instance<'tcx>,
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
_maybe_definition_llfn: Option<RValue<'gcc>>,
) -> Self::DIScope {
unimplemented!();
}

fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option<Self::DILocation>, _span: Span) -> Self::DILocation {
fn dbg_loc(
&self,
_scope: Self::DIScope,
_inlined_at: Option<Self::DILocation>,
_span: Span,
) -> Self::DILocation {
unimplemented!();
}
}
12 changes: 11 additions & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use smallvec::SmallVec;
use std::cell::OnceCell;
use std::cell::RefCell;
use std::iter;
use std::ops::Range;

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

if direct_offset.bytes() > 0 {
Expand All @@ -182,6 +185,13 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
addr_ops.push(offset.bytes() as u64);
}
}
if let Some(fragment) = fragment {
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
// offset and size, both of them in bits.
addr_ops.push(op_llvm_fragment());
addr_ops.push(fragment.start.bits() as u64);
addr_ops.push((fragment.end - fragment.start).bits() as u64);
}

unsafe {
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,7 @@ extern "C" {
) -> &'a DILocation;
pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;

#[allow(improper_ctypes)]
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
Expand Down
71 changes: 64 additions & 7 deletions compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use super::operand::{OperandRef, OperandValue};
use super::place::PlaceRef;
use super::{FunctionCx, LocalRef};

use std::ops::Range;

pub struct FunctionDebugContext<S, L> {
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
}
Expand All @@ -25,14 +27,18 @@ pub enum VariableKind {
}

/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
#[derive(Copy, Clone)]
#[derive(Clone)]
pub struct PerLocalVarDebugInfo<'tcx, D> {
pub name: Symbol,
pub source_info: mir::SourceInfo,

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

/// Byte range in the `dbg_var` covered by this fragment,
/// if this is a fragment of a composite `VarDebugInfo`.
pub fragment: Option<Range<Size>>,

/// `.place.projection` from `mir::VarDebugInfo`.
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
}
Expand Down Expand Up @@ -145,7 +151,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(per_local) => &per_local[local],
None => return,
};
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
let has_proj = || vars.iter().any(|var| !var.projection.is_empty());

let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
Expand Down Expand Up @@ -187,6 +193,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
name,
source_info: decl.source_info,
dbg_var,
fragment: None,
projection: ty::List::empty(),
})
}
Expand All @@ -199,7 +206,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let name = if bx.sess().fewer_names() {
None
} else {
Some(match whole_local_var.or(fallback_var) {
Some(match whole_local_var.or(fallback_var.clone()) {
Some(var) if var.name != kw::Empty => var.name.to_string(),
_ => format!("{:?}", local),
})
Expand Down Expand Up @@ -249,7 +256,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
LocalRef::UnsizedPlace(_) => return,
};

let vars = vars.iter().copied().chain(fallback_var);
let vars = vars.iter().cloned().chain(fallback_var);

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

// Point the debug info to `*alloca` for the current variable
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
} else {
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
bx.dbg_var_addr(
dbg_var,
dbg_loc,
base.llval,
direct_offset,
&indirect_offsets,
None,
);
}
}
}
Expand Down Expand Up @@ -382,6 +396,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let ty = self.monomorphize(c.ty());
(ty, VariableKind::LocalVariable)
}
mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
let ty = self.monomorphize(ty);
(ty, VariableKind::LocalVariable)
}
};

self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
Expand All @@ -393,6 +411,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment: None,
projection: place.projection,
});
}
Expand All @@ -407,10 +426,48 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx,
);

bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
}
}
}
mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
let var_ty = self.monomorphize(ty);
let var_layout = self.cx.layout_of(var_ty);
for fragment in fragments {
let mut fragment_start = Size::ZERO;
let mut fragment_layout = var_layout;

for elem in &fragment.projection {
match *elem {
mir::ProjectionElem::Field(field, _) => {
let i = field.index();
fragment_start += fragment_layout.fields.offset(i);
fragment_layout = fragment_layout.field(self.cx, i);
}
_ => span_bug!(
var.source_info.span,
"unsupported fragment projection `{:?}`",
elem,
),
}
}

let place = fragment.contents;
per_local[place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment: if fragment_layout.size == var_layout.size {
// Fragment covers entire variable, so as far as
// DWARF is concerned, it's not really a fragment.
None
} else {
Some(fragment_start..fragment_start + fragment_layout.size)
},
projection: place.projection,
});
}
}
}
}
Some(per_local)
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use rustc_span::{SourceFile, Span, Symbol};
use rustc_target::abi::call::FnAbi;
use rustc_target::abi::Size;

use std::ops::Range;

pub trait DebugInfoMethods<'tcx>: BackendTypes {
fn create_vtable_debuginfo(
&self,
Expand Down Expand Up @@ -72,6 +74,9 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
direct_offset: Size,
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
indirect_offsets: &[Size],
// Byte range in the `dbg_var` covered by this fragment,
// if this is a fragment of a composite `DIVariable`.
fragment: Option<Range<Size>>,
);
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,10 @@ extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() {
return dwarf::DW_OP_plus_uconst;
}

extern "C" int64_t LLVMRustDIBuilderCreateOpLLVMFragment() {
return dwarf::DW_OP_LLVM_fragment;
}

extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
RawRustStringOstream OS(Str);
unwrap<llvm::Type>(Ty)->print(OS);
Expand Down
53 changes: 53 additions & 0 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1071,14 +1071,67 @@ pub enum VarDebugInfoContents<'tcx> {
/// based on a `Local`, not a `Static`, and contains no indexing.
Place(Place<'tcx>),
Const(Constant<'tcx>),
/// The user variable's data is split across several fragments,
/// each described by a `VarDebugInfoFragment`.
/// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
/// and LLVM's `DW_OP_LLVM_fragment` for more details on
/// the underlying debuginfo feature this relies on.
Composite {
/// Type of the original user variable.
ty: Ty<'tcx>,
/// All the parts of the original user variable, which ended
/// up in disjoint places, due to optimizations.
fragments: Vec<VarDebugInfoFragment<'tcx>>,
},
}

impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match self {
VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
VarDebugInfoContents::Composite { ty, fragments } => {
write!(fmt, "{:?}{{ ", ty)?;
for f in fragments.iter() {
write!(fmt, "{:?}, ", f)?;
}
write!(fmt, "}}")
}
}
}
}

#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfoFragment<'tcx> {
/// Where in the composite user variable this fragment is,
/// represented as a "projection" into the composite variable.
/// At lower levels, this corresponds to a byte/bit range.
// NOTE(eddyb) there's an unenforced invariant that this contains
// only `Field`s, and not into `enum` variants or `union`s.
// FIXME(eddyb) support this for `enum`s by either using DWARF's
// more advanced control-flow features (unsupported by LLVM?)
// to match on the discriminant, or by using custom type debuginfo
// with non-overlapping variants for the composite variable.
pub projection: Vec<PlaceElem<'tcx>>,

/// Where the data for this fragment can be found.
// NOTE(eddyb) There's an unenforced invariant that this `Place` is
// contains no indexing (with a non-constant index).
pub contents: Place<'tcx>,
}

impl Debug for VarDebugInfoFragment<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter() {
match elem {
ProjectionElem::Field(field, _) => {
write!(fmt, ".{:?}", field.index())?;
}
_ => bug!("unsupported fragment projection `{:?}`", elem),
}
}

write!(fmt, " => {:?}", self.contents)
}
}

Expand Down
Loading