1
Fork 0

Auto merge of #68965 - eddyb:mir-inline-scope, r=nagisa,oli-obk

rustc_mir: track inlined callees in SourceScopeData.

We now record which MIR scopes are the roots of *other* (inlined) functions's scope trees, which allows us to generate the correct debuginfo in codegen, similar to what LLVM inlining generates.
This PR makes the `ui` test `backtrace-debuginfo` pass, if the MIR inliner is turned on by default.

Also, `#[track_caller]` is now correct in the face of MIR inlining (cc `@anp).`

Fixes #76997.

r? `@rust-lang/wg-mir-opt`
This commit is contained in:
bors 2020-10-26 18:50:22 +00:00
commit 0da6d42f29
68 changed files with 879 additions and 619 deletions

View file

@ -405,7 +405,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.set_debug_loc(&mut bx, terminator.source_info);
// Get the location information.
let location = self.get_caller_location(&mut bx, span).immediate();
let location = self.get_caller_location(&mut bx, terminator.source_info).immediate();
// Put together the arguments to the panic entry point.
let (lang_item, args) = match msg {
@ -442,7 +442,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx: &mut Bx,
intrinsic: Option<Symbol>,
instance: Option<Instance<'tcx>>,
span: Span,
source_info: mir::SourceInfo,
destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
cleanup: Option<mir::BasicBlock>,
) -> bool {
@ -484,11 +484,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
});
let msg = bx.const_str(Symbol::intern(&msg_str));
let location = self.get_caller_location(bx, span).immediate();
let location = self.get_caller_location(bx, source_info).immediate();
// Obtain the panic entry point.
// FIXME: dedup this with `codegen_assert_terminator` above.
let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::Panic);
let def_id =
common::langcall(bx.tcx(), Some(source_info.span), "", LangItem::Panic);
let instance = ty::Instance::mono(bx.tcx(), def_id);
let fn_abi = FnAbi::of_instance(bx, instance, &[]);
let llfn = bx.get_fn_addr(instance);
@ -529,7 +530,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
cleanup: Option<mir::BasicBlock>,
fn_span: Span,
) {
let span = terminator.source_info.span;
let source_info = terminator.source_info;
let span = source_info.span;
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
let callee = self.codegen_operand(&mut bx, func);
@ -606,7 +609,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&mut bx,
intrinsic,
instance,
span,
source_info,
destination,
cleanup,
) {
@ -627,7 +630,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if intrinsic == Some(sym::caller_location) {
if let Some((_, target)) = destination.as_ref() {
let location = self.get_caller_location(&mut bx, fn_span);
let location = self
.get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info });
if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
location.val.store(&mut bx, tmp);
@ -686,7 +690,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&fn_abi,
&args,
dest,
terminator.source_info.span,
span,
);
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
@ -793,7 +797,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
args.len() + 1,
"#[track_caller] fn's must have 1 more argument in their ABI than in their MIR",
);
let location = self.get_caller_location(&mut bx, fn_span);
let location =
self.get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info });
debug!(
"codegen_call_terminator({:?}): location={:?} (fn_span {:?})",
terminator, location, fn_span
@ -1179,17 +1184,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
fn get_caller_location(&mut self, bx: &mut Bx, span: Span) -> OperandRef<'tcx, Bx::Value> {
self.caller_location.unwrap_or_else(|| {
fn get_caller_location(
&mut self,
bx: &mut Bx,
mut source_info: mir::SourceInfo,
) -> OperandRef<'tcx, Bx::Value> {
let tcx = bx.tcx();
let mut span_to_caller_location = |span: Span| {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = bx.tcx().const_caller_location((
let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = tcx.const_caller_location((
Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
caller.col_display as u32 + 1,
));
OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
})
};
// Walk up the `SourceScope`s, in case some of them are from MIR inlining.
// If so, the starting `source_info.span` is in the innermost inlined
// function, and will be replaced with outer callsite spans as long
// as the inlined functions were `#[track_caller]`.
loop {
let scope_data = &self.mir.source_scopes[source_info.scope];
if let Some((callee, callsite_span)) = scope_data.inlined {
// Stop inside the most nested non-`#[track_caller]` function,
// before ever reaching its caller (which is irrelevant).
if !callee.def.requires_caller_location(tcx) {
return span_to_caller_location(source_info.span);
}
source_info.span = callsite_span;
}
// Skip past all of the parents with `inlined: None`.
match scope_data.inlined_parent_scope {
Some(parent) => source_info.scope = parent,
None => break,
}
}
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span))
}
fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> {

View file

@ -1,5 +1,4 @@
use crate::traits::*;
use rustc_hir::def_id::CrateNum;
use rustc_index::vec::IndexVec;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir;
@ -13,9 +12,8 @@ use super::operand::OperandValue;
use super::place::PlaceRef;
use super::{FunctionCx, LocalRef};
pub struct FunctionDebugContext<D> {
pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
pub defining_crate: CrateNum,
pub struct FunctionDebugContext<S, L> {
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
}
#[derive(Copy, Clone)]
@ -38,77 +36,84 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
}
#[derive(Clone, Copy, Debug)]
pub struct DebugScope<D> {
pub scope_metadata: Option<D>,
pub struct DebugScope<S, L> {
// FIXME(eddyb) this should never be `None`, after initialization.
pub dbg_scope: Option<S>,
/// Call site location, if this scope was inlined from another function.
pub inlined_at: Option<L>,
// Start and end offsets of the file to which this DIScope belongs.
// These are used to quickly determine whether some span refers to the same file.
pub file_start_pos: BytePos,
pub file_end_pos: BytePos,
}
impl<D> DebugScope<D> {
pub fn is_valid(&self) -> bool {
self.scope_metadata.is_some()
impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
/// DILocations inherit source file name from the parent DIScope. Due to macro expansions
/// it may so happen that the current span belongs to a different file than the DIScope
/// corresponding to span's containing source scope. If so, we need to create a DIScope
/// "extension" into that file.
pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
&self,
cx: &Cx,
span: Span,
) -> S {
// FIXME(eddyb) this should never be `None`.
let dbg_scope = self
.dbg_scope
.unwrap_or_else(|| bug!("`dbg_scope` is only `None` during initialization"));
let pos = span.lo();
if pos < self.file_start_pos || pos >= self.file_end_pos {
let sm = cx.sess().source_map();
cx.extend_scope_to_file(dbg_scope, &sm.lookup_char_pos(pos).file)
} else {
dbg_scope
}
}
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
let (scope, span) = self.debug_loc(source_info);
bx.set_span(span);
if let Some(scope) = scope {
bx.set_source_location(scope, span);
bx.set_span(source_info.span);
if let Some(dbg_loc) = self.dbg_loc(source_info) {
bx.set_dbg_loc(dbg_loc);
}
}
pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) {
fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?;
Some(self.cx.dbg_loc(dbg_scope, inlined_at, span))
}
fn adjusted_span_and_dbg_scope(
&self,
source_info: mir::SourceInfo,
) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
let span = self.adjust_span_for_debugging(source_info.span);
let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
}
/// In order to have a good line stepping behavior in debugger, we overwrite debug
/// locations of macro expansions with that of the outermost expansion site
/// (unless the crate is being compiled with `-Z debug-macros`).
fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
// Bail out if debug info emission is not enabled.
match self.debug_context {
None => return (None, source_info.span),
Some(_) => {}
if self.debug_context.is_none() {
return span;
}
// In order to have a good line stepping behavior in debugger, we overwrite debug
// locations of macro expansions with that of the outermost expansion site
// (unless the crate is being compiled with `-Z debug-macros`).
if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros {
let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo());
(scope, source_info.span)
} else {
if span.from_expansion() && !self.cx.sess().opts.debugging_opts.debug_macros {
// Walk up the macro expansion chain until we reach a non-expanded span.
// We also stop at the function body level because no line stepping can occur
// at the level above that.
let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt());
let scope = self.scope_metadata_for_loc(source_info.scope, span.lo());
// Use span of the outermost expansion site, while keeping the original lexical scope.
(scope, span)
span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
}
}
// DILocations inherit source file name from the parent DIScope. Due to macro expansions
// it may so happen that the current span belongs to a different file than the DIScope
// corresponding to span's containing source scope. If so, we need to create a DIScope
// "extension" into that file.
fn scope_metadata_for_loc(
&self,
scope_id: mir::SourceScope,
pos: BytePos,
) -> Option<Bx::DIScope> {
let debug_context = self.debug_context.as_ref()?;
let scope_metadata = debug_context.scopes[scope_id].scope_metadata;
if pos < debug_context.scopes[scope_id].file_start_pos
|| pos >= debug_context.scopes[scope_id].file_end_pos
{
let sm = self.cx.sess().source_map();
let defining_crate = debug_context.defining_crate;
Some(self.cx.extend_scope_to_file(
scope_metadata.unwrap(),
&sm.lookup_char_pos(pos).file,
defining_crate,
))
} else {
scope_metadata
}
span
}
/// Apply debuginfo and/or name, after creating the `alloca` for a local,
@ -149,24 +154,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else {
let name = kw::Invalid;
let decl = &self.mir.local_decls[local];
let (scope, span) = if full_debug_info {
self.debug_loc(decl.source_info)
} else {
(None, decl.source_info.span)
};
let dbg_var = scope.map(|scope| {
// FIXME(eddyb) is this `+ 1` needed at all?
let kind = VariableKind::ArgumentVariable(arg_index + 1);
let dbg_var = if full_debug_info {
self.adjusted_span_and_dbg_scope(decl.source_info).map(
|(dbg_scope, _, span)| {
// FIXME(eddyb) is this `+ 1` needed at all?
let kind = VariableKind::ArgumentVariable(arg_index + 1);
self.cx.create_dbg_var(
self.debug_context.as_ref().unwrap(),
name,
self.monomorphize(&decl.ty),
scope,
kind,
span,
let arg_ty = self.monomorphize(&decl.ty);
self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
},
)
});
} else {
None
};
Some(PerLocalVarDebugInfo {
name,
@ -247,6 +248,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let vars = vars.iter().copied().chain(fallback_var);
for var in vars {
let dbg_var = match var.dbg_var {
Some(dbg_var) => dbg_var,
None => continue,
};
let dbg_loc = match self.dbg_loc(var.source_info) {
Some(dbg_loc) => dbg_loc,
None => continue,
};
let mut layout = base.layout;
let mut direct_offset = Size::ZERO;
// FIXME(eddyb) use smallvec here.
@ -283,19 +293,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
let (scope, span) = self.debug_loc(var.source_info);
if let Some(scope) = scope {
if let Some(dbg_var) = var.dbg_var {
bx.dbg_var_addr(
dbg_var,
scope,
base.llval,
direct_offset,
&indirect_offsets,
span,
);
}
}
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
}
}
@ -319,12 +317,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
for var in &self.mir.var_debug_info {
let (scope, span) = if full_debug_info {
self.debug_loc(var.source_info)
let dbg_scope_and_span = if full_debug_info {
self.adjusted_span_and_dbg_scope(var.source_info)
} else {
(None, var.source_info.span)
None
};
let dbg_var = scope.map(|scope| {
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
let place = var.place;
let var_ty = self.monomorphized_place_ty(place.as_ref());
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
@ -340,14 +338,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else {
VariableKind::LocalVariable
};
self.cx.create_dbg_var(
self.debug_context.as_ref().unwrap(),
var.name,
var_ty,
scope,
var_kind,
span,
)
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
});
per_local[var.place.local].push(PerLocalVarDebugInfo {

View file

@ -26,7 +26,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
mir: &'tcx mir::Body<'tcx>,
debug_context: Option<FunctionDebugContext<Bx::DIScope>>,
debug_context: Option<FunctionDebugContext<Bx::DIScope, Bx::DILocation>>,
llfn: Bx::Function,

View file

@ -34,6 +34,7 @@ pub trait BackendTypes {
// FIXME(eddyb) find a common convention for all of the debuginfo-related
// names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.).
type DIScope: Copy;
type DILocation: Copy;
type DIVariable: Copy;
}

View file

@ -1,6 +1,5 @@
use super::BackendTypes;
use crate::mir::debuginfo::{FunctionDebugContext, VariableKind};
use rustc_hir::def_id::CrateNum;
use rustc_middle::mir;
use rustc_middle::ty::{Instance, Ty};
use rustc_span::{SourceFile, Span, Symbol};
@ -19,14 +18,29 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
instance: Instance<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
llfn: Self::Function,
mir: &mir::Body<'_>,
) -> Option<FunctionDebugContext<Self::DIScope>>;
mir: &mir::Body<'tcx>,
) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>>;
// FIXME(eddyb) find a common convention for all of the debuginfo-related
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
fn dbg_scope_fn(
&self,
instance: Instance<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
maybe_definition_llfn: Option<Self::Function>,
) -> Self::DIScope;
fn dbg_loc(
&self,
scope: Self::DIScope,
inlined_at: Option<Self::DILocation>,
span: Span,
) -> Self::DILocation;
fn extend_scope_to_file(
&self,
scope_metadata: Self::DIScope,
file: &SourceFile,
defining_crate: CrateNum,
) -> Self::DIScope;
fn debuginfo_finalize(&self);
@ -34,7 +48,6 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
fn create_dbg_var(
&self,
dbg_context: &FunctionDebugContext<Self::DIScope>,
variable_name: Symbol,
variable_type: Ty<'tcx>,
scope_metadata: Self::DIScope,
@ -49,14 +62,13 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
fn dbg_var_addr(
&mut self,
dbg_var: Self::DIVariable,
scope_metadata: Self::DIScope,
dbg_loc: Self::DILocation,
variable_alloca: Self::Value,
direct_offset: Size,
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
indirect_offsets: &[Size],
span: Span,
);
fn set_source_location(&mut self, scope: Self::DIScope, span: Span);
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
fn set_var_name(&mut self, value: Self::Value, name: &str);
}

View file

@ -95,6 +95,7 @@ pub trait HasCodegen<'tcx>:
Type = Self::Type,
Funclet = Self::Funclet,
DIScope = Self::DIScope,
DILocation = Self::DILocation,
DIVariable = Self::DIVariable,
>;
}