Skip to content
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

rustc_mir: add a pass for fragmenting locals into their fields (aka SROA). #48300

Closed
wants to merge 4 commits into from
Closed
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
62 changes: 58 additions & 4 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,10 +948,64 @@ pub struct VarDebugInfo<'tcx> {
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: SourceInfo,

/// Where the data for this user variable is to be found.
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
/// based on a `Local`, not a `Static`, and contains no indexing.
pub place: Place<'tcx>,
/// How the contents of this user variable are represented.
pub contents: VarDebugInfoContents<'tcx>,
}

#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
pub enum VarDebugInfoContents<'tcx> {
/// The user variable's data is entirely in one `Place`.
// NOTE(eddyb) there's an unenforced invariant that this `Place` is
wesleywiser marked this conversation as resolved.
Show resolved Hide resolved
// contains no indexing (with a non-constant index).
Compact(Place<'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>>,
},
}

#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
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<ProjectionKind>,

/// 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
27 changes: 21 additions & 6 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,16 +739,31 @@ macro_rules! make_mir_visitor {
let VarDebugInfo {
name: _,
source_info,
place,
contents,
} = var_debug_info;

self.visit_source_info(source_info);
let location = START_BLOCK.start_location();
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
match contents {
VarDebugInfoContents::Compact(place) => {
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
}
VarDebugInfoContents::Composite { ty, fragments } => {
// FIXME(eddyb) use a better `TyContext` here.
self.visit_ty(ty, TyContext::Location(location));
for VarDebugInfoFragment { projection: _, contents } in fragments {
self.visit_place(
contents,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
}
}
}
}

fn super_source_scope(&mut self,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl BackendTypes for Builder<'_, 'll, 'tcx> {
type Funclet = <CodegenCx<'ll, 'tcx> as BackendTypes>::Funclet;

type DIScope = <CodegenCx<'ll, 'tcx> as BackendTypes>::DIScope;
type DIVariable = <CodegenCx<'ll, 'tcx> as BackendTypes>::DIVariable;
}

impl ty::layout::HasDataLayout for Builder<'_, '_, '_> {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl BackendTypes for CodegenCx<'ll, 'tcx> {
type Funclet = Funclet<'ll>;

type DIScope = &'ll llvm::debuginfo::DIScope;
type DIVariable = &'ll llvm::debuginfo::DIVariable;
}

impl CodegenCx<'ll, 'tcx> {
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_codegen_llvm/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,10 +1288,14 @@ fn generator_layout_and_saved_local_names(

let state_arg = mir::Local::new(1);
for var in &body.var_debug_info {
if var.place.local != state_arg {
let place = match var.contents {
mir::VarDebugInfoContents::Compact(place) => place,
mir::VarDebugInfoContents::Composite { .. } => continue,
};
if place.local != state_arg {
continue;
}
match var.place.projection[..] {
match place.projection[..] {
[
// Deref of the `Pin<&mut Self>` state argument.
mir::ProjectionElem::Field(..),
Expand Down
113 changes: 71 additions & 42 deletions src/librustc_codegen_llvm/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use self::utils::{create_DIArray, is_node_local_to_unit, span_start, DIB};

use crate::llvm;
use crate::llvm::debuginfo::{
DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DISPFlags, DIScope, DIType,
DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DISPFlags, DIScope, DIType, DIVariable,
};
use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc::ty::subst::{GenericArgKind, SubstsRef};
Expand All @@ -34,6 +34,7 @@ use libc::c_uint;
use log::debug;
use std::cell::RefCell;
use std::ffi::CString;
use std::ops::Range;

use rustc::ty::layout::{self, HasTyCtxt, LayoutOf, Size};
use rustc_codegen_ssa::traits::*;
Expand Down Expand Up @@ -143,79 +144,66 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
};
}

impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
fn declare_local(
impl DebugInfoBuilderMethods for Builder<'a, 'll, '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_context: &FunctionDebugContext<&'ll DIScope>,
variable_name: ast::Name,
variable_type: Ty<'tcx>,
dbg_var: &'ll DIVariable,
scope_metadata: &'ll DIScope,
variable_alloca: Self::Value,
direct_offset: Size,
indirect_offsets: &[Size],
variable_kind: VariableKind,
fragment: Option<Range<Size>>,
span: Span,
) {
assert!(!dbg_context.source_locations_enabled);
let cx = self.cx();

let file = span_start(cx, span).file;
let file_metadata = file_metadata(cx, &file.name, dbg_context.defining_crate);

let loc = span_start(cx, span);
let type_metadata = type_metadata(cx, variable_type, span);

let (argument_index, dwarf_tag) = match variable_kind {
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
LocalVariable => (0, DW_TAG_auto_variable),
};
let align = cx.align_of(variable_type);

// Convert the direct and indirect offsets to address ops.
// Convert the direct/indirect offsets and fragment byte range to DWARF OPs.
let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
let mut addr_ops = SmallVec::<[_; 8]>::new();
let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() };
let mut dwarf_ops = SmallVec::<[_; 8]>::new();

if direct_offset.bytes() > 0 {
addr_ops.push(op_plus_uconst());
addr_ops.push(direct_offset.bytes() as i64);
dwarf_ops.push(op_plus_uconst());
dwarf_ops.push(direct_offset.bytes() as i64);
}
for &offset in indirect_offsets {
addr_ops.push(op_deref());
dwarf_ops.push(op_deref());
if offset.bytes() > 0 {
addr_ops.push(op_plus_uconst());
addr_ops.push(offset.bytes() as i64);
dwarf_ops.push(op_plus_uconst());
dwarf_ops.push(offset.bytes() as i64);
}
}

let name = SmallCStr::new(&variable_name.as_str());
let metadata = unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(cx),
dwarf_tag,
scope_metadata,
name.as_ptr(),
file_metadata,
loc.line as c_uint,
type_metadata,
cx.sess().opts.optimize != config::OptLevel::No,
DIFlags::FlagZero,
argument_index,
align.bytes() as u32,
)
};
if let Some(fragment) = fragment {
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
// offset and size, both of them in bits.
dwarf_ops.push(op_llvm_fragment());
dwarf_ops.push(fragment.start.bits() as i64);
dwarf_ops.push((fragment.end - fragment.start).bits() as i64);
}

// FIXME(eddyb) maybe this information could be extracted from `var`,
// to avoid having to pass it down in both places?
source_loc::set_debug_location(
self,
InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize()),
);
unsafe {
let debug_loc = llvm::LLVMGetCurrentDebugLocation(self.llbuilder);
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(cx),
variable_alloca,
metadata,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
dbg_var,
dwarf_ops.as_ptr(),
dwarf_ops.len() as c_uint,
debug_loc,
self.llbb(),
);
Expand Down Expand Up @@ -558,4 +546,45 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn debuginfo_finalize(&self) {
finalize(self)
}

// FIXME(eddyb) find a common convention for all of the debuginfo-related
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
fn create_dbg_var(
&self,
dbg_context: &FunctionDebugContext<&'ll DIScope>,
variable_name: ast::Name,
variable_type: Ty<'tcx>,
scope_metadata: &'ll DIScope,
variable_kind: VariableKind,
span: Span,
) -> &'ll DIVariable {
let file = span_start(self, span).file;
let file_metadata = file_metadata(self, &file.name, dbg_context.defining_crate);

let loc = span_start(self, span);
let type_metadata = type_metadata(self, variable_type, span);

let (argument_index, dwarf_tag) = match variable_kind {
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
LocalVariable => (0, DW_TAG_auto_variable),
};
let align = self.align_of(variable_type);

let name = SmallCStr::new(&variable_name.as_str());
unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(self),
dwarf_tag,
scope_metadata,
name.as_ptr(),
file_metadata,
loc.line as c_uint,
type_metadata,
self.sess().opts.optimize != config::OptLevel::No,
DIFlags::FlagZero,
argument_index,
align.bytes() as u32,
)
}
}
}
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,7 @@ extern "C" {
) -> &'a Value;
pub fn LLVMRustDIBuilderCreateOpDeref() -> i64;
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> i64;
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> i64;

#[allow(improper_ctypes)]
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
Expand Down
Loading