diff options
| author | Maybe Waffle <waffle.lapkin@gmail.com> | 2024-02-15 19:54:37 +0000 |
|---|---|---|
| committer | Maybe Lapkin <waffle.lapkin@gmail.com> | 2024-07-07 17:11:04 +0200 |
| commit | 484152d562f6babaacb3fae08cc5f70ee550e9ee (patch) | |
| tree | 300d1010e369b3960213e11a75713fa64e0a0525 /compiler | |
| parent | e2cf31a6148725bde4ea48acf1e4fe72675257a2 (diff) | |
| download | rust-484152d562f6babaacb3fae08cc5f70ee550e9ee.tar.gz rust-484152d562f6babaacb3fae08cc5f70ee550e9ee.zip | |
Support tail calls in mir via `TerminatorKind::TailCall`
Diffstat (limited to 'compiler')
33 files changed, 254 insertions, 53 deletions
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e6f8ffd428d..df532c73950 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -728,6 +728,12 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> } self.mutate_place(loc, (*destination, span), Deep, flow_state); } + TerminatorKind::TailCall { func, args, fn_span: _ } => { + self.consume_operand(loc, (func, span), flow_state); + for arg in args { + self.consume_operand(loc, (&arg.node, arg.span), flow_state); + } + } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(loc, (cond, span), flow_state); if let AssertKind::BoundsCheck { len, index } = &**msg { @@ -814,9 +820,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop => { - // Returning from the function implicitly kills storage for all locals and statics. - // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), // so this "extra check" serves as a kind of backup. let borrow_set = self.borrow_set.clone(); diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 6979910a02d..30dfc4c21b0 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -125,6 +125,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { } self.mutate_place(location, *destination, Deep); } + TerminatorKind::TailCall { func, args, .. } => { + self.consume_operand(location, func); + for arg in args { + self.consume_operand(location, &arg.node); + } + } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(location, cond); use rustc_middle::mir::AssertKind; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index aa25e3adf28..8bba7ef4255 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1352,7 +1352,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // FIXME: check the values } - TerminatorKind::Call { func, args, destination, call_source, target, .. } => { + TerminatorKind::Call { func, args, .. } + | TerminatorKind::TailCall { func, args, .. } => { + let call_source = match term.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; + self.check_operand(func, term_location); for arg in args { self.check_operand(&arg.node, term_location); @@ -1425,7 +1432,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - self.check_call_dest(body, term, &sig, *destination, *target, term_location); + if let TerminatorKind::Call { destination, target, .. } = term.kind { + self.check_call_dest(body, term, &sig, destination, target, term_location); + } // The ordinary liveness rules will ensure that all // regions in the type of the callee are live here. We @@ -1443,7 +1452,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .add_location(region_vid, term_location); } - self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source); + self.check_call_inputs(body, term, func, &sig, args, term_location, call_source); } TerminatorKind::Assert { cond, msg, .. } => { self.check_operand(cond, term_location); @@ -1675,6 +1684,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!(self, block_data, "return on cleanup block") } } + TerminatorKind::TailCall { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "tailcall on cleanup block") + } + } TerminatorKind::CoroutineDrop { .. } => { if is_cleanup { span_mirbug!(self, block_data, "coroutine_drop in cleanup block") diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index c5b4277015a..5adbbb09ac8 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -491,6 +491,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) }); } + // FIXME(explicit_tail_calls): add support for tail calls to the cranelift backend, once cranelift supports tail calls + TerminatorKind::TailCall { fn_span, .. } => span_bug!( + *fn_span, + "tail calls are not yet supported in `rustc_codegen_cranelift` backend" + ), TerminatorKind::InlineAsm { template, operands, diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 87c5da3b7c3..fc12f0ff738 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -565,6 +565,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( { return None; } + TerminatorKind::TailCall { .. } => return None, TerminatorKind::Call { .. } => {} } } diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 0577ba32ffd..ac2b6ca4e95 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop | TerminatorKind::Unreachable | TerminatorKind::SwitchInt { .. } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4adb95f85d6..6a5525dc2b3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1389,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn_span, mergeable_succ(), ), + mir::TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): implement tail calls in ssa backend + span_bug!( + terminator.source_info.span, + "`TailCall` terminator is not yet supported by `rustc_codegen_ssa`" + ) + } mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => { bug!("coroutine ops in codegen") } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 0d8a17775dd..412effba32d 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -135,6 +135,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { ccx: &'mir ConstCx<'mir, 'tcx>, tainted_by_errors: Option<ErrorGuaranteed>, ) -> ConstQualifs { + // FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything + // Find the `Return` terminator if one exists. // // If no `Return` terminator exists, this MIR is divergent. Just return the conservative @@ -711,7 +713,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Call { func, args, fn_span, call_source, .. } => { + TerminatorKind::Call { func, args, fn_span, .. } + | TerminatorKind::TailCall { func, args, fn_span, .. } => { + let call_source = match terminator.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; + let ConstCx { tcx, body, param_env, .. } = *self.ccx; let caller = self.def_id(); @@ -783,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee, args: fn_args, span: *fn_span, - call_source: *call_source, + call_source, feature: Some(if tcx.features().const_trait_impl { sym::effects } else { @@ -830,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee, args: fn_args, span: *fn_span, - call_source: *call_source, + call_source, feature: None, }); return; diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index f0763f1e490..f5e745454ab 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -108,6 +108,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { mir::TerminatorKind::UnwindTerminate(_) | mir::TerminatorKind::Call { .. } + | mir::TerminatorKind::TailCall { .. } | mir::TerminatorKind::Assert { .. } | mir::TerminatorKind::FalseEdge { .. } | mir::TerminatorKind::FalseUnwind { .. } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 68acddf63d8..7d1a48d6ded 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -172,6 +172,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + TailCall { func: _, args: _, fn_span: _ } => todo!(), + Drop { place, target, unwind, replace: _ } => { let place = self.eval_place(place)?; let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 4657f4dcf81..23251d7b7bf 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -845,6 +845,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 { @@ -912,7 +922,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 diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2c2884f1897..deaa1259f6b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -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: Vec<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", diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index ed592612358..5b035d9579d 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -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), diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 7628a1ed2fe..0d3c419748b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -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: _, diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 2bdeb579a02..60ab843257d 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -2,7 +2,9 @@ use crate::build::scope::BreakableTarget; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use rustc_middle::middle::region; use rustc_middle::mir::*; +use rustc_middle::span_bug; use rustc_middle::thir::*; +use rustc_span::source_map::Spanned; use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -91,9 +93,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Return { value } => { this.break_scope(block, value, BreakableTarget::Return, source_info) } - // FIXME(explicit_tail_calls): properly lower tail calls here ExprKind::Become { value } => { - this.break_scope(block, Some(value), BreakableTarget::Return, source_info) + let v = &this.thir[value]; + let ExprKind::Scope { value, .. } = v.kind else { + span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}") + }; + + let v = &this.thir[value]; + let ExprKind::Call { ref args, fun, fn_span, .. } = v.kind else { + span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}") + }; + + let fun = unpack!(block = this.as_local_operand(block, fun)); + let args: Vec<_> = args + .into_iter() + .copied() + .map(|arg| Spanned { + node: unpack!(block = this.as_local_call_operand(block, arg)), + span: this.thir.exprs[arg].span, + }) + .collect(); + + this.record_operands_moved(&args); + + debug!("expr_into_dest: fn_span={:?}", fn_span); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::TailCall { func: fun, args, fn_span }, + ); + + this.cfg.start_new_block().unit() } _ => { assert!( diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 5b6de39bb2e..9e7534a283d 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -1523,6 +1523,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } | TerminatorKind::CoroutineDrop diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 1c7aa9f9ed0..3cb83a48ffe 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -196,6 +196,8 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx | TerminatorKind::CoroutineDrop | TerminatorKind::UnwindResume | TerminatorKind::Return + // FIXME(explicit_tail_calls) Is this right?? + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive), diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 706bb796349..574da949b0e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -145,6 +145,7 @@ where | TerminatorKind::InlineAsm { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } => {} diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 29169c31263..f850a710277 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -288,6 +288,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} } @@ -325,6 +326,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 1fb77bef3d4..7b39db821d8 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -489,6 +489,12 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); } } + TerminatorKind::TailCall { ref func, ref args, .. } => { + self.gather_operand(func); + for arg in args { + self.gather_operand(&arg.node); + } + } TerminatorKind::InlineAsm { template: _, ref operands, diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 7c1ff6fda53..1582c2e8a90 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -269,6 +269,9 @@ pub trait ValueAnalysis<'tcx> { TerminatorKind::SwitchInt { discr, targets } => { return self.handle_switch_int(discr, targets, state); } + TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): determine if we need to do something here (probably not) + } TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 9d44001f915..fa5da89e8ba 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1367,6 +1367,10 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { | TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } | TerminatorKind::Assert { .. } => return true, + + TerminatorKind::TailCall { .. } => { + unreachable!("tail calls can't be present in generators") + } } } @@ -1916,6 +1920,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Assert { .. } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 360dccb240d..83fb9ff9743 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -358,9 +358,12 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera } // These terminators have no coverage-relevant successors. - CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate(_) => { - CoverageSuccessors::NotChainable(&[]) - } + CoroutineDrop + | Return + | TailCall { .. } + | Unreachable + | UnwindResume + | UnwindTerminate(_) => CoverageSuccessors::NotChainable(&[]), } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 2ca166929ee..a0f8f580b1d 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -193,7 +193,8 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { | TerminatorKind::Goto { .. } => None, // Call `func` operand can have a more specific span when part of a chain of calls - | TerminatorKind::Call { ref func, .. } => { + TerminatorKind::Call { ref func, .. } + | TerminatorKind::TailCall { ref func, .. } => { let mut span = terminator.source_info.span; if let mir::Operand::Constant(box constant) = func { if constant.span.lo() > span.lo() { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b1016c0867c..ab73a8af317 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -628,6 +628,12 @@ impl WriteInfo { self.add_operand(&arg.node); } } + TerminatorKind::TailCall { func, args, .. } => { + self.add_operand(func); + for arg in args { + self.add_operand(&arg.node); + } + } TerminatorKind::InlineAsm { operands, .. } => { for asm_operand in operands { match asm_operand { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 5075e072754..fe73715480f 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -383,6 +383,8 @@ impl<'tcx> Inliner<'tcx> { ) -> Option<CallSite<'tcx>> { // Only consider direct calls to functions let terminator = bb_data.terminator(); + + // FIXME(explicit_tail_calls): figure out if we can inline tail calls if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { let func_ty = func.ty(caller_body, self.tcx); if let ty::FnDef(def_id, args) = *func_ty.kind() { @@ -550,6 +552,9 @@ impl<'tcx> Inliner<'tcx> { // inline-asm is detected. LLVM will still possibly do an inline later on // if the no-attribute function ends up with the same instruction set anyway. return Err("Cannot move inline-asm across instruction sets"); + } else if let TerminatorKind::TailCall { .. } = term.kind { + // FIXME(explicit_tail_calls): figure out how exactly functions containing tail calls can be inlined (and if they even should) + return Err("can't inline functions with tail calls"); } else { work_list.extend(term.successors()) } @@ -1038,6 +1043,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { *target = self.map_block(*target); *unwind = self.map_unwind(*unwind); } + TerminatorKind::TailCall { .. } => { + // check_mir_body forbids tail calls + unreachable!() + } TerminatorKind::Call { ref mut target, ref mut unwind, .. } => { if let Some(ref mut tgt) = *target { *tgt = self.map_block(*tgt); diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 85a61f95ca3..97ec0cb39de 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -596,6 +596,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"), // Disallowed during optimizations. diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8d6c00bbedb..82ad8879d17 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -799,6 +799,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Yield { .. } diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index db2bb60bdac..d928d7cf764 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -38,7 +38,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { self.super_terminator(terminator, location); let span = || self.body.source_info(location).span; match &terminator.kind { - mir::TerminatorKind::Call { func, .. } => { + mir::TerminatorKind::Call { func, .. } | mir::TerminatorKind::TailCall { func, .. } => { let callee_ty = func.ty(self.body, self.tcx); self.mentioned_items .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index fb52bfa468a..1df5737e859 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -75,6 +75,7 @@ impl RemoveNoopLandingPads { | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Unreachable | TerminatorKind::Call { .. } + | TerminatorKind::TailCall { .. } | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::InlineAsm { .. } => false, diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 2018a8fe667..ab5c25c4937 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -400,40 +400,44 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); } - TerminatorKind::Call { args, destination, target, unwind, .. } => { - if let Some(target) = target { - self.check_edge(location, *target, EdgeKind::Normal); - } - self.check_unwind_edge(location, *unwind); + TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => { + // FIXME(explicit_tail_calls): refactor this & add tail-call specific checks + if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind { + if let Some(target) = target { + self.check_edge(location, target, EdgeKind::Normal); + } + self.check_unwind_edge(location, unwind); + + // The code generation assumes that there are no critical call edges. The assumption + // is used to simplify inserting code that should be executed along the return edge + // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, + // the code generation should be responsible for handling it. + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) + && self.is_critical_call_edge(target, unwind) + { + self.fail( + location, + format!( + "encountered critical edge in `Call` terminator {:?}", + terminator.kind, + ), + ); + } - // The code generation assumes that there are no critical call edges. The assumption - // is used to simplify inserting code that should be executed along the return edge - // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, - // the code generation should be responsible for handling it. - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) - && self.is_critical_call_edge(*target, *unwind) - { - self.fail( - location, - format!( - "encountered critical edge in `Call` terminator {:?}", - terminator.kind, - ), - ); + // The call destination place and Operand::Move place used as an argument might be + // passed by a reference to the callee. Consequently they cannot be packed. + if is_within_packed(self.tcx, &self.body.local_decls, destination).is_some() { + // This is bad! The callee will expect the memory to be aligned. + self.fail( + location, + format!( + "encountered packed place in `Call` terminator destination: {:?}", + terminator.kind, + ), + ); + } } - // The call destination place and Operand::Move place used as an argument might be - // passed by a reference to the callee. Consequently they cannot be packed. - if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() { - // This is bad! The callee will expect the memory to be aligned. - self.fail( - location, - format!( - "encountered packed place in `Call` terminator destination: {:?}", - terminator.kind, - ), - ); - } for arg in args { if let Operand::Move(place) = &arg.node { if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() { @@ -1498,15 +1502,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } - TerminatorKind::Call { func, .. } => { + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( location, - format!("encountered non-callable type {func_ty} in `Call` terminator"), + format!( + "encountered non-callable type {func_ty} in `{}` terminator", + terminator.kind.name() + ), ), } + + if let TerminatorKind::TailCall { .. } = terminator.kind { + // FIXME(explicit_tail_calls): implement tail-call specific checks here (such as signature matching, forbidding closures, etc) + } } TerminatorKind::Assert { cond, .. } => { let cond_ty = cond.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 342c01ff697..3ef8da4a9c5 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -755,7 +755,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { }; match terminator.kind { - mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { + mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } + | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { let callee_ty = func.ty(self.body, tcx); // *Before* monomorphizing, record that we already handled this mention. self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index f15b82d0c03..3367cea9897 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -644,6 +644,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { target: target.map(|t| t.as_usize()), unwind: unwind.stable(tables), }, + mir::TerminatorKind::TailCall { func: _, args: _, fn_span: _ } => todo!(), mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => { TerminatorKind::Assert { cond: cond.stable(tables), |
