From 9766fc3fcdd9764e800d44b8a98ba0eb8494cf7c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 30 Jun 2017 10:37:42 -0700 Subject: [PATCH] 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. --- filetests/isa/intel/abi64.cton | 20 ++++++ lib/cretonne/src/isa/intel/abi.rs | 114 ++++++++++++++++++++++++++++-- lib/cretonne/src/isa/intel/mod.rs | 2 +- 3 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 filetests/isa/intel/abi64.cton diff --git a/filetests/isa/intel/abi64.cton b/filetests/isa/intel/abi64.cton new file mode 100644 index 0000000000..ecd3d29fd6 --- /dev/null +++ b/filetests/isa/intel/abi64.cton @@ -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 +} diff --git a/lib/cretonne/src/isa/intel/abi.rs b/lib/cretonne/src/isa/intel/abi.rs index e6c5edd5bf..240e9d327d 100644 --- a/lib/cretonne/src/isa/intel/abi.rs +++ b/lib/cretonne/src/isa/intel/abi.rs @@ -1,16 +1,102 @@ //! Intel ABI implementation. use ir; -use isa::RegClass; +use isa::{RegClass, RegUnit}; use regalloc::AllocatableSet; 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`. -pub fn legalize_signature(_sig: &mut ir::Signature, - _flags: &shared_settings::Flags, +pub fn legalize_signature(sig: &mut ir::Signature, + flags: &shared_settings::Flags, _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. @@ -19,6 +105,20 @@ pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass { } /// Get the set of allocatable registers for `func`. -pub fn allocatable_registers(_func: &ir::Function) -> AllocatableSet { - unimplemented!() +pub fn allocatable_registers(_func: &ir::Function, + 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 } diff --git a/lib/cretonne/src/isa/intel/mod.rs b/lib/cretonne/src/isa/intel/mod.rs index 6cfd0b4b85..88df62c8ac 100644 --- a/lib/cretonne/src/isa/intel/mod.rs +++ b/lib/cretonne/src/isa/intel/mod.rs @@ -88,7 +88,7 @@ impl TargetIsa for Isa { } 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) {