about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_trans/base.rs4
-rw-r--r--src/librustc_trans/controlflow.rs8
-rw-r--r--src/librustc_trans/debuginfo/create_scope_map.rs81
-rw-r--r--src/librustc_trans/debuginfo/mod.rs48
-rw-r--r--src/librustc_trans/debuginfo/source_loc.rs104
-rw-r--r--src/librustc_trans/expr.rs10
-rw-r--r--src/librustc_trans/intrinsic.rs7
-rw-r--r--src/librustc_trans/mir/block.rs33
-rw-r--r--src/librustc_trans/mir/mod.rs91
-rw-r--r--src/librustc_trans/mir/rvalue.rs14
-rw-r--r--src/librustc_trans/mir/statement.rs12
11 files changed, 289 insertions, 123 deletions
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<DIScope> {
+    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<F>(cx: &CrateContext,
                      scope_span: Span,
@@ -74,7 +147,7 @@ fn with_new_scope<F>(cx: &CrateContext,
     F: FnOnce(&CrateContext, &mut Vec<ScopeStackEntry>, &mut NodeMap<DIScope>),
 {
     // 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.
-                return;
-            }
-
-            let cx = fcx.ccx;
-
-            debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span));
+        FunctionDebugContext::RegularContext(box ref data) => data
+    };
 
-            if function_debug_context.source_locations_enabled.get() {
-                let loc = span_start(cx, span);
-                let scope = scope_metadata(fcx, node_id, span);
+    if function_debug_context.source_location_override.get() {
+        // Just ignore any attempts to set a new debug location while
+        // the override is active.
+        return;
+    }
 
-                set_debug_location(cx, InternalDebugLocation::new(scope,
-                                                                  loc.line,
-                                                                  loc.col.to_usize()));
-            } else {
-                set_debug_location(cx, UnknownLocation);
+    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;
+            }
+        };
+
+        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<F, R>(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<F, R>(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<llvm::BuilderRef>,
+                          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());
-
-        match data.terminator().kind {
+        let terminator = data.terminator();
+        debug!("trans_block: terminator: {:?}", terminator);
+
+        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<LvalueRef<'tcx>>,
+
+    /// Debug information for MIR scopes.
+    scopes: Vec<DIScope>
 }
 
 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<Block<'blk,'tcx>> =
@@ -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<LvalueRef<'tcx>> {
     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)
                     }
                 }
             }