]> git.lizzy.rs Git - rust.git/commitdiff
debuginfo: Much improved handling of captured variables and by-value arguments.
authorMichael Woerister <michaelwoerister@gmail>
Tue, 3 Sep 2013 16:23:59 +0000 (18:23 +0200)
committerMichael Woerister <michaelwoerister@gmail>
Wed, 4 Sep 2013 16:38:46 +0000 (18:38 +0200)
src/librustc/middle/trans/_match.rs
src/librustc/middle/trans/closure.rs
src/librustc/middle/trans/debuginfo.rs
src/test/debug-info/var-captured-in-nested-closure.rs

index bc4cc2ce0e1617da6882bbbaf9a27668bb856191..39e3e97b48947a7ecb306dc2f3e75bffece05e92 100644 (file)
@@ -2000,6 +2000,9 @@ pub fn store_arg(mut bcx: @mut Block,
     let arg_ty = node_id_type(bcx, pat.id);
     add_clean(bcx, llval, arg_ty);
 
+    // Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an
+    // alloca, which only is the case on the general path, so lets disable the optimized path when
+    // debug info is enabled.
     let fast_path = !bcx.ccx().sess.opts.extra_debuginfo && simple_identifier(pat).is_some();
 
     if fast_path {
index d25fedf2d8e6edab7bc9598593bbf8fe527244e4..690d7343489e299909091845e09a8cf9cfb47c47 100644 (file)
@@ -308,7 +308,17 @@ pub fn load_environment(fcx: @mut FunctionContext,
     // Load a pointer to the closure data, skipping over the box header:
     let llcdata = opaque_box_body(bcx, cdata_ty, fcx.llenv);
 
-    // Populate the upvars from the environment.
+    // Store the pointer to closure data in an alloca for debug info because that's what the
+    // llvm.dbg.declare intrinsic expects
+    let env_pointer_alloca = if fcx.ccx.sess.opts.extra_debuginfo {
+        let alloc = alloc_ty(bcx, ty::mk_mut_ptr(bcx.tcx(), cdata_ty), "__debuginfo_env_ptr");
+        Store(bcx, llcdata, alloc);
+        Some(alloc)
+    } else {
+        None
+    };
+
+    // Populate the upvars from the environment
     let mut i = 0u;
     for cap_var in cap_vars.iter() {
         let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
@@ -319,8 +329,15 @@ pub fn load_environment(fcx: @mut FunctionContext,
         let def_id = ast_util::def_id_of_def(cap_var.def);
         fcx.llupvars.insert(def_id.node, upvarptr);
 
-        if fcx.ccx.sess.opts.extra_debuginfo {
-            debuginfo::create_captured_var_metadata(bcx, def_id.node, upvarptr, cap_var.span);
+        for &env_pointer_alloca in env_pointer_alloca.iter() {
+            debuginfo::create_captured_var_metadata(
+                bcx,
+                def_id.node,
+                cdata_ty,
+                env_pointer_alloca,
+                i,
+                sigil,
+                cap_var.span);
         }
 
         i += 1u;
index 09956438bb4b469ef590e736e76e702071c70250..112595d5576b87a4c34ed89d30ebfcd7fcbe5a95 100644 (file)
@@ -163,11 +163,12 @@ struct FunctionDebugContextData {
     source_locations_enabled: bool,
 }
 
-enum VariableAccess {
-    // The value given is a pointer to the data (T*)
-    DirectVariable,
-    // The value given has to be dereferenced once to get the pointer to data (T**)
-    IndirectVariable
+enum VariableAccess<'self> {
+    // The llptr given is an alloca containing the variable's value
+    DirectVariable { alloca: ValueRef },
+    // The llptr given is an alloca containing the start of some pointer chain leading to the
+    // variable's content.
+    IndirectVariable { alloca: ValueRef, address_operations: &'self [ValueRef] }
 }
 
 enum VariableKind {
@@ -213,11 +214,10 @@ pub fn create_local_var_metadata(bcx: @mut Block,
         let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
 
         declare_local(bcx,
-                      llptr,
                       var_ident,
                       var_type,
                       scope_metadata,
-                      DirectVariable,
+                      DirectVariable { alloca: llptr },
                       LocalVariable,
                       span);
     }
@@ -228,7 +228,10 @@ pub fn create_local_var_metadata(bcx: @mut Block,
 /// Adds the created metadata nodes directly to the crate's IR.
 pub fn create_captured_var_metadata(bcx: @mut Block,
                                     node_id: ast::NodeId,
-                                    llptr: ValueRef,
+                                    env_data_type: ty::t,
+                                    env_pointer: ValueRef,
+                                    env_index: uint,
+                                    closure_sigil: ast::Sigil,
                                     span: Span) {
     if fn_should_be_ignored(bcx.fcx) {
         return;
@@ -250,15 +253,39 @@ pub fn create_captured_var_metadata(bcx: @mut Block,
                 Captured var-id refers to unexpected ast_map variant: %?", ast_item));
         }
     };
+
     let variable_type = node_id_type(bcx, node_id);
     let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata;
 
+    let llvm_env_data_type = type_of::type_of(cx, env_data_type);
+    let byte_offset_of_var_in_env = machine::llelement_offset(cx, llvm_env_data_type, env_index);
+
+    let address_operations = unsafe {
+        [llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()),
+         llvm::LLVMDIBuilderCreateOpPlus(Type::i64().to_ref()),
+         C_i64(byte_offset_of_var_in_env as i64),
+         llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref())]
+    };
+
+    let address_op_count = match closure_sigil {
+        ast::BorrowedSigil => {
+            address_operations.len()
+        }
+        ast::ManagedSigil | ast::OwnedSigil => {
+            address_operations.len() - 1
+        }
+    };
+
+    let variable_access = IndirectVariable {
+        alloca: env_pointer,
+        address_operations: address_operations.slice_to(address_op_count)
+    };
+
     declare_local(bcx,
-                  llptr,
                   variable_ident,
                   variable_type,
                   scope_metadata,
-                  IndirectVariable,
+                  variable_access,
                   CapturedVariable,
                   span);
 }
@@ -285,11 +312,10 @@ pub fn create_match_binding_metadata(bcx: @mut Block,
     let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
 
     declare_local(bcx,
-                  llptr,
                   variable_ident,
                   variable_type,
                   scope_metadata,
-                  DirectVariable,
+                  DirectVariable { alloca: llptr },
                   LocalVariable,
                   span);
 }
@@ -333,14 +359,17 @@ pub fn create_self_argument_metadata(bcx: @mut Block,
         argument_index
     };
 
+    let address_operations = &[unsafe { llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()) }];
+
     let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
-        DirectVariable
+        DirectVariable { alloca: llptr }
     } else {
-        IndirectVariable
+        // This is not stable and may break with future LLVM versions. llptr should really always
+        // be an alloca. Anything else is not supported and just works by chance.
+        IndirectVariable { alloca: llptr, address_operations: address_operations }
     };
 
     declare_local(bcx,
-                  llptr,
                   special_idents::self_,
                   type_of_self,
                   scope_metadata,
@@ -373,11 +402,10 @@ pub fn create_argument_metadata(bcx: @mut Block,
             }
         };
 
-        let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
-            DirectVariable
-        } else {
-            IndirectVariable
-        };
+        if unsafe { llvm::LLVMIsAAllocaInst(llptr) } == ptr::null() {
+            cx.sess.span_bug(span, "debuginfo::create_argument_metadata() - \
+                                    Referenced variable location is not an alloca!");
+        }
 
         let argument_type = node_id_type(bcx, node_id);
         let argument_ident = ast_util::path_to_ident(path_ref);
@@ -390,11 +418,10 @@ pub fn create_argument_metadata(bcx: @mut Block,
         };
 
         declare_local(bcx,
-                      llptr,
                       argument_ident,
                       argument_type,
                       scope_metadata,
-                      variable_access,
+                      DirectVariable { alloca: llptr },
                       ArgumentVariable(argument_index),
                       span);
     }
@@ -783,7 +810,6 @@ fn compile_unit_metadata(cx: @mut CrateContext) {
 }
 
 fn declare_local(bcx: @mut Block,
-                 llptr: ValueRef,
                  variable_ident: ast::Ident,
                  variable_type: ty::t,
                  scope_metadata: DIScope,
@@ -805,37 +831,40 @@ fn declare_local(bcx: @mut Block,
         CapturedVariable => 0
     } as c_uint;
 
-    let var_metadata = do name.with_c_str |name| {
+    let (var_alloca, var_metadata) = do name.with_c_str |name| {
         match variable_access {
-            DirectVariable => unsafe {
-                llvm::LLVMDIBuilderCreateLocalVariable(
-                    DIB(cx),
-                    DW_TAG_auto_variable,
-                    scope_metadata,
-                    name,
-                    file_metadata,
-                    loc.line as c_uint,
-                    type_metadata,
-                    cx.sess.opts.optimize != session::No,
-                    0,
-                    argument_index)
-            },
-            IndirectVariable => unsafe {
-                let address_op = llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref());
-                let address_op_count = 1;
-
-                llvm::LLVMDIBuilderCreateComplexVariable(
-                    DIB(cx),
-                    DW_TAG_auto_variable,
-                    scope_metadata,
-                    name,
-                    file_metadata,
-                    loc.line as c_uint,
-                    type_metadata,
-                    ptr::to_unsafe_ptr(&address_op),
-                    address_op_count,
-                    argument_index)
-            }
+            DirectVariable { alloca } => (
+                alloca,
+                unsafe {
+                    llvm::LLVMDIBuilderCreateLocalVariable(
+                        DIB(cx),
+                        DW_TAG_auto_variable,
+                        scope_metadata,
+                        name,
+                        file_metadata,
+                        loc.line as c_uint,
+                        type_metadata,
+                        cx.sess.opts.optimize != session::No,
+                        0,
+                        argument_index)
+                }
+            ),
+            IndirectVariable { alloca, address_operations } => (
+                alloca,
+                unsafe {
+                    llvm::LLVMDIBuilderCreateComplexVariable(
+                        DIB(cx),
+                        DW_TAG_auto_variable,
+                        scope_metadata,
+                        name,
+                        file_metadata,
+                        loc.line as c_uint,
+                        type_metadata,
+                        vec::raw::to_ptr(address_operations),
+                        address_operations.len() as c_uint,
+                        argument_index)
+                }
+            )
         }
     };
 
@@ -843,7 +872,7 @@ fn declare_local(bcx: @mut Block,
     unsafe {
         let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
             DIB(cx),
-            llptr,
+            var_alloca,
             var_metadata,
             bcx.llbb);
 
index 60ad2a3544a3d135576b67b592cf84e4700d654b..cd20209ddfd0263594940b83715225ae69ad98eb 100644 (file)
 // check:$7 = 8
 // debugger:continue
 
+// debugger:finish
+// debugger:print variable
+// check:$8 = 1
+// debugger:print constant
+// check:$9 = 2
+// debugger:print a_struct
+// check:$10 = {a = -3, b = 4.5, c = 5}
+// debugger:print *struct_ref
+// check:$11 = {a = -3, b = 4.5, c = 5}
+// debugger:print *owned
+// check:$12 = 6
+// debugger:print managed->val
+// check:$13 = 7
+// debugger:print closure_local
+// check:$14 = 8
+// debugger:continue
+
 #[allow(unused_variable)];
 
 struct Struct {
@@ -59,11 +76,7 @@ fn main() {
             variable = constant + a_struct.a + struct_ref.a + *owned + *managed + closure_local;
         };
 
-        // breaking here will yield a wrong value for 'constant'. In particular, GDB will
-        // read the value of the register that supposedly contains the pointer to 'constant'
-        // and try derefence it. The register, however, already contains the actual value, and
-        // not a pointer to it. -mw
-        // zzz();
+        zzz();
 
         nested_closure();
     };