Skip to content

Commit

Permalink
Auto merge of #68965 - eddyb:mir-inline-scope, r=<try>
Browse files Browse the repository at this point in the history
 rustc_mir: track inlined callees in SourceScopeData.

This would be a prerequisite for generating the correct debuginfo in codegen, which would look similar to what LLVM inlining generates (we should be able to "fake it" as well).

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

However, the debuginfo side isn't implemented yet. I might take a stab at it soon, not sure.

r? @rust-lang/wg-mir-opt
  • Loading branch information
bors committed Feb 9, 2020
2 parents a19edd6 + c43d529 commit 5f38376
Show file tree
Hide file tree
Showing 25 changed files with 315 additions and 143 deletions.
14 changes: 9 additions & 5 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub struct Body<'tcx> {

/// A list of source scopes; these are referenced by statements
/// and used for debuginfo. Indexed by a `SourceScope`.
pub source_scopes: IndexVec<SourceScope, SourceScopeData>,
pub source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,

/// The yield type of the function, if it is a generator.
pub yield_ty: Option<Ty<'tcx>>,
Expand Down Expand Up @@ -179,7 +179,7 @@ pub struct Body<'tcx> {
impl<'tcx> Body<'tcx> {
pub fn new(
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
source_scopes: IndexVec<SourceScope, SourceScopeData>,
source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
local_decls: LocalDecls<'tcx>,
user_type_annotations: CanonicalUserTypeAnnotations<'tcx>,
arg_count: usize,
Expand Down Expand Up @@ -1916,11 +1916,16 @@ rustc_index::newtype_index! {
}
}

#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
pub struct SourceScopeData {
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
pub struct SourceScopeData<'tcx> {
pub span: Span,
pub parent_scope: Option<SourceScope>,

/// Whether this scope is the root of a scope tree of another body,
/// inlined into this body by the MIR inliner.
/// `ty::Instance` is the callee, and the `Span` is the call site.
pub inlined: Option<(ty::Instance<'tcx>, Span)>,

/// Crate-local information for this source scope, that can't (and
/// needn't) be tracked across crates.
pub local_data: ClearCrossCrate<SourceScopeLocalData>,
Expand Down Expand Up @@ -2621,7 +2626,6 @@ CloneTypeFoldableAndLiftImpls! {
FakeReadCause,
RetagKind,
SourceScope,
SourceScopeData,
SourceScopeLocalData,
UserTypeAnnotationIndex,
}
Expand Down
32 changes: 30 additions & 2 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ macro_rules! make_mir_visitor {
}

fn visit_source_scope_data(&mut self,
scope_data: & $($mutability)? SourceScopeData) {
scope_data: & $($mutability)? SourceScopeData<'tcx>) {
self.super_source_scope_data(scope_data);
}

Expand Down Expand Up @@ -329,17 +329,45 @@ macro_rules! make_mir_visitor {
}
}

fn super_source_scope_data(&mut self, scope_data: & $($mutability)? SourceScopeData) {
fn super_source_scope_data(
&mut self,
scope_data: & $($mutability)? SourceScopeData<'tcx>,
) {
let SourceScopeData {
span,
parent_scope,
inlined,
local_data: _,
} = scope_data;

self.visit_span(span);
if let Some(parent_scope) = parent_scope {
self.visit_source_scope(parent_scope);
}
if let Some((callee, callsite_span)) = inlined {
let location = START_BLOCK.start_location();

self.visit_span(callsite_span);

let ty::Instance { def: callee_def, substs: callee_substs } = callee;
match callee_def {
ty::InstanceDef::Item(_def_id) |
ty::InstanceDef::Intrinsic(_def_id) |
ty::InstanceDef::VtableShim(_def_id) |
ty::InstanceDef::ReifyShim(_def_id) |
ty::InstanceDef::Virtual(_def_id, _) |
ty::InstanceDef::ClosureOnceShim { call_once: _def_id } |
ty::InstanceDef::DropGlue(_def_id, None) => {}

ty::InstanceDef::FnPtrShim(_def_id, ty) |
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
ty::InstanceDef::CloneShim(_def_id, ty) => {
// FIXME(eddyb) use a better `TyContext` here.
self.visit_ty(ty, TyContext::Location(location));
}
}
self.visit_substs(callee_substs, location);
}
}

fn super_statement(&mut self,
Expand Down
87 changes: 67 additions & 20 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable};
use rustc_hir::def::Namespace;
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_macros::HashStable;
use rustc_span::sym;
use rustc_target::spec::abi::Abi;

use std::fmt;
Expand Down Expand Up @@ -40,6 +41,11 @@ pub enum InstanceDef<'tcx> {

/// `<fn() as FnTrait>::call_*`
/// `DefId` is `FnTrait::call_*`.
///
/// NB: the (`fn` pointer) type must be monomorphic for MIR shims to work.
// FIXME(eddyb) support generating shims for a "shallow type",
// e.g. `fn(_, _) -> _` instead of requiring a fully monomorphic
// `fn(Foo, Bar) -> Baz` etc.
FnPtrShim(DefId, Ty<'tcx>),

/// `<dyn Trait as Trait>::fn`, "direct calls" of which are implicitly
Expand All @@ -55,9 +61,19 @@ pub enum InstanceDef<'tcx> {
},

/// `drop_in_place::<T>; None` for empty drop glue.
///
/// NB: the type must be monomorphic for MIR shims to work.
// FIXME(eddyb) support generating shims for a "shallow type",
// e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic
// `Foo<Bar>` or `[String]` etc.
DropGlue(DefId, Option<Ty<'tcx>>),

///`<T as Clone>::clone` shim.
///
/// NB: the type must be monomorphic for MIR shims to work.
// FIXME(eddyb) support generating shims for a "shallow type",
// e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic
// `Foo<Bar>` or `[String]` etc.
CloneShim(DefId, Ty<'tcx>),
}

Expand Down Expand Up @@ -282,21 +298,28 @@ impl<'tcx> Instance<'tcx> {
debug!(" => intrinsic");
ty::InstanceDef::Intrinsic(def_id)
}
_ => {
if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
let ty = substs.type_at(0);
if ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) {
debug!(" => nontrivial drop glue");
ty::InstanceDef::DropGlue(def_id, Some(ty))
} else {
debug!(" => trivial drop glue");
ty::InstanceDef::DropGlue(def_id, None)
ty::FnDef(def_id, substs)
if Some(def_id) == tcx.lang_items().drop_in_place_fn() =>
{
let ty = substs.type_at(0);

if ty.needs_drop(tcx, param_env) {
// `DropGlue` requires a monomorphic aka concrete type.
if ty.needs_subst() {
return None;
}

debug!(" => nontrivial drop glue");
ty::InstanceDef::DropGlue(def_id, Some(ty))
} else {
debug!(" => free item");
ty::InstanceDef::Item(def_id)
debug!(" => trivial drop glue");
ty::InstanceDef::DropGlue(def_id, None)
}
}
_ => {
debug!(" => free item");
ty::InstanceDef::Item(def_id)
}
};
Some(Instance { def: def, substs: substs })
};
Expand Down Expand Up @@ -457,20 +480,44 @@ fn resolve_associated_item<'tcx>(
trait_closure_kind,
))
}
traits::VtableFnPointer(ref data) => Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty),
substs: rcvr_substs,
}),
traits::VtableFnPointer(ref data) => {
// `FnPtrShim` requires a monomorphic aka concrete type.
if data.fn_ty.needs_subst() {
return None;
}

Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty),
substs: rcvr_substs,
})
}
traits::VtableObject(ref data) => {
let index = traits::get_vtable_index_of_object_method(tcx, data, def_id);
Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs })
}
traits::VtableBuiltin(..) => {
if tcx.lang_items().clone_trait().is_some() {
Some(Instance {
def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()),
substs: rcvr_substs,
})
if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() {
// FIXME(eddyb) use lang items for methods instead of names.
let name = tcx.item_name(def_id);
if name == sym::clone {
let self_ty = trait_ref.self_ty();

// `CloneShim` requires a monomorphic aka concrete type.
if self_ty.needs_subst() {
return None;
}

Some(Instance {
def: ty::InstanceDef::CloneShim(def_id, self_ty),
substs: rcvr_substs,
})
} else {
assert_eq!(name, sym::clone_from);

// Use the default `fn clone_from` from `trait Clone`.
let substs = tcx.erase_regions(&rcvr_substs);
Some(ty::Instance::new(def_id, substs))
}
} else {
None
}
Expand Down
61 changes: 43 additions & 18 deletions src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc::mir::interpret::PanicInfo;
use rustc::ty::layout::{self, FnAbiExt, HasTyCtxt, LayoutOf};
use rustc::ty::{self, Instance, Ty, TypeFoldable};
use rustc_index::vec::Idx;
use rustc_span::{source_map::Span, symbol::Symbol};
use rustc_span::{Span, Symbol};
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use rustc_target::spec::abi::Abi;

Expand Down Expand Up @@ -408,7 +408,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 {
Expand Down Expand Up @@ -444,7 +444,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
cleanup: Option<mir::BasicBlock>,
) {
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);

Expand Down Expand Up @@ -530,7 +532,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if layout.abi.is_uninhabited() {
let msg_str = format!("Attempted to instantiate uninhabited type {}", ty);
let msg = bx.const_str(Symbol::intern(&msg_str));
let location = self.get_caller_location(&mut bx, span).immediate();
let location = self.get_caller_location(&mut bx, source_info).immediate();

// Obtain the panic entry point.
let def_id =
Expand Down Expand Up @@ -575,7 +577,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {

if intrinsic == Some("caller_location") {
if let Some((_, target)) = destination.as_ref() {
let location = self.get_caller_location(&mut bx, span);
let location = self.get_caller_location(&mut bx, source_info);

if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
location.val.store(&mut bx, tmp);
Expand Down Expand Up @@ -627,13 +629,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
.collect();

bx.codegen_intrinsic_call(
*instance.as_ref().unwrap(),
&fn_abi,
&args,
dest,
terminator.source_info.span,
);
bx.codegen_intrinsic_call(*instance.as_ref().unwrap(), &fn_abi, &args, dest, span);

if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
self.store_return(&mut bx, ret_dest, &fn_abi.ret, dst.llval);
Expand Down Expand Up @@ -739,7 +735,7 @@ 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, span);
let location = self.get_caller_location(&mut bx, source_info);
let last_arg = fn_abi.args.last().unwrap();
self.codegen_argument(&mut bx, location, &mut llargs, last_arg);
}
Expand Down Expand Up @@ -982,17 +978,46 @@ 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,
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)
})
};

// Walk up the `SourceScope`s, in case some of them are from MIR inlining.
let mut caller_span = source_info.span;
let mut scope = source_info.scope;
loop {
let scope_data = &self.mir.source_scopes[scope];

if let Some((callee, callsite_span)) = scope_data.inlined {
// Stop before ("inside") the callsite of a non-`#[track_caller]` function.
if !callee.def.requires_caller_location(tcx) {
return span_to_caller_location(caller_span);
}
caller_span = callsite_span;
}

match scope_data.parent_scope {
Some(parent) => 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(caller_span))
}

fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> {
Expand Down
28 changes: 25 additions & 3 deletions src/librustc_mir/interpret/intrinsics/caller_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,33 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// `None` is returned and the callsite of the function invocation itself should be used.
crate fn find_closest_untracked_caller_location(&self) -> Option<Span> {
let mut caller_span = None;
for next_caller in self.stack.iter().rev() {
if !next_caller.instance.def.requires_caller_location(*self.tcx) {
for frame in self.stack.iter().rev() {
if let Some(source_info) = frame.current_source_info() {
// Walk up the `SourceScope`s, in case some of them are from MIR inlining.
let mut scope = source_info.scope;
loop {
let scope_data = &frame.body.source_scopes[scope];

if let Some((callee, callsite_span)) = scope_data.inlined {
// Stop before ("inside") the callsite of a non-`#[track_caller]` function.
if !callee.def.requires_caller_location(*self.tcx) {
return caller_span;
}
caller_span = Some(callsite_span);
}

match scope_data.parent_scope {
Some(parent) => scope = parent,
None => break,
}
}
}

// Stop before ("inside") the callsite of a non-`#[track_caller]` function.
if !frame.instance.def.requires_caller_location(*self.tcx) {
return caller_span;
}
caller_span = Some(next_caller.span);
caller_span = Some(frame.span);
}

caller_span
Expand Down
Loading

0 comments on commit 5f38376

Please sign in to comment.