Skip to content

Commit f912c90

Browse files
committed
Implement support for explicit tail calls in the MIR block builders and the LLVM codegen bckend.
1 parent 0864097 commit f912c90

File tree

6 files changed

+139
-12
lines changed

6 files changed

+139
-12
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,21 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
17421742
call
17431743
}
17441744

1745+
fn tail_call(
1746+
&mut self,
1747+
_llty: Self::Type,
1748+
_fn_attrs: Option<&CodegenFnAttrs>,
1749+
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1750+
_llfn: Self::Value,
1751+
_args: &[Self::Value],
1752+
_funclet: Option<&Self::Funclet>,
1753+
_instance: Option<Instance<'tcx>>,
1754+
) {
1755+
bug!(
1756+
"Guaranteed tail calls with the 'become' keyword are not implemented in the GCC backend"
1757+
);
1758+
}
1759+
17451760
fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
17461761
// FIXME(antoyo): this does not zero-extend.
17471762
self.gcc_int_cast(value, dest_typ)

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef;
1414
use rustc_codegen_ssa::traits::*;
1515
use rustc_data_structures::small_c_str::SmallCStr;
1616
use rustc_hir::def_id::DefId;
17+
use rustc_middle::bug;
1718
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
1819
use rustc_middle::ty::layout::{
1920
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers,
@@ -23,7 +24,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2324
use rustc_sanitizers::{cfi, kcfi};
2425
use rustc_session::config::OptLevel;
2526
use rustc_span::Span;
26-
use rustc_target::callconv::FnAbi;
27+
use rustc_target::callconv::{FnAbi, PassMode};
2728
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
2829
use smallvec::SmallVec;
2930
use tracing::{debug, instrument};
@@ -1362,6 +1363,29 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13621363
call
13631364
}
13641365

1366+
fn tail_call(
1367+
&mut self,
1368+
llty: Self::Type,
1369+
fn_attrs: Option<&CodegenFnAttrs>,
1370+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1371+
llfn: Self::Value,
1372+
args: &[Self::Value],
1373+
funclet: Option<&Self::Funclet>,
1374+
instance: Option<Instance<'tcx>>,
1375+
) {
1376+
let call = self.call(llty, fn_attrs, Some(fn_abi), llfn, args, funclet, instance);
1377+
1378+
match &fn_abi.ret.mode {
1379+
PassMode::Ignore | PassMode::Indirect { .. } => self.ret_void(),
1380+
PassMode::Direct(_) | PassMode::Pair { .. } => self.ret(call),
1381+
mode @ PassMode::Cast { .. } => {
1382+
bug!("Encountered `PassMode::{mode:?}` during codegen")
1383+
}
1384+
}
1385+
1386+
llvm::LLVMRustSetTailCallKind(call, llvm::TailCallKind::MustTail);
1387+
}
1388+
13651389
fn zext(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
13661390
unsafe { llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty, UNNAMED) }
13671391
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ pub(crate) enum ModuleFlagMergeBehavior {
9797

9898
// Consts for the LLVM CallConv type, pre-cast to usize.
9999

100+
/// LLVM TailCallKind for musttail support
101+
#[derive(Copy, Clone, PartialEq, Debug)]
102+
#[repr(C)]
103+
#[allow(dead_code)]
104+
pub(crate) enum TailCallKind {
105+
None = 0,
106+
Tail = 1,
107+
MustTail = 2,
108+
NoTail = 3,
109+
}
110+
100111
/// LLVM CallingConv::ID. Should we wrap this?
101112
///
102113
/// See <https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/CallingConv.h>
@@ -1181,6 +1192,7 @@ unsafe extern "C" {
11811192
pub(crate) safe fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
11821193
pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
11831194
pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
1195+
pub(crate) safe fn LLVMRustSetTailCallKind(CallInst: &Value, Kind: TailCallKind);
11841196

11851197
// Operations on attributes
11861198
pub(crate) fn LLVMCreateStringAttribute(

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
160160
mut unwind: mir::UnwindAction,
161161
lifetime_ends_after_call: &[(Bx::Value, Size)],
162162
instance: Option<Instance<'tcx>>,
163+
tail: bool,
163164
mergeable_succ: bool,
164165
) -> MergingSucc {
165166
let tcx = bx.tcx();
@@ -221,6 +222,15 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
221222
}
222223
};
223224

225+
if tail {
226+
bx.tail_call(fn_ty, fn_attrs, fn_abi, fn_ptr, llargs, self.funclet(fx), instance);
227+
for &(tmp, size) in lifetime_ends_after_call {
228+
bx.lifetime_end(tmp, size);
229+
}
230+
231+
return MergingSucc::False;
232+
}
233+
224234
if let Some(unwind_block) = unwind_block {
225235
let ret_llbb = if let Some((_, target)) = destination {
226236
fx.llbb(target)
@@ -659,6 +669,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
659669
unwind,
660670
&[],
661671
Some(drop_instance),
672+
false,
662673
!maybe_null && mergeable_succ,
663674
)
664675
}
@@ -747,8 +758,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
747758
let (fn_abi, llfn, instance) = common::build_langcall(bx, span, lang_item);
748759

749760
// 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);
761+
let merging_succ = helper.do_call(
762+
self,
763+
bx,
764+
fn_abi,
765+
llfn,
766+
&args,
767+
None,
768+
unwind,
769+
&[],
770+
Some(instance),
771+
false,
772+
false,
773+
);
752774
assert_eq!(merging_succ, MergingSucc::False);
753775
MergingSucc::False
754776
}
@@ -778,6 +800,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
778800
&[],
779801
Some(instance),
780802
false,
803+
false,
781804
);
782805
assert_eq!(merging_succ, MergingSucc::False);
783806
}
@@ -845,6 +868,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
845868
unwind,
846869
&[],
847870
Some(instance),
871+
false,
848872
mergeable_succ,
849873
))
850874
}
@@ -860,6 +884,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
860884
target: Option<mir::BasicBlock>,
861885
unwind: mir::UnwindAction,
862886
fn_span: Span,
887+
tail: bool,
863888
mergeable_succ: bool,
864889
) -> MergingSucc {
865890
let source_info = mir::SourceInfo { span: fn_span, ..terminator.source_info };
@@ -1003,8 +1028,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10031028
// We still need to call `make_return_dest` even if there's no `target`, since
10041029
// `fn_abi.ret` could be `PassMode::Indirect`, even if it is uninhabited,
10051030
// 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));
1031+
let destination = if !tail {
1032+
let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs);
1033+
target.map(|target| (return_dest, target))
1034+
} else {
1035+
None
1036+
};
10081037

10091038
// Split the rust-call tupled arguments off.
10101039
let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall
@@ -1147,6 +1176,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11471176
unwind,
11481177
&lifetime_ends_after_call,
11491178
instance,
1179+
tail,
11501180
mergeable_succ,
11511181
)
11521182
}
@@ -1388,15 +1418,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13881418
target,
13891419
unwind,
13901420
fn_span,
1421+
false,
13911422
mergeable_succ(),
13921423
),
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-
}
1424+
mir::TerminatorKind::TailCall { ref func, ref args, fn_span } => self
1425+
.codegen_call_terminator(
1426+
helper,
1427+
bx,
1428+
terminator,
1429+
func,
1430+
args,
1431+
mir::Place::from(mir::RETURN_PLACE),
1432+
None,
1433+
mir::UnwindAction::Unreachable,
1434+
fn_span,
1435+
true,
1436+
mergeable_succ(),
1437+
),
14001438
mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => {
14011439
bug!("coroutine ops in codegen")
14021440
}

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,18 @@ pub trait BuilderMethods<'a, 'tcx>:
595595
funclet: Option<&Self::Funclet>,
596596
instance: Option<Instance<'tcx>>,
597597
) -> Self::Value;
598+
599+
fn tail_call(
600+
&mut self,
601+
llty: Self::Type,
602+
fn_attrs: Option<&CodegenFnAttrs>,
603+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
604+
llfn: Self::Value,
605+
args: &[Self::Value],
606+
funclet: Option<&Self::Funclet>,
607+
instance: Option<Instance<'tcx>>,
608+
);
609+
598610
fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
599611

600612
fn apply_attrs_to_cleanup_callsite(&mut self, llret: Self::Value);

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@
5151
//
5252
//===----------------------------------------------------------------------===
5353

54+
// Define TailCallKind enum values to match LLVM's
55+
enum LLVMRustTailCallKind {
56+
LLVMRustTailCallKindNone = 0,
57+
LLVMRustTailCallKindTail = 1,
58+
LLVMRustTailCallKindMustTail = 2,
59+
LLVMRustTailCallKindNoTail = 3
60+
};
61+
5462
using namespace llvm;
5563
using namespace llvm::sys;
5664
using namespace llvm::object;
@@ -1949,3 +1957,21 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) {
19491957
MD.NoHWAddress = true;
19501958
GV.setSanitizerMetadata(MD);
19511959
}
1960+
1961+
extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call, LLVMRustTailCallKind Kind) {
1962+
CallInst *CI = unwrap<CallInst>(Call);
1963+
switch (Kind) {
1964+
case LLVMRustTailCallKindNone:
1965+
CI->setTailCallKind(CallInst::TCK_None);
1966+
break;
1967+
case LLVMRustTailCallKindTail:
1968+
CI->setTailCallKind(CallInst::TCK_Tail);
1969+
break;
1970+
case LLVMRustTailCallKindMustTail:
1971+
CI->setTailCallKind(CallInst::TCK_MustTail);
1972+
break;
1973+
case LLVMRustTailCallKindNoTail:
1974+
CI->setTailCallKind(CallInst::TCK_NoTail);
1975+
break;
1976+
}
1977+
}

0 commit comments

Comments
 (0)