From 146e0bd2f54c77a7f54bf2504295e87a7839d1f7 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 11 Mar 2019 19:26:32 +0100 Subject: [PATCH] [meta] Port Typevar to the Rust crate; --- cranelift/codegen/meta/src/cdsl/mod.rs | 1 + cranelift/codegen/meta/src/cdsl/types.rs | 54 +- cranelift/codegen/meta/src/cdsl/typevar.rs | 738 +++++++++++++++++++++ 3 files changed, 788 insertions(+), 5 deletions(-) create mode 100644 cranelift/codegen/meta/src/cdsl/typevar.rs diff --git a/cranelift/codegen/meta/src/cdsl/mod.rs b/cranelift/codegen/meta/src/cdsl/mod.rs index 87c615d70e..dd3a7ec859 100644 --- a/cranelift/codegen/meta/src/cdsl/mod.rs +++ b/cranelift/codegen/meta/src/cdsl/mod.rs @@ -7,6 +7,7 @@ pub mod isa; pub mod regs; pub mod settings; pub mod types; +pub mod typevar; /// A macro that converts boolean settings into predicates to look more natural. #[macro_export] diff --git a/cranelift/codegen/meta/src/cdsl/types.rs b/cranelift/codegen/meta/src/cdsl/types.rs index 898c048b8b..643ecdc4b6 100644 --- a/cranelift/codegen/meta/src/cdsl/types.rs +++ b/cranelift/codegen/meta/src/cdsl/types.rs @@ -27,7 +27,7 @@ static _RUST_NAME_PREFIX: &'static str = "ir::types::"; /// /// All SSA values have a type that is described by an instance of `ValueType` /// or one of its subclasses. -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum ValueType { BV(BVType), Lane(LaneType), @@ -90,7 +90,7 @@ impl ValueType { } /// Return the name of this type for generated Rust source files. - pub fn _rust_name(&self) -> String { + pub fn rust_name(&self) -> String { format!("{}{}", _RUST_NAME_PREFIX, self.to_string().to_uppercase()) } @@ -205,6 +205,43 @@ impl LaneType { LaneType::FloatType(shared_types::Float::F64) => 10, } } + + pub fn bool_from_bits(num_bits: u16) -> LaneType { + LaneType::BoolType(match num_bits { + 1 => shared_types::Bool::B1, + 8 => shared_types::Bool::B8, + 16 => shared_types::Bool::B16, + 32 => shared_types::Bool::B32, + 64 => shared_types::Bool::B64, + _ => unreachable!("unxpected num bits for bool"), + }) + } + + pub fn int_from_bits(num_bits: u16) -> LaneType { + LaneType::IntType(match num_bits { + 8 => shared_types::Int::I8, + 16 => shared_types::Int::I16, + 32 => shared_types::Int::I32, + 64 => shared_types::Int::I64, + _ => unreachable!("unxpected num bits for int"), + }) + } + + pub fn float_from_bits(num_bits: u16) -> LaneType { + LaneType::FloatType(match num_bits { + 32 => shared_types::Float::F32, + 64 => shared_types::Float::F64, + _ => unreachable!("unxpected num bits for float"), + }) + } + + pub fn by(&self, lanes: u16) -> ValueType { + if lanes == 1 { + (*self).into() + } else { + ValueType::Vector(VectorType::new(*self, lanes.into())) + } + } } impl fmt::Display for LaneType { @@ -290,6 +327,7 @@ impl Iterator for LaneTypeIterator { /// /// A vector type has a lane type which is an instance of `LaneType`, /// and a positive number of lanes. +#[derive(Clone)] pub struct VectorType { base: LaneType, lanes: u64, @@ -320,6 +358,11 @@ impl VectorType { self.lanes } + /// Return the lane type. + pub fn lane_type(&self) -> LaneType { + self.base + } + /// Find the unique number associated with this vector type. /// /// Vector types are encoded with the lane type in the low 4 bits and @@ -350,14 +393,15 @@ impl fmt::Debug for VectorType { } /// A flat bitvector type. Used for semantics description only. +#[derive(Clone)] pub struct BVType { bits: u64, } impl BVType { /// Initialize a new bitvector type with `n` bits. - pub fn _new(bits: u64) -> Self { - Self { bits } + pub fn new(bits: u16) -> Self { + Self { bits: bits.into() } } /// Return a string containing the documentation comment for this bitvector type. @@ -386,7 +430,7 @@ impl fmt::Debug for BVType { /// A concrete scalar type that is neither a vector nor a lane type. /// /// Special types cannot be used to form vectors. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum SpecialType { Flag(shared_types::Flag), } diff --git a/cranelift/codegen/meta/src/cdsl/typevar.rs b/cranelift/codegen/meta/src/cdsl/typevar.rs new file mode 100644 index 0000000000..4db5c96e2a --- /dev/null +++ b/cranelift/codegen/meta/src/cdsl/typevar.rs @@ -0,0 +1,738 @@ +use std::collections::BTreeSet; +use std::iter::FromIterator; +use std::ops; +use std::rc::Rc; + +use crate::cdsl::types::{BVType, LaneType, SpecialType, ValueType}; + +const MAX_LANES: u16 = 256; +const MAX_BITS: u16 = 64; +const MAX_BITVEC: u16 = MAX_BITS * MAX_LANES; + +/// Type variables can be used in place of concrete types when defining +/// instructions. This makes the instructions *polymorphic*. +/// +/// A type variable is restricted to vary over a subset of the value types. +/// This subset is specified by a set of flags that control the permitted base +/// types and whether the type variable can assume scalar or vector types, or +/// both. +#[derive(Debug)] +pub struct TypeVarContent { + /// Short name of type variable used in instruction descriptions. + pub name: String, + + /// Documentation string. + pub doc: String, + + /// Type set associated to the type variable. + pub type_set: Rc, + + pub base: Option, +} + +#[derive(Clone, Debug)] +pub struct TypeVar { + content: Rc, +} + +impl TypeVar { + fn get_typeset(&self) -> Rc { + match &self.content.base { + Some(base) => Rc::new(base.type_var.get_typeset().image(base.derived_func)), + None => self.content.type_set.clone(), + } + } + + /// If the associated typeset has a single type return it. Otherwise return None. + pub fn singleton_type(&self) -> Option { + let type_set = self.get_typeset(); + if type_set.size() == 1 { + Some(type_set.get_singleton()) + } else { + None + } + } + + /// Get the free type variable controlling this one. + pub fn free_typevar(&self) -> Option { + match &self.content.base { + Some(base) => base.type_var.free_typevar(), + None => { + match self.singleton_type() { + // A singleton type isn't a proper free variable. + Some(_) => None, + None => Some(self.clone()), + } + } + } + } + + /// Create a type variable that is a function of another. + fn derived(&self, derived_func: DerivedFunc) -> TypeVar { + let ts = self.content.type_set.clone(); + + // Safety checks to avoid over/underflows. + assert!(ts.specials.len() == 0, "can't derive from special types"); + match derived_func { + DerivedFunc::HalfWidth => { + assert!( + ts.ints.len() == 0 || *ts.ints.iter().min().unwrap() > 8, + "can't halve all integer types" + ); + assert!( + ts.floats.len() == 0 || *ts.floats.iter().min().unwrap() > 32, + "can't halve all float types" + ); + assert!( + ts.bools.len() == 0 || *ts.bools.iter().min().unwrap() > 8, + "can't halve all boolean types" + ); + } + DerivedFunc::DoubleWidth => { + assert!( + ts.ints.len() == 0 || *ts.ints.iter().max().unwrap() < MAX_BITS, + "can't double all integer types" + ); + assert!( + ts.floats.len() == 0 || *ts.floats.iter().max().unwrap() < MAX_BITS, + "can't double all float types" + ); + assert!( + ts.bools.len() == 0 || *ts.bools.iter().max().unwrap() < MAX_BITS, + "can't double all boolean types" + ); + } + DerivedFunc::HalfVector => { + assert!( + *ts.lanes.iter().min().unwrap() > 1, + "can't halve a scalar type" + ); + } + DerivedFunc::DoubleVector => { + assert!( + *ts.lanes.iter().max().unwrap() < MAX_LANES, + "can't double 256 lanes" + ); + } + DerivedFunc::LaneOf | DerivedFunc::ToBitVec | DerivedFunc::AsBool => { + /* no particular assertions */ + } + } + + return TypeVar { + content: Rc::new(TypeVarContent { + name: "".into(), // XXX Python passes None to these two fields + doc: "".into(), + type_set: ts, + base: Some(TypeVarParent { + type_var: self.clone(), + derived_func, + }), + }), + }; + } + + pub fn lane_of(&self) -> TypeVar { + return self.derived(DerivedFunc::LaneOf); + } + pub fn as_bool(&self) -> TypeVar { + return self.derived(DerivedFunc::AsBool); + } + pub fn half_width(&self) -> TypeVar { + return self.derived(DerivedFunc::HalfWidth); + } + pub fn double_width(&self) -> TypeVar { + return self.derived(DerivedFunc::DoubleWidth); + } + pub fn half_vector(&self) -> TypeVar { + return self.derived(DerivedFunc::HalfVector); + } + pub fn double_vector(&self) -> TypeVar { + return self.derived(DerivedFunc::DoubleVector); + } + pub fn to_bitvec(&self) -> TypeVar { + return self.derived(DerivedFunc::ToBitVec); + } +} + +impl Into for &TypeVar { + fn into(self) -> TypeVar { + self.clone() + } +} +impl Into for ValueType { + fn into(self) -> TypeVar { + TypeVarBuilder::singleton(self) + } +} + +impl PartialEq for TypeVar { + fn eq(&self, other: &TypeVar) -> bool { + match (&self.content.base, &other.content.base) { + (Some(base1), Some(base2)) => base1.type_var.eq(&base2.type_var), + (None, None) => Rc::ptr_eq(&self.content, &other.content), + _ => false, + } + } +} + +impl ops::Deref for TypeVar { + type Target = TypeVarContent; + fn deref(&self) -> &Self::Target { + &*self.content + } +} + +#[derive(Clone, Copy, Debug)] +pub enum DerivedFunc { + LaneOf, + AsBool, + HalfWidth, + DoubleWidth, + HalfVector, + DoubleVector, + ToBitVec, +} + +impl DerivedFunc { + pub fn name(&self) -> &'static str { + match self { + DerivedFunc::LaneOf => "lane_of", + DerivedFunc::AsBool => "as_bool", + DerivedFunc::HalfWidth => "half_width", + DerivedFunc::DoubleWidth => "double_width", + DerivedFunc::HalfVector => "half_vector", + DerivedFunc::DoubleVector => "double_vector", + DerivedFunc::ToBitVec => "to_bitvec", + } + } +} + +#[derive(Debug)] +pub struct TypeVarParent { + pub type_var: TypeVar, + pub derived_func: DerivedFunc, +} + +/// A set of types. +/// +/// We don't allow arbitrary subsets of types, but use a parametrized approach +/// instead. +/// +/// Objects of this class can be used as dictionary keys. +/// +/// Parametrized type sets are specified in terms of ranges: +/// - The permitted range of vector lanes, where 1 indicates a scalar type. +/// - The permitted range of integer types. +/// - The permitted range of floating point types, and +/// - The permitted range of boolean types. +/// +/// The ranges are inclusive from smallest bit-width to largest bit-width. +/// +/// Finally, a type set can contain special types (derived from `SpecialType`) +/// which can't appear as lane types. + +type RangeBound = u16; +type Range = ops::Range; +type NumSet = BTreeSet; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TypeSet { + pub lanes: NumSet, + pub ints: NumSet, + pub floats: NumSet, + pub bools: NumSet, + pub bitvecs: NumSet, + pub specials: Vec, +} + +impl TypeSet { + fn new( + lanes: NumSet, + ints: NumSet, + floats: NumSet, + bools: NumSet, + bitvecs: NumSet, + specials: Vec, + ) -> Self { + Self { + lanes, + ints, + floats, + bools, + bitvecs, + specials, + } + } + + /// Return the number of concrete types represented by this typeset. + fn size(&self) -> usize { + self.lanes.len() + * (self.ints.len() + self.floats.len() + self.bools.len() + self.bitvecs.len()) + + self.specials.len() + } + + /// Return the image of self across the derived function func. + fn image(&self, derived_func: DerivedFunc) -> TypeSet { + match derived_func { + DerivedFunc::LaneOf => self.lane_of(), + DerivedFunc::AsBool => self.as_bool(), + DerivedFunc::HalfWidth => self.half_width(), + DerivedFunc::DoubleWidth => self.double_width(), + DerivedFunc::HalfVector => self.half_vector(), + DerivedFunc::DoubleVector => self.double_vector(), + DerivedFunc::ToBitVec => self.to_bitvec(), + } + } + + /// Return a TypeSet describing the image of self across lane_of. + fn lane_of(&self) -> TypeSet { + let mut copy = self.clone(); + copy.lanes = NumSet::from_iter(vec![1]); + copy.bitvecs = NumSet::new(); + copy + } + + /// Return a TypeSet describing the image of self across as_bool. + fn as_bool(&self) -> TypeSet { + let mut copy = self.clone(); + copy.ints = NumSet::new(); + copy.floats = NumSet::new(); + copy.bitvecs = NumSet::new(); + if (&self.lanes - &NumSet::from_iter(vec![1])).len() > 0 { + copy.bools = &self.ints | &self.floats; + copy.bools = ©.bools | &self.bools; + } + if self.lanes.contains(&1) { + copy.bools.insert(1); + } + copy + } + + /// Return a TypeSet describing the image of self across halfwidth. + fn half_width(&self) -> TypeSet { + let mut copy = self.clone(); + copy.ints = NumSet::from_iter(self.ints.iter().filter(|&&x| x > 8).map(|&x| x / 2)); + copy.floats = NumSet::from_iter(self.floats.iter().filter(|&&x| x > 32).map(|&x| x / 2)); + copy.bools = NumSet::from_iter(self.bools.iter().filter(|&&x| x > 8).map(|&x| x / 2)); + copy.bitvecs = NumSet::from_iter(self.bitvecs.iter().filter(|&&x| x > 1).map(|&x| x / 2)); + copy.specials = Vec::new(); + copy + } + + /// Return a TypeSet describing the image of self across doublewidth. + fn double_width(&self) -> TypeSet { + let mut copy = self.clone(); + copy.ints = NumSet::from_iter(self.ints.iter().filter(|&&x| x < MAX_BITS).map(|&x| x * 2)); + copy.floats = NumSet::from_iter( + self.floats + .iter() + .filter(|&&x| x < MAX_BITS) + .map(|&x| x * 2), + ); + copy.bools = NumSet::from_iter( + self.bools + .iter() + .filter(|&&x| x < MAX_BITS) + .map(|&x| x * 2) + .filter(legal_bool), + ); + copy.bitvecs = NumSet::from_iter( + self.bitvecs + .iter() + .filter(|&&x| x < MAX_BITVEC) + .map(|&x| x * 2), + ); + copy.specials = Vec::new(); + copy + } + + /// Return a TypeSet describing the image of self across halfvector. + fn half_vector(&self) -> TypeSet { + let mut copy = self.clone(); + copy.bitvecs = NumSet::new(); + copy.lanes = NumSet::from_iter(self.lanes.iter().filter(|&&x| x > 1).map(|&x| x / 2)); + copy.specials = Vec::new(); + copy + } + + /// Return a TypeSet describing the image of self across doublevector. + fn double_vector(&self) -> TypeSet { + let mut copy = self.clone(); + copy.bitvecs = NumSet::new(); + copy.lanes = NumSet::from_iter( + self.lanes + .iter() + .filter(|&&x| x < MAX_LANES) + .map(|&x| x * 2), + ); + copy.specials = Vec::new(); + copy + } + + /// Return a TypeSet describing the image of self across to_bitvec. + fn to_bitvec(&self) -> TypeSet { + assert!(self.bitvecs.is_empty()); + let all_scalars = &(&self.ints | &self.floats) | &self.bools; + + let mut copy = self.clone(); + copy.lanes = NumSet::from_iter(vec![1]); + copy.ints = NumSet::new(); + copy.bools = NumSet::new(); + copy.floats = NumSet::new(); + copy.bitvecs = self + .lanes + .iter() + .cycle() + .zip(all_scalars.iter()) + .map(|(num_lanes, lane_width)| num_lanes * lane_width) + .collect(); + + copy.specials = Vec::new(); + copy + } + + fn concrete_types(&self) -> Vec { + let mut ret = Vec::new(); + for &num_lanes in &self.lanes { + for &bits in &self.ints { + ret.push(LaneType::int_from_bits(bits).by(num_lanes)); + } + for &bits in &self.floats { + ret.push(LaneType::float_from_bits(bits).by(num_lanes)); + } + for &bits in &self.bools { + ret.push(LaneType::bool_from_bits(bits).by(num_lanes)); + } + for &bits in &self.bitvecs { + assert_eq!(num_lanes, 1); + ret.push(BVType::new(bits).into()); + } + } + for &special in &self.specials { + ret.push(special.into()); + } + ret + } + + /// Return the singleton type represented by self. Can only call on typesets containing 1 type. + fn get_singleton(&self) -> ValueType { + let mut types = self.concrete_types(); + assert_eq!(types.len(), 1); + return types.remove(0); + } +} + +#[derive(PartialEq)] +pub enum Interval { + None, + All, + Range(Range), +} + +impl Interval { + fn to_range(&self, full_range: Range, default: Option) -> Option { + match self { + Interval::None => { + if let Some(default_val) = default { + Some(default_val..default_val) + } else { + None + } + } + + Interval::All => Some(full_range), + + Interval::Range(range) => { + let (low, high) = (range.start, range.end); + assert!(low.is_power_of_two()); + assert!(high.is_power_of_two()); + assert!(low <= high); + assert!(low >= full_range.start); + assert!(high <= full_range.end); + Some(low..high) + } + } + } +} + +impl Into for Range { + fn into(self) -> Interval { + Interval::Range(self) + } +} + +pub struct TypeVarBuilder { + name: String, + doc: String, + ints: Interval, + floats: Interval, + bools: Interval, + bitvecs: Interval, + includes_scalars: bool, + simd_lanes: Interval, + specials: Vec, +} + +impl TypeVarBuilder { + pub fn new(name: impl Into, doc: impl Into) -> Self { + Self { + name: name.into(), + doc: doc.into(), + ints: Interval::None, + floats: Interval::None, + bools: Interval::None, + bitvecs: Interval::None, + includes_scalars: true, + simd_lanes: Interval::None, + specials: Vec::new(), + } + } + + pub fn singleton(value_type: ValueType) -> TypeVar { + let mut builder = TypeVarBuilder::new(value_type.to_string(), value_type.doc()); + + let (scalar_type, num_lanes) = match value_type { + ValueType::BV(bitvec_type) => { + let bits = bitvec_type.lane_bits() as RangeBound; + return builder.bitvecs(bits..bits).finish(); + } + ValueType::Special(special_type) => { + return builder.specials(vec![special_type]).finish(); + } + ValueType::Lane(lane_type) => (lane_type, 1), + ValueType::Vector(vec_type) => { + (vec_type.lane_type(), vec_type.lane_count() as RangeBound) + } + }; + + builder = builder.simd_lanes(num_lanes..num_lanes); + + match scalar_type { + LaneType::IntType(int_type) => { + let bits = int_type as RangeBound; + return builder.ints(bits..bits).finish(); + } + LaneType::FloatType(float_type) => { + let bits = float_type as RangeBound; + return builder.floats(bits..bits).finish(); + } + LaneType::BoolType(bool_type) => { + let bits = bool_type as RangeBound; + return builder.bools(bits..bits).finish(); + } + } + } + + pub fn ints(mut self, interval: impl Into) -> Self { + assert!(self.ints == Interval::None); + self.ints = interval.into(); + self + } + pub fn floats(mut self, interval: impl Into) -> Self { + assert!(self.floats == Interval::None); + self.floats = interval.into(); + self + } + pub fn bools(mut self, interval: impl Into) -> Self { + assert!(self.bools == Interval::None); + self.bools = interval.into(); + self + } + pub fn includes_scalars(mut self, includes_scalars: bool) -> Self { + self.includes_scalars = includes_scalars; + self + } + pub fn simd_lanes(mut self, interval: impl Into) -> Self { + assert!(self.simd_lanes == Interval::None); + self.simd_lanes = interval.into(); + self + } + pub fn bitvecs(mut self, interval: impl Into) -> Self { + assert!(self.bitvecs == Interval::None); + self.bitvecs = interval.into(); + self + } + pub fn specials(mut self, specials: Vec) -> Self { + assert!(self.specials.is_empty()); + self.specials = specials; + self + } + + pub fn finish(self) -> TypeVar { + let min_lanes = if self.includes_scalars { 1 } else { 2 }; + + let bools = range_to_set(self.bools.to_range(1..MAX_BITS, None)) + .into_iter() + .filter(legal_bool) + .collect(); + + let type_set = Rc::new(TypeSet::new( + range_to_set(self.simd_lanes.to_range(min_lanes..MAX_LANES, Some(1))), + range_to_set(self.ints.to_range(8..MAX_BITS, None)), + range_to_set(self.floats.to_range(32..64, None)), + bools, + range_to_set(self.bitvecs.to_range(1..MAX_BITVEC, None)), + self.specials, + )); + + TypeVar { + content: Rc::new(TypeVarContent { + name: self.name.to_string(), + doc: self.doc.to_string(), + type_set, + base: None, + }), + } + } +} + +fn legal_bool(bits: &RangeBound) -> bool { + // Only allow legal bit widths for bool types. + *bits == 1 || (*bits >= 8 && *bits <= MAX_BITS && bits.is_power_of_two()) +} + +/// Generates a set with all the powers of two included in the range. +fn range_to_set(range: Option) -> NumSet { + let mut set = NumSet::new(); + + let (low, high) = match range { + Some(range) => (range.start, range.end), + None => return set, + }; + + assert!(low.is_power_of_two()); + assert!(high.is_power_of_two()); + assert!(low <= high); + + for i in low.trailing_zeros()..high.trailing_zeros() + 1 { + assert!(1 << i <= RangeBound::max_value()); + set.insert(1 << i); + } + set +} + +#[test] +fn test_typevar_builder() { + let typevar = TypeVarBuilder::new("test", "scalar integers") + .ints(Interval::All) + .finish(); + assert_eq!(typevar.type_set.lanes, NumSet::from_iter(vec![1])); + assert!(typevar.type_set.floats.is_empty()); + assert_eq!( + typevar.type_set.ints, + NumSet::from_iter(vec![8, 16, 32, 64]) + ); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); + + let typevar = TypeVarBuilder::new("test", "scalar bools") + .bools(Interval::All) + .finish(); + assert_eq!(typevar.type_set.lanes, NumSet::from_iter(vec![1])); + assert!(typevar.type_set.floats.is_empty()); + assert!(typevar.type_set.ints.is_empty()); + assert_eq!( + typevar.type_set.bools, + NumSet::from_iter(vec![1, 8, 16, 32, 64]) + ); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); + + let typevar = TypeVarBuilder::new("test", "scalar floats") + .floats(Interval::All) + .finish(); + assert_eq!(typevar.type_set.lanes, NumSet::from_iter(vec![1])); + assert_eq!(typevar.type_set.floats, NumSet::from_iter(vec![32, 64])); + assert!(typevar.type_set.ints.is_empty()); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); + + let typevar = TypeVarBuilder::new("test", "float vectors (but not scalars)") + .floats(Interval::All) + .simd_lanes(Interval::All) + .includes_scalars(false) + .finish(); + assert_eq!( + typevar.type_set.lanes, + NumSet::from_iter(vec![2, 4, 8, 16, 32, 64, 128, 256]) + ); + assert_eq!(typevar.type_set.floats, NumSet::from_iter(vec![32, 64])); + assert!(typevar.type_set.ints.is_empty()); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); + + let typevar = TypeVarBuilder::new("test", "float vectors and scalars") + .floats(Interval::All) + .simd_lanes(Interval::All) + .includes_scalars(true) + .finish(); + assert_eq!( + typevar.type_set.lanes, + NumSet::from_iter(vec![1, 2, 4, 8, 16, 32, 64, 128, 256]) + ); + assert_eq!(typevar.type_set.floats, NumSet::from_iter(vec![32, 64])); + assert!(typevar.type_set.ints.is_empty()); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); + + let typevar = TypeVarBuilder::new("test", "range of ints") + .ints(16..64) + .finish(); + assert_eq!(typevar.type_set.lanes, NumSet::from_iter(vec![1])); + assert_eq!(typevar.type_set.ints, NumSet::from_iter(vec![16, 32, 64])); + assert!(typevar.type_set.floats.is_empty()); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); +} + +#[test] +#[should_panic] +fn test_typevar_builder_too_high_bound_panic() { + TypeVarBuilder::new("test", "invalid range of ints") + .ints(16..2 * MAX_BITS) + .finish(); +} + +#[test] +#[should_panic] +fn test_typevar_builder_inverted_bounds_panic() { + TypeVarBuilder::new("test", "inverted bounds") + .ints(32..16) + .finish(); +} + +#[test] +fn test_singleton() { + use crate::cdsl::types::VectorType; + use crate::shared::types as shared_types; + + // Test i32. + let typevar = + TypeVarBuilder::singleton(ValueType::Lane(LaneType::IntType(shared_types::Int::I32))); + assert_eq!(typevar.name, "i32"); + assert_eq!(typevar.type_set.ints, NumSet::from_iter(vec![32])); + assert!(typevar.type_set.floats.is_empty()); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); + assert_eq!(typevar.type_set.lanes, NumSet::from_iter(vec![1])); + + // Test f32x4. + let typevar = TypeVarBuilder::singleton(ValueType::Vector(VectorType::new( + LaneType::FloatType(shared_types::Float::F32), + 4, + ))); + assert_eq!(typevar.name, "f32x4"); + assert!(typevar.type_set.ints.is_empty()); + assert_eq!(typevar.type_set.floats, NumSet::from_iter(vec![32])); + assert_eq!(typevar.type_set.lanes, NumSet::from_iter(vec![4])); + assert!(typevar.type_set.bools.is_empty()); + assert!(typevar.type_set.bitvecs.is_empty()); + assert!(typevar.type_set.specials.is_empty()); +}