Skip to content

Commit c49eb07

Browse files
debuginfo: Much improved handling of captured variables and by-value arguments.
1 parent e0b63b0 commit c49eb07

File tree

4 files changed

+124
-62
lines changed

4 files changed

+124
-62
lines changed

src/librustc/middle/trans/_match.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,9 @@ pub fn store_arg(mut bcx: @mut Block,
20002000
let arg_ty = node_id_type(bcx, pat.id);
20012001
add_clean(bcx, llval, arg_ty);
20022002

2003+
// Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an
2004+
// alloca, which only is the case on the general path, so lets disable the optimized path when
2005+
// debug info is enabled.
20032006
let fast_path = !bcx.ccx().sess.opts.extra_debuginfo && simple_identifier(pat).is_some();
20042007

20052008
if fast_path {

src/librustc/middle/trans/closure.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,17 @@ pub fn load_environment(fcx: @mut FunctionContext,
308308
// Load a pointer to the closure data, skipping over the box header:
309309
let llcdata = opaque_box_body(bcx, cdata_ty, fcx.llenv);
310310

311-
// Populate the upvars from the environment.
311+
// Store the pointer to closure data in an alloca for debug info because that's what the
312+
// llvm.dbg.declare intrinsic expects
313+
let env_pointer_alloca = if fcx.ccx.sess.opts.extra_debuginfo {
314+
let alloc = alloc_ty(bcx, ty::mk_mut_ptr(bcx.tcx(), cdata_ty), "__debuginfo_env_ptr");
315+
Store(bcx, llcdata, alloc);
316+
Some(alloc)
317+
} else {
318+
None
319+
};
320+
321+
// Populate the upvars from the environment
312322
let mut i = 0u;
313323
for cap_var in cap_vars.iter() {
314324
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
@@ -319,8 +329,15 @@ pub fn load_environment(fcx: @mut FunctionContext,
319329
let def_id = ast_util::def_id_of_def(cap_var.def);
320330
fcx.llupvars.insert(def_id.node, upvarptr);
321331

322-
if fcx.ccx.sess.opts.extra_debuginfo {
323-
debuginfo::create_captured_var_metadata(bcx, def_id.node, upvarptr, cap_var.span);
332+
for &env_pointer_alloca in env_pointer_alloca.iter() {
333+
debuginfo::create_captured_var_metadata(
334+
bcx,
335+
def_id.node,
336+
cdata_ty,
337+
env_pointer_alloca,
338+
i,
339+
sigil,
340+
cap_var.span);
324341
}
325342

326343
i += 1u;

src/librustc/middle/trans/debuginfo.rs

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,12 @@ struct FunctionDebugContextData {
163163
source_locations_enabled: bool,
164164
}
165165

166-
enum VariableAccess {
167-
// The value given is a pointer to the data (T*)
168-
DirectVariable,
169-
// The value given has to be dereferenced once to get the pointer to data (T**)
170-
IndirectVariable
166+
enum VariableAccess<'self> {
167+
// The llptr given is an alloca containing the variable's value
168+
DirectVariable { alloca: ValueRef },
169+
// The llptr given is an alloca containing the start of some pointer chain leading to the
170+
// variable's content.
171+
IndirectVariable { alloca: ValueRef, address_operations: &'self [ValueRef] }
171172
}
172173

173174
enum VariableKind {
@@ -213,11 +214,10 @@ pub fn create_local_var_metadata(bcx: @mut Block,
213214
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
214215

215216
declare_local(bcx,
216-
llptr,
217217
var_ident,
218218
var_type,
219219
scope_metadata,
220-
DirectVariable,
220+
DirectVariable { alloca: llptr },
221221
LocalVariable,
222222
span);
223223
}
@@ -228,7 +228,10 @@ pub fn create_local_var_metadata(bcx: @mut Block,
228228
/// Adds the created metadata nodes directly to the crate's IR.
229229
pub fn create_captured_var_metadata(bcx: @mut Block,
230230
node_id: ast::NodeId,
231-
llptr: ValueRef,
231+
env_data_type: ty::t,
232+
env_pointer: ValueRef,
233+
env_index: uint,
234+
closure_sigil: ast::Sigil,
232235
span: Span) {
233236
if fn_should_be_ignored(bcx.fcx) {
234237
return;
@@ -250,15 +253,39 @@ pub fn create_captured_var_metadata(bcx: @mut Block,
250253
Captured var-id refers to unexpected ast_map variant: %?", ast_item));
251254
}
252255
};
256+
253257
let variable_type = node_id_type(bcx, node_id);
254258
let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata;
255259

260+
let llvm_env_data_type = type_of::type_of(cx, env_data_type);
261+
let byte_offset_of_var_in_env = machine::llelement_offset(cx, llvm_env_data_type, env_index);
262+
263+
let address_operations = unsafe {
264+
[llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()),
265+
llvm::LLVMDIBuilderCreateOpPlus(Type::i64().to_ref()),
266+
C_i64(byte_offset_of_var_in_env as i64),
267+
llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref())]
268+
};
269+
270+
let address_op_count = match closure_sigil {
271+
ast::BorrowedSigil => {
272+
address_operations.len()
273+
}
274+
ast::ManagedSigil | ast::OwnedSigil => {
275+
address_operations.len() - 1
276+
}
277+
};
278+
279+
let variable_access = IndirectVariable {
280+
alloca: env_pointer,
281+
address_operations: address_operations.slice_to(address_op_count)
282+
};
283+
256284
declare_local(bcx,
257-
llptr,
258285
variable_ident,
259286
variable_type,
260287
scope_metadata,
261-
IndirectVariable,
288+
variable_access,
262289
CapturedVariable,
263290
span);
264291
}
@@ -285,11 +312,10 @@ pub fn create_match_binding_metadata(bcx: @mut Block,
285312
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
286313

287314
declare_local(bcx,
288-
llptr,
289315
variable_ident,
290316
variable_type,
291317
scope_metadata,
292-
DirectVariable,
318+
DirectVariable { alloca: llptr },
293319
LocalVariable,
294320
span);
295321
}
@@ -333,14 +359,17 @@ pub fn create_self_argument_metadata(bcx: @mut Block,
333359
argument_index
334360
};
335361

362+
let address_operations = &[unsafe { llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()) }];
363+
336364
let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
337-
DirectVariable
365+
DirectVariable { alloca: llptr }
338366
} else {
339-
IndirectVariable
367+
// This is not stable and may break with future LLVM versions. llptr should really always
368+
// be an alloca. Anything else is not supported and just works by chance.
369+
IndirectVariable { alloca: llptr, address_operations: address_operations }
340370
};
341371

342372
declare_local(bcx,
343-
llptr,
344373
special_idents::self_,
345374
type_of_self,
346375
scope_metadata,
@@ -373,11 +402,10 @@ pub fn create_argument_metadata(bcx: @mut Block,
373402
}
374403
};
375404

376-
let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
377-
DirectVariable
378-
} else {
379-
IndirectVariable
380-
};
405+
if unsafe { llvm::LLVMIsAAllocaInst(llptr) } == ptr::null() {
406+
cx.sess.span_bug(span, "debuginfo::create_argument_metadata() - \
407+
Referenced variable location is not an alloca!");
408+
}
381409

382410
let argument_type = node_id_type(bcx, node_id);
383411
let argument_ident = ast_util::path_to_ident(path_ref);
@@ -390,11 +418,10 @@ pub fn create_argument_metadata(bcx: @mut Block,
390418
};
391419

392420
declare_local(bcx,
393-
llptr,
394421
argument_ident,
395422
argument_type,
396423
scope_metadata,
397-
variable_access,
424+
DirectVariable { alloca: llptr },
398425
ArgumentVariable(argument_index),
399426
span);
400427
}
@@ -783,7 +810,6 @@ fn compile_unit_metadata(cx: @mut CrateContext) {
783810
}
784811

785812
fn declare_local(bcx: @mut Block,
786-
llptr: ValueRef,
787813
variable_ident: ast::Ident,
788814
variable_type: ty::t,
789815
scope_metadata: DIScope,
@@ -805,45 +831,48 @@ fn declare_local(bcx: @mut Block,
805831
CapturedVariable => 0
806832
} as c_uint;
807833

808-
let var_metadata = do name.with_c_str |name| {
834+
let (var_alloca, var_metadata) = do name.with_c_str |name| {
809835
match variable_access {
810-
DirectVariable => unsafe {
811-
llvm::LLVMDIBuilderCreateLocalVariable(
812-
DIB(cx),
813-
DW_TAG_auto_variable,
814-
scope_metadata,
815-
name,
816-
file_metadata,
817-
loc.line as c_uint,
818-
type_metadata,
819-
cx.sess.opts.optimize != session::No,
820-
0,
821-
argument_index)
822-
},
823-
IndirectVariable => unsafe {
824-
let address_op = llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref());
825-
let address_op_count = 1;
826-
827-
llvm::LLVMDIBuilderCreateComplexVariable(
828-
DIB(cx),
829-
DW_TAG_auto_variable,
830-
scope_metadata,
831-
name,
832-
file_metadata,
833-
loc.line as c_uint,
834-
type_metadata,
835-
ptr::to_unsafe_ptr(&address_op),
836-
address_op_count,
837-
argument_index)
838-
}
836+
DirectVariable { alloca } => (
837+
alloca,
838+
unsafe {
839+
llvm::LLVMDIBuilderCreateLocalVariable(
840+
DIB(cx),
841+
DW_TAG_auto_variable,
842+
scope_metadata,
843+
name,
844+
file_metadata,
845+
loc.line as c_uint,
846+
type_metadata,
847+
cx.sess.opts.optimize != session::No,
848+
0,
849+
argument_index)
850+
}
851+
),
852+
IndirectVariable { alloca, address_operations } => (
853+
alloca,
854+
unsafe {
855+
llvm::LLVMDIBuilderCreateComplexVariable(
856+
DIB(cx),
857+
DW_TAG_auto_variable,
858+
scope_metadata,
859+
name,
860+
file_metadata,
861+
loc.line as c_uint,
862+
type_metadata,
863+
vec::raw::to_ptr(address_operations),
864+
address_operations.len() as c_uint,
865+
argument_index)
866+
}
867+
)
839868
}
840869
};
841870

842871
set_debug_location(cx, DebugLocation::new(scope_metadata, loc.line, *loc.col));
843872
unsafe {
844873
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
845874
DIB(cx),
846-
llptr,
875+
var_alloca,
847876
var_metadata,
848877
bcx.llbb);
849878

src/test/debug-info/var-captured-in-nested-closure.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@
2929
// check:$7 = 8
3030
// debugger:continue
3131

32+
// debugger:finish
33+
// debugger:print variable
34+
// check:$8 = 1
35+
// debugger:print constant
36+
// check:$9 = 2
37+
// debugger:print a_struct
38+
// check:$10 = {a = -3, b = 4.5, c = 5}
39+
// debugger:print *struct_ref
40+
// check:$11 = {a = -3, b = 4.5, c = 5}
41+
// debugger:print *owned
42+
// check:$12 = 6
43+
// debugger:print managed->val
44+
// check:$13 = 7
45+
// debugger:print closure_local
46+
// check:$14 = 8
47+
// debugger:continue
48+
3249
#[allow(unused_variable)];
3350

3451
struct Struct {
@@ -59,11 +76,7 @@ fn main() {
5976
variable = constant + a_struct.a + struct_ref.a + *owned + *managed + closure_local;
6077
};
6178

62-
// breaking here will yield a wrong value for 'constant'. In particular, GDB will
63-
// read the value of the register that supposedly contains the pointer to 'constant'
64-
// and try derefence it. The register, however, already contains the actual value, and
65-
// not a pointer to it. -mw
66-
// zzz();
79+
zzz();
6780

6881
nested_closure();
6982
};

0 commit comments

Comments
 (0)