use crate::ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type};
use core::cmp::Ordering;
use std::vec::Vec;
#[derive(Clone, Copy, Debug)]
pub enum ArgAction {
Assign(ArgumentLoc),
Convert(ValueConversion),
}
impl From<ArgumentLoc> for ArgAction {
fn from(x: ArgumentLoc) -> Self {
ArgAction::Assign(x)
}
}
impl From<ValueConversion> for ArgAction {
fn from(x: ValueConversion) -> Self {
ArgAction::Convert(x)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ValueConversion {
IntSplit,
VectorSplit,
IntBits,
Sext(Type),
Uext(Type),
}
impl ValueConversion {
pub fn apply(self, ty: Type) -> Type {
match self {
ValueConversion::IntSplit => ty.half_width().expect("Integer type too small to split"),
ValueConversion::VectorSplit => ty.half_vector().expect("Not a vector"),
ValueConversion::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
ValueConversion::Sext(nty) | ValueConversion::Uext(nty) => nty,
}
}
pub fn is_split(self) -> bool {
match self {
ValueConversion::IntSplit | ValueConversion::VectorSplit => true,
_ => false,
}
}
}
pub trait ArgAssigner {
fn assign(&mut self, arg: &AbiParam) -> ArgAction;
}
pub fn legalize_args<AA: ArgAssigner>(args: &mut Vec<AbiParam>, aa: &mut AA) {
let mut argno = 0;
while let Some(arg) = args.get(argno).cloned() {
if arg.location.is_assigned() {
argno += 1;
continue;
}
match aa.assign(&arg) {
ArgAction::Assign(loc) => {
args[argno].location = loc;
argno += 1;
}
ArgAction::Convert(conv) => {
let value_type = conv.apply(arg.value_type);
let new_arg = AbiParam { value_type, ..arg };
args[argno].value_type = value_type;
if conv.is_split() {
args.insert(argno + 1, new_arg);
}
}
}
}
}
pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion {
let have_bits = have.bits();
let arg_bits = arg.value_type.bits();
match have_bits.cmp(&arg_bits) {
Ordering::Less => {
debug_assert!(
have.is_int() && arg.value_type.is_int(),
"Can only extend integer values"
);
match arg.extension {
ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type),
ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type),
_ => panic!("No argument extension specified"),
}
}
Ordering::Equal => {
debug_assert!(arg.value_type.is_int());
debug_assert!(have.is_vector(), "expected vector type, got {}", have);
ValueConversion::VectorSplit
}
Ordering::Greater => {
if have.is_vector() {
ValueConversion::VectorSplit
} else if have.is_float() {
ValueConversion::IntBits
} else {
ValueConversion::IntSplit
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::types;
use crate::ir::AbiParam;
#[test]
fn legalize() {
let mut arg = AbiParam::new(types::I32);
assert_eq!(
legalize_abi_value(types::I64X2, &arg),
ValueConversion::VectorSplit
);
assert_eq!(
legalize_abi_value(types::I64, &arg),
ValueConversion::IntSplit
);
arg.extension = ArgumentExtension::Sext;
assert_eq!(
legalize_abi_value(types::I16X4, &arg),
ValueConversion::VectorSplit
);
assert_eq!(
legalize_abi_value(types::I16.by(2).unwrap(), &arg),
ValueConversion::VectorSplit
);
assert_eq!(
legalize_abi_value(types::I16, &arg),
ValueConversion::Sext(types::I32)
);
assert_eq!(
legalize_abi_value(types::F64, &arg),
ValueConversion::IntBits
);
}
}