Skip to content

Commit 6451b39

Browse files
committed
rustc_mir: support MIR-inlining #[track_caller] functions.
1 parent fb36440 commit 6451b39

File tree

8 files changed

+144
-52
lines changed

8 files changed

+144
-52
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
405405
self.set_debug_loc(&mut bx, terminator.source_info);
406406

407407
// Get the location information.
408-
let location = self.get_caller_location(&mut bx, span).immediate();
408+
let location = self.get_caller_location(&mut bx, terminator.source_info).immediate();
409409

410410
// Put together the arguments to the panic entry point.
411411
let (lang_item, args) = match msg {
@@ -442,7 +442,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
442442
bx: &mut Bx,
443443
intrinsic: Option<Symbol>,
444444
instance: Option<Instance<'tcx>>,
445-
span: Span,
445+
source_info: mir::SourceInfo,
446446
destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>,
447447
cleanup: Option<mir::BasicBlock>,
448448
) -> bool {
@@ -484,11 +484,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
484484
}
485485
});
486486
let msg = bx.const_str(Symbol::intern(&msg_str));
487-
let location = self.get_caller_location(bx, span).immediate();
487+
let location = self.get_caller_location(bx, source_info).immediate();
488488

489489
// Obtain the panic entry point.
490490
// FIXME: dedup this with `codegen_assert_terminator` above.
491-
let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::Panic);
491+
let def_id =
492+
common::langcall(bx.tcx(), Some(source_info.span), "", LangItem::Panic);
492493
let instance = ty::Instance::mono(bx.tcx(), def_id);
493494
let fn_abi = FnAbi::of_instance(bx, instance, &[]);
494495
let llfn = bx.get_fn_addr(instance);
@@ -529,7 +530,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
529530
cleanup: Option<mir::BasicBlock>,
530531
fn_span: Span,
531532
) {
532-
let span = terminator.source_info.span;
533+
let source_info = terminator.source_info;
534+
let span = source_info.span;
535+
533536
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
534537
let callee = self.codegen_operand(&mut bx, func);
535538

@@ -606,7 +609,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
606609
&mut bx,
607610
intrinsic,
608611
instance,
609-
span,
612+
source_info,
610613
destination,
611614
cleanup,
612615
) {
@@ -627,7 +630,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
627630

628631
if intrinsic == Some(sym::caller_location) {
629632
if let Some((_, target)) = destination.as_ref() {
630-
let location = self.get_caller_location(&mut bx, fn_span);
633+
let location = self
634+
.get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info });
631635

632636
if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
633637
location.val.store(&mut bx, tmp);
@@ -686,7 +690,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
686690
&fn_abi,
687691
&args,
688692
dest,
689-
terminator.source_info.span,
693+
span,
690694
);
691695

692696
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
@@ -793,7 +797,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
793797
args.len() + 1,
794798
"#[track_caller] fn's must have 1 more argument in their ABI than in their MIR",
795799
);
796-
let location = self.get_caller_location(&mut bx, fn_span);
800+
let location =
801+
self.get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info });
797802
debug!(
798803
"codegen_call_terminator({:?}): location={:?} (fn_span {:?})",
799804
terminator, location, fn_span
@@ -1179,17 +1184,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11791184
}
11801185
}
11811186

1182-
fn get_caller_location(&mut self, bx: &mut Bx, span: Span) -> OperandRef<'tcx, Bx::Value> {
1183-
self.caller_location.unwrap_or_else(|| {
1187+
fn get_caller_location(
1188+
&mut self,
1189+
bx: &mut Bx,
1190+
mut source_info: mir::SourceInfo,
1191+
) -> OperandRef<'tcx, Bx::Value> {
1192+
let tcx = bx.tcx();
1193+
1194+
let mut span_to_caller_location = |span: Span| {
11841195
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
1185-
let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo());
1186-
let const_loc = bx.tcx().const_caller_location((
1196+
let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
1197+
let const_loc = tcx.const_caller_location((
11871198
Symbol::intern(&caller.file.name.to_string()),
11881199
caller.line as u32,
11891200
caller.col_display as u32 + 1,
11901201
));
11911202
OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty())
1192-
})
1203+
};
1204+
1205+
// Walk up the `SourceScope`s, in case some of them are from MIR inlining.
1206+
// If so, the starting `source_info.span` is in the innermost inlined
1207+
// function, and will be replaced with outer callsite spans as long
1208+
// as the inlined functions were `#[track_caller]`.
1209+
loop {
1210+
let scope_data = &self.mir.source_scopes[source_info.scope];
1211+
1212+
if let Some((callee, callsite_span)) = scope_data.inlined {
1213+
// Stop inside the most nested non-`#[track_caller]` function,
1214+
// before ever reaching its caller (which is irrelevant).
1215+
if !callee.def.requires_caller_location(tcx) {
1216+
return span_to_caller_location(source_info.span);
1217+
}
1218+
source_info.span = callsite_span;
1219+
}
1220+
1221+
// Skip past all of the parents with `inlined: None`.
1222+
match scope_data.inlined_parent_scope {
1223+
Some(parent) => source_info.scope = parent,
1224+
None => break,
1225+
}
1226+
}
1227+
1228+
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
1229+
self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span))
11931230
}
11941231

11951232
fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> {

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,6 +1878,11 @@ pub struct SourceScopeData<'tcx> {
18781878
/// `ty::Instance` is the callee, and the `Span` is the call site.
18791879
pub inlined: Option<(ty::Instance<'tcx>, Span)>,
18801880

1881+
/// Nearest (transitive) parent scope (if any) which is inlined.
1882+
/// This is an optimization over walking up `parent_scope`
1883+
/// until a scope with `inlined: Some(...)` is found.
1884+
pub inlined_parent_scope: Option<SourceScope>,
1885+
18811886
/// Crate-local information for this source scope, that can't (and
18821887
/// needn't) be tracked across crates.
18831888
pub local_data: ClearCrossCrate<SourceScopeLocalData>,

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ macro_rules! make_mir_visitor {
325325
span,
326326
parent_scope,
327327
inlined,
328+
inlined_parent_scope,
328329
local_data: _,
329330
} = scope_data;
330331

@@ -357,6 +358,9 @@ macro_rules! make_mir_visitor {
357358
}
358359
self.visit_substs(callee_substs, location);
359360
}
361+
if let Some(inlined_parent_scope) = inlined_parent_scope {
362+
self.visit_source_scope(inlined_parent_scope);
363+
}
360364
}
361365

362366
fn super_statement(&mut self,

compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,38 +15,61 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1515
/// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
1616
/// frame which is not `#[track_caller]`.
1717
crate fn find_closest_untracked_caller_location(&self) -> Span {
18-
let frame = self
19-
.stack()
20-
.iter()
21-
.rev()
22-
// Find first non-`#[track_caller]` frame.
23-
.find(|frame| {
18+
for frame in self.stack().iter().rev() {
19+
debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
20+
21+
// Assert that the frame we look at is actually executing code currently
22+
// (`loc` is `Err` when we are unwinding and the frame does not require cleanup).
23+
let loc = frame.loc.unwrap();
24+
25+
// This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all
26+
// (such as `box`). Use the normal span by default.
27+
let mut source_info = *frame.body.source_info(loc);
28+
29+
// If this is a `Call` terminator, use the `fn_span` instead.
30+
let block = &frame.body.basic_blocks()[loc.block];
31+
if loc.statement_index == block.statements.len() {
2432
debug!(
25-
"find_closest_untracked_caller_location: checking frame {:?}",
26-
frame.instance
33+
"find_closest_untracked_caller_location: got terminator {:?} ({:?})",
34+
block.terminator(),
35+
block.terminator().kind
2736
);
28-
!frame.instance.def.requires_caller_location(*self.tcx)
29-
})
30-
// Assert that there is always such a frame.
31-
.unwrap();
32-
// Assert that the frame we look at is actually executing code currently
33-
// (`loc` is `Err` when we are unwinding and the frame does not require cleanup).
34-
let loc = frame.loc.unwrap();
35-
// If this is a `Call` terminator, use the `fn_span` instead.
36-
let block = &frame.body.basic_blocks()[loc.block];
37-
if loc.statement_index == block.statements.len() {
38-
debug!(
39-
"find_closest_untracked_caller_location:: got terminator {:?} ({:?})",
40-
block.terminator(),
41-
block.terminator().kind
42-
);
43-
if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
44-
return fn_span;
37+
if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind {
38+
source_info.span = fn_span;
39+
}
40+
}
41+
42+
// Walk up the `SourceScope`s, in case some of them are from MIR inlining.
43+
// If so, the starting `source_info.span` is in the innermost inlined
44+
// function, and will be replaced with outer callsite spans as long
45+
// as the inlined functions were `#[track_caller]`.
46+
loop {
47+
let scope_data = &frame.body.source_scopes[source_info.scope];
48+
49+
if let Some((callee, callsite_span)) = scope_data.inlined {
50+
// Stop inside the most nested non-`#[track_caller]` function,
51+
// before ever reaching its caller (which is irrelevant).
52+
if !callee.def.requires_caller_location(*self.tcx) {
53+
return source_info.span;
54+
}
55+
source_info.span = callsite_span;
56+
}
57+
58+
// Skip past all of the parents with `inlined: None`.
59+
match scope_data.inlined_parent_scope {
60+
Some(parent) => source_info.scope = parent,
61+
None => break,
62+
}
63+
}
64+
65+
// Stop inside the most nested non-`#[track_caller]` function,
66+
// before ever reaching its caller (which is irrelevant).
67+
if !frame.instance.def.requires_caller_location(*self.tcx) {
68+
return source_info.span;
4569
}
4670
}
47-
// This is a different terminator (such as `Drop`) or not a terminator at all
48-
// (such as `box`). Use the normal span.
49-
frame.body.source_info(loc).span
71+
72+
bug!("no non-`#[track_caller]` frame found")
5073
}
5174

5275
/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.

compiler/rustc_mir/src/shim.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ fn new_body<'tcx>(
216216
span,
217217
parent_scope: None,
218218
inlined: None,
219+
inlined_parent_scope: None,
219220
local_data: ClearCrossCrate::Clear,
220221
},
221222
1,

compiler/rustc_mir/src/transform/inline.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,6 @@ impl Inliner<'tcx> {
246246

247247
let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
248248

249-
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) {
250-
debug!("`#[track_caller]` present - not inlining");
251-
return false;
252-
}
253-
254249
let self_features = &self.codegen_fn_attrs.target_features;
255250
let callee_features = &codegen_fn_attrs.target_features;
256251
if callee_features.iter().any(|feature| !self_features.contains(feature)) {
@@ -441,11 +436,24 @@ impl Inliner<'tcx> {
441436

442437
for mut scope in callee_body.source_scopes.iter().cloned() {
443438
if scope.parent_scope.is_none() {
439+
let callsite_scope = &caller_body.source_scopes[callsite.source_info.scope];
440+
441+
// Attach the outermost callee scope as a child of the callsite
442+
// scope, via the `parent_scope` and `inlined_parent_scope` chains.
444443
scope.parent_scope = Some(callsite.source_info.scope);
444+
assert_eq!(scope.inlined_parent_scope, None);
445+
scope.inlined_parent_scope = if callsite_scope.inlined.is_some() {
446+
Some(callsite.source_info.scope)
447+
} else {
448+
callsite_scope.inlined_parent_scope
449+
};
445450

446451
// Mark the outermost callee scope as an inlined one.
447452
assert_eq!(scope.inlined, None);
448453
scope.inlined = Some((callsite.callee, callsite.source_info.span));
454+
} else if scope.inlined_parent_scope.is_none() {
455+
// Make it easy to find the scope with `inlined` set above.
456+
scope.inlined_parent_scope = Some(scope_map[OUTERMOST_SOURCE_SCOPE]);
449457
}
450458

451459
let idx = caller_body.source_scopes.push(scope);

compiler/rustc_mir/src/util/pretty.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -551,18 +551,31 @@ fn write_scope_tree(
551551
let child_data = &body.source_scopes[child];
552552
assert_eq!(child_data.parent_scope, Some(parent));
553553

554-
if let Some((callee, callsite_span)) = child_data.inlined {
555-
let indented_header =
556-
format!("{0:1$}scope {2} (inlined {3}) {{", "", indent, child.index(), callee);
554+
let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
555+
(
556+
format!(
557+
" (inlined {}{})",
558+
if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
559+
callee
560+
),
561+
Some(callsite_span),
562+
)
563+
} else {
564+
(String::new(), None)
565+
};
566+
567+
let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
568+
569+
if let Some(span) = span {
557570
writeln!(
558571
w,
559572
"{0:1$} // at {2}",
560573
indented_header,
561574
ALIGN,
562-
tcx.sess.source_map().span_to_string(callsite_span),
575+
tcx.sess.source_map().span_to_string(span),
563576
)?;
564577
} else {
565-
writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
578+
writeln!(w, "{}", indented_header)?;
566579
}
567580

568581
write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;

compiler/rustc_mir_build/src/build/scope.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
706706
span,
707707
parent_scope: Some(parent),
708708
inlined: None,
709+
inlined_parent_scope: None,
709710
local_data: ClearCrossCrate::Set(scope_local_data),
710711
})
711712
}

0 commit comments

Comments
 (0)