Merge pull request #2142 from cfallin/machinst-abi-x64
x64 new backend: port ABI implementation to shared infrastructure with AArch64.
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