diff --git a/filetests/isa/riscv/abi.cton b/filetests/isa/riscv/abi.cton new file mode 100644 index 0000000000..adb2dfed07 --- /dev/null +++ b/filetests/isa/riscv/abi.cton @@ -0,0 +1,23 @@ +; Test the legalization of function signatures. +test legalizer +isa riscv + +function f(i32) { + sig0 = signature(i32) -> i32 + +; check: sig0 = signature(i32 [%x10]) -> i32 [%x10] + + sig1 = signature(i64) -> b1 +; check: sig1 = signature(i32 [%x10], i32 [%x11]) -> b1 [%x10] + +; The i64 argument must go in an even-odd register pair. + sig2 = signature(f32, i64) -> f64 +; check: sig2 = signature(f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] + +; Spilling into the stack args. + sig3 = signature(f64, f64, f64, f64, f64, f64, f64, i64) -> f64 +; check: sig3 = signature(f64 [%f10], f64 [%f11], f64 [%f12], f64 [%f13], f64 [%f14], f64 [%f15], f64 [%f16], i32 [0], i32 [4]) -> f64 [%f10] + +ebb0(v0: i32): + return_reg v0 +} diff --git a/lib/cretonne/meta/gen_registers.py b/lib/cretonne/meta/gen_registers.py index 7dfdc45d00..b1532761a6 100644 --- a/lib/cretonne/meta/gen_registers.py +++ b/lib/cretonne/meta/gen_registers.py @@ -37,6 +37,7 @@ def gen_regclass(rc, fmt): fmt.line('name: "{}",'.format(rc.name)) fmt.line('index: {},'.format(rc.index)) fmt.line('width: {},'.format(rc.width)) + fmt.line('first: {},'.format(rc.bank.first_unit + rc.start)) fmt.line('subclasses: 0x{:x},'.format(rc.subclass_mask())) mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask()) fmt.line('mask: [{}],'.format(mask)) diff --git a/lib/cretonne/src/abi.rs b/lib/cretonne/src/abi.rs new file mode 100644 index 0000000000..897133bc76 --- /dev/null +++ b/lib/cretonne/src/abi.rs @@ -0,0 +1,72 @@ +//! Common helper code for ABI lowering. +//! +//! This module provides functions and data structures that are useful for implementing the +//! `TargetIsa::legalize_signature()` method. + +use ir::{ArgumentLoc, ArgumentType, Type}; + +/// Legalization action to perform on a single argument or return value. +/// +/// An argument may go through a sequence of legalization steps before it reaches the final +/// `Assign` action. +pub enum ArgAction { + /// Assign the argument to the given location. + Assign(ArgumentLoc), + + /// Split the argument into smaller parts, then call again. + /// + /// This action can split an integer type into two smaller integer arguments, or it can split a + /// SIMD vector into halves. + /// + /// Floating point scalar types can't be split. + Split, +} + +/// Common trait for assigning arguments to registers or stack locations. +/// +/// This will be implemented by individual ISAs. +pub trait ArgAssigner { + /// Pick an assignment action for function argument (or return value) `arg`. + fn assign(&mut self, arg: &ArgumentType) -> ArgAction; +} + +/// Legalize the arguments in `args` using the given argument assigner. +/// +/// This function can be used for both arguments and return values. +pub fn legalize_args(args: &mut Vec, aa: &mut AA) { + // Iterate over the arguments. + // We may need to mutate the vector in place, so don't use a normal iterator, and clone the + // argument to avoid holding a reference. + let mut argno = 0; + while let Some(arg) = args.get(argno).cloned() { + // Leave the pre-assigned arguments alone. + // We'll assume that they don't interfere with our assignments. + if arg.location.is_assigned() { + argno += 1; + continue; + } + + match aa.assign(&arg) { + // Assign argument to a location and move on to the next one. + ArgAction::Assign(loc) => { + args[argno].location = loc; + argno += 1; + } + // Split this argument into two smaller ones. Then revisit both. + ArgAction::Split => { + let new_arg = ArgumentType { value_type: split_type(arg.value_type), ..arg }; + args[argno].value_type = new_arg.value_type; + args.insert(argno + 1, new_arg); + } + } + } +} + +/// Given a value type that isn't legal, compute a replacement type half the size. +fn split_type(ty: Type) -> Type { + if ty.is_int() { + ty.half_width().expect("Integer type too small to split") + } else { + ty.half_vector().expect("Can only split integers and vectors") + } +} diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs index c785ab2ac9..c51413eb20 100644 --- a/lib/cretonne/src/isa/registers.rs +++ b/lib/cretonne/src/isa/registers.rs @@ -108,6 +108,9 @@ pub struct RegClassData { /// How many register units to allocate per register. pub width: u8, + /// The first register unit in this class. + pub first: RegUnit, + /// Bit-mask of sub-classes of this register class, including itself. /// /// Bits correspond to RC indexes. @@ -142,6 +145,12 @@ impl RegClassData { pub fn has_subclass>(&self, other: RCI) -> bool { self.subclasses & (1 << other.into().0) != 0 } + + /// Get a specific register unit in this class. + pub fn unit(&self, offset: usize) -> RegUnit { + let uoffset = offset * self.width as usize; + self.first + uoffset as RegUnit + } } /// A small reference to a register class. diff --git a/lib/cretonne/src/isa/riscv/abi.rs b/lib/cretonne/src/isa/riscv/abi.rs index 7025bf8c0e..5c9630f8f4 100644 --- a/lib/cretonne/src/isa/riscv/abi.rs +++ b/lib/cretonne/src/isa/riscv/abi.rs @@ -2,11 +2,79 @@ //! //! This module implements the RISC-V calling convention through the primary `legalize_signature()` //! entry point. +//! +//! This doesn't support the soft-float ABI at the moment. -use ir::Signature; +use abi::{ArgAction, ArgAssigner, legalize_args}; +use ir::{Signature, ArgumentType, ArgumentLoc}; +use isa::riscv::registers::{GPR, FPR}; use settings as shared_settings; -/// Legalize `sig` for RISC-V. -pub fn legalize_signature(_sig: &mut Signature, _flags: &shared_settings::Flags) { - // TODO: Actually do something. +struct Args { + pointer_bits: u16, + pointer_bytes: u32, + regs: u32, + offset: u32, +} + +impl Args { + fn new(bits: u16) -> Args { + Args { + pointer_bits: bits, + pointer_bytes: bits as u32 / 8, + regs: 0, + offset: 0, + } + } +} + +impl ArgAssigner for Args { + fn assign(&mut self, arg: &ArgumentType) -> ArgAction { + fn align(value: u32, to: u32) -> u32 { + (value + to - 1) & !(to - 1) + } + + let ty = arg.value_type; + + // Check for a legal type. + // RISC-V doesn't have SIMD at all, so break all vectors down. + if !ty.is_scalar() { + return ArgAction::Split; + } + + // Large integers and booleans are broken down to fit in a register. + if !ty.is_float() && ty.bits() > self.pointer_bits { + // Align registers and stack to a multiple of two pointers. + self.regs = align(self.regs, 2); + self.offset = align(self.offset, 2 * self.pointer_bytes); + return ArgAction::Split; + } + + if self.regs < 8 { + // Assign to a register. + let reg = if ty.is_float() { + FPR.unit(10 + self.regs as usize) + } else { + GPR.unit(10 + self.regs as usize) + }; + self.regs += 1; + ArgAction::Assign(ArgumentLoc::Reg(reg)) + } else { + // Assign a stack location. + let loc = ArgumentLoc::Stack(self.offset); + self.offset += self.pointer_bytes; + ArgAction::Assign(loc) + } + } +} + +/// Legalize `sig` for RISC-V. +pub fn legalize_signature(sig: &mut Signature, flags: &shared_settings::Flags) { + let bits = if flags.is_64bit() { 64 } else { 32 }; + + let mut args = Args::new(bits); + legalize_args(&mut sig.argument_types, &mut args); + + let mut rets = Args::new(bits); + legalize_args(&mut sig.return_types, &mut rets); } diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index 47339a39ca..2839ecfc1f 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -21,6 +21,7 @@ pub mod settings; pub mod sparse_map; pub mod verifier; +mod abi; mod constant_hash; mod context; mod legalizer; diff --git a/lib/cretonne/src/regalloc/allocatable_set.rs b/lib/cretonne/src/regalloc/allocatable_set.rs index 39058c04e4..40ef3c3636 100644 --- a/lib/cretonne/src/regalloc/allocatable_set.rs +++ b/lib/cretonne/src/regalloc/allocatable_set.rs @@ -121,6 +121,7 @@ mod tests { name: "GPR", index: 0, width: 1, + first: 28, subclasses: 0, mask: [0xf0000000, 0x0000000f, 0], }; @@ -128,6 +129,7 @@ mod tests { name: "DPR", index: 0, width: 2, + first: 28, subclasses: 0, mask: [0x50000000, 0x0000000a, 0], };