Implement legalize_signature for RISC-V.
Add an abi module with code that is probably useful to all ISAs when implementing this function. Add a unit() method to RegClassData which can be used to index the register units in a class.
This commit is contained in:
23
filetests/isa/riscv/abi.cton
Normal file
23
filetests/isa/riscv/abi.cton
Normal file
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
72
lib/cretonne/src/abi.rs
Normal file
72
lib/cretonne/src/abi.rs
Normal file
@@ -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<AA: ArgAssigner>(args: &mut Vec<ArgumentType>, 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")
|
||||
}
|
||||
}
|
||||
@@ -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<RCI: Into<RegClassIndex>>(&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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ pub mod settings;
|
||||
pub mod sparse_map;
|
||||
pub mod verifier;
|
||||
|
||||
mod abi;
|
||||
mod constant_hash;
|
||||
mod context;
|
||||
mod legalizer;
|
||||
|
||||
@@ -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],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user