1
Fork 0

rustc_mir: support MIR-inlining #[track_caller] functions.

This commit is contained in:
Eduard-Mihai Burtescu 2020-09-21 06:52:37 +03:00
parent fb36440b7a
commit 6451b39a25
8 changed files with 144 additions and 52 deletions

View file

@ -15,38 +15,61 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
/// frame which is not `#[track_caller]`.
crate fn find_closest_untracked_caller_location(&self) -> Span {
let frame = self
.stack()
.iter()
.rev()
// Find first non-`#[track_caller]` frame.
.find(|frame| {
for frame in self.stack().iter().rev() {
debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
// Assert that the frame we look at is actually executing code currently
// (`loc` is `Err` when we are unwinding and the frame does not require cleanup).
let loc = frame.loc.unwrap();
// This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all
// (such as `box`). Use the normal span by default.
let mut source_info = *frame.body.source_info(loc);
// If this is a `Call` terminator, use the `fn_span` instead.
let block = &frame.body.basic_blocks()[loc.block];
if loc.statement_index == block.statements.len() {
debug!(
"find_closest_untracked_caller_location: checking frame {:?}",
frame.instance
"find_closest_untracked_caller_location: got terminator {:?} ({:?})",
block.terminator(),
block.terminator().kind
);
!frame.instance.def.requires_caller_location(*self.tcx)
})
// Assert that there is always such a frame.
.unwrap();
// Assert that the frame we look at is actually executing code currently
// (`loc` is `Err` when we are unwinding and the frame does not require cleanup).
let loc = frame.loc.unwrap();
// If this is a `Call` terminator, use the `fn_span` instead.
let block = &frame.body.basic_blocks()[loc.block];
if loc.statement_index == block.statements.len() {
debug!(
"find_closest_untracked_caller_location:: got terminator {:?} ({:?})",
block.terminator(),
block.terminator().kind
);
if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
return fn_span;
if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
source_info.span = fn_span;
}
}
// 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 = &frame.body.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(*self.tcx) {
return 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,
}
}
// Stop inside the most nested non-`#[track_caller]` function,
// before ever reaching its caller (which is irrelevant).
if !frame.instance.def.requires_caller_location(*self.tcx) {
return source_info.span;
}
}
// This is a different terminator (such as `Drop`) or not a terminator at all
// (such as `box`). Use the normal span.
frame.body.source_info(loc).span
bug!("no non-`#[track_caller]` frame found")
}
/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.

View file

@ -216,6 +216,7 @@ fn new_body<'tcx>(
span,
parent_scope: None,
inlined: None,
inlined_parent_scope: None,
local_data: ClearCrossCrate::Clear,
},
1,

View file

@ -246,11 +246,6 @@ impl Inliner<'tcx> {
let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) {
debug!("`#[track_caller]` present - not inlining");
return false;
}
let self_features = &self.codegen_fn_attrs.target_features;
let callee_features = &codegen_fn_attrs.target_features;
if callee_features.iter().any(|feature| !self_features.contains(feature)) {
@ -441,11 +436,24 @@ impl Inliner<'tcx> {
for mut scope in callee_body.source_scopes.iter().cloned() {
if scope.parent_scope.is_none() {
let callsite_scope = &caller_body.source_scopes[callsite.source_info.scope];
// Attach the outermost callee scope as a child of the callsite
// scope, via the `parent_scope` and `inlined_parent_scope` chains.
scope.parent_scope = Some(callsite.source_info.scope);
assert_eq!(scope.inlined_parent_scope, None);
scope.inlined_parent_scope = if callsite_scope.inlined.is_some() {
Some(callsite.source_info.scope)
} else {
callsite_scope.inlined_parent_scope
};
// Mark the outermost callee scope as an inlined one.
assert_eq!(scope.inlined, None);
scope.inlined = Some((callsite.callee, callsite.source_info.span));
} else if scope.inlined_parent_scope.is_none() {
// Make it easy to find the scope with `inlined` set above.
scope.inlined_parent_scope = Some(scope_map[OUTERMOST_SOURCE_SCOPE]);
}
let idx = caller_body.source_scopes.push(scope);

View file

@ -551,18 +551,31 @@ fn write_scope_tree(
let child_data = &body.source_scopes[child];
assert_eq!(child_data.parent_scope, Some(parent));
if let Some((callee, callsite_span)) = child_data.inlined {
let indented_header =
format!("{0:1$}scope {2} (inlined {3}) {{", "", indent, child.index(), callee);
let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
(
format!(
" (inlined {}{})",
if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
callee
),
Some(callsite_span),
)
} else {
(String::new(), None)
};
let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
if let Some(span) = span {
writeln!(
w,
"{0:1$} // at {2}",
indented_header,
ALIGN,
tcx.sess.source_map().span_to_string(callsite_span),
tcx.sess.source_map().span_to_string(span),
)?;
} else {
writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
writeln!(w, "{}", indented_header)?;
}
write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;