Files
wasmtime/lib/cretonne/src/isa/riscv/abi.rs
Jakob Stoklund Olesen cd99eee86b Add a TargetIsa::regclass_for_abi_type() function.
The legalize_signature() function will return ArgumentLoc::Reg arguments
that contain a register unit. However, the register also needs to be
able to associate a register class with the argument values to fully
track the used registers.

When values are defined by instructions, the register class is part for
the operand constraints for the instruction. For values defined on ABI
boundaries like function arguments and return values from a call, the
register class is provided by the new regclass_for_abi_type() function.

Provide implementations of this function in abi modules of all the
targets, even those that don't have a legalize_signature()
implementation yet.

Since we're adding abi modules to all targets, move the
legalize_signature() stubs in there and make the function mandatory in
TargetIsa. All targets will eventually need this function.
2017-04-26 10:29:18 -07:00

111 lines
3.6 KiB
Rust

//! RISC-V ABI implementation.
//!
//! 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 abi::{ArgAction, ValueConversion, ArgAssigner, legalize_args};
use ir::{Signature, Type, ArgumentType, ArgumentLoc, ArgumentExtension, ArgumentPurpose};
use isa::RegClass;
use isa::riscv::registers::{GPR, FPR};
use settings as shared_settings;
struct Args {
pointer_bits: u16,
pointer_bytes: u32,
pointer_type: Type,
regs: u32,
offset: u32,
}
impl Args {
fn new(bits: u16) -> Args {
Args {
pointer_bits: bits,
pointer_bytes: bits as u32 / 8,
pointer_type: Type::int(bits).unwrap(),
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 ValueConversion::VectorSplit.into();
}
// 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 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(),
}
}
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;
ArgumentLoc::Reg(reg).into()
} else {
// Assign a stack location.
let loc = ArgumentLoc::Stack(self.offset);
self.offset += self.pointer_bytes;
loc.into()
}
}
}
/// Legalize `sig` for RISC-V.
pub fn legalize_signature(sig: &mut Signature, flags: &shared_settings::Flags, current: bool) {
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);
if current {
let ptr = Type::int(bits).unwrap();
// Add the link register as an argument and return value.
//
// The `jalr` instruction implementing a return can technically accept the return address
// in any register, but a micro-architecture with a return address predictor will only
// recognize it as a return if the address is in `x1`.
let link = ArgumentType::special_reg(ptr, ArgumentPurpose::Link, GPR.unit(1));
sig.argument_types.push(link);
sig.return_types.push(link);
}
}
/// Get register class for a type appearing in a legalized signature.
pub fn regclass_for_abi_type(ty: Type) -> RegClass {
if ty.is_float() { FPR } else { GPR }
}