x64 new backend: port ABI implementation to shared infrastructure with AArch64.
Previously, in #2128, we factored out a common "vanilla 64-bit ABI" implementation from the AArch64 ABI code, with the idea that this should be largely compatible with x64. This PR alters the new x64 backend to make use of the shared infrastructure, removing the duplication that existed previously. The generated code is nearly (not exactly) the same; the only difference relates to how the clobber-save region is padded in the prologue. This also changes some register allocations in the aarch64 code because call support in the shared ABI infra now passes a temp vreg in, rather than requiring use of a fixed, non-allocable temp; tests have been updated, and the runtime behavior is unchanged.
This commit is contained in:
@@ -13,16 +13,15 @@ use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
||||
use smallvec::SmallVec;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
// We use a generic implementation that factors out AArch64 and x64 ABI commonalities, because
|
||||
// these ABIs are very similar.
|
||||
|
||||
/// Support for the AArch64 ABI from the callee side (within a function body).
|
||||
pub type AArch64ABIBody = ABIBodyImpl<AArch64MachineImpl>;
|
||||
pub(crate) type AArch64ABICallee = ABICalleeImpl<AArch64MachineDeps>;
|
||||
|
||||
/// Support for the AArch64 ABI from the caller side (at a callsite).
|
||||
pub type AArch64ABICall = ABICallImpl<AArch64MachineImpl>;
|
||||
pub(crate) type AArch64ABICaller = ABICallerImpl<AArch64MachineDeps>;
|
||||
|
||||
// Spidermonkey specific ABI convention.
|
||||
|
||||
@@ -105,9 +104,9 @@ impl Into<AMode> for StackAMode {
|
||||
|
||||
/// AArch64-specific ABI behavior. This struct just serves as an implementation
|
||||
/// point for the trait; it is never actually instantiated.
|
||||
pub struct AArch64MachineImpl;
|
||||
pub(crate) struct AArch64MachineDeps;
|
||||
|
||||
impl ABIMachineImpl for AArch64MachineImpl {
|
||||
impl ABIMachineSpec for AArch64MachineDeps {
|
||||
type I = Inst;
|
||||
|
||||
fn compute_arg_locs(
|
||||
@@ -285,7 +284,8 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
Inst::Ret
|
||||
}
|
||||
|
||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u64) -> SmallVec<[Inst; 4]> {
|
||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallVec<[Inst; 4]> {
|
||||
let imm = imm as u64;
|
||||
let mut insts = SmallVec::new();
|
||||
if let Some(imm12) = Imm12::maybe_from_u64(imm) {
|
||||
insts.push(Inst::AluRRImm12 {
|
||||
@@ -296,6 +296,7 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
});
|
||||
} else {
|
||||
let scratch2 = writable_tmp2_reg();
|
||||
assert_ne!(scratch2.to_reg(), from_reg);
|
||||
insts.extend(Inst::load_constant(scratch2, imm.into()));
|
||||
insts.push(Inst::AluRRRExtend {
|
||||
alu_op: ALUOp::Add64,
|
||||
@@ -334,29 +335,29 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
Inst::LoadAddr { rd: into_reg, mem }
|
||||
}
|
||||
|
||||
fn get_fixed_tmp_reg() -> Reg {
|
||||
fn get_stacklimit_reg() -> Reg {
|
||||
spilltmp_reg()
|
||||
}
|
||||
|
||||
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i64, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset, ty);
|
||||
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset as i64, ty);
|
||||
Inst::gen_load(into_reg, mem, ty)
|
||||
}
|
||||
|
||||
fn gen_store_base_offset(base: Reg, offset: i64, from_reg: Reg, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset, ty);
|
||||
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset as i64, ty);
|
||||
Inst::gen_store(mem, from_reg, ty)
|
||||
}
|
||||
|
||||
fn gen_sp_reg_adjust(amount: i64) -> SmallVec<[Inst; 2]> {
|
||||
fn gen_sp_reg_adjust(amount: i32) -> SmallVec<[Inst; 2]> {
|
||||
if amount == 0 {
|
||||
return SmallVec::new();
|
||||
}
|
||||
|
||||
let (amount, is_sub) = if amount > 0 {
|
||||
(u64::try_from(amount).unwrap(), false)
|
||||
(amount as u64, false)
|
||||
} else {
|
||||
(u64::try_from(-amount).unwrap(), true)
|
||||
(-amount as u64, true)
|
||||
};
|
||||
|
||||
let alu_op = if is_sub { ALUOp::Sub64 } else { ALUOp::Add64 };
|
||||
@@ -389,8 +390,10 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
ret
|
||||
}
|
||||
|
||||
fn gen_nominal_sp_adj(offset: i64) -> Inst {
|
||||
Inst::VirtualSPOffsetAdj { offset }
|
||||
fn gen_nominal_sp_adj(offset: i32) -> Inst {
|
||||
Inst::VirtualSPOffsetAdj {
|
||||
offset: offset as i64,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_prologue_frame_setup() -> SmallVec<[Inst; 2]> {
|
||||
@@ -553,11 +556,12 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
) -> SmallVec<[(/* is_safepoint = */ bool, Inst); 2]> {
|
||||
tmp: Writable<Reg>,
|
||||
) -> SmallVec<[(InstIsSafepoint, Inst); 2]> {
|
||||
let mut insts = SmallVec::new();
|
||||
match &dest {
|
||||
&CallDest::ExtName(ref name, RelocDistance::Near) => insts.push((
|
||||
true,
|
||||
InstIsSafepoint::Yes,
|
||||
Inst::Call {
|
||||
info: Box::new(CallInfo {
|
||||
dest: name.clone(),
|
||||
@@ -570,19 +574,19 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
)),
|
||||
&CallDest::ExtName(ref name, RelocDistance::Far) => {
|
||||
insts.push((
|
||||
false,
|
||||
InstIsSafepoint::No,
|
||||
Inst::LoadExtName {
|
||||
rd: writable_spilltmp_reg(),
|
||||
rd: tmp,
|
||||
name: Box::new(name.clone()),
|
||||
offset: 0,
|
||||
srcloc: loc,
|
||||
},
|
||||
));
|
||||
insts.push((
|
||||
true,
|
||||
InstIsSafepoint::Yes,
|
||||
Inst::CallInd {
|
||||
info: Box::new(CallIndInfo {
|
||||
rn: spilltmp_reg(),
|
||||
rn: tmp.to_reg(),
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
@@ -592,7 +596,7 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
));
|
||||
}
|
||||
&CallDest::Reg(reg) => insts.push((
|
||||
true,
|
||||
InstIsSafepoint::Yes,
|
||||
Inst::CallInd {
|
||||
info: Box::new(CallIndInfo {
|
||||
rn: *reg,
|
||||
@@ -608,7 +612,7 @@ impl ABIMachineImpl for AArch64MachineImpl {
|
||||
insts
|
||||
}
|
||||
|
||||
fn get_spillslot_size(rc: RegClass, ty: Type) -> u32 {
|
||||
fn get_number_of_spillslots_for_value(rc: RegClass, ty: Type) -> u32 {
|
||||
// We allocate in terms of 8-byte slots.
|
||||
match (rc, ty) {
|
||||
(RegClass::I64, _) => 1,
|
||||
@@ -698,9 +702,10 @@ fn get_callee_saves(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort registers for deterministic code output.
|
||||
int_saves.sort_by_key(|r| r.to_reg().get_index());
|
||||
vec_saves.sort_by_key(|r| r.to_reg().get_index());
|
||||
// Sort registers for deterministic code output. We can do an unstable sort because the
|
||||
// registers will be unique (there are no dups).
|
||||
int_saves.sort_unstable_by_key(|r| r.to_reg().get_index());
|
||||
vec_saves.sort_unstable_by_key(|r| r.to_reg().get_index());
|
||||
(int_saves, vec_saves)
|
||||
}
|
||||
|
||||
|
||||
@@ -440,7 +440,7 @@ pub struct EmitState {
|
||||
}
|
||||
|
||||
impl MachInstEmitState<Inst> for EmitState {
|
||||
fn new(abi: &dyn ABIBody<I = Inst>) -> Self {
|
||||
fn new(abi: &dyn ABICallee<I = Inst>) -> Self {
|
||||
EmitState {
|
||||
virtual_sp_offset: 0,
|
||||
nominal_sp_to_fp: abi.frame_size() as i64,
|
||||
|
||||
@@ -1837,7 +1837,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
assert!(inputs.len() == sig.params.len());
|
||||
assert!(outputs.len() == sig.returns.len());
|
||||
(
|
||||
AArch64ABICall::from_func(sig, &extname, dist, loc)?,
|
||||
AArch64ABICaller::from_func(sig, &extname, dist, loc)?,
|
||||
&inputs[..],
|
||||
)
|
||||
}
|
||||
@@ -1846,7 +1846,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
let sig = ctx.call_sig(insn).unwrap();
|
||||
assert!(inputs.len() - 1 == sig.params.len());
|
||||
assert!(outputs.len() == sig.returns.len());
|
||||
(AArch64ABICall::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
|
||||
(AArch64ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ impl AArch64Backend {
|
||||
func: &Function,
|
||||
flags: settings::Flags,
|
||||
) -> CodegenResult<VCode<inst::Inst>> {
|
||||
let abi = Box::new(abi::AArch64ABIBody::new(func, flags)?);
|
||||
let abi = Box::new(abi::AArch64ABICallee::new(func, flags)?);
|
||||
compile::compile::<AArch64Backend>(func, self, abi)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2498,7 +2498,7 @@ impl MachInstEmit for Inst {
|
||||
}
|
||||
|
||||
impl MachInstEmitState<Inst> for EmitState {
|
||||
fn new(abi: &dyn ABIBody<I = Inst>) -> Self {
|
||||
fn new(abi: &dyn ABICallee<I = Inst>) -> Self {
|
||||
EmitState {
|
||||
virtual_sp_offset: 0,
|
||||
nominal_sp_to_fp: abi.frame_size() as i64,
|
||||
|
||||
@@ -396,7 +396,7 @@ fn emit_vm_call<C: LowerCtx<I = Inst>>(
|
||||
let sig = make_libcall_sig(ctx, insn, call_conv, types::I64);
|
||||
|
||||
let loc = ctx.srcloc(insn);
|
||||
let mut abi = X64ABICall::from_func(&sig, &extname, dist, loc)?;
|
||||
let mut abi = X64ABICaller::from_func(&sig, &extname, dist, loc)?;
|
||||
|
||||
abi.emit_stack_pre_adjust(ctx);
|
||||
|
||||
@@ -1277,7 +1277,7 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
assert_eq!(inputs.len(), sig.params.len());
|
||||
assert_eq!(outputs.len(), sig.returns.len());
|
||||
(
|
||||
X64ABICall::from_func(sig, &extname, dist, loc)?,
|
||||
X64ABICaller::from_func(sig, &extname, dist, loc)?,
|
||||
&inputs[..],
|
||||
)
|
||||
}
|
||||
@@ -1287,7 +1287,7 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
let sig = ctx.call_sig(insn).unwrap();
|
||||
assert_eq!(inputs.len() - 1, sig.params.len());
|
||||
assert_eq!(outputs.len(), sig.returns.len());
|
||||
(X64ABICall::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
|
||||
(X64ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -41,7 +41,7 @@ impl X64Backend {
|
||||
fn compile_vcode(&self, func: &Function, flags: Flags) -> CodegenResult<VCode<inst::Inst>> {
|
||||
// This performs lowering to VCode, register-allocates the code, computes
|
||||
// block layout and finalizes branches. The result is ready for binary emission.
|
||||
let abi = Box::new(abi::X64ABIBody::new(&func, flags)?);
|
||||
let abi = Box::new(abi::X64ABICallee::new(&func, flags)?);
|
||||
compile::compile::<Self>(&func, self, abi)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use regalloc::{Reg, Set, SpillSlot, Writable};
|
||||
|
||||
/// Trait implemented by an object that tracks ABI-related state (e.g., stack
|
||||
/// layout) and can generate code while emitting the *body* of a function.
|
||||
pub trait ABIBody {
|
||||
pub trait ABICallee {
|
||||
/// The instruction type for the ISA associated with this ABI.
|
||||
type I: VCodeInst;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub trait ABIBody {
|
||||
/// as the `maybe_tmp` arg if so.
|
||||
fn temp_needed(&self) -> bool;
|
||||
|
||||
/// Initialize. This is called after the ABIBody is constructed because it
|
||||
/// Initialize. This is called after the ABICallee is constructed because it
|
||||
/// may be provided with a temp vreg, which can only be allocated once the
|
||||
/// lowering context exists.
|
||||
fn init(&mut self, maybe_tmp: Option<Writable<Reg>>);
|
||||
@@ -155,14 +155,14 @@ pub trait ABIBody {
|
||||
/// callsite. It will usually be computed from the called function's
|
||||
/// signature.
|
||||
///
|
||||
/// Unlike `ABIBody` above, methods on this trait are not invoked directly
|
||||
/// Unlike `ABICallee` above, methods on this trait are not invoked directly
|
||||
/// by the machine-independent code. Rather, the machine-specific lowering
|
||||
/// code will typically create an `ABICall` when creating machine instructions
|
||||
/// code will typically create an `ABICaller` when creating machine instructions
|
||||
/// for an IR call instruction inside `lower()`, directly emit the arg and
|
||||
/// and retval copies, and attach the register use/def info to the call.
|
||||
///
|
||||
/// This trait is thus provided for convenience to the backends.
|
||||
pub trait ABICall {
|
||||
pub trait ABICaller {
|
||||
/// The instruction type for the ISA associated with this ABI.
|
||||
type I: VCodeInst;
|
||||
|
||||
@@ -203,6 +203,6 @@ pub trait ABICall {
|
||||
/// sense.)
|
||||
///
|
||||
/// This function should only be called once, as it is allowed to re-use
|
||||
/// parts of the ABICall object in emitting instructions.
|
||||
/// parts of the ABICaller object in emitting instructions.
|
||||
fn emit_call<C: LowerCtx<I = Self::I>>(&mut self, ctx: &mut C);
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ use crate::{ir, isa};
|
||||
use alloc::vec::Vec;
|
||||
use log::{debug, trace};
|
||||
use regalloc::{RealReg, Reg, RegClass, Set, SpillSlot, Writable};
|
||||
use std::convert::TryFrom;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
||||
@@ -142,6 +143,16 @@ pub enum ArgsOrRets {
|
||||
Rets,
|
||||
}
|
||||
|
||||
/// Is an instruction returned by an ABI machine-specific backend a safepoint,
|
||||
/// or not?
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum InstIsSafepoint {
|
||||
/// The instruction is a safepoint.
|
||||
Yes,
|
||||
/// The instruction is not a safepoint.
|
||||
No,
|
||||
}
|
||||
|
||||
/// Abstract location for a machine-specific ABI impl to translate into the
|
||||
/// appropriate addressing mode.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -160,7 +171,7 @@ pub enum StackAMode {
|
||||
/// Trait implemented by machine-specific backend to provide information about
|
||||
/// register assignments and to allow generating the specific instructions for
|
||||
/// stack loads/saves, prologues/epilogues, etc.
|
||||
pub trait ABIMachineImpl {
|
||||
pub trait ABIMachineSpec {
|
||||
/// The instruction type.
|
||||
type I: VCodeInst;
|
||||
|
||||
@@ -207,13 +218,15 @@ pub trait ABIMachineImpl {
|
||||
fn gen_epilogue_placeholder() -> Self::I;
|
||||
|
||||
/// Generate an add-with-immediate. Note that even if this uses a scratch
|
||||
/// register, the sequence must still be correct if the given source or dest
|
||||
/// is the register returned by `get_fixed_tmp_reg()`; hence, for machines
|
||||
/// that may need a scratch register to synthesize an arbitrary constant,
|
||||
/// the machine backend should reserve *another* fixed temp register for
|
||||
/// this purpose. (E.g., on AArch64, x16 is the ordinary fixed tmp, and x17
|
||||
/// is the secondary fixed tmp used to implement this.)
|
||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u64) -> SmallVec<[Self::I; 4]>;
|
||||
/// register, it must satisfy two requirements:
|
||||
///
|
||||
/// - The add-imm sequence must only clobber caller-save registers, because
|
||||
/// it will be placed in the prologue before the clobbered callee-save
|
||||
/// registers are saved.
|
||||
///
|
||||
/// - The add-imm sequence must work correctly when `from_reg` and/or
|
||||
/// `into_reg` are the register returned by `get_stacklimit_reg()`.
|
||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallVec<[Self::I; 4]>;
|
||||
|
||||
/// Generate a sequence that traps with a `TrapCode::StackOverflow` code if
|
||||
/// the stack pointer is less than the given limit register (assuming the
|
||||
@@ -224,21 +237,30 @@ pub trait ABIMachineImpl {
|
||||
/// SP-based offset).
|
||||
fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Self::I;
|
||||
|
||||
/// Get a fixed (not used by regalloc) temp. This is needed for certain
|
||||
/// sequences generated after the register allocator has already run.
|
||||
fn get_fixed_tmp_reg() -> Reg;
|
||||
/// Get a fixed register to use to compute a stack limit. This is needed for
|
||||
/// certain sequences generated after the register allocator has already
|
||||
/// run. This must satisfy two requirements:
|
||||
///
|
||||
/// - It must be a caller-save register, because it will be clobbered in the
|
||||
/// prologue before the clobbered callee-save registers are saved.
|
||||
///
|
||||
/// - It must be safe to pass as an argument and/or destination to
|
||||
/// `gen_add_imm()`. This is relevant when an addition with a large
|
||||
/// immediate needs its own temporary; it cannot use the same fixed
|
||||
/// temporary as this one.
|
||||
fn get_stacklimit_reg() -> Reg;
|
||||
|
||||
/// Generate a store to the given [base+offset] address.
|
||||
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i64, ty: Type) -> Self::I;
|
||||
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Self::I;
|
||||
|
||||
/// Generate a load from the given [base+offset] address.
|
||||
fn gen_store_base_offset(base: Reg, offset: i64, from_reg: Reg, ty: Type) -> Self::I;
|
||||
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Self::I;
|
||||
|
||||
/// Adjust the stack pointer up or down.
|
||||
fn gen_sp_reg_adjust(amount: i64) -> SmallVec<[Self::I; 2]>;
|
||||
fn gen_sp_reg_adjust(amount: i32) -> SmallVec<[Self::I; 2]>;
|
||||
|
||||
/// Generate a meta-instruction that adjusts the nominal SP offset.
|
||||
fn gen_nominal_sp_adj(amount: i64) -> Self::I;
|
||||
fn gen_nominal_sp_adj(amount: i32) -> Self::I;
|
||||
|
||||
/// Generate the usual frame-setup sequence for this architecture: e.g.,
|
||||
/// `push rbp / mov rbp, rsp` on x86-64, or `stp fp, lr, [sp, #-16]!` on
|
||||
@@ -272,18 +294,20 @@ pub trait ABIMachineImpl {
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
) -> SmallVec<[Self::I; 16]>;
|
||||
|
||||
/// Generate a call instruction/sequence.
|
||||
/// Generate a call instruction/sequence. This method is provided one
|
||||
/// temporary register to use to synthesize the called address, if needed.
|
||||
fn gen_call(
|
||||
dest: &CallDest,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
) -> SmallVec<[(/* is_safepoint = */ bool, Self::I); 2]>;
|
||||
tmp: Writable<Reg>,
|
||||
) -> SmallVec<[(InstIsSafepoint, Self::I); 2]>;
|
||||
|
||||
/// Get the number of spillslots required for the given register-class and
|
||||
/// type.
|
||||
fn get_spillslot_size(rc: RegClass, ty: Type) -> u32;
|
||||
fn get_number_of_spillslots_for_value(rc: RegClass, ty: Type) -> u32;
|
||||
|
||||
/// Get the current virtual-SP offset from an instruction-emission state.
|
||||
fn get_virtual_sp_offset_from_state(s: &<Self::I as MachInstEmit>::State) -> i64;
|
||||
@@ -314,7 +338,7 @@ struct ABISig {
|
||||
}
|
||||
|
||||
impl ABISig {
|
||||
fn from_func_sig<M: ABIMachineImpl>(sig: &ir::Signature) -> CodegenResult<ABISig> {
|
||||
fn from_func_sig<M: ABIMachineSpec>(sig: &ir::Signature) -> CodegenResult<ABISig> {
|
||||
// Compute args and retvals from signature. Handle retvals first,
|
||||
// because we may need to add a return-area arg to the args.
|
||||
let (rets, stack_ret_space, _) = M::compute_arg_locs(
|
||||
@@ -353,7 +377,7 @@ impl ABISig {
|
||||
}
|
||||
|
||||
/// ABI object for a function body.
|
||||
pub struct ABIBodyImpl<M: ABIMachineImpl> {
|
||||
pub struct ABICalleeImpl<M: ABIMachineSpec> {
|
||||
/// Signature: arg and retval regs.
|
||||
sig: ABISig,
|
||||
/// Offsets to each stackslot.
|
||||
@@ -405,7 +429,7 @@ fn get_special_purpose_param_register(
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: ABIMachineImpl> ABIBodyImpl<M> {
|
||||
impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
||||
/// Create a new body ABI instance.
|
||||
pub fn new(f: &ir::Function, flags: settings::Flags) -> CodegenResult<Self> {
|
||||
debug!("ABI: func signature {:?}", f.signature);
|
||||
@@ -506,8 +530,7 @@ impl<M: ABIMachineImpl> ABIBodyImpl<M> {
|
||||
// `scratch`. If our stack size doesn't fit into an immediate this
|
||||
// means we need a second scratch register for loading the stack size
|
||||
// into a register.
|
||||
let scratch = Writable::from_reg(M::get_fixed_tmp_reg());
|
||||
let stack_size = u64::from(stack_size);
|
||||
let scratch = Writable::from_reg(M::get_stacklimit_reg());
|
||||
insts.extend(M::gen_add_imm(scratch, stack_limit, stack_size).into_iter());
|
||||
insts.extend(M::gen_stack_lower_bound_trap(scratch.to_reg()));
|
||||
}
|
||||
@@ -532,7 +555,7 @@ impl<M: ABIMachineImpl> ABIBodyImpl<M> {
|
||||
/// temporary register to store values in if necessary. Currently after we write
|
||||
/// to this register there's guaranteed to be no spilled values between where
|
||||
/// it's used, because we're not participating in register allocation anyway!
|
||||
fn gen_stack_limit<M: ABIMachineImpl>(
|
||||
fn gen_stack_limit<M: ABIMachineSpec>(
|
||||
f: &ir::Function,
|
||||
abi: &ABISig,
|
||||
gv: ir::GlobalValue,
|
||||
@@ -542,7 +565,7 @@ fn gen_stack_limit<M: ABIMachineImpl>(
|
||||
return (reg, insts);
|
||||
}
|
||||
|
||||
fn generate_gv<M: ABIMachineImpl>(
|
||||
fn generate_gv<M: ABIMachineSpec>(
|
||||
f: &ir::Function,
|
||||
abi: &ABISig,
|
||||
gv: ir::GlobalValue,
|
||||
@@ -563,7 +586,7 @@ fn generate_gv<M: ABIMachineImpl>(
|
||||
readonly: _,
|
||||
} => {
|
||||
let base = generate_gv::<M>(f, abi, base, insts);
|
||||
let into_reg = Writable::from_reg(M::get_fixed_tmp_reg());
|
||||
let into_reg = Writable::from_reg(M::get_stacklimit_reg());
|
||||
insts.push(M::gen_load_base_offset(into_reg, base, offset.into(), I64));
|
||||
return into_reg.to_reg();
|
||||
}
|
||||
@@ -591,7 +614,7 @@ fn ty_from_ty_hint_or_reg_class(r: Reg, ty: Option<Type>) -> Type {
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: ABIMachineImpl> ABIBody for ABIBodyImpl<M> {
|
||||
impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
type I = M::I;
|
||||
|
||||
fn temp_needed(&self) -> bool {
|
||||
@@ -676,6 +699,11 @@ impl<M: ABIMachineImpl> ABIBody for ABIBodyImpl<M> {
|
||||
}
|
||||
&ABIArg::Stack(off, mut ty, ext) => {
|
||||
let from_bits = ty_bits(ty) as u8;
|
||||
// A machine ABI implementation should ensure that stack frames
|
||||
// have "reasonable" size. All current ABIs for machinst
|
||||
// backends (aarch64 and x64) enforce a 128MB limit.
|
||||
let off = i32::try_from(off)
|
||||
.expect("Argument stack offset greater than 2GB; should hit impl limit first");
|
||||
// Trash the from_reg; it should be its last use.
|
||||
match (ext, from_bits) {
|
||||
(ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n) if n < 64 => {
|
||||
@@ -864,7 +892,7 @@ impl<M: ABIMachineImpl> ABIBody for ABIBodyImpl<M> {
|
||||
|
||||
if total_sp_adjust > 0 {
|
||||
// sub sp, sp, #total_stacksize
|
||||
let adj = total_sp_adjust as i64;
|
||||
let adj = total_sp_adjust as i32;
|
||||
insts.extend(M::gen_sp_reg_adjust(-adj));
|
||||
}
|
||||
|
||||
@@ -873,7 +901,7 @@ impl<M: ABIMachineImpl> ABIBody for ABIBodyImpl<M> {
|
||||
insts.extend(clobber_insts);
|
||||
|
||||
if clobber_size > 0 {
|
||||
insts.push(M::gen_nominal_sp_adj(clobber_size as i64));
|
||||
insts.push(M::gen_nominal_sp_adj(clobber_size as i32));
|
||||
}
|
||||
|
||||
self.total_frame_size = Some(total_stacksize);
|
||||
@@ -911,7 +939,7 @@ impl<M: ABIMachineImpl> ABIBody for ABIBodyImpl<M> {
|
||||
}
|
||||
|
||||
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
||||
M::get_spillslot_size(rc, ty)
|
||||
M::get_number_of_spillslots_for_value(rc, ty)
|
||||
}
|
||||
|
||||
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Option<Type>) -> Self::I {
|
||||
@@ -930,7 +958,7 @@ impl<M: ABIMachineImpl> ABIBody for ABIBodyImpl<M> {
|
||||
}
|
||||
}
|
||||
|
||||
fn abisig_to_uses_and_defs<M: ABIMachineImpl>(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
|
||||
fn abisig_to_uses_and_defs<M: ABIMachineSpec>(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
|
||||
// Compute uses: all arg regs.
|
||||
let mut uses = Vec::new();
|
||||
for arg in &sig.args {
|
||||
@@ -953,7 +981,7 @@ fn abisig_to_uses_and_defs<M: ABIMachineImpl>(sig: &ABISig) -> (Vec<Reg>, Vec<Wr
|
||||
}
|
||||
|
||||
/// ABI object for a callsite.
|
||||
pub struct ABICallImpl<M: ABIMachineImpl> {
|
||||
pub struct ABICallerImpl<M: ABIMachineSpec> {
|
||||
/// The called function's signature.
|
||||
sig: ABISig,
|
||||
/// All uses for the callsite, i.e., function args.
|
||||
@@ -979,17 +1007,17 @@ pub enum CallDest {
|
||||
Reg(Reg),
|
||||
}
|
||||
|
||||
impl<M: ABIMachineImpl> ABICallImpl<M> {
|
||||
impl<M: ABIMachineSpec> ABICallerImpl<M> {
|
||||
/// Create a callsite ABI object for a call directly to the specified function.
|
||||
pub fn from_func(
|
||||
sig: &ir::Signature,
|
||||
extname: &ir::ExternalName,
|
||||
dist: RelocDistance,
|
||||
loc: ir::SourceLoc,
|
||||
) -> CodegenResult<ABICallImpl<M>> {
|
||||
) -> CodegenResult<ABICallerImpl<M>> {
|
||||
let sig = ABISig::from_func_sig::<M>(sig)?;
|
||||
let (uses, defs) = abisig_to_uses_and_defs::<M>(&sig);
|
||||
Ok(ABICallImpl {
|
||||
Ok(ABICallerImpl {
|
||||
sig,
|
||||
uses,
|
||||
defs,
|
||||
@@ -1007,10 +1035,10 @@ impl<M: ABIMachineImpl> ABICallImpl<M> {
|
||||
ptr: Reg,
|
||||
loc: ir::SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
) -> CodegenResult<ABICallImpl<M>> {
|
||||
) -> CodegenResult<ABICallerImpl<M>> {
|
||||
let sig = ABISig::from_func_sig::<M>(sig)?;
|
||||
let (uses, defs) = abisig_to_uses_and_defs::<M>(&sig);
|
||||
Ok(ABICallImpl {
|
||||
Ok(ABICallerImpl {
|
||||
sig,
|
||||
uses,
|
||||
defs,
|
||||
@@ -1022,15 +1050,14 @@ impl<M: ABIMachineImpl> ABICallImpl<M> {
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_stack_and_nominal_sp<M: ABIMachineImpl, C: LowerCtx<I = M::I>>(
|
||||
fn adjust_stack_and_nominal_sp<M: ABIMachineSpec, C: LowerCtx<I = M::I>>(
|
||||
ctx: &mut C,
|
||||
off: u64,
|
||||
off: i32,
|
||||
is_sub: bool,
|
||||
) {
|
||||
if off == 0 {
|
||||
return;
|
||||
}
|
||||
let off = off as i64;
|
||||
let amt = if is_sub { -off } else { off };
|
||||
for inst in M::gen_sp_reg_adjust(amt) {
|
||||
ctx.emit(inst);
|
||||
@@ -1038,7 +1065,7 @@ fn adjust_stack_and_nominal_sp<M: ABIMachineImpl, C: LowerCtx<I = M::I>>(
|
||||
ctx.emit(M::gen_nominal_sp_adj(-amt));
|
||||
}
|
||||
|
||||
impl<M: ABIMachineImpl> ABICall for ABICallImpl<M> {
|
||||
impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
||||
type I = M::I;
|
||||
|
||||
fn num_args(&self) -> usize {
|
||||
@@ -1051,12 +1078,12 @@ impl<M: ABIMachineImpl> ABICall for ABICallImpl<M> {
|
||||
|
||||
fn emit_stack_pre_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
||||
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
||||
adjust_stack_and_nominal_sp::<M, C>(ctx, off as u64, /* is_sub = */ true)
|
||||
adjust_stack_and_nominal_sp::<M, C>(ctx, off as i32, /* is_sub = */ true)
|
||||
}
|
||||
|
||||
fn emit_stack_post_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
||||
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
||||
adjust_stack_and_nominal_sp::<M, C>(ctx, off as u64, /* is_sub = */ false)
|
||||
adjust_stack_and_nominal_sp::<M, C>(ctx, off as i32, /* is_sub = */ false)
|
||||
}
|
||||
|
||||
fn emit_copy_reg_to_arg<C: LowerCtx<I = Self::I>>(
|
||||
@@ -1152,13 +1179,13 @@ impl<M: ABIMachineImpl> ABICall for ABICallImpl<M> {
|
||||
));
|
||||
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
|
||||
}
|
||||
let tmp = ctx.alloc_tmp(RegClass::I64, I64);
|
||||
for (is_safepoint, inst) in
|
||||
M::gen_call(&self.dest, uses, defs, self.loc, self.opcode).into_iter()
|
||||
M::gen_call(&self.dest, uses, defs, self.loc, self.opcode, tmp).into_iter()
|
||||
{
|
||||
if is_safepoint {
|
||||
ctx.emit_safepoint(inst);
|
||||
} else {
|
||||
ctx.emit(inst);
|
||||
match is_safepoint {
|
||||
InstIsSafepoint::Yes => ctx.emit_safepoint(inst),
|
||||
InstIsSafepoint::No => ctx.emit(inst),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use regalloc::{allocate_registers_with_opts, Algorithm, Options};
|
||||
pub fn compile<B: LowerBackend + MachBackend>(
|
||||
f: &Function,
|
||||
b: &B,
|
||||
abi: Box<dyn ABIBody<I = B::MInst>>,
|
||||
abi: Box<dyn ABICallee<I = B::MInst>>,
|
||||
) -> CodegenResult<VCode<B::MInst>>
|
||||
where
|
||||
B::MInst: ShowWithRRU,
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::ir::{
|
||||
ValueDef,
|
||||
};
|
||||
use crate::machinst::{
|
||||
ABIBody, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode, VCodeBuilder,
|
||||
ABICallee, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode, VCodeBuilder,
|
||||
VCodeInst,
|
||||
};
|
||||
use crate::CodegenResult;
|
||||
@@ -61,8 +61,8 @@ pub trait LowerCtx {
|
||||
|
||||
// Function-level queries:
|
||||
|
||||
/// Get the `ABIBody`.
|
||||
fn abi(&mut self) -> &dyn ABIBody<I = Self::I>;
|
||||
/// Get the `ABICallee`.
|
||||
fn abi(&mut self) -> &dyn ABICallee<I = Self::I>;
|
||||
/// Get the (virtual) register that receives the return value. A return
|
||||
/// instruction should lower into a sequence that fills this register. (Why
|
||||
/// not allow the backend to specify its own result register for the return?
|
||||
@@ -312,7 +312,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
/// Prepare a new lowering context for the given IR function.
|
||||
pub fn new(
|
||||
f: &'func Function,
|
||||
abi: Box<dyn ABIBody<I = I>>,
|
||||
abi: Box<dyn ABICallee<I = I>>,
|
||||
block_order: BlockLoweringOrder,
|
||||
) -> CodegenResult<Lower<'func, I>> {
|
||||
let mut vcode = VCodeBuilder::new(abi, block_order);
|
||||
@@ -844,7 +844,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
|
||||
type I = I;
|
||||
|
||||
fn abi(&mut self) -> &dyn ABIBody<I = I> {
|
||||
fn abi(&mut self) -> &dyn ABICallee<I = I> {
|
||||
self.vcode.abi()
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ pub trait MachInstEmit: MachInst {
|
||||
/// emitting a function body.
|
||||
pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug {
|
||||
/// Create a new emission state given the ABI object.
|
||||
fn new(abi: &dyn ABIBody<I = I>) -> Self;
|
||||
fn new(abi: &dyn ABICallee<I = I>) -> Self;
|
||||
/// Update the emission state before emitting an instruction that is a
|
||||
/// safepoint.
|
||||
fn pre_safepoint(&mut self, _stack_map: StackMap) {}
|
||||
|
||||
@@ -86,7 +86,7 @@ pub struct VCode<I: VCodeInst> {
|
||||
block_order: BlockLoweringOrder,
|
||||
|
||||
/// ABI object.
|
||||
abi: Box<dyn ABIBody<I = I>>,
|
||||
abi: Box<dyn ABICallee<I = I>>,
|
||||
|
||||
/// Safepoint instruction indices. Filled in post-regalloc. (Prior to
|
||||
/// regalloc, the safepoint instructions are listed in the separate
|
||||
@@ -132,7 +132,7 @@ pub struct VCodeBuilder<I: VCodeInst> {
|
||||
|
||||
impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
/// Create a new VCodeBuilder.
|
||||
pub fn new(abi: Box<dyn ABIBody<I = I>>, block_order: BlockLoweringOrder) -> VCodeBuilder<I> {
|
||||
pub fn new(abi: Box<dyn ABICallee<I = I>>, block_order: BlockLoweringOrder) -> VCodeBuilder<I> {
|
||||
let reftype_class = I::ref_type_regclass(abi.flags());
|
||||
let vcode = VCode::new(abi, block_order);
|
||||
let stack_map_info = StackmapRequestInfo {
|
||||
@@ -151,7 +151,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
}
|
||||
|
||||
/// Access the ABI object.
|
||||
pub fn abi(&mut self) -> &mut dyn ABIBody<I = I> {
|
||||
pub fn abi(&mut self) -> &mut dyn ABICallee<I = I> {
|
||||
&mut *self.vcode.abi
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ fn is_reftype(ty: Type) -> bool {
|
||||
|
||||
impl<I: VCodeInst> VCode<I> {
|
||||
/// New empty VCode.
|
||||
fn new(abi: Box<dyn ABIBody<I = I>>, block_order: BlockLoweringOrder) -> VCode<I> {
|
||||
fn new(abi: Box<dyn ABICallee<I = I>>, block_order: BlockLoweringOrder) -> VCode<I> {
|
||||
VCode {
|
||||
liveins: abi.liveins(),
|
||||
liveouts: abi.liveouts(),
|
||||
|
||||
@@ -11,8 +11,8 @@ block0(v0: i64):
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: ldr x16, 8 ; b 12 ; data
|
||||
; nextln: blr x16
|
||||
; nextln: ldr x1, 8 ; b 12 ; data
|
||||
; nextln: blr x1
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
@@ -28,8 +28,8 @@ block0(v0: i32):
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: mov w0, w0
|
||||
; nextln: ldr x16, 8 ; b 12 ; data
|
||||
; nextln: blr x16
|
||||
; nextln: ldr x1, 8 ; b 12 ; data
|
||||
; nextln: blr x1
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
@@ -57,8 +57,8 @@ block0(v0: i32):
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: sxtw x0, w0
|
||||
; nextln: ldr x16, 8 ; b 12 ; data
|
||||
; nextln: blr x16
|
||||
; nextln: ldr x1, 8 ; b 12 ; data
|
||||
; nextln: blr x1
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
@@ -99,8 +99,8 @@ block0(v0: i8):
|
||||
; nextln: movz x7, #42
|
||||
; nextln: sxtb x8, w8
|
||||
; nextln: stur x8, [sp]
|
||||
; nextln: ldr x16, 8 ; b 12 ; data
|
||||
; nextln: blr x16
|
||||
; nextln: ldr x8, 8 ; b 12 ; data
|
||||
; nextln: blr x8
|
||||
; nextln: add sp, sp, #16
|
||||
; nextln: virtual_sp_offset_adjust -16
|
||||
; nextln: mov sp, fp
|
||||
|
||||
@@ -83,12 +83,12 @@ block3(v7: r64, v8: r64):
|
||||
; nextln: mov x19, x0
|
||||
; nextln: mov x20, x1
|
||||
; nextln: mov x0, x19
|
||||
; nextln: ldr x16, 8 ; b 12 ; data
|
||||
; nextln: ldr x1, 8 ; b 12 ; data
|
||||
; nextln: stur x0, [sp, #24]
|
||||
; nextln: stur x19, [sp, #32]
|
||||
; nextln: stur x20, [sp, #40]
|
||||
; nextln: (safepoint: slots [S0, S1, S2]
|
||||
; nextln: blr x16
|
||||
; nextln: blr x1
|
||||
; nextln: ldur x19, [sp, #32]
|
||||
; nextln: ldur x20, [sp, #40]
|
||||
; nextln: add x1, sp, #16
|
||||
|
||||
@@ -44,8 +44,8 @@ block0(v0: i64):
|
||||
; nextln: mov fp, sp
|
||||
; nextln: subs xzr, sp, x0
|
||||
; nextln: b.hs 8 ; udf
|
||||
; nextln: ldr x16
|
||||
; nextln: blr x16
|
||||
; nextln: ldr x0
|
||||
; nextln: blr x0
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
@@ -67,8 +67,8 @@ block0(v0: i64):
|
||||
; nextln: ldur x16, [x16, #4]
|
||||
; nextln: subs xzr, sp, x16
|
||||
; nextln: b.hs 8 ; udf
|
||||
; nextln: ldr x16
|
||||
; nextln: blr x16
|
||||
; nextln: ldr x0
|
||||
; nextln: blr x0
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
Reference in New Issue
Block a user