diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs index 604deffcf949b..2e5dc3db31af9 100644 --- a/src/librustc_codegen_ssa/mir/analyze.rs +++ b/src/librustc_codegen_ssa/mir/analyze.rs @@ -7,6 +7,7 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc::mir::{self, Location, TerminatorKind}; use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext}; use rustc::mir::traversal; +use rustc::session::config::DebugInfo; use rustc::ty; use rustc::ty::layout::{LayoutOf, HasTyCtxt}; use syntax_pos::DUMMY_SP; @@ -21,13 +22,20 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( analyzer.visit_body(mir); - for (index, (ty, span)) in mir.local_decls.iter() - .map(|l| (l.ty, l.source_info.span)) - .enumerate() + for (local, decl) in mir.local_decls.iter_enumerated() { - let ty = fx.monomorphize(&ty); - debug!("local {} has type {:?}", index, ty); - let layout = fx.cx.spanned_layout_of(ty, span); + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + if fx.cx.sess().opts.debuginfo == DebugInfo::Full { + if mir.local_kind(local) == mir::LocalKind::Arg || decl.name.is_some() { + analyzer.not_ssa(local); + continue; + } + } + + let ty = fx.monomorphize(&decl.ty); + debug!("local {:?} has type `{}`", local, ty); + let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span); if fx.cx.is_backend_immediate(layout) { // These sorts of types are immediates that we can store // in an Value without an alloca. @@ -40,7 +48,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // (e.g., structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.not_ssa(mir::Local::new(index)); + analyzer.not_ssa(local); } } diff --git a/src/librustc_codegen_ssa/mir/debuginfo.rs b/src/librustc_codegen_ssa/mir/debuginfo.rs index a253523f18120..d6b91c1f6bbb8 100644 --- a/src/librustc_codegen_ssa/mir/debuginfo.rs +++ b/src/librustc_codegen_ssa/mir/debuginfo.rs @@ -1,10 +1,16 @@ +use rustc_index::vec::Idx; use rustc::hir::def_id::CrateNum; use rustc::mir; +use rustc::session::config::DebugInfo; +use rustc::ty::{self, UpvarSubsts}; +use rustc::ty::layout::HasTyCtxt; +use rustc_target::abi::{Variants, VariantIdx}; use crate::traits::*; use syntax_pos::{DUMMY_SP, BytePos, Span}; +use syntax::symbol::kw; -use super::FunctionCx; +use super::{FunctionCx, LocalRef}; pub enum FunctionDebugContext { RegularContext(FunctionDebugContextData), @@ -142,4 +148,175 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { scope_metadata } } + + pub fn debug_declare_locals(&self, bx: &mut Bx) { + let tcx = self.cx.tcx(); + let upvar_debuginfo = &self.mir.__upvar_debuginfo_codegen_only_do_not_use; + + if bx.sess().opts.debuginfo != DebugInfo::Full { + return; + } + + for (local, local_ref) in self.locals.iter_enumerated() { + if local == mir::RETURN_PLACE { + continue; + } + + // FIXME(eddyb) add debuginfo for unsized places too. + let place = match local_ref { + LocalRef::Place(place) => place, + _ => continue, + }; + + let decl = &self.mir.local_decls[local]; + let (name, kind) = 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 name = if arg_index == 0 && !upvar_debuginfo.is_empty() { + // Hide closure environments from debuginfo. + // FIXME(eddyb) shouldn't `ArgumentVariable` indices + // be offset to account for the hidden environment? + None + } else { + Some(decl.name.unwrap_or(kw::Invalid)) + }; + (name, VariableKind::ArgumentVariable(arg_index + 1)) + } else { + (decl.name, VariableKind::LocalVariable) + }; + if let Some(name) = name { + let (scope, span) = self.debug_loc(mir::SourceInfo { + span: decl.source_info.span, + scope: decl.visibility_scope, + }); + if let Some(scope) = scope { + bx.declare_local(&self.debug_context, name, place.layout.ty, scope, + VariableAccess::DirectVariable { alloca: place.llval }, + kind, span); + } + } + } + + // Declare closure captures as if they were local variables. + // FIXME(eddyb) generalize this to `name => place` mappings. + let upvar_scope = if !upvar_debuginfo.is_empty() { + self.scopes[mir::OUTERMOST_SOURCE_SCOPE].scope_metadata + } else { + None + }; + if let Some(scope) = upvar_scope { + let place = match self.locals[mir::Local::new(1)] { + LocalRef::Place(place) => place, + _ => bug!(), + }; + + let pin_did = tcx.lang_items().pin_type(); + let (closure_layout, env_ref) = match place.layout.ty.kind { + ty::RawPtr(ty::TypeAndMut { ty, .. }) | + ty::Ref(_, ty, _) => (bx.layout_of(ty), true), + ty::Adt(def, substs) if Some(def.did) == pin_did => { + match substs.type_at(0).kind { + ty::Ref(_, ty, _) => (bx.layout_of(ty), true), + _ => (place.layout, false), + } + } + _ => (place.layout, false) + }; + + let (def_id, upvar_substs) = match closure_layout.ty.kind { + ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), + ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), + _ => bug!("upvar debuginfo with non-closure arg0 type `{}`", closure_layout.ty) + }; + let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); + + let extra_locals = { + let upvars = upvar_debuginfo + .iter() + .zip(upvar_tys) + .enumerate() + .map(|(i, (upvar, ty))| { + (None, i, upvar.debug_name, upvar.by_ref, ty, scope, DUMMY_SP) + }); + + let generator_fields = self.mir.generator_layout.as_ref().map(|generator_layout| { + let (def_id, gen_substs) = match closure_layout.ty.kind { + ty::Generator(def_id, substs, _) => (def_id, substs), + _ => bug!("generator layout without generator substs"), + }; + let state_tys = gen_substs.as_generator().state_tys(def_id, tcx); + + generator_layout.variant_fields.iter() + .zip(state_tys) + .enumerate() + .flat_map(move |(variant_idx, (fields, tys))| { + let variant_idx = Some(VariantIdx::from(variant_idx)); + fields.iter() + .zip(tys) + .enumerate() + .filter_map(move |(i, (field, ty))| { + let decl = &generator_layout. + __local_debuginfo_codegen_only_do_not_use[*field]; + if let Some(name) = decl.name { + let ty = self.monomorphize(&ty); + let (var_scope, var_span) = self.debug_loc(mir::SourceInfo { + span: decl.source_info.span, + scope: decl.visibility_scope, + }); + let var_scope = var_scope.unwrap_or(scope); + Some((variant_idx, i, name, false, ty, var_scope, var_span)) + } else { + None + } + }) + }) + }).into_iter().flatten(); + + upvars.chain(generator_fields) + }; + + for (variant_idx, field, name, by_ref, ty, var_scope, var_span) in extra_locals { + let fields = match variant_idx { + Some(variant_idx) => { + match &closure_layout.variants { + Variants::Multiple { variants, .. } => { + &variants[variant_idx].fields + }, + _ => bug!("variant index on univariant layout"), + } + } + None => &closure_layout.fields, + }; + let byte_offset_of_var_in_env = fields.offset(field).bytes(); + + let ops = bx.debuginfo_upvar_ops_sequence(byte_offset_of_var_in_env); + + // The environment and the capture can each be indirect. + let mut ops = if env_ref { &ops[..] } else { &ops[1..] }; + + let ty = if let (true, &ty::Ref(_, ty, _)) = (by_ref, &ty.kind) { + ty + } else { + ops = &ops[..ops.len() - 1]; + ty + }; + + let variable_access = VariableAccess::IndirectVariable { + alloca: place.llval, + address_operations: &ops + }; + bx.declare_local( + &self.debug_context, + name, + ty, + var_scope, + variable_access, + VariableKind::LocalVariable, + var_span + ); + } + } + } } diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index 650584fbdf7bd..8e9e21393738f 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -1,22 +1,17 @@ -use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts, Instance}; +use rustc::ty::{self, Ty, TypeFoldable, Instance}; use rustc::ty::layout::{TyLayout, HasTyCtxt, FnTypeExt}; use rustc::mir::{self, Body}; -use rustc::session::config::DebugInfo; use rustc_target::abi::call::{FnType, PassMode}; -use rustc_target::abi::{Variants, VariantIdx}; use crate::base; use crate::traits::*; -use syntax_pos::DUMMY_SP; -use syntax::symbol::kw; - use std::iter; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use self::analyze::CleanupKind; -use self::debuginfo::{VariableAccess, VariableKind, FunctionDebugContext}; +use self::debuginfo::FunctionDebugContext; use self::place::PlaceRef; use rustc::mir::traversal; @@ -189,62 +184,38 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let layout = bx.layout_of(fx.monomorphize(&decl.ty)); assert!(!layout.ty.has_erasable_regions()); - if let Some(name) = decl.name { - // User variable - let debug_scope = fx.scopes[decl.visibility_scope]; - let dbg = debug_scope.is_valid() && - bx.sess().opts.debuginfo == DebugInfo::Full; + if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { + debug!("alloc: {:?} (return place) -> place", local); + let llretptr = bx.get_param(0); + return LocalRef::Place(PlaceRef::new_sized(llretptr, layout)); + } - if !memory_locals.contains(local) && !dbg { - debug!("alloc: {:?} ({}) -> operand", local, name); - return LocalRef::new_operand(&mut bx, layout); - } + let decl_name = decl.name.map(|name| name.as_str()); + let decl_name = decl_name.as_ref().map(|name| &name[..]); + let name; + let name = if let Some(name) = decl_name { + name + } else { + // FIXME(eddyb) compute something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). + name = format!("{:?}", local); + &name + }; + if memory_locals.contains(local) { debug!("alloc: {:?} ({}) -> place", local, name); if layout.is_unsized() { - let indirect_place = - PlaceRef::alloca_unsized_indirect(&mut bx, layout); + let indirect_place = PlaceRef::alloca_unsized_indirect(&mut bx, layout); bx.set_var_name(indirect_place.llval, name); - // FIXME: add an appropriate debuginfo LocalRef::UnsizedPlace(indirect_place) } else { let place = PlaceRef::alloca(&mut bx, layout); bx.set_var_name(place.llval, name); - if dbg { - let (scope, span) = fx.debug_loc(mir::SourceInfo { - span: decl.source_info.span, - scope: decl.visibility_scope, - }); - bx.declare_local(&fx.debug_context, name, layout.ty, scope.unwrap(), - VariableAccess::DirectVariable { alloca: place.llval }, - VariableKind::LocalVariable, span); - } LocalRef::Place(place) } } else { - // Temporary or return place - if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { - debug!("alloc: {:?} (return place) -> place", local); - let llretptr = bx.get_param(0); - LocalRef::Place(PlaceRef::new_sized(llretptr, layout)) - } else if memory_locals.contains(local) { - debug!("alloc: {:?} -> place", local); - if layout.is_unsized() { - let indirect_place = PlaceRef::alloca_unsized_indirect(&mut bx, layout); - bx.set_var_name(indirect_place.llval, format_args!("{:?}", local)); - LocalRef::UnsizedPlace(indirect_place) - } else { - let place = PlaceRef::alloca(&mut bx, layout); - bx.set_var_name(place.llval, format_args!("{:?}", local)); - LocalRef::Place(place) - } - } else { - // If this is an immediate local, we do not create an - // alloca in advance. Instead we wait until we see the - // definition and update the operand there. - debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(&mut bx, layout) - } + debug!("alloc: {:?} ({}) -> operand", local, name); + LocalRef::new_operand(&mut bx, layout) } }; @@ -255,6 +226,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( .collect() }; + // Apply debuginfo to the newly allocated locals. + fx.debug_declare_locals(&mut bx); + // Branch to the START block, if it's not the entry block. if reentrant_start_block { bx.br(fx.blocks[mir::START_BLOCK]); @@ -363,18 +337,9 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( memory_locals: &BitSet, ) -> Vec> { let mir = fx.mir; - let tcx = fx.cx.tcx(); let mut idx = 0; let mut llarg_idx = fx.fn_ty.ret.is_indirect() as usize; - // Get the argument scope, if it exists and if we need it. - let arg_scope = fx.scopes[mir::OUTERMOST_SOURCE_SCOPE]; - let arg_scope = if bx.sess().opts.debuginfo == DebugInfo::Full { - arg_scope.scope_metadata - } else { - None - }; - mir.args_iter().enumerate().map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; @@ -409,22 +374,6 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.store_fn_arg(arg, &mut llarg_idx, pr_field); } - // Now that we have one alloca that contains the aggregate value, - // we can create one debuginfo entry for the argument. - arg_scope.map(|scope| { - let variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - bx.declare_local( - &fx.debug_context, - arg_decl.name.unwrap_or(kw::Invalid), - arg_ty, scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - }); - return LocalRef::Place(place); } @@ -435,21 +384,6 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.set_var_name(va_list.llval, name); bx.va_start(va_list.llval); - arg_scope.map(|scope| { - let variable_access = VariableAccess::DirectVariable { - alloca: va_list.llval - }; - bx.declare_local( - &fx.debug_context, - arg_decl.name.unwrap_or(kw::Invalid), - va_list.layout.ty, - scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - }); - return LocalRef::Place(va_list); } @@ -459,7 +393,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( llarg_idx += 1; } - if arg_scope.is_none() && !memory_locals.contains(local) { + if !memory_locals.contains(local) { // We don't have to cast or keep the argument in the alloca. // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead // of putting everything in allocas just so we can use llvm.dbg.declare. @@ -493,14 +427,14 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } - let place = if arg.is_sized_indirect() { + if arg.is_sized_indirect() { // Don't copy an indirect argument to an alloca, the caller // already put it in a temporary alloca and gave it up. // FIXME: lifetimes let llarg = bx.get_param(llarg_idx); bx.set_var_name(llarg, &name); llarg_idx += 1; - PlaceRef::new_sized(llarg, arg.layout) + LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) } else if arg.is_unsized_indirect() { // As the storage for the indirect argument lives during // the whole function call, we just copy the fat pointer. @@ -513,149 +447,12 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); bx.set_var_name(tmp.llval, name); indirect_operand.store(bx, tmp); - tmp + LocalRef::UnsizedPlace(tmp) } else { let tmp = PlaceRef::alloca(bx, arg.layout); bx.set_var_name(tmp.llval, name); bx.store_fn_arg(arg, &mut llarg_idx, tmp); - tmp - }; - let upvar_debuginfo = &mir.__upvar_debuginfo_codegen_only_do_not_use; - arg_scope.map(|scope| { - // Is this a regular argument? - if arg_index > 0 || upvar_debuginfo.is_empty() { - // The Rust ABI passes indirect variables using a pointer and a manual copy, so we - // need to insert a deref here, but the C ABI uses a pointer and a copy using the - // byval attribute, for which LLVM always does the deref itself, - // so we must not add it. - let variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - - bx.declare_local( - &fx.debug_context, - arg_decl.name.unwrap_or(kw::Invalid), - arg.layout.ty, - scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - return; - } - - let pin_did = tcx.lang_items().pin_type(); - // Or is it the closure environment? - let (closure_layout, env_ref) = match arg.layout.ty.kind { - ty::RawPtr(ty::TypeAndMut { ty, .. }) | - ty::Ref(_, ty, _) => (bx.layout_of(ty), true), - ty::Adt(def, substs) if Some(def.did) == pin_did => { - match substs.type_at(0).kind { - ty::Ref(_, ty, _) => (bx.layout_of(ty), true), - _ => (arg.layout, false), - } - } - _ => (arg.layout, false) - }; - - let (def_id, upvar_substs) = match closure_layout.ty.kind { - ty::Closure(def_id, substs) => (def_id, - UpvarSubsts::Closure(substs)), - ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - _ => bug!("upvar debuginfo with non-closure arg0 type `{}`", closure_layout.ty) - }; - let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); - - let extra_locals = { - let upvars = upvar_debuginfo - .iter() - .zip(upvar_tys) - .enumerate() - .map(|(i, (upvar, ty))| { - (None, i, upvar.debug_name, upvar.by_ref, ty, scope, DUMMY_SP) - }); - - let generator_fields = mir.generator_layout.as_ref().map(|generator_layout| { - let (def_id, gen_substs) = match closure_layout.ty.kind { - ty::Generator(def_id, substs, _) => (def_id, substs), - _ => bug!("generator layout without generator substs"), - }; - let state_tys = gen_substs.as_generator().state_tys(def_id, tcx); - - generator_layout.variant_fields.iter() - .zip(state_tys) - .enumerate() - .flat_map(move |(variant_idx, (fields, tys))| { - let variant_idx = Some(VariantIdx::from(variant_idx)); - fields.iter() - .zip(tys) - .enumerate() - .filter_map(move |(i, (field, ty))| { - let decl = &generator_layout. - __local_debuginfo_codegen_only_do_not_use[*field]; - if let Some(name) = decl.name { - let ty = fx.monomorphize(&ty); - let (var_scope, var_span) = fx.debug_loc(mir::SourceInfo { - span: decl.source_info.span, - scope: decl.visibility_scope, - }); - let var_scope = var_scope.unwrap_or(scope); - Some((variant_idx, i, name, false, ty, var_scope, var_span)) - } else { - None - } - }) - }) - }).into_iter().flatten(); - - upvars.chain(generator_fields) - }; - - for (variant_idx, field, name, by_ref, ty, var_scope, var_span) in extra_locals { - let fields = match variant_idx { - Some(variant_idx) => { - match &closure_layout.variants { - Variants::Multiple { variants, .. } => { - &variants[variant_idx].fields - }, - _ => bug!("variant index on univariant layout"), - } - } - None => &closure_layout.fields, - }; - let byte_offset_of_var_in_env = fields.offset(field).bytes(); - - let ops = bx.debuginfo_upvar_ops_sequence(byte_offset_of_var_in_env); - - // The environment and the capture can each be indirect. - let mut ops = if env_ref { &ops[..] } else { &ops[1..] }; - - let ty = if let (true, &ty::Ref(_, ty, _)) = (by_ref, &ty.kind) { - ty - } else { - ops = &ops[..ops.len() - 1]; - ty - }; - - let variable_access = VariableAccess::IndirectVariable { - alloca: place.llval, - address_operations: &ops - }; - bx.declare_local( - &fx.debug_context, - name, - ty, - var_scope, - variable_access, - VariableKind::LocalVariable, - var_span - ); - } - }); - if arg.is_unsized_indirect() { - LocalRef::UnsizedPlace(place) - } else { - LocalRef::Place(place) + LocalRef::Place(tmp) } }).collect() } diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 1d1bc2a81a2ca..3e7c4ef49fb5a 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -68,6 +68,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } } + // FIXME(eddyb) pass something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). pub fn alloca>( bx: &mut Bx, layout: TyLayout<'tcx>, @@ -78,6 +80,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } /// Returns a place for an indirect reference to an unsized place. + // FIXME(eddyb) pass something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). pub fn alloca_unsized_indirect>( bx: &mut Bx, layout: TyLayout<'tcx>,