Skip to content

Commit c19a4f3

Browse files
committed
add codegen methods for setting tailcall kind
1 parent 7f3383f commit c19a4f3

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
17551755
// FIXME(bjorn3): implement
17561756
}
17571757

1758+
fn set_tail_call(&mut self, _call_inst: RValue<'gcc>) {
1759+
// Explicitly fail when this method is called
1760+
bug!(
1761+
"Guaranteed tail calls with the 'become' keyword are not implemented in the GCC backend"
1762+
);
1763+
}
1764+
17581765
fn set_span(&mut self, _span: Span) {}
17591766

17601767
fn from_immediate(&mut self, val: Self::Value) -> Self::Value {

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13711371
let cold_inline = llvm::AttributeKind::Cold.create_attr(self.llcx);
13721372
attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[cold_inline]);
13731373
}
1374+
1375+
fn set_tail_call(&mut self, call_inst: &'ll Value) {
1376+
// Use musttail for guaranteed tail call optimization required by 'become'
1377+
llvm::LLVMRustSetTailCallKind(call_inst, llvm::TailCallKind::MustTail);
1378+
}
13741379
}
13751380

13761381
impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> {

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: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,99 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
342342

343343
/// Codegen implementations for some terminator variants.
344344
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
345+
#[allow(dead_code)]
346+
fn codegen_tail_call_terminator(
347+
&mut self,
348+
bx: &mut Bx,
349+
func: &mir::Operand<'tcx>,
350+
args: &[Spanned<mir::Operand<'tcx>>],
351+
fn_span: Span,
352+
) {
353+
// We don't need source_info as we already have fn_span for diagnostics
354+
let func = self.codegen_operand(bx, func);
355+
let fn_ty = func.layout.ty;
356+
357+
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
358+
let (fn_ptr, fn_abi, instance) = match *fn_ty.kind() {
359+
ty::FnDef(def_id, substs) => {
360+
let instance = ty::Instance::expect_resolve(
361+
bx.tcx(),
362+
bx.typing_env(),
363+
def_id,
364+
substs,
365+
fn_span,
366+
);
367+
let fn_ptr = bx.get_fn_addr(instance);
368+
let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
369+
(fn_ptr, fn_abi, Some(instance))
370+
}
371+
ty::FnPtr(..) => {
372+
let sig = fn_ty.fn_sig(bx.tcx());
373+
let extra_args = bx.tcx().mk_type_list(&[]);
374+
let fn_ptr = func.immediate();
375+
let fn_abi = bx.fn_abi_of_fn_ptr(sig, extra_args);
376+
(fn_ptr, fn_abi, None)
377+
}
378+
_ => bug!("{} is not callable", func.layout.ty),
379+
};
380+
381+
let mut llargs = Vec::with_capacity(args.len());
382+
let mut lifetime_ends_after_call = Vec::new();
383+
384+
// Process arguments
385+
for arg in args {
386+
let op = self.codegen_operand(bx, &arg.node);
387+
let arg_idx = llargs.len();
388+
389+
if arg_idx < fn_abi.args.len() {
390+
self.codegen_argument(
391+
bx,
392+
op,
393+
&mut llargs,
394+
&fn_abi.args[arg_idx],
395+
&mut lifetime_ends_after_call,
396+
);
397+
} else {
398+
// This can happen in case of C-variadic functions
399+
let is_immediate = match op.val {
400+
Immediate(_) => true,
401+
_ => false,
402+
};
403+
404+
if is_immediate {
405+
llargs.push(op.immediate());
406+
} else {
407+
let temp = PlaceRef::alloca(bx, op.layout);
408+
op.val.store(bx, temp);
409+
llargs.push(bx.load(
410+
bx.backend_type(op.layout),
411+
temp.val.llval,
412+
temp.val.align,
413+
));
414+
}
415+
}
416+
}
417+
418+
// Call the function
419+
let fn_ty = bx.fn_decl_backend_type(fn_abi);
420+
let fn_attrs = if let Some(instance) = instance
421+
&& bx.tcx().def_kind(instance.def_id()).has_codegen_attrs()
422+
{
423+
Some(bx.tcx().codegen_fn_attrs(instance.def_id()))
424+
} else {
425+
None
426+
};
427+
428+
// Perform the actual function call
429+
let llret = bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &llargs, None, instance);
430+
431+
// Mark as tail call - this is the critical part
432+
bx.set_tail_call(llret);
433+
434+
// Return the result - musttail requires ret immediately after the call
435+
bx.ret(llret);
436+
}
437+
345438
/// Generates code for a `Resume` terminator.
346439
fn codegen_resume_terminator(&mut self, helper: TerminatorCodegenHelper<'tcx>, bx: &mut Bx) {
347440
if let Some(funclet) = helper.funclet(self) {

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,8 @@ pub trait BuilderMethods<'a, 'tcx>:
595595
funclet: Option<&Self::Funclet>,
596596
instance: Option<Instance<'tcx>>,
597597
) -> Self::Value;
598+
599+
fn set_tail_call(&mut self, call_inst: Self::Value);
598600
fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
599601

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

0 commit comments

Comments
 (0)