Skip to content

Commit 9af6fee

Browse files
committed
Auto merge of rust-lang#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`
2 parents b1de36f + 14e5d5f commit 9af6fee

File tree

75 files changed

+2386
-174
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+2386
-174
lines changed

compiler/rustc_borrowck/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,12 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
727727
}
728728
self.mutate_place(loc, (*destination, span), Deep, flow_state);
729729
}
730+
TerminatorKind::TailCall { func, args, fn_span: _ } => {
731+
self.consume_operand(loc, (func, span), flow_state);
732+
for arg in args {
733+
self.consume_operand(loc, (&arg.node, arg.span), flow_state);
734+
}
735+
}
730736
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
731737
self.consume_operand(loc, (cond, span), flow_state);
732738
if let AssertKind::BoundsCheck { len, index } = &**msg {
@@ -813,9 +819,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
813819

814820
TerminatorKind::UnwindResume
815821
| TerminatorKind::Return
822+
| TerminatorKind::TailCall { .. }
816823
| TerminatorKind::CoroutineDrop => {
817-
// Returning from the function implicitly kills storage for all locals and statics.
818-
// Often, the storage will already have been killed by an explicit
819824
// StorageDead, but we don't always emit those (notably on unwind paths),
820825
// so this "extra check" serves as a kind of backup.
821826
let borrow_set = self.borrow_set.clone();

compiler/rustc_borrowck/src/polonius/loan_invalidations.rs

+6
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
125125
}
126126
self.mutate_place(location, *destination, Deep);
127127
}
128+
TerminatorKind::TailCall { func, args, .. } => {
129+
self.consume_operand(location, func);
130+
for arg in args {
131+
self.consume_operand(location, &arg.node);
132+
}
133+
}
128134
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
129135
self.consume_operand(location, cond);
130136
use rustc_middle::mir::AssertKind;

compiler/rustc_borrowck/src/type_check/mod.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -1352,7 +1352,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
13521352
}
13531353
// FIXME: check the values
13541354
}
1355-
TerminatorKind::Call { func, args, destination, call_source, target, .. } => {
1355+
TerminatorKind::Call { func, args, .. }
1356+
| TerminatorKind::TailCall { func, args, .. } => {
1357+
let call_source = match term.kind {
1358+
TerminatorKind::Call { call_source, .. } => call_source,
1359+
TerminatorKind::TailCall { .. } => CallSource::Normal,
1360+
_ => unreachable!(),
1361+
};
1362+
13561363
self.check_operand(func, term_location);
13571364
for arg in args {
13581365
self.check_operand(&arg.node, term_location);
@@ -1425,7 +1432,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
14251432
);
14261433
}
14271434

1428-
self.check_call_dest(body, term, &sig, *destination, *target, term_location);
1435+
if let TerminatorKind::Call { destination, target, .. } = term.kind {
1436+
self.check_call_dest(body, term, &sig, destination, target, term_location);
1437+
}
14291438

14301439
// The ordinary liveness rules will ensure that all
14311440
// regions in the type of the callee are live here. We
@@ -1443,7 +1452,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
14431452
.add_location(region_vid, term_location);
14441453
}
14451454

1446-
self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source);
1455+
self.check_call_inputs(body, term, func, &sig, args, term_location, call_source);
14471456
}
14481457
TerminatorKind::Assert { cond, msg, .. } => {
14491458
self.check_operand(cond, term_location);
@@ -1675,6 +1684,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
16751684
span_mirbug!(self, block_data, "return on cleanup block")
16761685
}
16771686
}
1687+
TerminatorKind::TailCall { .. } => {
1688+
if is_cleanup {
1689+
span_mirbug!(self, block_data, "tailcall on cleanup block")
1690+
}
1691+
}
16781692
TerminatorKind::CoroutineDrop { .. } => {
16791693
if is_cleanup {
16801694
span_mirbug!(self, block_data, "coroutine_drop in cleanup block")

compiler/rustc_codegen_cranelift/src/base.rs

+5
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
491491
)
492492
});
493493
}
494+
// FIXME(explicit_tail_calls): add support for tail calls to the cranelift backend, once cranelift supports tail calls
495+
TerminatorKind::TailCall { fn_span, .. } => span_bug!(
496+
*fn_span,
497+
"tail calls are not yet supported in `rustc_codegen_cranelift` backend"
498+
),
494499
TerminatorKind::InlineAsm {
495500
template,
496501
operands,

compiler/rustc_codegen_cranelift/src/constant.rs

+1
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
567567
{
568568
return None;
569569
}
570+
TerminatorKind::TailCall { .. } => return None,
570571
TerminatorKind::Call { .. } => {}
571572
}
572573
}

compiler/rustc_codegen_ssa/src/mir/analyze.rs

+1
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
281281
| TerminatorKind::UnwindResume
282282
| TerminatorKind::UnwindTerminate(_)
283283
| TerminatorKind::Return
284+
| TerminatorKind::TailCall { .. }
284285
| TerminatorKind::CoroutineDrop
285286
| TerminatorKind::Unreachable
286287
| TerminatorKind::SwitchInt { .. }

compiler/rustc_codegen_ssa/src/mir/block.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13891389
fn_span,
13901390
mergeable_succ(),
13911391
),
1392+
mir::TerminatorKind::TailCall { .. } => {
1393+
// FIXME(explicit_tail_calls): implement tail calls in ssa backend
1394+
span_bug!(
1395+
terminator.source_info.span,
1396+
"`TailCall` terminator is not yet supported by `rustc_codegen_ssa`"
1397+
)
1398+
}
13921399
mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => {
13931400
bug!("coroutine ops in codegen")
13941401
}

compiler/rustc_const_eval/src/check_consts/check.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
135135
ccx: &'mir ConstCx<'mir, 'tcx>,
136136
tainted_by_errors: Option<ErrorGuaranteed>,
137137
) -> ConstQualifs {
138+
// FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything
139+
138140
// Find the `Return` terminator if one exists.
139141
//
140142
// If no `Return` terminator exists, this MIR is divergent. Just return the conservative
@@ -711,7 +713,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
711713
self.super_terminator(terminator, location);
712714

713715
match &terminator.kind {
714-
TerminatorKind::Call { func, args, fn_span, call_source, .. } => {
716+
TerminatorKind::Call { func, args, fn_span, .. }
717+
| TerminatorKind::TailCall { func, args, fn_span, .. } => {
718+
let call_source = match terminator.kind {
719+
TerminatorKind::Call { call_source, .. } => call_source,
720+
TerminatorKind::TailCall { .. } => CallSource::Normal,
721+
_ => unreachable!(),
722+
};
723+
715724
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
716725
let caller = self.def_id();
717726

@@ -783,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
783792
callee,
784793
args: fn_args,
785794
span: *fn_span,
786-
call_source: *call_source,
795+
call_source,
787796
feature: Some(if tcx.features().const_trait_impl {
788797
sym::effects
789798
} else {
@@ -830,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
830839
callee,
831840
args: fn_args,
832841
span: *fn_span,
833-
call_source: *call_source,
842+
call_source,
834843
feature: None,
835844
});
836845
return;

compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
108108

109109
mir::TerminatorKind::UnwindTerminate(_)
110110
| mir::TerminatorKind::Call { .. }
111+
| mir::TerminatorKind::TailCall { .. }
111112
| mir::TerminatorKind::Assert { .. }
112113
| mir::TerminatorKind::FalseEdge { .. }
113114
| mir::TerminatorKind::FalseUnwind { .. }

0 commit comments

Comments
 (0)