Auto merge of #113128 - WaffleLapkin:become_trully_unuwuable, r=oli-obk,RalfJung
Support tail calls in mir via `TerminatorKind::TailCall` This is one of the interesting bits in tail call implementation — MIR support. This adds a new `TerminatorKind` which represents a tail call: ```rust TailCall { func: Operand<'tcx>, args: Vec<Operand<'tcx>>, fn_span: Span, }, ``` *Structurally* this is very similar to a normal `Call` but is missing a few fields: - `destination` — tail calls don't write to destination, instead they pass caller's destination to the callee (such that eventual `return` will write to the caller of the function that used tail call) - `target` — similarly to `destination` tail calls pass the caller's return address to the callee, so there is nothing to do - `unwind` — I _think_ this is applicable too, although it's a bit confusing - `call_source` — `become` forbids operators and is not created as a lowering of something else; tail calls always come from HIR (at least for now) It might be helpful to read the interpreter implementation to understand what `TailCall` means exactly, although I've tried documenting it too. ----- There are a few `FIXME`-questions still left, ideally we'd be able to answer them during review ':) ----- r? `@oli-obk` cc `@scottmcm` `@DrMeepster` `@JakobDegen`
This commit is contained in:
commit
9af6fee87d
75 changed files with 2385 additions and 173 deletions
|
@ -854,6 +854,16 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
}
|
||||
write!(fmt, ")")
|
||||
}
|
||||
TailCall { func, args, .. } => {
|
||||
write!(fmt, "tailcall {func:?}(")?;
|
||||
for (index, arg) in args.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(fmt, ", ")?;
|
||||
}
|
||||
write!(fmt, "{:?}", arg)?;
|
||||
}
|
||||
write!(fmt, ")")
|
||||
}
|
||||
Assert { cond, expected, msg, .. } => {
|
||||
write!(fmt, "assert(")?;
|
||||
if !expected {
|
||||
|
@ -921,7 +931,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
||||
use self::TerminatorKind::*;
|
||||
match *self {
|
||||
Return | UnwindResume | UnwindTerminate(_) | Unreachable | CoroutineDrop => vec![],
|
||||
Return
|
||||
| TailCall { .. }
|
||||
| UnwindResume
|
||||
| UnwindTerminate(_)
|
||||
| Unreachable
|
||||
| CoroutineDrop => vec![],
|
||||
Goto { .. } => vec!["".into()],
|
||||
SwitchInt { ref targets, .. } => targets
|
||||
.values
|
||||
|
|
|
@ -744,6 +744,36 @@ pub enum TerminatorKind<'tcx> {
|
|||
fn_span: Span,
|
||||
},
|
||||
|
||||
/// Tail call.
|
||||
///
|
||||
/// Roughly speaking this is a chimera of [`Call`] and [`Return`], with some caveats.
|
||||
/// Semantically tail calls consists of two actions:
|
||||
/// - pop of the current stack frame
|
||||
/// - a call to the `func`, with the return address of the **current** caller
|
||||
/// - so that a `return` inside `func` returns to the caller of the caller
|
||||
/// of the function that is currently being executed
|
||||
///
|
||||
/// Note that in difference with [`Call`] this is missing
|
||||
/// - `destination` (because it's always the return place)
|
||||
/// - `target` (because it's always taken from the current stack frame)
|
||||
/// - `unwind` (because it's always taken from the current stack frame)
|
||||
///
|
||||
/// [`Call`]: TerminatorKind::Call
|
||||
/// [`Return`]: TerminatorKind::Return
|
||||
TailCall {
|
||||
/// The function that’s being called.
|
||||
func: Operand<'tcx>,
|
||||
/// Arguments the function is called with.
|
||||
/// These are owned by the callee, which is free to modify them.
|
||||
/// This allows the memory occupied by "by-value" arguments to be
|
||||
/// reused across function calls without duplicating the contents.
|
||||
args: Box<[Spanned<Operand<'tcx>>]>,
|
||||
// FIXME(explicit_tail_calls): should we have the span for `become`? is this span accurate? do we need it?
|
||||
/// This `Span` is the span of the function, without the dot and receiver
|
||||
/// (e.g. `foo(a, b)` in `x.foo(a, b)`
|
||||
fn_span: Span,
|
||||
},
|
||||
|
||||
/// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
|
||||
/// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
|
||||
/// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
|
||||
|
@ -870,6 +900,7 @@ impl TerminatorKind<'_> {
|
|||
TerminatorKind::Unreachable => "Unreachable",
|
||||
TerminatorKind::Drop { .. } => "Drop",
|
||||
TerminatorKind::Call { .. } => "Call",
|
||||
TerminatorKind::TailCall { .. } => "TailCall",
|
||||
TerminatorKind::Assert { .. } => "Assert",
|
||||
TerminatorKind::Yield { .. } => "Yield",
|
||||
TerminatorKind::CoroutineDrop => "CoroutineDrop",
|
||||
|
|
|
@ -439,6 +439,7 @@ mod helper {
|
|||
| CoroutineDrop
|
||||
| Return
|
||||
| Unreachable
|
||||
| TailCall { .. }
|
||||
| Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
|
||||
InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
|
||||
targets.iter().copied().chain(Some(u))
|
||||
|
@ -479,6 +480,7 @@ mod helper {
|
|||
| CoroutineDrop
|
||||
| Return
|
||||
| Unreachable
|
||||
| TailCall { .. }
|
||||
| Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
|
||||
InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
|
||||
targets.iter_mut().chain(Some(u))
|
||||
|
@ -501,6 +503,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
| TerminatorKind::UnwindResume
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::TailCall { .. }
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::CoroutineDrop
|
||||
| TerminatorKind::Yield { .. }
|
||||
|
@ -521,6 +524,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
| TerminatorKind::UnwindResume
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::TailCall { .. }
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::CoroutineDrop
|
||||
| TerminatorKind::Yield { .. }
|
||||
|
@ -606,9 +610,12 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
|
||||
use TerminatorKind::*;
|
||||
match *self {
|
||||
Return | UnwindResume | UnwindTerminate(_) | CoroutineDrop | Unreachable => {
|
||||
TerminatorEdges::None
|
||||
}
|
||||
Return
|
||||
| TailCall { .. }
|
||||
| UnwindResume
|
||||
| UnwindTerminate(_)
|
||||
| CoroutineDrop
|
||||
| Unreachable => TerminatorEdges::None,
|
||||
|
||||
Goto { target } => TerminatorEdges::Single(target),
|
||||
|
||||
|
|
|
@ -540,6 +540,17 @@ macro_rules! make_mir_visitor {
|
|||
);
|
||||
}
|
||||
|
||||
TerminatorKind::TailCall {
|
||||
func,
|
||||
args,
|
||||
fn_span: _,
|
||||
} => {
|
||||
self.visit_operand(func, location);
|
||||
for arg in args {
|
||||
self.visit_operand(&$($mutability)? arg.node, location);
|
||||
}
|
||||
},
|
||||
|
||||
TerminatorKind::Assert {
|
||||
cond,
|
||||
expected: _,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue