diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 956e1a5ce96..464e5c0cf1c 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -1276,7 +1276,7 @@ pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef { return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } } - debuginfo::clear_source_location(cx.fcx); + DebugLoc::None.apply(cx.fcx); Alloca(cx, ty, name) } @@ -1739,7 +1739,7 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> { self.build_return_block(ret_cx, ret_debug_loc); - debuginfo::clear_source_location(self); + DebugLoc::None.apply(self); self.cleanup(); } diff --git a/src/librustc_trans/controlflow.rs b/src/librustc_trans/controlflow.rs index 58971dec8a9..f793f0a6d55 100644 --- a/src/librustc_trans/controlflow.rs +++ b/src/librustc_trans/controlflow.rs @@ -167,11 +167,11 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, if cv == 1 { // if true { .. } [else { .. }] bcx = trans_block(bcx, &thn, dest); - debuginfo::clear_source_location(bcx.fcx); + DebugLoc::None.apply(bcx.fcx); } else { if let Some(elexpr) = els { bcx = expr::trans_into(bcx, &elexpr, dest); - debuginfo::clear_source_location(bcx.fcx); + DebugLoc::None.apply(bcx.fcx); } } @@ -181,7 +181,7 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let name = format!("then-block-{}-", thn.id); let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id); let then_bcx_out = trans_block(then_bcx_in, &thn, dest); - debuginfo::clear_source_location(bcx.fcx); + DebugLoc::None.apply(bcx.fcx); let cond_source_loc = cond.debug_loc(); @@ -204,7 +204,7 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Clear the source location because it is still set to whatever has been translated // right before. - debuginfo::clear_source_location(next_bcx.fcx); + DebugLoc::None.apply(next_bcx.fcx); next_bcx } diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index b1cfeb8125b..3eebd1f5ea2 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -8,19 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use super::FunctionDebugContext; use super::metadata::file_metadata; -use super::utils::DIB; +use super::utils::{DIB, span_start}; use llvm; use llvm::debuginfo::{DIScope, DISubprogram}; -use common::CrateContext; +use common::{CrateContext, FunctionContext}; use rustc::hir::pat_util; +use rustc::mir::repr::{Mir, ScopeId}; use rustc::util::nodemap::NodeMap; use libc::c_uint; +use std::ptr; + use syntax::codemap::{Span, Pos}; use syntax::{ast, codemap}; +use rustc_data_structures::bitvec::BitVector; use rustc::hir::{self, PatKind}; // This procedure builds the *scope map* for a given function, which maps any @@ -65,6 +70,74 @@ pub fn create_scope_map(cx: &CrateContext, return scope_map; } +/// Produce DIScope DIEs for each MIR Scope which has variables defined in it. +/// If debuginfo is disabled, the returned vector is empty. +pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec { + let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn"); + let mut scopes = vec![ptr::null_mut(); mir.scopes.len()]; + + let fn_metadata = match fcx.debug_context { + FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata, + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + return scopes; + } + }; + + // Find all the scopes with variables defined in them. + let mut has_variables = BitVector::new(mir.scopes.len()); + for var in &mir.var_decls { + has_variables.insert(var.scope.index()); + } + + // Instantiate all scopes. + for idx in 0..mir.scopes.len() { + let scope = ScopeId::new(idx); + make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes); + } + + scopes +} + +fn make_mir_scope(ccx: &CrateContext, + mir: &Mir, + has_variables: &BitVector, + fn_metadata: DISubprogram, + scope: ScopeId, + scopes: &mut [DIScope]) { + let idx = scope.index(); + if !scopes[idx].is_null() { + return; + } + + let scope_data = &mir.scopes[scope]; + let parent_scope = if let Some(parent) = scope_data.parent_scope { + make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes); + scopes[parent.index()] + } else { + // The root is the function itself. + scopes[idx] = fn_metadata; + return; + }; + + scopes[idx] = if !has_variables.contains(idx) { + // Do not create a DIScope if there are no variables + // defined in this MIR Scope, to avoid debuginfo bloat. + parent_scope + } else { + let loc = span_start(ccx, scope_data.span); + let file_metadata = file_metadata(ccx, &loc.file.name); + unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(ccx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + } + }; +} + // local helper functions for walking the AST. fn with_new_scope(cx: &CrateContext, scope_span: Span, @@ -74,7 +147,7 @@ fn with_new_scope(cx: &CrateContext, F: FnOnce(&CrateContext, &mut Vec, &mut NodeMap), { // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); + let loc = span_start(cx, scope_span); let file_metadata = file_metadata(cx, &loc.file.name); let parent_scope = scope_stack.last().unwrap().scope_metadata; @@ -199,7 +272,7 @@ fn walk_pattern(cx: &CrateContext, if need_new_scope { // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); + let loc = span_start(cx, pat.span); let file_metadata = file_metadata(cx, &loc.file.name); let parent_scope = scope_stack.last().unwrap().scope_metadata; diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 2ecd6c5ebf7..371f6b5efaa 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -20,7 +20,7 @@ use self::namespace::mangled_name_of_item; use self::type_names::compute_debuginfo_type_name; use self::metadata::{type_metadata, diverging_type_metadata}; use self::metadata::{file_metadata, scope_metadata, TypeMap, compile_unit_metadata}; -use self::source_loc::InternalDebugLocation; +use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; use llvm; use llvm::{ModuleRef, ContextRef, ValueRef}; @@ -32,7 +32,7 @@ use rustc::ty::subst::Substs; use rustc::hir; use abi::Abi; -use common::{NodeIdAndSpan, CrateContext, FunctionContext, Block}; +use common::{NodeIdAndSpan, CrateContext, FunctionContext, Block, BlockAndBuilder}; use monomorphize::Instance; use rustc::ty::{self, Ty}; use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; @@ -55,8 +55,7 @@ mod metadata; mod create_scope_map; mod source_loc; -pub use self::source_loc::set_source_location; -pub use self::source_loc::clear_source_location; +pub use self::create_scope_map::create_mir_scopes; pub use self::source_loc::start_emitting_source_locations; pub use self::source_loc::get_cleanup_debug_loc_for_ast_node; pub use self::source_loc::with_source_location_override; @@ -218,7 +217,7 @@ pub fn empty_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>) } // Clear the debug location so we don't assign them in the function prelude. - source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation); + source_loc::set_debug_location(cx, None, UnknownLocation); FunctionDebugContext::FunctionWithoutDebugInfo } @@ -239,7 +238,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // Clear the debug location so we don't assign them in the function prelude. // Do this here already, in case we do an early exit from this function. - source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation); + source_loc::set_debug_location(cx, None, UnknownLocation); // This can be the case for functions inlined from another crate let (containing_scope, span) = get_namespace_and_span_for_item(cx, instance.def); @@ -425,13 +424,13 @@ pub fn fill_scope_map_for_function<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, } } -fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - variable_name: ast::Name, - variable_type: Ty<'tcx>, - scope_metadata: DIScope, - variable_access: VariableAccess, - variable_kind: VariableKind, - span: Span) { +pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + variable_name: ast::Name, + variable_type: Ty<'tcx>, + scope_metadata: DIScope, + variable_access: VariableAccess, + variable_kind: VariableKind, + span: Span) { let cx: &CrateContext = bcx.ccx(); let filename = span_start(cx, span).file.name.clone(); @@ -465,9 +464,8 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, address_operations.len() as c_uint, argument_index) }; - source_loc::set_debug_location(cx, InternalDebugLocation::new(scope_metadata, - loc.line, - loc.col.to_usize())); + source_loc::set_debug_location(cx, None, + InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); unsafe { let debug_loc = llvm::LLVMGetCurrentDebugLocation(cx.raw_builder()); let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( @@ -491,7 +489,7 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, .get_ref(span) .source_locations_enabled .get()); - source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation); + source_loc::set_debug_location(cx, None, UnknownLocation); } _ => { /* nothing to do */ } } @@ -500,19 +498,17 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DebugLoc { At(ast::NodeId, Span), + ScopeAt(DIScope, Span), None } impl DebugLoc { - pub fn apply(&self, fcx: &FunctionContext) { - match *self { - DebugLoc::At(node_id, span) => { - source_loc::set_source_location(fcx, node_id, span); - } - DebugLoc::None => { - source_loc::clear_source_location(fcx); - } - } + pub fn apply(self, fcx: &FunctionContext) { + source_loc::set_source_location(fcx, None, self); + } + + pub fn apply_to_bcx(self, bcx: &BlockAndBuilder) { + source_loc::set_source_location(bcx.fcx(), Some(bcx), self); } } diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs index 2879da7d03b..6b00c1bb1a8 100644 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ b/src/librustc_trans/debuginfo/source_loc.rs @@ -10,12 +10,13 @@ use self::InternalDebugLocation::*; -use super::utils::{debug_context, span_start, fn_should_be_ignored}; +use super::utils::{debug_context, span_start}; use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER}; use super::{FunctionDebugContext, DebugLoc}; use llvm; use llvm::debuginfo::DIScope; +use builder::Builder; use common::{NodeIdAndSpan, CrateContext, FunctionContext}; use libc::c_uint; @@ -86,41 +87,46 @@ pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, /// Sets the current debug location at the beginning of the span. /// -/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id -/// parameter is used to reliably find the correct visibility scope for the code -/// position. +/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). pub fn set_source_location(fcx: &FunctionContext, - node_id: ast::NodeId, - span: Span) { - match fcx.debug_context { + builder: Option<&Builder>, + debug_loc: DebugLoc) { + let builder = builder.map(|b| b.llbuilder); + let function_debug_context = match fcx.debug_context { FunctionDebugContext::DebugInfoDisabled => return, FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(fcx.ccx, UnknownLocation); + set_debug_location(fcx.ccx, builder, UnknownLocation); return; } - FunctionDebugContext::RegularContext(box ref function_debug_context) => { - if function_debug_context.source_location_override.get() { - // Just ignore any attempts to set a new debug location while - // the override is active. + FunctionDebugContext::RegularContext(box ref data) => data + }; + + if function_debug_context.source_location_override.get() { + // Just ignore any attempts to set a new debug location while + // the override is active. + return; + } + + let dbg_loc = if function_debug_context.source_locations_enabled.get() { + let (scope, span) = match debug_loc { + DebugLoc::At(node_id, span) => { + (scope_metadata(fcx, node_id, span), span) + } + DebugLoc::ScopeAt(scope, span) => (scope, span), + DebugLoc::None => { + set_debug_location(fcx.ccx, builder, UnknownLocation); return; } + }; - let cx = fcx.ccx; - - debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span)); - - if function_debug_context.source_locations_enabled.get() { - let loc = span_start(cx, span); - let scope = scope_metadata(fcx, node_id, span); - - set_debug_location(cx, InternalDebugLocation::new(scope, - loc.line, - loc.col.to_usize())); - } else { - set_debug_location(cx, UnknownLocation); - } - } - } + debug!("set_source_location: {}", + fcx.ccx.sess().codemap().span_to_string(span)); + let loc = span_start(fcx.ccx, span); + InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) + } else { + UnknownLocation + }; + set_debug_location(fcx.ccx, builder, dbg_loc); } /// This function makes sure that all debug locations emitted while executing @@ -135,7 +141,7 @@ pub fn with_source_location_override(fcx: &FunctionContext, wrapped_function() } FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(fcx.ccx, UnknownLocation); + set_debug_location(fcx.ccx, None, UnknownLocation); wrapped_function() } FunctionDebugContext::RegularContext(box ref function_debug_context) => { @@ -152,17 +158,6 @@ pub fn with_source_location_override(fcx: &FunctionContext, } } -/// Clears the current debug location. -/// -/// Instructions generated hereafter won't be assigned a source location. -pub fn clear_source_location(fcx: &FunctionContext) { - if fn_should_be_ignored(fcx) { - return; - } - - set_debug_location(fcx.ccx, UnknownLocation); -} - /// Enables emitting source locations for the given functions. /// /// Since we don't want source locations to be emitted for the function prelude, @@ -195,37 +190,42 @@ impl InternalDebugLocation { } } -pub fn set_debug_location(cx: &CrateContext, debug_location: InternalDebugLocation) { - if debug_location == debug_context(cx).current_debug_location.get() { - return; +pub fn set_debug_location(cx: &CrateContext, + builder: Option, + debug_location: InternalDebugLocation) { + if builder.is_none() { + if debug_location == debug_context(cx).current_debug_location.get() { + return; + } } - let metadata_node; - - match debug_location { + let metadata_node = match debug_location { KnownLocation { scope, line, .. } => { // Always set the column to zero like Clang and GCC let col = UNKNOWN_COLUMN_NUMBER; debug!("setting debug location to {} {}", line, col); unsafe { - metadata_node = llvm::LLVMDIBuilderCreateDebugLocation( + llvm::LLVMDIBuilderCreateDebugLocation( debug_context(cx).llcontext, line as c_uint, col as c_uint, scope, - ptr::null_mut()); + ptr::null_mut()) } } UnknownLocation => { debug!("clearing debug location "); - metadata_node = ptr::null_mut(); + ptr::null_mut() } }; - unsafe { - llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); + if builder.is_none() { + debug_context(cx).current_debug_location.set(debug_location); } - debug_context(cx).current_debug_location.set(debug_location); + let builder = builder.unwrap_or_else(|| cx.raw_builder()); + unsafe { + llvm::LLVMSetCurrentDebugLocation(builder, metadata_node); + } } diff --git a/src/librustc_trans/expr.rs b/src/librustc_trans/expr.rs index beca81da05f..6955d51cecc 100644 --- a/src/librustc_trans/expr.rs +++ b/src/librustc_trans/expr.rs @@ -115,7 +115,7 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, -> Block<'blk, 'tcx> { let mut bcx = bcx; - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + expr.debug_loc().apply(bcx.fcx); if adjustment_required(bcx, expr) { // use trans, which may be less efficient but @@ -587,7 +587,7 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, debug!("trans_unadjusted(expr={:?})", expr); let _indenter = indenter(); - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + expr.debug_loc().apply(bcx.fcx); return match expr_kind(bcx.tcx(), expr) { ExprKind::Lvalue | ExprKind::RvalueDatum => { @@ -923,7 +923,7 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, return bcx; } - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + expr.debug_loc().apply(bcx.fcx); match expr.node { hir::ExprBreak(label_opt) => { @@ -987,7 +987,7 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // // We could avoid this intermediary with some analysis // to determine whether `dst` may possibly own `src`. - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + expr.debug_loc().apply(bcx.fcx); let src_datum = unpack_datum!( bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign")); let opt_hint_datum = dst_datum.kind.drop_flag_info.hint_datum(bcx); @@ -1062,7 +1062,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); let mut bcx = bcx; - debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); + expr.debug_loc().apply(bcx.fcx); // Entry into the method table if this is an overloaded call/op. let method_call = MethodCall::expr(expr.id); diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 4aadfe7d3f9..d7932046243 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -195,8 +195,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let name = tcx.item_name(def_id).as_str(); let span = match call_debug_location { - DebugLoc::At(_, span) => span, - DebugLoc::None => fcx.span.unwrap_or(DUMMY_SP) + DebugLoc::At(_, span) | DebugLoc::ScopeAt(_, span) => span, + DebugLoc::None => { + span_bug!(fcx.span.unwrap_or(DUMMY_SP), + "intrinsic `{}` called with missing span", name); + } }; let cleanup_scope = fcx.push_custom_cleanup_scope(); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 303cf61ad33..f70dc0183fd 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -54,9 +54,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx = self.trans_statement(bcx, statement); } - debug!("trans_block: terminator: {:?}", data.terminator()); + let terminator = data.terminator(); + debug!("trans_block: terminator: {:?}", terminator); - match data.terminator().kind { + let debug_loc = DebugLoc::ScopeAt(self.scopes[terminator.scope.index()], + terminator.span); + debug_loc.apply_to_bcx(&bcx); + debug_loc.apply(bcx.fcx()); + match terminator.kind { mir::TerminatorKind::Resume => { if let Some(cleanup_pad) = cleanup_pad { bcx.cleanup_ret(cleanup_pad, None); @@ -117,7 +122,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::TerminatorKind::Return => { bcx.with_block(|bcx| { - self.fcx.build_return_block(bcx, DebugLoc::None); + self.fcx.build_return_block(bcx, debug_loc); }) } @@ -144,7 +149,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.llblock(target), unwind.llbb(), cleanup_bundle.as_ref()); - self.bcx(target).at_start(|bcx| drop::drop_fill(bcx, lvalue.llval, ty)); + self.bcx(target).at_start(|bcx| { + debug_loc.apply_to_bcx(bcx); + drop::drop_fill(bcx, lvalue.llval, ty) + }); } else { bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref()); drop::drop_fill(&bcx, lvalue.llval, ty); @@ -267,7 +275,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx.with_block(|bcx| { trans_intrinsic_call(bcx, callee.ty, &fn_ty, ArgVals(llargs), dest, - DebugLoc::None); + debug_loc); }); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { @@ -311,13 +319,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { cleanup_bundle.as_ref()); fn_ty.apply_attrs_callsite(invokeret); - landingpad.at_start(|bcx| for op in args { - self.set_operand_dropped(bcx, op); + landingpad.at_start(|bcx| { + debug_loc.apply_to_bcx(bcx); + for op in args { + self.set_operand_dropped(bcx, op); + } }); if destination.is_some() { let ret_bcx = ret_bcx.build(); ret_bcx.at_start(|ret_bcx| { + debug_loc.apply_to_bcx(ret_bcx); let op = OperandRef { val: OperandValue::Immediate(invokeret), ty: sig.output.unwrap() @@ -514,7 +526,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let use_funclets = base::wants_msvc_seh(bcx.sess()) && data.is_cleanup; let cleanup_pad = if use_funclets { bcx.set_personality_fn(self.fcx.eh_personality()); - bcx.at_start(|bcx| Some(bcx.cleanup_pad(None, &[]))) + bcx.at_start(|bcx| { + DebugLoc::None.apply_to_bcx(bcx); + Some(bcx.cleanup_pad(None, &[])) + }) } else { None }; diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 7e44b72db7f..1869845ccb1 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -10,11 +10,19 @@ use libc::c_uint; use llvm::{self, ValueRef}; +use llvm::debuginfo::DIScope; use rustc::ty; use rustc::mir::repr as mir; use rustc::mir::tcx::LvalueTy; +use session::config::FullDebugInfo; use base; use common::{self, Block, BlockAndBuilder, FunctionContext}; +use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind}; +use machine; +use type_of; + +use syntax::codemap::DUMMY_SP; +use syntax::parse::token; use std::ops::Deref; use std::rc::Rc; @@ -44,8 +52,6 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { } } -// FIXME DebugLoc is always None right now - /// Master context for translating MIR. pub struct MirContext<'bcx, 'tcx:'bcx> { mir: CachedMir<'bcx, 'tcx>, @@ -92,6 +98,9 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// always indirect, though we try to avoid creating an alloca /// when we can (and just reuse the pointer the caller provided). args: Vec>, + + /// Debug information for MIR scopes. + scopes: Vec } enum TempRef<'tcx> { @@ -113,11 +122,26 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { analyze::lvalue_temps(bcx, &mir) }); + // Compute debuginfo scopes from MIR scopes. + let scopes = debuginfo::create_mir_scopes(fcx); + // Allocate variable and temp allocas let vars = mir.var_decls.iter() - .map(|decl| (bcx.monomorphize(&decl.ty), decl.name)) - .map(|(mty, name)| LvalueRef::alloca(&bcx, mty, &name.as_str())) - .collect(); + .map(|decl| (bcx.monomorphize(&decl.ty), decl)) + .map(|(mty, decl)| { + let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str()); + + let scope = scopes[decl.scope.index()]; + if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo { + bcx.with_block(|bcx| { + declare_local(bcx, decl.name, mty, scope, + VariableAccess::DirectVariable { alloca: lvalue.llval }, + VariableKind::LocalVariable, decl.span); + }); + } + + lvalue + }).collect(); let temps = mir.temp_decls.iter() .map(|decl| bcx.monomorphize(&decl.ty)) .enumerate() @@ -132,7 +156,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { TempRef::Operand(None) }) .collect(); - let args = arg_value_refs(&bcx, &mir); + let args = arg_value_refs(&bcx, &mir, &scopes); // Allocate a `Block` for every basic block let block_bcxs: Vec> = @@ -152,6 +176,11 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let start_bcx = block_bcxs[mir::START_BLOCK.index()]; bcx.br(start_bcx.llbb); + // Up until here, IR instructions for this function have explicitly not been annotated with + // source code location, so we don't step into call setup code. From here on, source location + // emitting should be enabled. + debuginfo::start_emitting_source_locations(fcx); + let mut mircx = MirContext { mir: mir.clone(), fcx: fcx, @@ -161,6 +190,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { vars: vars, temps: temps, args: args, + scopes: scopes }; let mut visited = BitVector::new(mir_blocks.len()); @@ -185,6 +215,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { } } + DebugLoc::None.apply(fcx); fcx.cleanup(); } @@ -192,12 +223,25 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { /// argument's value. As arguments are lvalues, these are always /// indirect. fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, - mir: &mir::Mir<'tcx>) + mir: &mir::Mir<'tcx>, + scopes: &[DIScope]) -> Vec> { let fcx = bcx.fcx(); let tcx = bcx.tcx(); let mut idx = 0; let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; + + // Get the argument scope assuming ScopeId(0) has no parent. + let arg_scope = mir.scopes.get(0).and_then(|data| { + let scope = scopes[0]; + if data.parent_scope.is_none() && !scope.is_null() && + bcx.sess().opts.debuginfo == FullDebugInfo { + Some(scope) + } else { + None + } + }); + mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| { let arg_ty = bcx.monomorphize(&arg_decl.ty); if arg_decl.spread { @@ -211,13 +255,14 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, _ => bug!("spread argument isn't a tuple?!") }; + let lltuplety = type_of::type_of(bcx.ccx(), arg_ty); let lltemp = bcx.with_block(|bcx| { base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) }); for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { let dst = bcx.struct_gep(lltemp, i); let arg = &fcx.fn_ty.args[idx]; - idx += 1; + idx += 1; if common::type_is_fat_ptr(tcx, tupled_arg_ty) { // We pass fat pointers as two words, but inside the tuple // they are the two sub-fields of a single aggregate field. @@ -228,17 +273,37 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, } else { arg.store_fn_arg(bcx, &mut llarg_idx, dst); } + + bcx.with_block(|bcx| arg_scope.map(|scope| { + let byte_offset_of_var_in_tuple = + machine::llelement_offset(bcx.ccx(), lltuplety, i); + + let address_operations = unsafe { + [llvm::LLVMDIBuilderCreateOpDeref(), + llvm::LLVMDIBuilderCreateOpPlus(), + byte_offset_of_var_in_tuple as i64] + }; + + let variable_access = VariableAccess::IndirectVariable { + alloca: lltemp, + address_operations: &address_operations + }; + declare_local(bcx, token::special_idents::invalid.name, + tupled_arg_ty, scope, variable_access, + VariableKind::ArgumentVariable(arg_index + i + 1), + bcx.fcx().span.unwrap_or(DUMMY_SP)); + })); } return LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty)); } let arg = &fcx.fn_ty.args[idx]; idx += 1; - let llval = if arg.is_indirect() { + let llval = if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo { // Don't copy an indirect argument to an alloca, the caller // already put it in a temporary alloca and gave it up, unless // we emit extra-debug-info, which requires local allocas :(. - // FIXME: lifetimes, debug info + // FIXME: lifetimes let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); llarg_idx += 1; llarg @@ -261,6 +326,12 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, } lltemp }; + bcx.with_block(|bcx| arg_scope.map(|scope| { + declare_local(bcx, token::special_idents::invalid.name, arg_ty, scope, + VariableAccess::DirectVariable { alloca: llval }, + VariableKind::ArgumentVariable(arg_index + 1), + bcx.fcx().span.unwrap_or(DUMMY_SP)); + })); LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)) }).collect() } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 8e5d220b4f4..641603f5aaa 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -38,7 +38,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_rvalue(&mut self, bcx: BlockAndBuilder<'bcx, 'tcx>, dest: LvalueRef<'tcx>, - rvalue: &mir::Rvalue<'tcx>) + rvalue: &mir::Rvalue<'tcx>, + debug_loc: DebugLoc) -> BlockAndBuilder<'bcx, 'tcx> { debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})", @@ -58,7 +59,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { // into-coerce of a thin pointer to a fat pointer - just // use the operand path. - let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc); self.store_operand(&bcx, dest.llval, temp); return bcx; } @@ -217,7 +218,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); - let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc); self.store_operand(&bcx, dest.llval, temp); bcx } @@ -226,7 +227,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_rvalue_operand(&mut self, bcx: BlockAndBuilder<'bcx, 'tcx>, - rvalue: &mir::Rvalue<'tcx>) + rvalue: &mir::Rvalue<'tcx>, + debug_loc: DebugLoc) -> (BlockAndBuilder<'bcx, 'tcx>, OperandRef<'tcx>) { assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); @@ -419,7 +421,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { lhs_addr, lhs_extra, rhs_addr, rhs_extra, lhs.ty, op.to_hir_binop(), - DebugLoc::None) + debug_loc) }) } _ => bug!() @@ -470,7 +472,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { box_ty, llsize, llalign, - DebugLoc::None); + debug_loc); llval = Some(val); bcx }); diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index 1d85ac6fb79..e4967cead07 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -10,6 +10,7 @@ use rustc::mir::repr as mir; use common::BlockAndBuilder; +use debuginfo::DebugLoc; use super::MirContext; use super::TempRef; @@ -21,6 +22,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { -> BlockAndBuilder<'bcx, 'tcx> { debug!("trans_statement(statement={:?})", statement); + let debug_loc = DebugLoc::ScopeAt(self.scopes[statement.scope.index()], + statement.span); + debug_loc.apply_to_bcx(&bcx); + debug_loc.apply(bcx.fcx()); match statement.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { match *lvalue { @@ -28,10 +33,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let index = index as usize; match self.temps[index as usize] { TempRef::Lvalue(tr_dest) => { - self.trans_rvalue(bcx, tr_dest, rvalue) + self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) } TempRef::Operand(None) => { - let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue); + let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, + debug_loc); self.temps[index] = TempRef::Operand(Some(operand)); bcx } @@ -44,7 +50,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } _ => { let tr_dest = self.trans_lvalue(&bcx, lvalue); - self.trans_rvalue(bcx, tr_dest, rvalue) + self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) } } }