debuginfo: Much improved handling of captured variables and by-value arguments.
This commit is contained in:
parent
e0b63b0e2a
commit
c49eb075db
4 changed files with 124 additions and 62 deletions
|
@ -2000,6 +2000,9 @@ pub fn store_arg(mut bcx: @mut Block,
|
||||||
let arg_ty = node_id_type(bcx, pat.id);
|
let arg_ty = node_id_type(bcx, pat.id);
|
||||||
add_clean(bcx, llval, arg_ty);
|
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();
|
let fast_path = !bcx.ccx().sess.opts.extra_debuginfo && simple_identifier(pat).is_some();
|
||||||
|
|
||||||
if fast_path {
|
if fast_path {
|
||||||
|
|
|
@ -308,7 +308,17 @@ pub fn load_environment(fcx: @mut FunctionContext,
|
||||||
// Load a pointer to the closure data, skipping over the box header:
|
// Load a pointer to the closure data, skipping over the box header:
|
||||||
let llcdata = opaque_box_body(bcx, cdata_ty, fcx.llenv);
|
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;
|
let mut i = 0u;
|
||||||
for cap_var in cap_vars.iter() {
|
for cap_var in cap_vars.iter() {
|
||||||
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
|
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);
|
let def_id = ast_util::def_id_of_def(cap_var.def);
|
||||||
fcx.llupvars.insert(def_id.node, upvarptr);
|
fcx.llupvars.insert(def_id.node, upvarptr);
|
||||||
|
|
||||||
if fcx.ccx.sess.opts.extra_debuginfo {
|
for &env_pointer_alloca in env_pointer_alloca.iter() {
|
||||||
debuginfo::create_captured_var_metadata(bcx, def_id.node, upvarptr, cap_var.span);
|
debuginfo::create_captured_var_metadata(
|
||||||
|
bcx,
|
||||||
|
def_id.node,
|
||||||
|
cdata_ty,
|
||||||
|
env_pointer_alloca,
|
||||||
|
i,
|
||||||
|
sigil,
|
||||||
|
cap_var.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1u;
|
i += 1u;
|
||||||
|
|
|
@ -163,11 +163,12 @@ struct FunctionDebugContextData {
|
||||||
source_locations_enabled: bool,
|
source_locations_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VariableAccess {
|
enum VariableAccess<'self> {
|
||||||
// The value given is a pointer to the data (T*)
|
// The llptr given is an alloca containing the variable's value
|
||||||
DirectVariable,
|
DirectVariable { alloca: ValueRef },
|
||||||
// The value given has to be dereferenced once to get the pointer to data (T**)
|
// The llptr given is an alloca containing the start of some pointer chain leading to the
|
||||||
IndirectVariable
|
// variable's content.
|
||||||
|
IndirectVariable { alloca: ValueRef, address_operations: &'self [ValueRef] }
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VariableKind {
|
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);
|
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
|
||||||
|
|
||||||
declare_local(bcx,
|
declare_local(bcx,
|
||||||
llptr,
|
|
||||||
var_ident,
|
var_ident,
|
||||||
var_type,
|
var_type,
|
||||||
scope_metadata,
|
scope_metadata,
|
||||||
DirectVariable,
|
DirectVariable { alloca: llptr },
|
||||||
LocalVariable,
|
LocalVariable,
|
||||||
span);
|
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.
|
/// Adds the created metadata nodes directly to the crate's IR.
|
||||||
pub fn create_captured_var_metadata(bcx: @mut Block,
|
pub fn create_captured_var_metadata(bcx: @mut Block,
|
||||||
node_id: ast::NodeId,
|
node_id: ast::NodeId,
|
||||||
llptr: ValueRef,
|
env_data_type: ty::t,
|
||||||
|
env_pointer: ValueRef,
|
||||||
|
env_index: uint,
|
||||||
|
closure_sigil: ast::Sigil,
|
||||||
span: Span) {
|
span: Span) {
|
||||||
if fn_should_be_ignored(bcx.fcx) {
|
if fn_should_be_ignored(bcx.fcx) {
|
||||||
return;
|
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));
|
Captured var-id refers to unexpected ast_map variant: %?", ast_item));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let variable_type = node_id_type(bcx, node_id);
|
let variable_type = node_id_type(bcx, node_id);
|
||||||
let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata;
|
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,
|
declare_local(bcx,
|
||||||
llptr,
|
|
||||||
variable_ident,
|
variable_ident,
|
||||||
variable_type,
|
variable_type,
|
||||||
scope_metadata,
|
scope_metadata,
|
||||||
IndirectVariable,
|
variable_access,
|
||||||
CapturedVariable,
|
CapturedVariable,
|
||||||
span);
|
span);
|
||||||
}
|
}
|
||||||
|
@ -285,11 +312,10 @@ pub fn create_match_binding_metadata(bcx: @mut Block,
|
||||||
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
|
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
|
||||||
|
|
||||||
declare_local(bcx,
|
declare_local(bcx,
|
||||||
llptr,
|
|
||||||
variable_ident,
|
variable_ident,
|
||||||
variable_type,
|
variable_type,
|
||||||
scope_metadata,
|
scope_metadata,
|
||||||
DirectVariable,
|
DirectVariable { alloca: llptr },
|
||||||
LocalVariable,
|
LocalVariable,
|
||||||
span);
|
span);
|
||||||
}
|
}
|
||||||
|
@ -333,14 +359,17 @@ pub fn create_self_argument_metadata(bcx: @mut Block,
|
||||||
argument_index
|
argument_index
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let address_operations = &[unsafe { llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()) }];
|
||||||
|
|
||||||
let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
|
let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
|
||||||
DirectVariable
|
DirectVariable { alloca: llptr }
|
||||||
} else {
|
} 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,
|
declare_local(bcx,
|
||||||
llptr,
|
|
||||||
special_idents::self_,
|
special_idents::self_,
|
||||||
type_of_self,
|
type_of_self,
|
||||||
scope_metadata,
|
scope_metadata,
|
||||||
|
@ -373,11 +402,10 @@ pub fn create_argument_metadata(bcx: @mut Block,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
|
if unsafe { llvm::LLVMIsAAllocaInst(llptr) } == ptr::null() {
|
||||||
DirectVariable
|
cx.sess.span_bug(span, "debuginfo::create_argument_metadata() - \
|
||||||
} else {
|
Referenced variable location is not an alloca!");
|
||||||
IndirectVariable
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let argument_type = node_id_type(bcx, node_id);
|
let argument_type = node_id_type(bcx, node_id);
|
||||||
let argument_ident = ast_util::path_to_ident(path_ref);
|
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,
|
declare_local(bcx,
|
||||||
llptr,
|
|
||||||
argument_ident,
|
argument_ident,
|
||||||
argument_type,
|
argument_type,
|
||||||
scope_metadata,
|
scope_metadata,
|
||||||
variable_access,
|
DirectVariable { alloca: llptr },
|
||||||
ArgumentVariable(argument_index),
|
ArgumentVariable(argument_index),
|
||||||
span);
|
span);
|
||||||
}
|
}
|
||||||
|
@ -783,7 +810,6 @@ fn compile_unit_metadata(cx: @mut CrateContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_local(bcx: @mut Block,
|
fn declare_local(bcx: @mut Block,
|
||||||
llptr: ValueRef,
|
|
||||||
variable_ident: ast::Ident,
|
variable_ident: ast::Ident,
|
||||||
variable_type: ty::t,
|
variable_type: ty::t,
|
||||||
scope_metadata: DIScope,
|
scope_metadata: DIScope,
|
||||||
|
@ -805,37 +831,40 @@ fn declare_local(bcx: @mut Block,
|
||||||
CapturedVariable => 0
|
CapturedVariable => 0
|
||||||
} as c_uint;
|
} 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 {
|
match variable_access {
|
||||||
DirectVariable => unsafe {
|
DirectVariable { alloca } => (
|
||||||
llvm::LLVMDIBuilderCreateLocalVariable(
|
alloca,
|
||||||
DIB(cx),
|
unsafe {
|
||||||
DW_TAG_auto_variable,
|
llvm::LLVMDIBuilderCreateLocalVariable(
|
||||||
scope_metadata,
|
DIB(cx),
|
||||||
name,
|
DW_TAG_auto_variable,
|
||||||
file_metadata,
|
scope_metadata,
|
||||||
loc.line as c_uint,
|
name,
|
||||||
type_metadata,
|
file_metadata,
|
||||||
cx.sess.opts.optimize != session::No,
|
loc.line as c_uint,
|
||||||
0,
|
type_metadata,
|
||||||
argument_index)
|
cx.sess.opts.optimize != session::No,
|
||||||
},
|
0,
|
||||||
IndirectVariable => unsafe {
|
argument_index)
|
||||||
let address_op = llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref());
|
}
|
||||||
let address_op_count = 1;
|
),
|
||||||
|
IndirectVariable { alloca, address_operations } => (
|
||||||
llvm::LLVMDIBuilderCreateComplexVariable(
|
alloca,
|
||||||
DIB(cx),
|
unsafe {
|
||||||
DW_TAG_auto_variable,
|
llvm::LLVMDIBuilderCreateComplexVariable(
|
||||||
scope_metadata,
|
DIB(cx),
|
||||||
name,
|
DW_TAG_auto_variable,
|
||||||
file_metadata,
|
scope_metadata,
|
||||||
loc.line as c_uint,
|
name,
|
||||||
type_metadata,
|
file_metadata,
|
||||||
ptr::to_unsafe_ptr(&address_op),
|
loc.line as c_uint,
|
||||||
address_op_count,
|
type_metadata,
|
||||||
argument_index)
|
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 {
|
unsafe {
|
||||||
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
|
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
|
||||||
DIB(cx),
|
DIB(cx),
|
||||||
llptr,
|
var_alloca,
|
||||||
var_metadata,
|
var_metadata,
|
||||||
bcx.llbb);
|
bcx.llbb);
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,23 @@
|
||||||
// check:$7 = 8
|
// check:$7 = 8
|
||||||
// debugger:continue
|
// 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)];
|
#[allow(unused_variable)];
|
||||||
|
|
||||||
struct Struct {
|
struct Struct {
|
||||||
|
@ -59,11 +76,7 @@ fn main() {
|
||||||
variable = constant + a_struct.a + struct_ref.a + *owned + *managed + closure_local;
|
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
|
zzz();
|
||||||
// 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();
|
|
||||||
|
|
||||||
nested_closure();
|
nested_closure();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue