use itertools::Itertools;
use std::vec::Vec;
use wasmer_types::Type;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReturnSlot {
Natural(Type),
Raw(Type),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PairSlot {
F32Vector(Type, Type),
Raw(Type, Type),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReturnAbi {
Void,
Single(Type),
Pair(ReturnSlot, ReturnSlot),
PackedPair(PairSlot),
PackedFirst(PairSlot, ReturnSlot),
PackedLast(ReturnSlot, PairSlot),
PackedQuads(PairSlot, PairSlot),
Unpacked(Vec<Type>),
Sret(Vec<Type>),
}
fn pair_slot(t0: Type, t1: Type) -> PairSlot {
if t0 == Type::F32 && t1 == Type::F32 {
PairSlot::F32Vector(t0, t1)
} else {
PairSlot::Raw(t0, t1)
}
}
pub fn classify_return_type_x86_64(types: &[Type]) -> ReturnAbi {
let widths = types.iter().map(|ty| ty.bit_size(64)).collect_vec();
match (types, widths.as_slice()) {
([], []) => ReturnAbi::Void,
([value], [_]) => ReturnAbi::Single(*value),
([first, second], [32, 64] | [64, 32] | [64, 64]) => {
ReturnAbi::Pair(ReturnSlot::Natural(*first), ReturnSlot::Natural(*second))
}
([first, second], [32, 32]) => ReturnAbi::PackedPair(pair_slot(*first, *second)),
([first, second, third], [32, 32, 32 | 64]) => {
ReturnAbi::PackedFirst(pair_slot(*first, *second), ReturnSlot::Natural(*third))
}
([first, second, third], [64, 32, 32]) => {
ReturnAbi::PackedLast(ReturnSlot::Natural(*first), pair_slot(*second, *third))
}
([first, second, third, fourth], [32, 32, 32, 32]) => {
ReturnAbi::PackedQuads(pair_slot(*first, *second), pair_slot(*third, *fourth))
}
_ => ReturnAbi::Sret(types.to_vec()),
}
}
pub fn classify_return_type_aarch64(types: &[Type]) -> ReturnAbi {
let widths = types.iter().map(|ty| ty.bit_size(64)).collect_vec();
if (2..=4).contains(&types.len())
&& (types.iter().all(|ty| ty == &Type::F32) || types.iter().all(|ty| ty == &Type::F64))
{
return ReturnAbi::Unpacked(types.to_vec());
}
if let [first, second] = types
&& matches!(
first,
Type::I32 | Type::I64 | Type::F32 | Type::F64 | Type::ExceptionRef
)
&& matches!(second, Type::FuncRef | Type::ExternRef)
{
return ReturnAbi::Pair(ReturnSlot::Raw(*first), ReturnSlot::Raw(*second));
}
match (types, widths.as_slice()) {
([], []) => ReturnAbi::Void,
([value], [_]) => ReturnAbi::Single(*value),
([first, second], [32, 64] | [64, 32] | [64, 64]) => {
ReturnAbi::Pair(ReturnSlot::Raw(*first), ReturnSlot::Raw(*second))
}
([first, second], [32, 32]) => ReturnAbi::PackedPair(PairSlot::Raw(*first, *second)),
([first, second, third], [32, 32, 32 | 64]) => {
ReturnAbi::PackedFirst(PairSlot::Raw(*first, *second), ReturnSlot::Raw(*third))
}
([first, second, third], [64, 32, 32]) => {
ReturnAbi::PackedLast(ReturnSlot::Raw(*first), PairSlot::Raw(*second, *third))
}
([first, second, third, fourth], [32, 32, 32, 32]) => ReturnAbi::PackedQuads(
PairSlot::Raw(*first, *second),
PairSlot::Raw(*third, *fourth),
),
_ => ReturnAbi::Sret(types.to_vec()),
}
}
pub fn classify_return_type_loongarch64(types: &[Type]) -> ReturnAbi {
classify_return_type_riscv64(types)
}
pub fn classify_return_type_riscv64(types: &[Type]) -> ReturnAbi {
let widths = types.iter().map(|ty| ty.bit_size(64)).collect_vec();
if let [first, second] = types {
let is_float = |ty| matches!(ty, Type::F32 | Type::F64);
let eligible = |ty, width| is_float(ty) || width <= 64;
if (is_float(*first) || is_float(*second))
&& eligible(*first, widths[0])
&& eligible(*second, widths[1])
{
return ReturnAbi::Pair(ReturnSlot::Natural(*first), ReturnSlot::Natural(*second));
}
}
match (types, widths.as_slice()) {
([], []) => ReturnAbi::Void,
([value], [_]) => ReturnAbi::Single(*value),
([first, second], [32, 64] | [64, 32] | [64, 64]) => {
ReturnAbi::Pair(ReturnSlot::Raw(*first), ReturnSlot::Raw(*second))
}
([first, second], [32, 32]) => ReturnAbi::PackedPair(PairSlot::Raw(*first, *second)),
([first, second, third], [32, 32, 32 | 64]) => {
ReturnAbi::PackedFirst(PairSlot::Raw(*first, *second), ReturnSlot::Raw(*third))
}
([first, second, third], [64, 32, 32]) => {
ReturnAbi::PackedLast(ReturnSlot::Raw(*first), PairSlot::Raw(*second, *third))
}
([first, second, third, fourth], [32, 32, 32, 32]) => ReturnAbi::PackedQuads(
PairSlot::Raw(*first, *second),
PairSlot::Raw(*third, *fourth),
),
_ => ReturnAbi::Sret(types.to_vec()),
}
}
#[cfg(test)]
mod tests {
use super::classify_return_type_x86_64;
use crate::abi::{PairSlot, ReturnAbi, ReturnSlot};
use wasmer_types::Type;
#[test]
fn classify_x86_64_return_type_abi() {
assert_eq!(classify_return_type_x86_64(&[]), ReturnAbi::Void);
assert_eq!(
classify_return_type_x86_64(&[Type::I64]),
ReturnAbi::Single(Type::I64)
);
assert_eq!(
classify_return_type_x86_64(&[Type::I32, Type::F64]),
ReturnAbi::Pair(
ReturnSlot::Natural(Type::I32),
ReturnSlot::Natural(Type::F64)
)
);
assert_eq!(
classify_return_type_x86_64(&[Type::I32, Type::F32]),
ReturnAbi::PackedPair(PairSlot::Raw(Type::I32, Type::F32))
);
assert_eq!(
classify_return_type_x86_64(&[Type::F32, Type::F32, Type::I64]),
ReturnAbi::PackedFirst(
PairSlot::F32Vector(Type::F32, Type::F32),
ReturnSlot::Natural(Type::I64)
)
);
assert_eq!(
classify_return_type_x86_64(&[Type::F64, Type::I32, Type::F32]),
ReturnAbi::PackedLast(
ReturnSlot::Natural(Type::F64),
PairSlot::Raw(Type::I32, Type::F32)
)
);
assert_eq!(
classify_return_type_x86_64(&[Type::I32, Type::F32, Type::F32, Type::I32]),
ReturnAbi::PackedQuads(
PairSlot::Raw(Type::I32, Type::F32),
PairSlot::Raw(Type::F32, Type::I32)
)
);
assert_eq!(
classify_return_type_x86_64(&[Type::V128, Type::I32]),
ReturnAbi::Sret(vec![Type::V128, Type::I32])
);
}
}