use core::default::Default;
use core::fmt::{self, Debug, Display, Formatter};
use cranelift_codegen_shared::constants;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use target_lexicon::{PointerWidth, Triple};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct Type(u16);
pub const INVALID: Type = Type(0);
include!(concat!(env!("OUT_DIR"), "/types.rs"));
impl Type {
pub fn lane_type(self) -> Self {
if self.0 < constants::VECTOR_BASE {
self
} else {
Self(constants::LANE_BASE | (self.0 & 0x0f))
}
}
pub fn lane_of(self) -> Self {
self.lane_type()
}
pub fn log2_lane_bits(self) -> u32 {
match self.lane_type() {
B1 => 0,
B8 | I8 => 3,
B16 | I16 => 4,
B32 | I32 | F32 | R32 => 5,
B64 | I64 | F64 | R64 => 6,
B128 | I128 => 7,
_ => 0,
}
}
pub fn lane_bits(self) -> u32 {
match self.lane_type() {
B1 => 1,
B8 | I8 => 8,
B16 | I16 => 16,
B32 | I32 | F32 | R32 => 32,
B64 | I64 | F64 | R64 => 64,
B128 | I128 => 128,
_ => 0,
}
}
pub fn bounds(self, signed: bool) -> (u128, u128) {
if signed {
match self.lane_type() {
I8 => (i8::MIN as u128, i8::MAX as u128),
I16 => (i16::MIN as u128, i16::MAX as u128),
I32 => (i32::MIN as u128, i32::MAX as u128),
I64 => (i64::MIN as u128, i64::MAX as u128),
I128 => (i128::MIN as u128, i128::MAX as u128),
_ => unimplemented!(),
}
} else {
match self.lane_type() {
I8 => (u8::MIN as u128, u8::MAX as u128),
I16 => (u16::MIN as u128, u16::MAX as u128),
I32 => (u32::MIN as u128, u32::MAX as u128),
I64 => (u64::MIN as u128, u64::MAX as u128),
I128 => (u128::MIN, u128::MAX),
_ => unimplemented!(),
}
}
}
pub fn int(bits: u16) -> Option<Self> {
match bits {
8 => Some(I8),
16 => Some(I16),
32 => Some(I32),
64 => Some(I64),
128 => Some(I128),
_ => None,
}
}
pub fn int_with_byte_size(bytes: u16) -> Option<Self> {
Self::int(bytes.checked_mul(8)?)
}
fn replace_lanes(self, lane: Self) -> Self {
debug_assert!(lane.is_lane() && !self.is_special());
Self((lane.0 & 0x0f) | (self.0 & 0xf0))
}
pub fn as_bool_pedantic(self) -> Self {
self.replace_lanes(match self.lane_type() {
B8 | I8 => B8,
B16 | I16 => B16,
B32 | I32 | F32 => B32,
B64 | I64 | F64 => B64,
R32 | R64 => panic!("Reference types should not convert to bool"),
B128 | I128 => B128,
_ => B1,
})
}
pub fn as_bool(self) -> Self {
if !self.is_vector() {
B1
} else {
self.as_bool_pedantic()
}
}
pub fn as_int(self) -> Self {
self.replace_lanes(match self.lane_type() {
I8 | B1 | B8 => I8,
I16 | B16 => I16,
I32 | B32 | F32 => I32,
I64 | B64 | F64 => I64,
I128 | B128 => I128,
_ => unimplemented!(),
})
}
pub fn half_width(self) -> Option<Self> {
Some(self.replace_lanes(match self.lane_type() {
I16 => I8,
I32 => I16,
I64 => I32,
I128 => I64,
F64 => F32,
B16 => B8,
B32 => B16,
B64 => B32,
B128 => B64,
_ => return None,
}))
}
pub fn double_width(self) -> Option<Self> {
Some(self.replace_lanes(match self.lane_type() {
I8 => I16,
I16 => I32,
I32 => I64,
I64 => I128,
F32 => F64,
B8 => B16,
B16 => B32,
B32 => B64,
B64 => B128,
_ => return None,
}))
}
pub fn is_invalid(self) -> bool {
self == INVALID
}
pub fn is_special(self) -> bool {
self.0 < constants::LANE_BASE
}
pub fn is_lane(self) -> bool {
constants::LANE_BASE <= self.0 && self.0 < constants::VECTOR_BASE
}
pub fn is_vector(self) -> bool {
self.0 >= constants::VECTOR_BASE && !self.is_dynamic_vector()
}
pub fn is_dynamic_vector(self) -> bool {
self.0 >= constants::DYNAMIC_VECTOR_BASE
}
pub fn is_bool(self) -> bool {
match self {
B1 | B8 | B16 | B32 | B64 | B128 => true,
_ => false,
}
}
pub fn is_bool_vector(self) -> bool {
self.is_vector() && self.lane_type().is_bool()
}
pub fn is_int(self) -> bool {
match self {
I8 | I16 | I32 | I64 | I128 => true,
_ => false,
}
}
pub fn is_float(self) -> bool {
match self {
F32 | F64 => true,
_ => false,
}
}
pub fn is_flags(self) -> bool {
match self {
IFLAGS | FFLAGS => true,
_ => false,
}
}
pub fn is_ref(self) -> bool {
match self {
R32 | R64 => true,
_ => false,
}
}
pub fn log2_lane_count(self) -> u32 {
if self.is_dynamic_vector() {
0
} else {
(self.0.saturating_sub(constants::LANE_BASE) >> 4) as u32
}
}
pub fn log2_min_lane_count(self) -> u32 {
if self.is_dynamic_vector() {
(self
.0
.saturating_sub(constants::VECTOR_BASE + constants::LANE_BASE)
>> 4) as u32
} else {
self.log2_lane_count()
}
}
pub fn lane_count(self) -> u32 {
if self.is_dynamic_vector() {
0
} else {
1 << self.log2_lane_count()
}
}
pub fn bits(self) -> u32 {
if self.is_dynamic_vector() {
0
} else {
self.lane_bits() * self.lane_count()
}
}
pub fn min_lane_count(self) -> u32 {
if self.is_dynamic_vector() {
1 << self.log2_min_lane_count()
} else {
1 << self.log2_lane_count()
}
}
pub fn min_bits(self) -> u32 {
if self.is_dynamic_vector() {
self.lane_bits() * self.min_lane_count()
} else {
self.bits()
}
}
pub fn bytes(self) -> u32 {
(self.bits() + 7) / 8
}
pub fn by(self, n: u32) -> Option<Self> {
if self.is_dynamic_vector() {
return None;
}
if self.lane_bits() == 0 || !n.is_power_of_two() {
return None;
}
let log2_lanes: u32 = n.trailing_zeros();
let new_type = u32::from(self.0) + (log2_lanes << 4);
if new_type < constants::DYNAMIC_VECTOR_BASE as u32
&& (new_type as u16) < constants::DYNAMIC_VECTOR_BASE
{
Some(Self(new_type as u16))
} else {
None
}
}
pub fn vector_to_dynamic(self) -> Option<Self> {
assert!(self.is_vector());
if self.bits() > 256 {
return None;
}
let new_ty = self.0 + constants::VECTOR_BASE;
let ty = Some(Self(new_ty));
assert!(ty.unwrap().is_dynamic_vector());
return ty;
}
pub fn dynamic_to_vector(self) -> Option<Self> {
assert!(self.is_dynamic_vector());
Some(Self(self.0 - constants::VECTOR_BASE))
}
pub fn half_vector(self) -> Option<Self> {
if self.is_vector() && !self.is_dynamic_vector() {
Some(Self(self.0 - 0x10))
} else {
None
}
}
pub fn split_lanes(self) -> Option<Self> {
match self.half_width() {
Some(half_width) => half_width.by(2),
None => None,
}
}
pub fn merge_lanes(self) -> Option<Self> {
match self.double_width() {
Some(double_width) => double_width.half_vector(),
None => None,
}
}
pub fn index(self) -> usize {
usize::from(self.0)
}
pub fn wider_or_equal(self, other: Self) -> bool {
self.lane_count() == other.lane_count() && self.lane_bits() >= other.lane_bits()
}
pub fn triple_pointer_type(triple: &Triple) -> Self {
match triple.pointer_width() {
Ok(PointerWidth::U16) => I16,
Ok(PointerWidth::U32) => I32,
Ok(PointerWidth::U64) => I64,
Err(()) => panic!("unable to determine architecture pointer width"),
}
}
pub fn coerce_bools_to_ints(self) -> Self {
let is_scalar_bool = self.is_bool();
let is_vector_bool = self.is_vector() && self.lane_type().is_bool();
if is_scalar_bool || is_vector_bool {
self.as_int()
} else {
self
}
}
pub(crate) fn repr(self) -> u16 {
self.0
}
pub(crate) fn from_repr(bits: u16) -> Type {
Type(bits)
}
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.is_bool() {
write!(f, "b{}", self.lane_bits())
} else if self.is_int() {
write!(f, "i{}", self.lane_bits())
} else if self.is_float() {
write!(f, "f{}", self.lane_bits())
} else if self.is_vector() {
write!(f, "{}x{}", self.lane_type(), self.lane_count())
} else if self.is_dynamic_vector() {
write!(f, "{:?}x{}xN", self.lane_type(), self.min_lane_count())
} else if self.is_ref() {
write!(f, "r{}", self.lane_bits())
} else {
f.write_str(match *self {
IFLAGS => "iflags",
FFLAGS => "fflags",
INVALID => panic!("INVALID encountered"),
_ => panic!("Unknown Type(0x{:x})", self.0),
})
}
}
}
impl Debug for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.is_bool() {
write!(f, "types::B{}", self.lane_bits())
} else if self.is_int() {
write!(f, "types::I{}", self.lane_bits())
} else if self.is_float() {
write!(f, "types::F{}", self.lane_bits())
} else if self.is_vector() {
write!(f, "{:?}X{}", self.lane_type(), self.lane_count())
} else if self.is_dynamic_vector() {
write!(f, "{:?}X{}XN", self.lane_type(), self.min_lane_count())
} else if self.is_ref() {
write!(f, "types::R{}", self.lane_bits())
} else {
match *self {
INVALID => write!(f, "types::INVALID"),
IFLAGS => write!(f, "types::IFLAGS"),
FFLAGS => write!(f, "types::FFLAGS"),
_ => write!(f, "Type(0x{:x})", self.0),
}
}
}
}
impl Default for Type {
fn default() -> Self {
INVALID
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn basic_scalars() {
assert_eq!(INVALID, INVALID.lane_type());
assert_eq!(0, INVALID.bits());
assert_eq!(IFLAGS, IFLAGS.lane_type());
assert_eq!(0, IFLAGS.bits());
assert_eq!(FFLAGS, FFLAGS.lane_type());
assert_eq!(0, FFLAGS.bits());
assert_eq!(B1, B1.lane_type());
assert_eq!(B8, B8.lane_type());
assert_eq!(B16, B16.lane_type());
assert_eq!(B32, B32.lane_type());
assert_eq!(B64, B64.lane_type());
assert_eq!(B128, B128.lane_type());
assert_eq!(I8, I8.lane_type());
assert_eq!(I16, I16.lane_type());
assert_eq!(I32, I32.lane_type());
assert_eq!(I64, I64.lane_type());
assert_eq!(I128, I128.lane_type());
assert_eq!(F32, F32.lane_type());
assert_eq!(F64, F64.lane_type());
assert_eq!(B1, B1.by(8).unwrap().lane_type());
assert_eq!(I32, I32X4.lane_type());
assert_eq!(F64, F64X2.lane_type());
assert_eq!(R32, R32.lane_type());
assert_eq!(R64, R64.lane_type());
assert_eq!(INVALID.lane_bits(), 0);
assert_eq!(IFLAGS.lane_bits(), 0);
assert_eq!(FFLAGS.lane_bits(), 0);
assert_eq!(B1.lane_bits(), 1);
assert_eq!(B8.lane_bits(), 8);
assert_eq!(B16.lane_bits(), 16);
assert_eq!(B32.lane_bits(), 32);
assert_eq!(B64.lane_bits(), 64);
assert_eq!(B128.lane_bits(), 128);
assert_eq!(I8.lane_bits(), 8);
assert_eq!(I16.lane_bits(), 16);
assert_eq!(I32.lane_bits(), 32);
assert_eq!(I64.lane_bits(), 64);
assert_eq!(I128.lane_bits(), 128);
assert_eq!(F32.lane_bits(), 32);
assert_eq!(F64.lane_bits(), 64);
assert_eq!(R32.lane_bits(), 32);
assert_eq!(R64.lane_bits(), 64);
}
#[test]
fn typevar_functions() {
assert_eq!(INVALID.half_width(), None);
assert_eq!(INVALID.half_width(), None);
assert_eq!(FFLAGS.half_width(), None);
assert_eq!(B1.half_width(), None);
assert_eq!(B8.half_width(), None);
assert_eq!(B16.half_width(), Some(B8));
assert_eq!(B32.half_width(), Some(B16));
assert_eq!(B64.half_width(), Some(B32));
assert_eq!(B128.half_width(), Some(B64));
assert_eq!(I8.half_width(), None);
assert_eq!(I16.half_width(), Some(I8));
assert_eq!(I32.half_width(), Some(I16));
assert_eq!(I32X4.half_width(), Some(I16X4));
assert_eq!(I64.half_width(), Some(I32));
assert_eq!(I128.half_width(), Some(I64));
assert_eq!(F32.half_width(), None);
assert_eq!(F64.half_width(), Some(F32));
assert_eq!(INVALID.double_width(), None);
assert_eq!(IFLAGS.double_width(), None);
assert_eq!(FFLAGS.double_width(), None);
assert_eq!(B1.double_width(), None);
assert_eq!(B8.double_width(), Some(B16));
assert_eq!(B16.double_width(), Some(B32));
assert_eq!(B32.double_width(), Some(B64));
assert_eq!(B64.double_width(), Some(B128));
assert_eq!(B128.double_width(), None);
assert_eq!(I8.double_width(), Some(I16));
assert_eq!(I16.double_width(), Some(I32));
assert_eq!(I32.double_width(), Some(I64));
assert_eq!(I32X4.double_width(), Some(I64X4));
assert_eq!(I64.double_width(), Some(I128));
assert_eq!(I128.double_width(), None);
assert_eq!(F32.double_width(), Some(F64));
assert_eq!(F64.double_width(), None);
}
#[test]
fn vectors() {
let big = F64.by(256).unwrap();
assert_eq!(big.lane_bits(), 64);
assert_eq!(big.lane_count(), 256);
assert_eq!(big.bits(), 64 * 256);
assert_eq!(big.half_vector().unwrap().to_string(), "f64x128");
assert_eq!(B1.by(2).unwrap().half_vector().unwrap().to_string(), "b1");
assert_eq!(I32.half_vector(), None);
assert_eq!(INVALID.half_vector(), None);
assert_eq!(I32.by(4), Some(I32X4));
assert_eq!(F64.by(8), Some(F64X8));
}
#[test]
fn dynamic_vectors() {
assert_eq!(I8X16XN.is_dynamic_vector(), true);
assert_eq!(B16X4XN.is_dynamic_vector(), true);
assert_eq!(F32X8XN.is_dynamic_vector(), true);
assert_eq!(F64X4XN.is_dynamic_vector(), true);
assert_eq!(I128X2XN.is_dynamic_vector(), true);
assert_eq!(I16X8XN.lane_count(), 0);
assert_eq!(I16X8XN.min_lane_count(), 8);
assert_eq!(B32X2XN.bits(), 0);
assert_eq!(B32X2XN.min_bits(), 64);
assert_eq!(F64X4XN.half_vector(), None);
assert_eq!(I8X8XN.by(2), None);
assert_eq!(B8.by(8).unwrap().vector_to_dynamic(), Some(B8X8XN));
assert_eq!(I8.by(16).unwrap().vector_to_dynamic(), Some(I8X16XN));
assert_eq!(I16.by(8).unwrap().vector_to_dynamic(), Some(I16X8XN));
assert_eq!(B16.by(16).unwrap().vector_to_dynamic(), Some(B16X16XN));
assert_eq!(B32.by(2).unwrap().vector_to_dynamic(), Some(B32X2XN));
assert_eq!(B32.by(8).unwrap().vector_to_dynamic(), Some(B32X8XN));
assert_eq!(I32.by(4).unwrap().vector_to_dynamic(), Some(I32X4XN));
assert_eq!(F32.by(4).unwrap().vector_to_dynamic(), Some(F32X4XN));
assert_eq!(F64.by(2).unwrap().vector_to_dynamic(), Some(F64X2XN));
assert_eq!(I128.by(2).unwrap().vector_to_dynamic(), Some(I128X2XN));
assert_eq!(I128X2XN.dynamic_to_vector(), Some(I128X2));
assert_eq!(B64X2XN.dynamic_to_vector(), Some(B64X2));
assert_eq!(F32X4XN.dynamic_to_vector(), Some(F32X4));
assert_eq!(F64X4XN.dynamic_to_vector(), Some(F64X4));
assert_eq!(I32X2XN.dynamic_to_vector(), Some(I32X2));
assert_eq!(I32X8XN.dynamic_to_vector(), Some(I32X8));
assert_eq!(I16X16XN.dynamic_to_vector(), Some(I16X16));
assert_eq!(I8X32XN.dynamic_to_vector(), Some(I8X32));
assert_eq!(I8X64.vector_to_dynamic(), None);
assert_eq!(B16X32.vector_to_dynamic(), None);
assert_eq!(F32X16.vector_to_dynamic(), None);
assert_eq!(I64X8.vector_to_dynamic(), None);
assert_eq!(I128X4.vector_to_dynamic(), None);
}
#[test]
fn format_scalars() {
assert_eq!(IFLAGS.to_string(), "iflags");
assert_eq!(FFLAGS.to_string(), "fflags");
assert_eq!(B1.to_string(), "b1");
assert_eq!(B8.to_string(), "b8");
assert_eq!(B16.to_string(), "b16");
assert_eq!(B32.to_string(), "b32");
assert_eq!(B64.to_string(), "b64");
assert_eq!(B128.to_string(), "b128");
assert_eq!(I8.to_string(), "i8");
assert_eq!(I16.to_string(), "i16");
assert_eq!(I32.to_string(), "i32");
assert_eq!(I64.to_string(), "i64");
assert_eq!(I128.to_string(), "i128");
assert_eq!(F32.to_string(), "f32");
assert_eq!(F64.to_string(), "f64");
assert_eq!(R32.to_string(), "r32");
assert_eq!(R64.to_string(), "r64");
}
#[test]
fn format_vectors() {
assert_eq!(B1.by(8).unwrap().to_string(), "b1x8");
assert_eq!(B8.by(1).unwrap().to_string(), "b8");
assert_eq!(B16.by(256).unwrap().to_string(), "b16x256");
assert_eq!(B32.by(4).unwrap().by(2).unwrap().to_string(), "b32x8");
assert_eq!(B64.by(8).unwrap().to_string(), "b64x8");
assert_eq!(I8.by(64).unwrap().to_string(), "i8x64");
assert_eq!(F64.by(2).unwrap().to_string(), "f64x2");
assert_eq!(I8.by(3), None);
assert_eq!(I8.by(512), None);
assert_eq!(INVALID.by(4), None);
}
#[test]
fn as_bool() {
assert_eq!(I32X4.as_bool(), B32X4);
assert_eq!(I32.as_bool(), B1);
assert_eq!(I32X4.as_bool_pedantic(), B32X4);
assert_eq!(I32.as_bool_pedantic(), B32);
}
#[test]
fn as_int() {
assert_eq!(B32X4.as_int(), I32X4);
assert_eq!(B8X8.as_int(), I8X8);
assert_eq!(B1.as_int(), I8);
assert_eq!(B8.as_int(), I8);
assert_eq!(B128.as_int(), I128);
}
#[test]
fn int_from_size() {
assert_eq!(Type::int(0), None);
assert_eq!(Type::int(8), Some(I8));
assert_eq!(Type::int(33), None);
assert_eq!(Type::int(64), Some(I64));
assert_eq!(Type::int_with_byte_size(0), None);
assert_eq!(Type::int_with_byte_size(2), Some(I16));
assert_eq!(Type::int_with_byte_size(6), None);
assert_eq!(Type::int_with_byte_size(16), Some(I128));
let evil = 0xE001_u16;
assert_eq!(evil.wrapping_mul(8), 8, "check the constant is correct");
assert_eq!(Type::int_with_byte_size(evil), None);
}
}