diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 4e9835a52b779..68e0190f1d148 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -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 + // 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>, + }, +} + +#[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, + + /// 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) + } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 4c5db1b07d225..c3170332349f9 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -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, diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index c59b81eb4cc32..342ac437d3c24 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -57,6 +57,7 @@ impl BackendTypes for Builder<'_, 'll, 'tcx> { type Funclet = as BackendTypes>::Funclet; type DIScope = as BackendTypes>::DIScope; + type DIVariable = as BackendTypes>::DIVariable; } impl ty::layout::HasDataLayout for Builder<'_, '_, '_> { diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index 76f6eeb3f7907..1d6bfb321598c 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs @@ -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> { diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index d1f70ad43bd28..3c06299b344a3 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -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(..), diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 0462dcff42919..78dba1d42cf3b 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -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}; @@ -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::*; @@ -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>, 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(), ); @@ -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, + ) + } + } } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 875b2c47b3b36..405ff7cbf9221 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -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); diff --git a/src/librustc_codegen_ssa/mir/debuginfo.rs b/src/librustc_codegen_ssa/mir/debuginfo.rs index e0aec5d6dd512..a989f8c02a036 100644 --- a/src/librustc_codegen_ssa/mir/debuginfo.rs +++ b/src/librustc_codegen_ssa/mir/debuginfo.rs @@ -1,13 +1,13 @@ use crate::traits::*; use rustc::mir; use rustc::session::config::DebugInfo; +use rustc::ty; use rustc::ty::layout::{LayoutOf, Size}; -use rustc::ty::TyCtxt; use rustc_hir::def_id::CrateNum; use rustc_index::vec::IndexVec; - -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; +use std::ops::Range; use super::OperandValue; use super::{FunctionCx, LocalRef}; @@ -24,6 +24,23 @@ pub enum VariableKind { LocalVariable, } +/// Like `mir::VarDebugInfo`, but within a `mir::Local`. +#[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, + + /// Byte range in the `dbg_var` covered by this fragment, + /// if this is a fragment of a composite `VarDebugInfo`. + pub fragment: Option>, + + /// `.place.projection` from `mir::VarDebugInfo`. + pub projection: &'tcx ty::List>, +} + #[derive(Clone, Copy, Debug)] pub struct DebugScope { pub scope_metadata: Option, @@ -103,6 +120,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME(eddyb) use `llvm.dbg.value` (which would work for operands), // not just `llvm.dbg.declare` (which requires `alloca`). pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { + let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; + // FIXME(eddyb) maybe name the return place as `_0` or `return`? if local == mir::RETURN_PLACE { return; @@ -112,35 +131,65 @@ 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().copied().find(|var| var.place.projection.is_empty()); - let has_proj = || vars.iter().any(|var| !var.place.projection.is_empty()); + let whole_local_var = + vars.iter().find(|var| var.fragment.is_none() && var.projection.is_empty()).cloned(); + let has_proj = || vars.iter().any(|var| !var.projection.is_empty()); - let (fallback_var, kind) = if self.mir.local_kind(local) == mir::LocalKind::Arg { + let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg { let arg_index = local.index() - 1; // Add debuginfo even to unnamed arguments. // FIXME(eddyb) is this really needed? - let var = if arg_index == 0 && has_proj() { + if arg_index == 0 && has_proj() { // Hide closure environments from debuginfo. // FIXME(eddyb) shouldn't `ArgumentVariable` indices // be offset to account for the hidden environment? None + } else if whole_local_var.is_some() { + // No need to make up anything, there is a `mir::VarDebugInfo` + // covering the whole local. + // FIXME(eddyb) take `whole_local_var.source_info.scope` into + // account, just in case it doesn't use `ArgumentVariable` + // (after #67586 gets fixed). + None } else { - Some(mir::VarDebugInfo { - name: kw::Invalid, - source_info: self.mir.local_decls[local].source_info, - place: local.into(), + let name = kw::Invalid; + let decl = &self.mir.local_decls[local]; + let (scope, span) = if full_debug_info { + self.debug_loc(decl.source_info) + } else { + (None, decl.source_info.span) + }; + let dbg_var = scope.map(|scope| { + // FIXME(eddyb) is this `+ 1` needed at all? + let kind = VariableKind::ArgumentVariable(arg_index + 1); + + self.cx.create_dbg_var( + self.debug_context.as_ref().unwrap(), + name, + self.monomorphize(&decl.ty), + scope, + kind, + span, + ) + }); + + Some(PerLocalVarDebugInfo { + name, + source_info: decl.source_info, + dbg_var, + fragment: None, + projection: ty::List::empty(), }) - }; - (var, VariableKind::ArgumentVariable(arg_index + 1)) + } } else { - (None, VariableKind::LocalVariable) + None }; let local_ref = &self.locals[local]; if !bx.sess().fewer_names() { - let name = match whole_local_var.or(fallback_var.as_ref()) { + let name = match whole_local_var.or(fallback_var.clone()) { Some(var) if var.name != kw::Invalid => var.name.to_string(), _ => format!("{:?}", local), }; @@ -163,7 +212,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - if bx.sess().opts.debuginfo != DebugInfo::Full { + if !full_debug_info { return; } @@ -178,11 +227,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => return, }; - let vars = vars.iter().copied().chain(if whole_local_var.is_none() { - fallback_var.as_ref() - } else { - None - }); + let vars = vars.iter().cloned().chain(fallback_var); for var in vars { let mut layout = base.layout; @@ -190,10 +235,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME(eddyb) use smallvec here. let mut indirect_offsets = vec![]; - let kind = - if var.place.projection.is_empty() { kind } else { VariableKind::LocalVariable }; - - for elem in &var.place.projection[..] { + for elem in &var.projection[..] { match *elem { mir::ProjectionElem::Deref => { indirect_offsets.push(Size::ZERO); @@ -202,7 +244,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .ty .builtin_deref(true) .unwrap_or_else(|| { - span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty,) + span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty) }) .ty, ); @@ -219,24 +261,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => span_bug!( var.source_info.span, "unsupported var debuginfo place `{:?}`", - var.place, + mir::Place { local, projection: var.projection }, ), } } let (scope, span) = self.debug_loc(var.source_info); if let Some(scope) = scope { - bx.declare_local( - debug_context, - var.name, - layout.ty, - scope, - base.llval, - direct_offset, - &indirect_offsets, - kind, - span, - ); + if let Some(dbg_var) = var.dbg_var { + bx.dbg_var_addr( + debug_context, + dbg_var, + scope, + base.llval, + direct_offset, + &indirect_offsets, + var.fragment, + span, + ); + } } } } @@ -248,20 +291,108 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } -} -/// Partition all `VarDebuginfo` in `body`, by their base `Local`. -pub fn per_local_var_debug_info( - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, -) -> Option>>> { - if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() { - let mut per_local = IndexVec::from_elem(vec![], &body.local_decls); - for var in &body.var_debug_info { - per_local[var.place.local].push(var); + /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`. + pub fn compute_per_local_var_debug_info( + &self, + ) -> Option>>> { + let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; + + if !(full_debug_info || !self.cx.sess().fewer_names()) { + return None; + } + + let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls); + for var in &self.mir.var_debug_info { + let (scope, span) = if full_debug_info { + self.debug_loc(var.source_info) + } else { + (None, var.source_info.span) + }; + let (var_ty, var_kind) = match var.contents { + mir::VarDebugInfoContents::Compact(place) => ( + self.monomorphized_place_ty(place.as_ref()), + if self.mir.local_kind(place.local) == mir::LocalKind::Arg + && place.projection.is_empty() + { + // FIXME(eddyb, #67586) take `var.source_info.scope` into + // account to avoid using `ArgumentVariable` more than once + // per argument local. + + let arg_index = place.local.index() - 1; + + // FIXME(eddyb) shouldn't `ArgumentVariable` indices be + // offset in closures to account for the hidden environment? + // Also, is this `+ 1` needed at all? + VariableKind::ArgumentVariable(arg_index + 1) + } else { + VariableKind::LocalVariable + }, + ), + mir::VarDebugInfoContents::Composite { ty, fragments: _ } => { + (self.monomorphize(&ty), VariableKind::LocalVariable) + } + }; + let dbg_var = scope.map(|scope| { + self.cx.create_dbg_var( + self.debug_context.as_ref().unwrap(), + var.name, + var_ty, + scope, + var_kind, + span, + ) + }); + + match &var.contents { + mir::VarDebugInfoContents::Compact(place) => { + per_local[place.local].push(PerLocalVarDebugInfo { + name: var.name, + source_info: var.source_info, + dbg_var, + fragment: None, + projection: place.projection, + }); + } + mir::VarDebugInfoContents::Composite { ty: _, fragments } => { + 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) - } else { - None } } diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 02b07ebfb7df5..8a6284479c722 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -11,7 +11,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use self::analyze::CleanupKind; -use self::debuginfo::FunctionDebugContext; +use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo}; use self::place::PlaceRef; use rustc::mir::traversal; @@ -74,9 +74,10 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// notably `expect`. locals: IndexVec>, - /// All `VarDebuginfo` from the MIR body, partitioned by `Local`. - /// This is `None` if no variable debuginfo/names are needed. - per_local_var_debug_info: Option>>>, + /// All `VarDebugInfo` from the MIR body, partitioned by `Local`. + /// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed. + per_local_var_debug_info: + Option>>>, /// Caller location propagated if this function has `#[track_caller]`. caller_location: Option>, @@ -178,10 +179,12 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( funclets, locals: IndexVec::new(), debug_context, - per_local_var_debug_info: debuginfo::per_local_var_debug_info(cx.tcx(), mir_body), + per_local_var_debug_info: None, caller_location: None, }; + fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(); + let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index bc3a75250bf70..7b22961cd46e2 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs @@ -21,7 +21,10 @@ pub trait BackendTypes { type Type: CodegenObject; type Funclet; + // FIXME(eddyb) find a common convention for all of the debuginfo-related + // names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.). type DIScope: Copy; + type DIVariable: Copy; } pub trait Backend<'tcx>: diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index 6f331e14a0d37..b3c830eef8698 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -28,7 +28,7 @@ pub enum OverflowOp { pub trait BuilderMethods<'a, 'tcx>: HasCodegen<'tcx> - + DebugInfoBuilderMethods<'tcx> + + DebugInfoBuilderMethods + ArgAbiMethods<'tcx> + AbiBuilderMethods<'tcx> + IntrinsicCallMethods<'tcx> diff --git a/src/librustc_codegen_ssa/traits/debuginfo.rs b/src/librustc_codegen_ssa/traits/debuginfo.rs index d2784f5b860f7..35a9edc42a641 100644 --- a/src/librustc_codegen_ssa/traits/debuginfo.rs +++ b/src/librustc_codegen_ssa/traits/debuginfo.rs @@ -6,6 +6,7 @@ use rustc::ty::{Instance, Ty}; use rustc_hir::def_id::CrateNum; use rustc_span::{SourceFile, Span}; use rustc_target::abi::call::FnAbi; +use std::ops::Range; use syntax::ast::Name; pub trait DebugInfoMethods<'tcx>: BackendTypes { @@ -30,20 +31,35 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes { defining_crate: CrateNum, ) -> Self::DIScope; fn debuginfo_finalize(&self); -} -pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes { - fn declare_local( - &mut 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, variable_name: Name, variable_type: Ty<'tcx>, scope_metadata: Self::DIScope, + variable_kind: VariableKind, + span: Span, + ) -> Self::DIVariable; +} + +pub trait DebugInfoBuilderMethods: BackendTypes { + // 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, + dbg_var: Self::DIVariable, + scope_metadata: Self::DIScope, variable_alloca: Self::Value, direct_offset: Size, // NB: each offset implies a deref (i.e. they're steps in a pointer chain). indirect_offsets: &[Size], - variable_kind: VariableKind, + // Byte range in the `dbg_var` covered by this fragment, + // if this is a fragment of a composite `DIVariable`. + fragment: Option>, span: Span, ); fn set_source_location( diff --git a/src/librustc_codegen_ssa/traits/mod.rs b/src/librustc_codegen_ssa/traits/mod.rs index fff1b35507ce6..b8afadaadef38 100644 --- a/src/librustc_codegen_ssa/traits/mod.rs +++ b/src/librustc_codegen_ssa/traits/mod.rs @@ -93,5 +93,6 @@ pub trait HasCodegen<'tcx>: Type = Self::Type, Funclet = Self::Funclet, DIScope = Self::DIScope, + DIVariable = Self::DIVariable, >; } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 717359d75c3be..919959bad71df 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -5,7 +5,7 @@ use rustc::lint::builtin::MUTABLE_BORROW_RESERVATION_CONFLICT; use rustc::lint::builtin::UNUSED_MUT; use rustc::mir::{ read_only, Body, BodyAndCache, ClearCrossCrate, Local, Location, Mutability, Operand, Place, - PlaceElem, PlaceRef, ReadOnlyBodyAndCache, + PlaceElem, PlaceRef, ReadOnlyBodyAndCache, VarDebugInfoContents, }; use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; use rustc::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; @@ -121,7 +121,11 @@ fn do_mir_borrowck<'a, 'tcx>( let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); for var_debug_info in &input_body.var_debug_info { - if let Some(local) = var_debug_info.place.as_local() { + let place = match var_debug_info.contents { + VarDebugInfoContents::Compact(place) => place, + VarDebugInfoContents::Composite { .. } => continue, + }; + if let Some(local) = place.as_local() { if let Some(prev_name) = local_names[local] { if var_debug_info.name != prev_name { span_bug!( diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 5e42ba3279027..3712cfd89e83d 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -26,6 +26,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(try_blocks)] #![feature(associated_type_bounds)] #![feature(range_is_empty)] +#![feature(step_trait)] #![feature(stmt_expr_attributes)] #![feature(trait_alias)] #![recursion_limit = "256"] diff --git a/src/librustc_mir/transform/fragment_locals.rs b/src/librustc_mir/transform/fragment_locals.rs new file mode 100644 index 0000000000000..98cea8d2b296b --- /dev/null +++ b/src/librustc_mir/transform/fragment_locals.rs @@ -0,0 +1,494 @@ +use crate::transform::{MirPass, MirSource}; +use rustc::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc::mir::*; +use rustc::ty::layout::VariantIdx; +use rustc::ty::util::IntTypeExt; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc_index::vec::IndexVec; +use rustc_span::Span; +use std::collections::BTreeMap; +use std::iter::Step; +use std::ops::Range; + +pub struct FragmentLocals; + +impl MirPass<'tcx> for FragmentLocals { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { + let mut collector = FragmentTreeCollector { + tcx, + locals: body.local_decls.iter().map(|decl| FragmentTree::new(decl.ty)).collect(), + }; + + // Can't fragment return and arguments. + collector.locals[RETURN_PLACE].make_opaque(); + for arg in body.args_iter() { + collector.locals[arg].make_opaque(); + } + + collector.visit_body(read_only!(body)); + + // Enforce current limitations of `VarDebugInfo` wrt fragmentation. + for var_debug_info in &body.var_debug_info { + match &var_debug_info.contents { + VarDebugInfoContents::Compact(place) => { + if let Some(node) = collector.place_node(place) { + node.ensure_debug_info_compatible_descendents(); + } + } + VarDebugInfoContents::Composite { ty: _, fragments } => { + for fragment in fragments { + if let Some(node) = collector.place_node(&fragment.contents) { + node.ensure_debug_info_compatible_descendents(); + } + } + } + } + } + + let replacements = collector + .locals + .iter_enumerated_mut() + .map(|(local, root)| { + // Don't rename locals that are entirely opaque. + match root.kind { + FragmentTreeNodeKind::OpaqueLeaf { .. } => local..local.add_one(), + FragmentTreeNodeKind::Nested(_) => { + let source_info = body.local_decls[local].source_info; + let first = body.local_decls.next_index(); + root.assign_locals(&mut body.local_decls, source_info); + first..body.local_decls.next_index() + } + } + }) + .collect::>>(); + + // Expand `Storage{Live,Dead}` statements to refer to the replacement locals. + for bb in body.basic_blocks_mut() { + bb.expand_statements(|stmt| { + let (local, is_live) = match stmt.kind { + StatementKind::StorageLive(local) => (local, true), + StatementKind::StorageDead(local) => (local, false), + _ => return None, + }; + let range = replacements[local].clone(); + // FIXME(eddyb) `Range` should itself be iterable. + let range = (range.start.as_u32()..range.end.as_u32()).map(Local::from_u32); + let source_info = stmt.source_info; + Some(range.map(move |new_local| Statement { + source_info, + kind: if is_live { + StatementKind::StorageLive(new_local) + } else { + StatementKind::StorageDead(new_local) + }, + })) + }); + } + drop(replacements); + + // Lastly, replace all the opaque nodes with their new locals. + let mut replacer = FragmentTreeReplacer { tcx, span: body.span, locals: collector.locals }; + replacer.visit_body(body); + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum Fragment { + Discriminant, + Field(Option, Field), +} + +struct FragmentTree<'tcx> { + ty: Ty<'tcx>, + kind: FragmentTreeNodeKind<'tcx>, +} + +// FIXME(eddyb) find a shorter name for this +enum FragmentTreeNodeKind<'tcx> { + /// This node needs to remain compact, e.g. due to accesses / borrows. + OpaqueLeaf { replacement_local: Option }, + + /// This node can be fragmented into separate locals for its fields. + Nested(BTreeMap>), +} + +impl FragmentTree<'tcx> { + fn new(ty: Ty<'tcx>) -> Self { + let mut node = FragmentTree { ty, kind: FragmentTreeNodeKind::Nested(BTreeMap::new()) }; + + if let ty::Adt(adt_def, _) = ty.kind { + // Unions have (observably) overlapping members, so don't fragment them. + if adt_def.is_union() { + node.make_opaque(); + } + } + + node + } + + fn fragment(&mut self, fragment: Fragment, ty: Ty<'tcx>) -> Option<&mut Self> { + match self.kind { + FragmentTreeNodeKind::Nested(ref mut fragments) => { + Some(fragments.entry(fragment).or_insert_with(|| FragmentTree::new(ty))) + } + FragmentTreeNodeKind::OpaqueLeaf { .. } => None, + } + } + + fn discriminant(&mut self, tcx: TyCtxt<'tcx>) -> Option<&mut Self> { + match self.ty.kind { + ty::Adt(adt_def, _) if adt_def.is_enum() => { + let discr_ty = adt_def.repr.discr_type().to_ty(tcx); + self.fragment(Fragment::Discriminant, discr_ty) + } + _ => None, + } + } + + fn make_opaque(&mut self) { + if let FragmentTreeNodeKind::Nested(_) = self.kind { + self.kind = FragmentTreeNodeKind::OpaqueLeaf { replacement_local: None }; + } + } + + /// Make any descendent node which has discriminant/variant fragments opaque, + /// as `enum`s (and similarly, generators) are not compatible with variable + /// debuginfo currently (also see comments in `VarDebugInfoFragment`). + fn ensure_debug_info_compatible_descendents(&mut self) { + if let FragmentTreeNodeKind::Nested(ref mut fragments) = self.kind { + let enum_like = fragments.keys().any(|f| match f { + Fragment::Discriminant => true, + Fragment::Field(variant_index, _) => variant_index.is_some(), + }); + if enum_like { + self.make_opaque(); + } else { + for fragment in fragments.values_mut() { + fragment.ensure_debug_info_compatible_descendents(); + } + } + } + } + + fn project( + mut self: &'a mut Self, + mut proj_elems: &'tcx [PlaceElem<'tcx>], + ) -> (&'a mut Self, &'tcx [PlaceElem<'tcx>]) { + let mut variant_index = None; + while let [elem, rest @ ..] = proj_elems { + if let FragmentTreeNodeKind::OpaqueLeaf { .. } = self.kind { + break; + } + + match *elem { + ProjectionElem::Field(f, ty) => { + let field = Fragment::Field(variant_index, f); + // FIXME(eddyb) use `self.fragment(field)` post-Polonius(?). + match self.kind { + FragmentTreeNodeKind::Nested(ref mut fragments) => { + self = fragments.entry(field).or_insert_with(|| FragmentTree::new(ty)); + } + FragmentTreeNodeKind::OpaqueLeaf { .. } => unreachable!(), + } + } + + ProjectionElem::Downcast(..) => {} + + // FIXME(eddyb) support indexing by constants. + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | + // Can't support without alias analysis. + ProjectionElem::Index(_) | ProjectionElem::Deref => { + // If we can't project, we must be opaque. + self.make_opaque(); + break; + } + } + + proj_elems = rest; + variant_index = match *elem { + ProjectionElem::Downcast(_, v) => Some(v), + _ => None, + }; + } + + (self, proj_elems) + } + + fn assign_locals( + &mut self, + local_decls: &mut IndexVec>, + source_info: SourceInfo, + ) { + match self.kind { + FragmentTreeNodeKind::OpaqueLeaf { ref mut replacement_local } => { + let mut decl = LocalDecl::new_internal(self.ty, source_info.span); + decl.source_info = source_info; + *replacement_local = Some(local_decls.push(decl)); + } + FragmentTreeNodeKind::Nested(ref mut fragments) => { + for fragment in fragments.values_mut() { + fragment.assign_locals(local_decls, source_info); + } + } + } + } + + /// Push debuginfo for all leaves into `fragments`, pointing to + /// their respective `replacement_local`s (set by `assign_locals`). + fn gather_debug_info_fragments( + &self, + dbg_fragment_projection: &mut Vec, + dbg_fragments: &mut Vec>, + ) { + match self.kind { + FragmentTreeNodeKind::OpaqueLeaf { replacement_local } => { + dbg_fragments.push(VarDebugInfoFragment { + projection: dbg_fragment_projection.clone(), + contents: Place::from(replacement_local.expect("missing replacement")), + }) + } + FragmentTreeNodeKind::Nested(ref fragments) => { + for (&f, fragment) in fragments { + match f { + Fragment::Discriminant => unreachable!(), + Fragment::Field(variant_index, field) => { + assert_eq!(variant_index, None); + + dbg_fragment_projection.push(ProjectionElem::Field(field, ())); + } + } + fragment.gather_debug_info_fragments(dbg_fragment_projection, dbg_fragments); + dbg_fragment_projection.pop(); + } + } + } + } +} + +struct FragmentTreeCollector<'tcx> { + tcx: TyCtxt<'tcx>, + locals: IndexVec>, +} + +impl FragmentTreeCollector<'tcx> { + fn place_node(&'a mut self, place: &Place<'tcx>) -> Option<&'a mut FragmentTree<'tcx>> { + let (node, proj_elems) = self.locals[place.local].project(place.projection); + if proj_elems.is_empty() { Some(node) } else { None } + } +} + +impl Visitor<'tcx> for FragmentTreeCollector<'tcx> { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _: Location) { + // Borrows of fields might be used to access the entire local, + // by unsafe code, so it's better for the time being to remain + // conservative, until such uses have been definitely deemed UB. + if context.is_borrow() { + self.locals[place.local].make_opaque(); + } + + if let Some(node) = self.place_node(place) { + if context.is_use() { + node.make_opaque(); + } + } + } + + // Special-case `(Set)Discriminant(place)` to only mark `Fragment::Discriminant` as opaque. + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + let tcx = self.tcx; + + if let Rvalue::Discriminant(ref place) = *rvalue { + if let Some(node) = self.place_node(place) { + if let Some(discr) = node.discriminant(tcx) { + discr.make_opaque(); + } + } + } else { + self.super_rvalue(rvalue, location); + } + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + let tcx = self.tcx; + + if let StatementKind::SetDiscriminant { ref place, .. } = statement.kind { + if let Some(node) = self.place_node(place) { + if let Some(discr) = node.discriminant(tcx) { + discr.make_opaque(); + } + } + } else { + self.super_statement(statement, location); + } + } +} + +struct FragmentTreeReplacer<'tcx> { + tcx: TyCtxt<'tcx>, + span: Span, + locals: IndexVec>, +} + +impl FragmentTreeReplacer<'tcx> { + fn replace( + &mut self, + place: &Place<'tcx>, + ) -> Option, &mut FragmentTree<'tcx>>> { + let base_node = &mut self.locals[place.local]; + + // Avoid identity replacements, which would re-intern projections. + if let FragmentTreeNodeKind::OpaqueLeaf { replacement_local: None } = base_node.kind { + return None; + } + + let (node, proj_elems) = base_node.project(place.projection); + + Some(match node.kind { + FragmentTreeNodeKind::OpaqueLeaf { replacement_local } => Ok(Place { + local: replacement_local.expect("missing replacement"), + projection: self.tcx.intern_place_elems(proj_elems), + }), + + // HACK(eddyb) this only exists to support `(Set)Discriminant` below. + FragmentTreeNodeKind::Nested(_) => { + assert_eq!(proj_elems, &[]); + + Err(node) + } + }) + } +} + +impl MutVisitor<'tcx> for FragmentTreeReplacer<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _: Location) { + if let Some(place_replacement) = self.replace(place) { + match place_replacement { + Ok(place_replacement) => *place = place_replacement, + // HACK(eddyb) this only exists to support `(Set)Discriminant` below. + Err(_) => unreachable!(), + } + } + } + + // Break up `VarDebugInfo` into fragments where necessary. + fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) { + match &mut var_debug_info.contents { + VarDebugInfoContents::Compact(place) => { + if let Some(place_replacement) = self.replace(place) { + match place_replacement { + Ok(place_replacement) => *place = place_replacement, + Err(node) => { + let mut fragments = vec![]; + node.gather_debug_info_fragments(&mut vec![], &mut fragments); + var_debug_info.contents = + VarDebugInfoContents::Composite { ty: node.ty, fragments }; + } + } + } + } + VarDebugInfoContents::Composite { ty: _, fragments } => { + for fragment in fragments { + if let Some(place_replacement) = self.replace(&fragment.contents) { + match place_replacement { + Ok(place_replacement) => fragment.contents = place_replacement, + // FIXME(eddyb) implement!! + Err(_) => span_bug!( + var_debug_info.source_info.span, + "FIXME: implement fragmentation for {:?}", + var_debug_info, + ), + } + } + } + } + } + } + + // Special-case `(Set)Discriminant(place)` to use `discr_local` for `place`. + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + let tcx = self.tcx; + let span = self.span; + + if let Rvalue::Discriminant(ref mut place) = rvalue { + if let Some(place_replacement) = self.replace(place) { + match place_replacement { + Ok(place_replacement) => *place = place_replacement, + Err(node) => { + let discr = if let Some(discr) = node.discriminant(tcx) { + let discr = match discr.kind { + FragmentTreeNodeKind::OpaqueLeaf { replacement_local } => { + replacement_local + } + FragmentTreeNodeKind::Nested(_) => unreachable!(), + }; + Operand::Copy(Place::from(discr.expect("missing discriminant"))) + } else { + // Non-enums don't have discriminants other than `0u8`. + let discr_value = ty::Const::from_bits( + tcx, + 0, + ty::ParamEnv::empty().and(tcx.types.u8), + ); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: discr_value, + }) + }; + *rvalue = Rvalue::Use(discr); + } + } + } + } else { + self.super_rvalue(rvalue, location); + } + } + + fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { + self.span = statement.source_info.span; + + let tcx = self.tcx; + let span = self.span; + + if let StatementKind::SetDiscriminant { ref mut place, variant_index } = statement.kind { + if let Some(place_replacement) = self.replace(place) { + match place_replacement { + Ok(place_replacement) => **place = place_replacement, + Err(node) => { + if let Some(discr) = node.discriminant(tcx) { + let discr_ty = discr.ty; + let discr_local = match discr.kind { + FragmentTreeNodeKind::OpaqueLeaf { replacement_local } => { + replacement_local + } + FragmentTreeNodeKind::Nested(_) => unreachable!(), + }; + let discr_place = + Place::from(discr_local.expect("missing discriminant")); + let discr_value = ty::Const::from_bits( + tcx, + node.ty.discriminant_for_variant(tcx, variant_index).unwrap().val, + ty::ParamEnv::empty().and(discr_ty), + ); + let discr_rvalue = Rvalue::Use(Operand::Constant(box Constant { + span, + user_ty: None, + literal: discr_value, + })); + statement.kind = StatementKind::Assign(box (discr_place, discr_rvalue)); + } else { + // Non-enums don't have discriminants to set. + statement.kind = StatementKind::Nop; + } + } + } + } + } else { + self.super_statement(statement, location); + } + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 3c37eccc1843b..169df061064c2 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -24,6 +24,7 @@ pub mod deaggregator; pub mod dump_mir; pub mod elaborate_drops; pub mod erase_regions; +pub mod fragment_locals; pub mod generator; pub mod inline; pub mod instcombine; @@ -311,6 +312,14 @@ fn run_optimization_passes<'tcx>( &const_prop::ConstProp, &simplify_branches::SimplifyBranches::new("after-const-prop"), &deaggregator::Deaggregator, + // FIXME(eddyb) move this (and `Deaggregator` above) to happen + // before `ConstProp`, to maximize its effectiveness. + // NB: it's not known yet if `ConstProp` can create new + // opportunities for this optimization, or any others. + // Perhaps we should do a crater run erroring if any optimizations + // performed better with `ConstProp` running right before them + // (this would require a lot of MIR cloning, but seems doable). + &fragment_locals::FragmentLocals, ©_prop::CopyPropagation, &simplify_branches::SimplifyBranches::new("after-copy-prop"), &remove_noop_landing_pads::RemoveNoopLandingPads, diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs index 8291bc958808d..724dfda9c6bf7 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -195,12 +195,22 @@ fn write_graph_label<'tcx, W: Write>( } for var_debug_info in &body.var_debug_info { - write!( - w, - r#"debug {} => {};
"#, - var_debug_info.name, - escape(&var_debug_info.place) - )?; + write!(w, "debug {}", var_debug_info.name)?; + + match &var_debug_info.contents { + VarDebugInfoContents::Compact(place) => { + write!(w, " => {}", escape(place))?; + } + VarDebugInfoContents::Composite { ty, fragments } => { + write!(w, r#": {} {{
"#, escape(ty))?; + for fragment in fragments { + write!(w, r#" {},
"#, escape(fragment))?; + } + write!(w, r#"}}"#)?; + } + } + + write!(w, r#";
"#)?; } writeln!(w, ">;") diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index e0919e7e8f879..184cb864f443a 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -441,18 +441,34 @@ fn write_scope_tree( continue; } - let indented_debug_info = format!( - "{0:1$}debug {2} => {3:?};", - INDENT, indent, var_debug_info.name, var_debug_info.place, - ); + let mut indented_header = format!("{0:1$}debug {2}", INDENT, indent, var_debug_info.name); + + match &var_debug_info.contents { + VarDebugInfoContents::Compact(place) => { + write!(indented_header, " => {:?};", place).unwrap(); + } + VarDebugInfoContents::Composite { ty, fragments: _ } => { + write!(indented_header, ": {} {{", ty).unwrap(); + } + } writeln!( w, "{0:1$} // in {2}", - indented_debug_info, + indented_header, ALIGN, comment(tcx, var_debug_info.source_info), )?; + + match &var_debug_info.contents { + VarDebugInfoContents::Compact(_) => {} + VarDebugInfoContents::Composite { ty: _, fragments } => { + for fragment in fragments { + writeln!(w, "{0:1$}{0}{2:?},", INDENT, indent, fragment).unwrap(); + } + writeln!(w, "{0:1$}}};", INDENT, indent)?; + } + } } // Local variable types (including the user's name in a comment). diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index ad55a9fb7b81a..637cb5b3d3bd6 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -1642,7 +1642,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: for_arm_body.into(), + contents: VarDebugInfoContents::Compact(for_arm_body.into()), }); let locals = if has_guard.0 { let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { @@ -1659,7 +1659,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name, source_info: debug_source_info, - place: ref_for_guard.into(), + contents: VarDebugInfoContents::Compact(ref_for_guard.into()), }); LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs index fa5f266c76b14..44ace3e0d8be9 100644 --- a/src/librustc_mir_build/build/mod.rs +++ b/src/librustc_mir_build/build/mod.rs @@ -768,7 +768,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info.push(VarDebugInfo { name: ident.name, source_info, - place: arg_local.into(), + contents: VarDebugInfoContents::Compact(arg_local.into()), }); } } @@ -833,10 +833,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scope: OUTERMOST_SOURCE_SCOPE, span: tcx_hir.span(var_id), }, - place: Place { + contents: VarDebugInfoContents::Compact(Place { local: closure_env_arg.into(), projection: tcx.intern_place_elems(&projs), - }, + }), }); mutability diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 46e467011b91a..e01d5ad222afe 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -967,6 +967,10 @@ extern "C" int64_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(Ty)->print(OS); diff --git a/src/test/incremental/hashes/enum_constructors.rs b/src/test/incremental/hashes/enum_constructors.rs index 575b2e92966ea..10ec262e3ccd0 100644 --- a/src/test/incremental/hashes/enum_constructors.rs +++ b/src/test/incremental/hashes/enum_constructors.rs @@ -96,7 +96,7 @@ pub fn change_constructor_path_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_struct_like() { let _ = Enum2::Struct { @@ -119,7 +119,7 @@ pub fn change_constructor_variant_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="HirBody,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_struct_like() { let _ = Enum2::Struct2 { @@ -197,7 +197,7 @@ pub fn change_constructor_path_tuple_like() { #[cfg(not(cfail1))] #[rustc_clean( cfg="cfail2", - except="HirBody,optimized_mir,mir_built,typeck_tables_of" + except="HirBody,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_tuple_like() { @@ -215,7 +215,7 @@ pub fn change_constructor_variant_tuple_like() { #[cfg(not(cfail1))] #[rustc_clean( cfg="cfail2", - except="HirBody,optimized_mir,mir_built,typeck_tables_of" + except="HirBody,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_tuple_like() { @@ -278,7 +278,7 @@ pub fn change_constructor_path_c_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="HirBody,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_c_like() { let _ = Clike2::B; @@ -293,7 +293,7 @@ pub fn change_constructor_variant_c_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="HirBody,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_c_like() { let _ = Clike::C; diff --git a/src/test/mir-opt/simplify-arm-identity.rs b/src/test/mir-opt/simplify-arm-identity.rs index a8fa64255fb9a..06d1bea969745 100644 --- a/src/test/mir-opt/simplify-arm-identity.rs +++ b/src/test/mir-opt/simplify-arm-identity.rs @@ -28,20 +28,21 @@ fn main() { // StorageLive(_1); // ((_1 as Foo).0: u8) = const 0u8; // discriminant(_1) = 0; -// StorageLive(_2); +// StorageLive(_6); +// StorageLive(_7); // _3 = discriminant(_1); // switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // } // bb1: { -// ((_2 as Foo).0: u8) = const 0u8; -// discriminant(_2) = 0; +// _7 = const 0u8; +// _6 = const 0isize; // goto -> bb4; // } // ... // bb3: { // _4 = ((_1 as Foo).0: u8); -// ((_2 as Foo).0: u8) = move _4; -// discriminant(_2) = 0; +// _7 = move _4; +// _6 = const 0isize; // goto -> bb4; // } // ... @@ -54,20 +55,21 @@ fn main() { // StorageLive(_1); // ((_1 as Foo).0: u8) = const 0u8; // discriminant(_1) = 0; -// StorageLive(_2); +// StorageLive(_6); +// StorageLive(_7); // _3 = discriminant(_1); // switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // } // bb1: { -// ((_2 as Foo).0: u8) = const 0u8; -// discriminant(_2) = 0; +// _7 = const 0u8; +// _6 = const 0isize; // goto -> bb4; // } // ... // bb3: { // _4 = ((_1 as Foo).0: u8); -// ((_2 as Foo).0: u8) = move _4; -// discriminant(_2) = 0; +// _7 = move _4; +// _6 = const 0isize; // goto -> bb4; // } // ...