@@ -35,6 +35,14 @@ enum MergingSucc {
3535 True ,
3636}
3737
38+ /// Indicates to the call terminator codegen whether a cal
39+ /// is a normal call or an explicit tail call.
40+ #[ derive( Debug , PartialEq ) ]
41+ enum CallKind {
42+ Normal ,
43+ Tail ,
44+ }
45+
3846/// Used by `FunctionCx::codegen_terminator` for emitting common patterns
3947/// e.g., creating a basic block, calling a function, etc.
4048struct TerminatorCodegenHelper < ' tcx > {
@@ -160,6 +168,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
160168 mut unwind : mir:: UnwindAction ,
161169 lifetime_ends_after_call : & [ ( Bx :: Value , Size ) ] ,
162170 instance : Option < Instance < ' tcx > > ,
171+ kind : CallKind ,
163172 mergeable_succ : bool ,
164173 ) -> MergingSucc {
165174 let tcx = bx. tcx ( ) ;
@@ -221,6 +230,11 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
221230 }
222231 } ;
223232
233+ if kind == CallKind :: Tail {
234+ bx. tail_call ( fn_ty, fn_attrs, fn_abi, fn_ptr, llargs, self . funclet ( fx) , instance) ;
235+ return MergingSucc :: False ;
236+ }
237+
224238 if let Some ( unwind_block) = unwind_block {
225239 let ret_llbb = if let Some ( ( _, target) ) = destination {
226240 fx. llbb ( target)
@@ -659,6 +673,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
659673 unwind,
660674 & [ ] ,
661675 Some ( drop_instance) ,
676+ CallKind :: Normal ,
662677 !maybe_null && mergeable_succ,
663678 )
664679 }
@@ -747,8 +762,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
747762 let ( fn_abi, llfn, instance) = common:: build_langcall ( bx, span, lang_item) ;
748763
749764 // Codegen the actual panic invoke/call.
750- let merging_succ =
751- helper. do_call ( self , bx, fn_abi, llfn, & args, None , unwind, & [ ] , Some ( instance) , false ) ;
765+ let merging_succ = helper. do_call (
766+ self ,
767+ bx,
768+ fn_abi,
769+ llfn,
770+ & args,
771+ None ,
772+ unwind,
773+ & [ ] ,
774+ Some ( instance) ,
775+ CallKind :: Normal ,
776+ false ,
777+ ) ;
752778 assert_eq ! ( merging_succ, MergingSucc :: False ) ;
753779 MergingSucc :: False
754780 }
@@ -777,6 +803,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
777803 mir:: UnwindAction :: Unreachable ,
778804 & [ ] ,
779805 Some ( instance) ,
806+ CallKind :: Normal ,
780807 false ,
781808 ) ;
782809 assert_eq ! ( merging_succ, MergingSucc :: False ) ;
@@ -845,6 +872,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
845872 unwind,
846873 & [ ] ,
847874 Some ( instance) ,
875+ CallKind :: Normal ,
848876 mergeable_succ,
849877 ) )
850878 }
@@ -860,6 +888,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
860888 target : Option < mir:: BasicBlock > ,
861889 unwind : mir:: UnwindAction ,
862890 fn_span : Span ,
891+ kind : CallKind ,
863892 mergeable_succ : bool ,
864893 ) -> MergingSucc {
865894 let source_info = mir:: SourceInfo { span : fn_span, ..terminator. source_info } ;
@@ -1003,8 +1032,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10031032 // We still need to call `make_return_dest` even if there's no `target`, since
10041033 // `fn_abi.ret` could be `PassMode::Indirect`, even if it is uninhabited,
10051034 // and `make_return_dest` adds the return-place indirect pointer to `llargs`.
1006- let return_dest = self . make_return_dest ( bx, destination, & fn_abi. ret , & mut llargs) ;
1007- let destination = target. map ( |target| ( return_dest, target) ) ;
1035+ let destination = match kind {
1036+ CallKind :: Normal => {
1037+ let return_dest = self . make_return_dest ( bx, destination, & fn_abi. ret , & mut llargs) ;
1038+ target. map ( |target| ( return_dest, target) )
1039+ }
1040+ CallKind :: Tail => None ,
1041+ } ;
10081042
10091043 // Split the rust-call tupled arguments off.
10101044 let ( first_args, untuple) = if sig. abi ( ) == ExternAbi :: RustCall
@@ -1020,6 +1054,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10201054 // to generate `lifetime_end` when the call returns.
10211055 let mut lifetime_ends_after_call: Vec < ( Bx :: Value , Size ) > = Vec :: new ( ) ;
10221056 ' make_args: for ( i, arg) in first_args. iter ( ) . enumerate ( ) {
1057+ if kind == CallKind :: Tail && matches ! ( fn_abi. args[ i] . mode, PassMode :: Indirect { .. } ) {
1058+ // FIXME: https://github.com/rust-lang/rust/pull/144232#discussion_r2218543841
1059+ span_bug ! (
1060+ fn_span,
1061+ "arguments using PassMode::Indirect are currently not supported for tail calls"
1062+ ) ;
1063+ }
1064+
10231065 let mut op = self . codegen_operand ( bx, & arg. node ) ;
10241066
10251067 if let ( 0 , Some ( ty:: InstanceKind :: Virtual ( _, idx) ) ) = ( i, instance. map ( |i| i. def ) ) {
@@ -1147,6 +1189,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11471189 unwind,
11481190 & lifetime_ends_after_call,
11491191 instance,
1192+ kind,
11501193 mergeable_succ,
11511194 )
11521195 }
@@ -1388,15 +1431,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13881431 target,
13891432 unwind,
13901433 fn_span,
1434+ CallKind :: Normal ,
13911435 mergeable_succ ( ) ,
13921436 ) ,
1393- mir:: TerminatorKind :: TailCall { .. } => {
1394- // FIXME(explicit_tail_calls): implement tail calls in ssa backend
1395- span_bug ! (
1396- terminator. source_info. span,
1397- "`TailCall` terminator is not yet supported by `rustc_codegen_ssa`"
1398- )
1399- }
1437+ mir:: TerminatorKind :: TailCall { ref func, ref args, fn_span } => self
1438+ . codegen_call_terminator (
1439+ helper,
1440+ bx,
1441+ terminator,
1442+ func,
1443+ args,
1444+ mir:: Place :: from ( mir:: RETURN_PLACE ) ,
1445+ None ,
1446+ mir:: UnwindAction :: Unreachable ,
1447+ fn_span,
1448+ CallKind :: Tail ,
1449+ mergeable_succ ( ) ,
1450+ ) ,
14001451 mir:: TerminatorKind :: CoroutineDrop | mir:: TerminatorKind :: Yield { .. } => {
14011452 bug ! ( "coroutine ops in codegen" )
14021453 }
0 commit comments