debuginfo: argument and upvar names for MIR.

This commit is contained in:
Eduard Burtescu 2016-04-16 21:51:26 +03:00
parent 7fd2881ed4
commit f06bab7758
9 changed files with 176 additions and 58 deletions

View file

@ -52,6 +52,10 @@ pub struct Mir<'tcx> {
/// through the resulting reference.
pub temp_decls: Vec<TempDecl<'tcx>>,
/// Names and capture modes of all the closure upvars, assuming
/// the first argument is either the closure or a reference to it.
pub upvar_decls: Vec<UpvarDecl>,
/// A span representing this MIR, for error reporting
pub span: Span,
}
@ -197,7 +201,20 @@ pub struct ArgDecl<'tcx> {
/// If true, this argument is a tuple after monomorphization,
/// and has to be collected from multiple actual arguments.
pub spread: bool
pub spread: bool,
/// Either special_idents::invalid or the name of a single-binding
/// pattern associated with this argument. Useful for debuginfo.
pub debug_name: Name
}
/// A closure capture, with its name and mode.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct UpvarDecl {
pub debug_name: Name,
/// If true, the capture is behind a reference.
pub by_ref: bool
}
///////////////////////////////////////////////////////////////////////////

View file

@ -248,6 +248,7 @@ fn super_mir(&mut self,
ref $($mutability)* var_decls,
ref $($mutability)* arg_decls,
ref $($mutability)* temp_decls,
upvar_decls: _,
ref $($mutability)* span,
} = *mir;
@ -599,7 +600,8 @@ fn super_arg_decl(&mut self,
arg_decl: & $($mutability)* ArgDecl<'tcx>) {
let ArgDecl {
ref $($mutability)* ty,
spread: _
spread: _,
debug_name: _
} = *arg_decl;
self.visit_ty(ty);

View file

@ -10,13 +10,15 @@
use hair::cx::Cx;
use rustc::middle::region::{CodeExtent, CodeExtentData};
use rustc::ty::{FnOutput, Ty};
use rustc::ty::{self, FnOutput, Ty};
use rustc::mir::repr::*;
use rustc_data_structures::fnv::FnvHashMap;
use rustc::hir;
use rustc::hir::pat_util::pat_is_binding;
use std::ops::{Index, IndexMut};
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token;
pub struct Builder<'a, 'tcx: 'a> {
hir: Cx<'a, 'tcx>,
@ -224,6 +226,29 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
true
}));
// Gather the upvars of a closure, if any.
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
freevars.iter().map(|fv| {
let by_ref = tcx.upvar_capture(ty::UpvarId {
var_id: fv.def.var_id(),
closure_expr_id: fn_id
}).map_or(false, |capture| match capture {
ty::UpvarCapture::ByValue => false,
ty::UpvarCapture::ByRef(..) => true
});
let mut decl = UpvarDecl {
debug_name: token::special_idents::invalid.name,
by_ref: by_ref
};
if let Some(hir::map::NodeLocal(pat)) = tcx.map.find(fv.def.var_id()) {
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
decl.debug_name = ident.node.name;
}
}
decl
}).collect()
});
(
Mir {
basic_blocks: builder.cfg.basic_blocks,
@ -231,6 +256,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
var_decls: builder.var_decls,
arg_decls: arg_decls.take().expect("args never built?"),
temp_decls: builder.temp_decls,
upvar_decls: upvar_decls,
return_ty: return_ty,
span: span
},
@ -269,7 +295,20 @@ fn args_and_body(&mut self,
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
argument_extent, &lvalue, ty);
ArgDecl { ty: ty, spread: false }
let mut name = token::special_idents::invalid.name;
if let Some(pat) = pattern {
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
name = ident.node.name;
}
}
}
ArgDecl {
ty: ty,
spread: false,
debug_name: name
}
})
.collect();

View file

@ -120,21 +120,28 @@ fn make_mir_scope(ccx: &CrateContext,
return;
};
scopes[idx] = if !has_variables.contains(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)
// However, we don't skip creating a nested scope if
// our parent is the root, because we might want to
// put arguments in the root and not have shadowing.
if parent_scope != fn_metadata {
scopes[idx] = parent_scope;
return;
}
}
let loc = span_start(ccx, scope_data.span);
let file_metadata = file_metadata(ccx, &loc.file.name);
scopes[idx] = unsafe {
llvm::LLVMDIBuilderCreateLexicalBlock(
DIB(ccx),
parent_scope,
file_metadata,
loc.line as c_uint,
loc.col.to_usize() as c_uint)
};
}

View file

@ -126,6 +126,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
let scopes = debuginfo::create_mir_scopes(fcx);
// Allocate variable and temp allocas
let args = arg_value_refs(&bcx, &mir, &scopes);
let vars = mir.var_decls.iter()
.map(|decl| (bcx.monomorphize(&decl.ty), decl))
.map(|(mty, decl)| {
@ -156,7 +157,6 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
TempRef::Operand(None)
})
.collect();
let args = arg_value_refs(&bcx, &mir, &scopes);
// Allocate a `Block` for every basic block
let block_bcxs: Vec<Block<'blk,'tcx>> =
@ -278,7 +278,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
let byte_offset_of_var_in_tuple =
machine::llelement_offset(bcx.ccx(), lltuplety, i);
let address_operations = unsafe {
let ops = unsafe {
[llvm::LLVMDIBuilderCreateOpDeref(),
llvm::LLVMDIBuilderCreateOpPlus(),
byte_offset_of_var_in_tuple as i64]
@ -286,7 +286,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
let variable_access = VariableAccess::IndirectVariable {
alloca: lltemp,
address_operations: &address_operations
address_operations: &ops
};
declare_local(bcx, token::special_idents::invalid.name,
tupled_arg_ty, scope, variable_access,
@ -327,10 +327,78 @@ 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));
// Is this a regular argument?
if arg_index > 0 || mir.upvar_decls.is_empty() {
declare_local(bcx, arg_decl.debug_name, arg_ty, scope,
VariableAccess::DirectVariable { alloca: llval },
VariableKind::ArgumentVariable(arg_index + 1),
bcx.fcx().span.unwrap_or(DUMMY_SP));
return;
}
// Or is it the closure environment?
let (closure_ty, env_ref) = if let ty::TyRef(_, mt) = arg_ty.sty {
(mt.ty, true)
} else {
(arg_ty, false)
};
let upvar_tys = if let ty::TyClosure(_, ref substs) = closure_ty.sty {
&substs.upvar_tys[..]
} else {
bug!("upvar_decls with non-closure arg0 type `{}`", closure_ty);
};
// Store the pointer to closure data in an alloca for debuginfo
// because that's what the llvm.dbg.declare intrinsic expects.
// FIXME(eddyb) this shouldn't be necessary but SROA seems to
// mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it
// doesn't actually strip the offset when splitting the closure
// environment into its components so it ends up out of bounds.
let env_ptr = if !env_ref {
use base::*;
use build::*;
use common::*;
let alloc = alloca(bcx, val_ty(llval), "__debuginfo_env_ptr");
Store(bcx, llval, alloc);
alloc
} else {
llval
};
let llclosurety = type_of::type_of(bcx.ccx(), closure_ty);
for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() {
let byte_offset_of_var_in_env =
machine::llelement_offset(bcx.ccx(), llclosurety, i);
let ops = unsafe {
[llvm::LLVMDIBuilderCreateOpDeref(),
llvm::LLVMDIBuilderCreateOpPlus(),
byte_offset_of_var_in_env as i64,
llvm::LLVMDIBuilderCreateOpDeref()]
};
// The environment and the capture can each be indirect.
// FIXME(eddyb) see above why we have to keep
// a pointer in an alloca for debuginfo atm.
let mut ops = if env_ref || true { &ops[..] } else { &ops[1..] };
let ty = if let (true, &ty::TyRef(_, mt)) = (decl.by_ref, &ty.sty) {
mt.ty
} else {
ops = &ops[..ops.len() - 1];
ty
};
let variable_access = VariableAccess::IndirectVariable {
alloca: env_ptr,
address_operations: &ops
};
declare_local(bcx, decl.debug_name, ty, scope, variable_access,
VariableKind::CapturedVariable,
bcx.fcx().span.unwrap_or(DUMMY_SP));
}
}));
LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
}).collect()

View file

@ -251,7 +251,7 @@
#![omit_gdb_pretty_printer_section]
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn immediate_args(a: isize, b: bool, c: f64) {
println!("");
}
@ -268,51 +268,51 @@ struct BigStruct {
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn non_immediate_args(a: BigStruct, b: BigStruct) {
println!("");
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn binding(a: i64, b: u64, c: f64) {
let x = 0;
println!("");
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn assignment(mut a: u64, b: u64, c: f64) {
a = b;
println!("");
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn function_call(x: u64, y: u64, z: f64) {
println!("Hi!")
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn identifier(x: u64, y: u64, z: f64) -> u64 {
x
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn return_expr(x: u64, y: u64, z: f64) -> u64 {
return x;
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 {
x + y
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn if_expr(x: u64, y: u64, z: f64) -> u64 {
if x + y < 1000 {
x
@ -322,7 +322,7 @@ fn if_expr(x: u64, y: u64, z: f64) -> u64 {
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
while x + y < 1000 {
x += z
@ -331,7 +331,7 @@ fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
}
#[no_stack_check]
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing argument names.
#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 {
loop {
x += z;

View file

@ -78,7 +78,7 @@
// lldb-command:continue
#![allow(unused_variables)]
#![feature(box_syntax, rustc_attrs, stmt_expr_attributes)]
#![feature(box_syntax)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]
@ -88,7 +88,6 @@ struct Struct {
c: usize
}
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
fn main() {
let mut variable = 1;
let constant = 2;
@ -102,14 +101,10 @@ fn main() {
let struct_ref = &a_struct;
let owned: Box<_> = box 6;
let mut closure =
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
|| {
let mut closure = || {
let closure_local = 8;
let mut nested_closure =
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
|| {
let mut nested_closure = || {
zzz(); // #break
variable = constant + a_struct.a + struct_ref.a + *owned + closure_local;
};

View file

@ -40,7 +40,7 @@
// lldb-check:[...]$2 = 5
#![allow(unused_variables)]
#![feature(unboxed_closures, box_syntax, rustc_attrs, stmt_expr_attributes)]
#![feature(unboxed_closures, box_syntax)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]
@ -50,7 +50,6 @@ struct Struct {
c: usize
}
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
fn main() {
let constant = 1;
@ -62,9 +61,7 @@ fn main() {
let owned: Box<_> = box 5;
let closure =
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
move || {
let closure = move || {
zzz(); // #break
do_something(&constant, &a_struct.a, &*owned);
};
@ -76,9 +73,7 @@ fn main() {
// The `self` argument of the following closure should be passed by value
// to FnOnce::call_once(self, args), which gets translated a bit differently
// than the regular case. Let's make sure this is supported too.
let immedate_env =
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
move || {
let immedate_env = move || {
zzz(); // #break
return constant2;
};

View file

@ -69,7 +69,7 @@
// lldb-command:print *owned
// lldb-check:[...]$9 = 6
#![feature(unboxed_closures, box_syntax, rustc_attrs, stmt_expr_attributes)]
#![feature(unboxed_closures, box_syntax)]
#![allow(unused_variables)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]
@ -80,7 +80,6 @@ struct Struct {
c: usize
}
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
fn main() {
let mut variable = 1;
let constant = 2;
@ -95,9 +94,7 @@ fn main() {
let owned: Box<_> = box 6;
{
let mut first_closure =
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
|| {
let mut first_closure = || {
zzz(); // #break
variable = constant + a_struct.a + struct_ref.a + *owned;
};
@ -106,9 +103,7 @@ fn main() {
}
{
let mut second_closure =
#[rustc_no_mir] // FIXME(#31005) MIR debuginfo is missing captures.
|| {
let mut second_closure = || {
zzz(); // #break
variable = constant + a_struct.a + struct_ref.a + *owned;
};