Skip to content

Commit 6892277

Browse files
committed
Auto merge of #32952 - eddyb:mir-debuginfo-2, r=michaelwoerister
Get all (but one) of debuginfo tests to pass with MIR codegen. I didn't get much feedback in #31005 so I went ahead and implemented something simple. Closes #31005, as MIR debuginfo should work now for most usecases. The `no-debug-attribute` test no longer assumes variables are in scope of `return`. We might also want to revisit that in #32949, but the test is more reliable now either way. In order to get one last function in the `associated-type` test pass, this PR also fixes #32790.
2 parents 054a4b4 + e2ac989 commit 6892277

12 files changed

+206
-80
lines changed

src/librustc/mir/repr.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ pub struct Mir<'tcx> {
5252
/// through the resulting reference.
5353
pub temp_decls: Vec<TempDecl<'tcx>>,
5454

55+
/// Names and capture modes of all the closure upvars, assuming
56+
/// the first argument is either the closure or a reference to it.
57+
pub upvar_decls: Vec<UpvarDecl>,
58+
5559
/// A span representing this MIR, for error reporting
5660
pub span: Span,
5761
}
@@ -197,7 +201,20 @@ pub struct ArgDecl<'tcx> {
197201

198202
/// If true, this argument is a tuple after monomorphization,
199203
/// and has to be collected from multiple actual arguments.
200-
pub spread: bool
204+
pub spread: bool,
205+
206+
/// Either special_idents::invalid or the name of a single-binding
207+
/// pattern associated with this argument. Useful for debuginfo.
208+
pub debug_name: Name
209+
}
210+
211+
/// A closure capture, with its name and mode.
212+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
213+
pub struct UpvarDecl {
214+
pub debug_name: Name,
215+
216+
/// If true, the capture is behind a reference.
217+
pub by_ref: bool
201218
}
202219

203220
///////////////////////////////////////////////////////////////////////////

src/librustc/mir/visit.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ macro_rules! make_mir_visitor {
248248
ref $($mutability)* var_decls,
249249
ref $($mutability)* arg_decls,
250250
ref $($mutability)* temp_decls,
251+
upvar_decls: _,
251252
ref $($mutability)* span,
252253
} = *mir;
253254

@@ -599,7 +600,8 @@ macro_rules! make_mir_visitor {
599600
arg_decl: & $($mutability)* ArgDecl<'tcx>) {
600601
let ArgDecl {
601602
ref $($mutability)* ty,
602-
spread: _
603+
spread: _,
604+
debug_name: _
603605
} = *arg_decl;
604606

605607
self.visit_ty(ty);

src/librustc_mir/build/matches/mod.rs

+23-16
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,28 @@ impl<'a,'tcx> Builder<'a,'tcx> {
3737
-> BlockAnd<()> {
3838
let discriminant_lvalue = unpack!(block = self.as_lvalue(block, discriminant));
3939

40-
// Before we do anything, create uninitialized variables with
41-
// suitable extent for all of the bindings in this match. It's
42-
// easiest to do this up front because some of these arms may
43-
// be unreachable or reachable multiple times.
44-
let var_scope_id = self.innermost_scope_id();
45-
for arm in &arms {
46-
self.declare_bindings(var_scope_id, &arm.patterns[0]);
47-
}
48-
4940
let mut arm_blocks = ArmBlocks {
5041
blocks: arms.iter()
5142
.map(|_| self.cfg.start_new_block())
5243
.collect(),
5344
};
5445

55-
let arm_bodies: Vec<ExprRef<'tcx>> =
56-
arms.iter()
57-
.map(|arm| arm.body.clone())
58-
.collect();
46+
// Get the body expressions and their scopes, while declaring bindings.
47+
let arm_bodies: Vec<_> = arms.iter().enumerate().map(|(i, arm)| {
48+
// Assume that all expressions are wrapped in Scope.
49+
let body = self.hir.mirror(arm.body.clone());
50+
match body.kind {
51+
ExprKind::Scope { extent, value } => {
52+
let scope_id = self.push_scope(extent, arm_blocks.blocks[i]);
53+
self.declare_bindings(scope_id, &arm.patterns[0]);
54+
(extent, self.scopes.pop().unwrap(), value)
55+
}
56+
_ => {
57+
span_bug!(body.span, "arm body is not wrapped in Scope {:?}",
58+
body.kind);
59+
}
60+
}
61+
}).collect();
5962

6063
// assemble a list of candidates: there is one candidate per
6164
// pattern, which means there may be more than one candidate
@@ -95,11 +98,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
9598
// all the arm blocks will rejoin here
9699
let end_block = self.cfg.start_new_block();
97100

98-
for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() {
101+
let scope_id = self.innermost_scope_id();
102+
for (arm_index, (extent, scope, body)) in arm_bodies.into_iter().enumerate() {
99103
let mut arm_block = arm_blocks.blocks[arm_index];
100-
unpack!(arm_block = self.into(destination, arm_block, arm_body));
104+
// Re-enter the scope we created the bindings in.
105+
self.scopes.push(scope);
106+
unpack!(arm_block = self.into(destination, arm_block, body));
107+
unpack!(arm_block = self.pop_scope(extent, arm_block));
101108
self.cfg.terminate(arm_block,
102-
var_scope_id,
109+
scope_id,
103110
span,
104111
TerminatorKind::Goto { target: end_block });
105112
}

src/librustc_mir/build/mod.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010

1111
use hair::cx::Cx;
1212
use rustc::middle::region::{CodeExtent, CodeExtentData};
13-
use rustc::ty::{FnOutput, Ty};
13+
use rustc::ty::{self, FnOutput, Ty};
1414
use rustc::mir::repr::*;
1515
use rustc_data_structures::fnv::FnvHashMap;
1616
use rustc::hir;
17+
use rustc::hir::pat_util::pat_is_binding;
1718
use std::ops::{Index, IndexMut};
1819
use syntax::ast;
1920
use syntax::codemap::Span;
21+
use syntax::parse::token;
2022

2123
pub struct Builder<'a, 'tcx: 'a> {
2224
hir: Cx<'a, 'tcx>,
@@ -224,13 +226,37 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
224226
true
225227
}));
226228

229+
// Gather the upvars of a closure, if any.
230+
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
231+
freevars.iter().map(|fv| {
232+
let by_ref = tcx.upvar_capture(ty::UpvarId {
233+
var_id: fv.def.var_id(),
234+
closure_expr_id: fn_id
235+
}).map_or(false, |capture| match capture {
236+
ty::UpvarCapture::ByValue => false,
237+
ty::UpvarCapture::ByRef(..) => true
238+
});
239+
let mut decl = UpvarDecl {
240+
debug_name: token::special_idents::invalid.name,
241+
by_ref: by_ref
242+
};
243+
if let Some(hir::map::NodeLocal(pat)) = tcx.map.find(fv.def.var_id()) {
244+
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
245+
decl.debug_name = ident.node.name;
246+
}
247+
}
248+
decl
249+
}).collect()
250+
});
251+
227252
(
228253
Mir {
229254
basic_blocks: builder.cfg.basic_blocks,
230255
scopes: builder.scope_datas,
231256
var_decls: builder.var_decls,
232257
arg_decls: arg_decls.take().expect("args never built?"),
233258
temp_decls: builder.temp_decls,
259+
upvar_decls: upvar_decls,
234260
return_ty: return_ty,
235261
span: span
236262
},
@@ -269,7 +295,20 @@ impl<'a,'tcx> Builder<'a,'tcx> {
269295
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
270296
argument_extent, &lvalue, ty);
271297

272-
ArgDecl { ty: ty, spread: false }
298+
let mut name = token::special_idents::invalid.name;
299+
if let Some(pat) = pattern {
300+
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
301+
if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
302+
name = ident.node.name;
303+
}
304+
}
305+
}
306+
307+
ArgDecl {
308+
ty: ty,
309+
spread: false,
310+
debug_name: name
311+
}
273312
})
274313
.collect();
275314

src/librustc_trans/debuginfo/create_scope_map.rs

+19-12
Original file line numberDiff line numberDiff line change
@@ -120,21 +120,28 @@ fn make_mir_scope(ccx: &CrateContext,
120120
return;
121121
};
122122

123-
scopes[idx] = if !has_variables.contains(idx) {
123+
if !has_variables.contains(idx) {
124124
// Do not create a DIScope if there are no variables
125125
// defined in this MIR Scope, to avoid debuginfo bloat.
126-
parent_scope
127-
} else {
128-
let loc = span_start(ccx, scope_data.span);
129-
let file_metadata = file_metadata(ccx, &loc.file.name);
130-
unsafe {
131-
llvm::LLVMDIBuilderCreateLexicalBlock(
132-
DIB(ccx),
133-
parent_scope,
134-
file_metadata,
135-
loc.line as c_uint,
136-
loc.col.to_usize() as c_uint)
126+
127+
// However, we don't skip creating a nested scope if
128+
// our parent is the root, because we might want to
129+
// put arguments in the root and not have shadowing.
130+
if parent_scope != fn_metadata {
131+
scopes[idx] = parent_scope;
132+
return;
137133
}
134+
}
135+
136+
let loc = span_start(ccx, scope_data.span);
137+
let file_metadata = file_metadata(ccx, &loc.file.name);
138+
scopes[idx] = unsafe {
139+
llvm::LLVMDIBuilderCreateLexicalBlock(
140+
DIB(ccx),
141+
parent_scope,
142+
file_metadata,
143+
loc.line as c_uint,
144+
loc.col.to_usize() as c_uint)
138145
};
139146
}
140147

src/librustc_trans/mir/mod.rs

+75-7
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
126126
let scopes = debuginfo::create_mir_scopes(fcx);
127127

128128
// Allocate variable and temp allocas
129+
let args = arg_value_refs(&bcx, &mir, &scopes);
129130
let vars = mir.var_decls.iter()
130131
.map(|decl| (bcx.monomorphize(&decl.ty), decl))
131132
.map(|(mty, decl)| {
@@ -156,7 +157,6 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
156157
TempRef::Operand(None)
157158
})
158159
.collect();
159-
let args = arg_value_refs(&bcx, &mir, &scopes);
160160

161161
// Allocate a `Block` for every basic block
162162
let block_bcxs: Vec<Block<'blk,'tcx>> =
@@ -278,15 +278,15 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
278278
let byte_offset_of_var_in_tuple =
279279
machine::llelement_offset(bcx.ccx(), lltuplety, i);
280280

281-
let address_operations = unsafe {
281+
let ops = unsafe {
282282
[llvm::LLVMDIBuilderCreateOpDeref(),
283283
llvm::LLVMDIBuilderCreateOpPlus(),
284284
byte_offset_of_var_in_tuple as i64]
285285
};
286286

287287
let variable_access = VariableAccess::IndirectVariable {
288288
alloca: lltemp,
289-
address_operations: &address_operations
289+
address_operations: &ops
290290
};
291291
declare_local(bcx, token::special_idents::invalid.name,
292292
tupled_arg_ty, scope, variable_access,
@@ -327,10 +327,78 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
327327
lltemp
328328
};
329329
bcx.with_block(|bcx| arg_scope.map(|scope| {
330-
declare_local(bcx, token::special_idents::invalid.name, arg_ty, scope,
331-
VariableAccess::DirectVariable { alloca: llval },
332-
VariableKind::ArgumentVariable(arg_index + 1),
333-
bcx.fcx().span.unwrap_or(DUMMY_SP));
330+
// Is this a regular argument?
331+
if arg_index > 0 || mir.upvar_decls.is_empty() {
332+
declare_local(bcx, arg_decl.debug_name, arg_ty, scope,
333+
VariableAccess::DirectVariable { alloca: llval },
334+
VariableKind::ArgumentVariable(arg_index + 1),
335+
bcx.fcx().span.unwrap_or(DUMMY_SP));
336+
return;
337+
}
338+
339+
// Or is it the closure environment?
340+
let (closure_ty, env_ref) = if let ty::TyRef(_, mt) = arg_ty.sty {
341+
(mt.ty, true)
342+
} else {
343+
(arg_ty, false)
344+
};
345+
let upvar_tys = if let ty::TyClosure(_, ref substs) = closure_ty.sty {
346+
&substs.upvar_tys[..]
347+
} else {
348+
bug!("upvar_decls with non-closure arg0 type `{}`", closure_ty);
349+
};
350+
351+
// Store the pointer to closure data in an alloca for debuginfo
352+
// because that's what the llvm.dbg.declare intrinsic expects.
353+
354+
// FIXME(eddyb) this shouldn't be necessary but SROA seems to
355+
// mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it
356+
// doesn't actually strip the offset when splitting the closure
357+
// environment into its components so it ends up out of bounds.
358+
let env_ptr = if !env_ref {
359+
use base::*;
360+
use build::*;
361+
use common::*;
362+
let alloc = alloca(bcx, val_ty(llval), "__debuginfo_env_ptr");
363+
Store(bcx, llval, alloc);
364+
alloc
365+
} else {
366+
llval
367+
};
368+
369+
let llclosurety = type_of::type_of(bcx.ccx(), closure_ty);
370+
for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() {
371+
let byte_offset_of_var_in_env =
372+
machine::llelement_offset(bcx.ccx(), llclosurety, i);
373+
374+
let ops = unsafe {
375+
[llvm::LLVMDIBuilderCreateOpDeref(),
376+
llvm::LLVMDIBuilderCreateOpPlus(),
377+
byte_offset_of_var_in_env as i64,
378+
llvm::LLVMDIBuilderCreateOpDeref()]
379+
};
380+
381+
// The environment and the capture can each be indirect.
382+
383+
// FIXME(eddyb) see above why we have to keep
384+
// a pointer in an alloca for debuginfo atm.
385+
let mut ops = if env_ref || true { &ops[..] } else { &ops[1..] };
386+
387+
let ty = if let (true, &ty::TyRef(_, mt)) = (decl.by_ref, &ty.sty) {
388+
mt.ty
389+
} else {
390+
ops = &ops[..ops.len() - 1];
391+
ty
392+
};
393+
394+
let variable_access = VariableAccess::IndirectVariable {
395+
alloca: env_ptr,
396+
address_operations: &ops
397+
};
398+
declare_local(bcx, decl.debug_name, ty, scope, variable_access,
399+
VariableKind::CapturedVariable,
400+
bcx.fcx().span.unwrap_or(DUMMY_SP));
401+
}
334402
}));
335403
LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
336404
}).collect()

src/test/debuginfo/associated-types.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080

8181
#![allow(unused_variables)]
8282
#![allow(dead_code)]
83-
#![feature(omit_gdb_pretty_printer_section, rustc_attrs)]
83+
#![feature(omit_gdb_pretty_printer_section)]
8484
#![omit_gdb_pretty_printer_section]
8585

8686
trait TraitWithAssocType {
@@ -127,7 +127,6 @@ fn assoc_tuple<T: TraitWithAssocType>(arg: (T, T::Type)) {
127127
zzz(); // #break
128128
}
129129

130-
#[rustc_no_mir] // FIXME(#32790) MIR reuses scopes for match arms.
131130
fn assoc_enum<T: TraitWithAssocType>(arg: Enum<T>) {
132131

133132
match arg {

0 commit comments

Comments
 (0)