Implement the basics of the x86-64 ABI.

This is just a rough sketch to get us started. There are bound to be
some issues.

This also legalizes signatures for x86-32, but probably not correctly.
It's basically implementing the x86-64 ABI for 32-bit.
This commit is contained in:
Jakob Stoklund Olesen
2017-06-30 10:37:42 -07:00
parent 68ca285507
commit 9766fc3fcd
3 changed files with 128 additions and 8 deletions

View File

@@ -0,0 +1,20 @@
; Test the legalization of function signatures.
test legalizer
set is_64bit
isa intel
; regex: V=v\d+
function %f() {
sig0 = signature(i32) -> i32
; check: sig0 = signature(i32 [%rdi]) -> i32 [%rax]
sig1 = signature(i64) -> b1
; check: sig1 = signature(i64 [%rdi]) -> b1 [%rax]
sig2 = signature(f32, i64) -> f64
; check: sig2 = signature(f32 [%xmm0], i64 [%rdi]) -> f64 [%xmm0]
ebb0:
return
}

View File

@@ -1,16 +1,102 @@
//! Intel ABI implementation. //! Intel ABI implementation.
use ir; use ir;
use isa::RegClass; use isa::{RegClass, RegUnit};
use regalloc::AllocatableSet; use regalloc::AllocatableSet;
use settings as shared_settings; use settings as shared_settings;
use super::registers::{GPR, FPR}; use super::registers::{GPR, FPR, RU};
use abi::{ArgAction, ValueConversion, ArgAssigner, legalize_args};
use ir::{ArgumentType, ArgumentLoc, ArgumentExtension};
/// Argument registers for x86-64
static ARG_GPRS: [RU; 6] = [RU::rdi, RU::rsi, RU::rdx, RU::rcx, RU::r8, RU::r9];
/// Return value registers.
static RET_GPRS: [RU; 3] = [RU::rax, RU::rdx, RU::rcx];
struct Args {
pointer_bytes: u32,
pointer_bits: u16,
pointer_type: ir::Type,
gpr: &'static [RU],
gpr_used: usize,
fpr_limit: usize,
fpr_used: usize,
offset: u32,
}
impl Args {
fn new(bits: u16, gpr: &'static [RU], fpr_limit: usize) -> Args {
Args {
pointer_bytes: bits as u32 / 8,
pointer_bits: bits,
pointer_type: ir::Type::int(bits).unwrap(),
gpr,
gpr_used: 0,
fpr_limit,
fpr_used: 0,
offset: 0,
}
}
}
impl ArgAssigner for Args {
fn assign(&mut self, arg: &ArgumentType) -> ArgAction {
let ty = arg.value_type;
// Check for a legal type.
// We don't support SIMD yet, so break all vectors down.
if !ty.is_scalar() {
return ValueConversion::VectorSplit.into();
}
// Large integers and booleans are broken down to fit in a register.
if !ty.is_float() && ty.bits() > self.pointer_bits {
return ValueConversion::IntSplit.into();
}
// Small integers are extended to the size of a pointer register.
if ty.is_int() && ty.bits() < self.pointer_bits {
match arg.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => return ValueConversion::Uext(self.pointer_type).into(),
ArgumentExtension::Sext => return ValueConversion::Sext(self.pointer_type).into(),
}
}
// Try to use a GPR.
if !ty.is_float() && self.gpr_used < self.gpr.len() {
let reg = self.gpr[self.gpr_used] as RegUnit;
self.gpr_used += 1;
return ArgumentLoc::Reg(reg).into();
}
// Try to use an FPR.
if ty.is_float() && self.fpr_used < self.fpr_limit {
let reg = FPR.unit(self.fpr_used);
self.fpr_used += 1;
return ArgumentLoc::Reg(reg).into();
}
// Assign a stack location.
let loc = ArgumentLoc::Stack(self.offset as i32);
self.offset += self.pointer_bytes;
assert!(self.offset <= i32::max_value() as u32);
loc.into()
}
}
/// Legalize `sig`. /// Legalize `sig`.
pub fn legalize_signature(_sig: &mut ir::Signature, pub fn legalize_signature(sig: &mut ir::Signature,
_flags: &shared_settings::Flags, flags: &shared_settings::Flags,
_current: bool) { _current: bool) {
unimplemented!() let bits = if flags.is_64bit() { 64 } else { 32 };
let mut args = Args::new(bits, &ARG_GPRS, 8);
legalize_args(&mut sig.argument_types, &mut args);
let mut rets = Args::new(bits, &RET_GPRS, 2);
legalize_args(&mut sig.return_types, &mut rets);
} }
/// Get register class for a type appearing in a legalized signature. /// Get register class for a type appearing in a legalized signature.
@@ -19,6 +105,20 @@ pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass {
} }
/// Get the set of allocatable registers for `func`. /// Get the set of allocatable registers for `func`.
pub fn allocatable_registers(_func: &ir::Function) -> AllocatableSet { pub fn allocatable_registers(_func: &ir::Function,
unimplemented!() flags: &shared_settings::Flags)
-> AllocatableSet {
let mut regs = AllocatableSet::new();
regs.take(GPR, RU::rsp as RegUnit);
regs.take(GPR, RU::rbp as RegUnit);
// 32-bit arch only has 8 registers.
if !flags.is_64bit() {
for i in 8..16 {
regs.take(GPR, GPR.unit(i));
regs.take(FPR, FPR.unit(i));
}
}
regs
} }

View File

@@ -88,7 +88,7 @@ impl TargetIsa for Isa {
} }
fn allocatable_registers(&self, func: &ir::Function) -> regalloc::AllocatableSet { fn allocatable_registers(&self, func: &ir::Function) -> regalloc::AllocatableSet {
abi::allocatable_registers(func) abi::allocatable_registers(func, &self.shared_flags)
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {