Skip to content

Commit

Permalink
Cranelift: Implement tail calls for x86_64
Browse files Browse the repository at this point in the history
Co-Authored-By: Jamey Sharp <jsharp@fastly.com>
  • Loading branch information
fitzgen and jameysharp committed Jun 23, 2023
1 parent 2d34b46 commit 1c6f497
Show file tree
Hide file tree
Showing 27 changed files with 1,768 additions and 99 deletions.
13 changes: 13 additions & 0 deletions cranelift/codegen/src/isa/aarch64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,19 @@ impl ABIMachineSpec for AArch64MachineDeps {
insts
}

fn gen_return_call(
_callee: CallDest,
_new_stack_arg_size: u32,
_old_stack_arg_size: u32,
_ret_addr: Reg,
_fp: Reg,
_tmp: Writable<Reg>,
_tmp2: Writable<Reg>,
_uses: abi::CallArgList,
) -> SmallVec<[Self::I; 2]> {
todo!();
}

fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
call_conv: isa::CallConv,
dst: Reg,
Expand Down
21 changes: 21 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower/isle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,27 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
AArch64CallSite
);

fn gen_return_call(
&mut self,
callee_sig: SigRef,
callee: ExternalName,
distance: RelocDistance,
args: ValueSlice,
) -> InstOutput {
let _ = (callee_sig, callee, distance, args);
todo!()
}

fn gen_return_call_indirect(
&mut self,
callee_sig: SigRef,
callee: Value,
args: ValueSlice,
) -> InstOutput {
let _ = (callee_sig, callee, args);
todo!()
}

fn sign_return_address_disabled(&mut self) -> Option<()> {
if self.backend.isa_flags.sign_return_address() {
None
Expand Down
13 changes: 13 additions & 0 deletions cranelift/codegen/src/isa/riscv64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,19 @@ impl ABIMachineSpec for Riscv64MachineDeps {
insts
}

fn gen_return_call(
_callee: CallDest,
_new_stack_arg_size: u32,
_old_stack_arg_size: u32,
_ret_addr: Reg,
_fp: Reg,
_tmp: Writable<Reg>,
_tmp2: Writable<Reg>,
_uses: abi::CallArgList,
) -> SmallVec<[Self::I; 2]> {
todo!();
}

fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
call_conv: isa::CallConv,
dst: Reg,
Expand Down
1 change: 0 additions & 1 deletion cranelift/codegen/src/isa/riscv64/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -1564,7 +1564,6 @@
(rule (lower (return args))
(lower_return args))


;;; Rules for `get_{frame,stack}_pointer` and `get_return_address` ;;;;;;;;;;;;;

(rule (lower (get_frame_pointer))
Expand Down
21 changes: 21 additions & 0 deletions cranelift/codegen/src/isa/riscv64/lower/isle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,27 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
isle_lower_prelude_methods!();
isle_prelude_caller_methods!(Riscv64MachineDeps, Riscv64ABICallSite);

fn gen_return_call(
&mut self,
callee_sig: SigRef,
callee: ExternalName,
distance: RelocDistance,
args: ValueSlice,
) -> InstOutput {
let _ = (callee_sig, callee, distance, args);
todo!()
}

fn gen_return_call_indirect(
&mut self,
callee_sig: SigRef,
callee: Value,
args: ValueSlice,
) -> InstOutput {
let _ = (callee_sig, callee, args);
todo!()
}

fn vreg_new(&mut self, r: Reg) -> VReg {
VReg::new(r).unwrap()
}
Expand Down
13 changes: 13 additions & 0 deletions cranelift/codegen/src/isa/s390x/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,19 @@ impl ABIMachineSpec for S390xMachineDeps {
unreachable!();
}

fn gen_return_call(
_callee: CallDest,
_new_stack_arg_size: u32,
_old_stack_arg_size: u32,
_ret_addr: Reg,
_fp: Reg,
_tmp: Writable<Reg>,
_tmp2: Writable<Reg>,
_uses: abi::CallArgList,
) -> SmallVec<[Self::I; 2]> {
todo!();
}

fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
_call_conv: isa::CallConv,
_dst: Reg,
Expand Down
3 changes: 0 additions & 3 deletions cranelift/codegen/src/isa/s390x/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -3518,9 +3518,6 @@

;; Helpers for generating `call` instructions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; List of argument registers for a call instruction.
(type CallArgList extern (enum))

;; Partial (mutable) argument list in the process of being created.
(type CallArgListBuilder extern (enum))

Expand Down
21 changes: 21 additions & 0 deletions cranelift/codegen/src/isa/s390x/lower/isle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ pub(crate) fn lower_branch(
impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
isle_lower_prelude_methods!();

fn gen_return_call(
&mut self,
callee_sig: SigRef,
callee: ExternalName,
distance: RelocDistance,
args: ValueSlice,
) -> InstOutput {
let _ = (callee_sig, callee, distance, args);
todo!()
}

fn gen_return_call_indirect(
&mut self,
callee_sig: SigRef,
callee: Value,
args: ValueSlice,
) -> InstOutput {
let _ = (callee_sig, callee, args);
todo!()
}

#[inline]
fn args_builder_new(&mut self) -> CallArgListBuilder {
Cell::new(CallArgList::new())
Expand Down
61 changes: 57 additions & 4 deletions cranelift/codegen/src/isa/x64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,60 @@ impl ABIMachineSpec for X64ABIMachineSpec {
insts
}

fn gen_return_call(
callee: CallDest,
new_stack_arg_size: u32,
old_stack_arg_size: u32,
ret_addr: Reg,
fp: Reg,
tmp: Writable<Reg>,
tmp2: Writable<Reg>,
uses: abi::CallArgList,
) -> SmallVec<[Self::I; 2]> {
let ret_addr = Gpr::new(ret_addr).unwrap();
let fp = Gpr::new(fp).unwrap();
let tmp = WritableGpr::from_writable_reg(tmp).unwrap();
match callee {
CallDest::ExtName(callee, RelocDistance::Near) => smallvec![Inst::ReturnCallKnown {
callee,
new_stack_arg_size,
old_stack_arg_size,
ret_addr,
fp,
tmp,
uses,
}],
CallDest::ExtName(callee, RelocDistance::Far) => {
smallvec![
Inst::LoadExtName {
dst: tmp2,
name: Box::new(callee.clone()),
offset: 0,
distance: RelocDistance::Far,
},
Inst::ReturnCallUnknown {
callee: tmp2.into(),
new_stack_arg_size,
old_stack_arg_size,
ret_addr,
fp,
tmp,
uses,
}
]
}
CallDest::Reg(callee) => smallvec![Inst::ReturnCallUnknown {
callee: callee.into(),
new_stack_arg_size,
old_stack_arg_size,
ret_addr,
fp,
tmp,
uses,
}],
}
}

fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
call_conv: isa::CallConv,
dst: Reg,
Expand Down Expand Up @@ -878,10 +932,9 @@ fn get_intreg_for_arg(call_conv: &CallConv, idx: usize, arg_idx: usize) -> Optio
7 => Some(regs::r9()),
8 => Some(regs::r10()),
9 => Some(regs::r11()),
10 => Some(regs::r12()),
11 => Some(regs::r13()),
12 => Some(regs::r14()),
// NB: `r15` is reserved as a scratch register.
// NB: `r12`, `r13`, `r14` and `r15` are reserved for indirect
// callee addresses and temporaries required for our tail call
// sequence (fp, ret_addr, tmp).
_ => None,
};
}
Expand Down
2 changes: 1 addition & 1 deletion cranelift/codegen/src/isa/x64/encoding/rex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub(crate) fn encode_sib(shift: u8, enc_index: u8, enc_base: u8) -> u8 {
#[inline(always)]
pub(crate) fn int_reg_enc(reg: impl Into<Reg>) -> u8 {
let reg = reg.into();
debug_assert!(reg.is_real());
debug_assert!(reg.is_real(), "reg = {reg:?}");
debug_assert_eq!(reg.class(), RegClass::Int);
reg.to_real_reg().unwrap().hw_enc()
}
Expand Down
42 changes: 42 additions & 0 deletions cranelift/codegen/src/isa/x64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,48 @@
(CallUnknown (dest RegMem)
(info BoxCallInfo))

;; Tail call to a direct destination.
(ReturnCallKnown
;; The function we are calling.
(callee ExternalName)
;; The size of the new stack frame's stack arguments. This is necessary
;; for copying the frame over our current frame. It must already be
;; allocated on the stack.
(new_stack_arg_size u32)
;; The size of the current/old stack frame's stack arguments.
(old_stack_arg_size u32)
;; The return address. Needs to be written into the correct stack slot
;; after the new stack frame is copied into place.
(ret_addr Gpr)
;; A copy of the frame pointer, because we will overwrite the current
;; `rbp`.
(fp Gpr)
;; A temporary register.
(tmp WritableGpr)
;; The in-register arguments and their constraints.
(uses CallArgList))

;; Tail call to an indirect destination.
(ReturnCallUnknown
;; The function we are calling.
(callee RegMem)
;; The size of the new stack frame's stack arguments. This is necessary
;; for copying the frame over our current frame. It must already be
;; allocated on the stack.
(new_stack_arg_size u32)
;; The size of the current/old stack frame's stack arguments.
(old_stack_arg_size u32)
;; The return address. Needs to be written into the correct stack slot
;; after the new stack frame is copied into place.
(ret_addr Gpr)
;; A copy of the frame pointer, because we will overwrite the current
;; `rbp`.
(fp Gpr)
;; A temporary register.
(tmp WritableGpr)
;; The in-register arguments and their constraints.
(uses CallArgList))

;; A pseudo-instruction that captures register arguments in vregs.
(Args
(args VecArgPair))
Expand Down
Loading

0 comments on commit 1c6f497

Please sign in to comment.