Skip to content

Commit

Permalink
Initial f16 and f128 support (bytecodealliance#8860)
Browse files Browse the repository at this point in the history
  • Loading branch information
beetrees authored Jun 27, 2024
1 parent be67ee8 commit 7ac3fda
Show file tree
Hide file tree
Showing 12 changed files with 741 additions and 77 deletions.
30 changes: 23 additions & 7 deletions cranelift/codegen/meta/src/cdsl/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ impl LaneType {
/// Return a string containing the documentation comment for this lane type.
pub fn doc(self) -> String {
match self {
LaneType::Float(shared_types::Float::F16) => String::from(
"A 16-bit floating point type represented in the IEEE 754-2008
*binary16* interchange format. This corresponds to the :c:type:`_Float16`
type in most C implementations.
WARNING: f16 support is a work-in-progress and is incomplete",
),
LaneType::Float(shared_types::Float::F32) => String::from(
"A 32-bit floating point type represented in the IEEE 754-2008
*binary32* interchange format. This corresponds to the :c:type:`float`
Expand All @@ -146,6 +152,12 @@ impl LaneType {
*binary64* interchange format. This corresponds to the :c:type:`double`
type in most C implementations.",
),
LaneType::Float(shared_types::Float::F128) => String::from(
"A 128-bit floating point type represented in the IEEE 754-2008
*binary128* interchange format. This corresponds to the :c:type:`_Float128`
type in most C implementations.
WARNING: f128 support is a work-in-progress and is incomplete",
),
LaneType::Int(_) if self.lane_bits() < 32 => format!(
"An integer type with {} bits.
WARNING: arithmetic on {}bit integers is incomplete",
Expand All @@ -168,13 +180,15 @@ impl LaneType {
pub fn number(self) -> u16 {
constants::LANE_BASE
+ match self {
LaneType::Int(shared_types::Int::I8) => 6,
LaneType::Int(shared_types::Int::I16) => 7,
LaneType::Int(shared_types::Int::I32) => 8,
LaneType::Int(shared_types::Int::I64) => 9,
LaneType::Int(shared_types::Int::I128) => 10,
LaneType::Float(shared_types::Float::F32) => 11,
LaneType::Float(shared_types::Float::F64) => 12,
LaneType::Int(shared_types::Int::I8) => 4,
LaneType::Int(shared_types::Int::I16) => 5,
LaneType::Int(shared_types::Int::I32) => 6,
LaneType::Int(shared_types::Int::I64) => 7,
LaneType::Int(shared_types::Int::I128) => 8,
LaneType::Float(shared_types::Float::F16) => 9,
LaneType::Float(shared_types::Float::F32) => 10,
LaneType::Float(shared_types::Float::F64) => 11,
LaneType::Float(shared_types::Float::F128) => 12,
}
}

Expand All @@ -191,8 +205,10 @@ impl LaneType {

pub fn float_from_bits(num_bits: u16) -> LaneType {
LaneType::Float(match num_bits {
16 => shared_types::Float::F16,
32 => shared_types::Float::F32,
64 => shared_types::Float::F64,
128 => shared_types::Float::F128,
_ => unreachable!("unexpected num bits for float"),
})
}
Expand Down
32 changes: 16 additions & 16 deletions cranelift/codegen/meta/src/cdsl/typevar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::cdsl::types::{LaneType, ReferenceType, ValueType};

const MAX_LANES: u16 = 256;
const MAX_BITS: u16 = 128;
const MAX_FLOAT_BITS: u16 = 64;
const MAX_FLOAT_BITS: u16 = 128;

/// Type variables can be used in place of concrete types when defining
/// instructions. This makes the instructions *polymorphic*.
Expand Down Expand Up @@ -159,7 +159,7 @@ impl TypeVar {
"can't halve all integer types"
);
assert!(
ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 32,
ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 16,
"can't halve all float types"
);
}
Expand All @@ -179,7 +179,7 @@ impl TypeVar {
"can't halve all integer types"
);
assert!(
ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 32,
ts.floats.is_empty() || *ts.floats.iter().min().unwrap() > 16,
"can't halve all float types"
);
assert!(
Expand Down Expand Up @@ -464,7 +464,7 @@ impl TypeSet {
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.floats = NumSet::from_iter(self.floats.iter().filter(|&&x| x > 16).map(|&x| x / 2));
copy
}

Expand Down Expand Up @@ -643,7 +643,7 @@ impl TypeSetBuilder {
range_to_set(self.simd_lanes.to_range(min_lanes..MAX_LANES, Some(1))),
range_to_set(self.dynamic_simd_lanes.to_range(2..MAX_LANES, None)),
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
range_to_set(self.floats.to_range(32..64, None)),
range_to_set(self.floats.to_range(16..MAX_FLOAT_BITS, None)),
range_to_set(self.refs.to_range(32..64, None)),
)
}
Expand Down Expand Up @@ -711,7 +711,7 @@ fn test_typevar_builder() {

let type_set = TypeSetBuilder::new().floats(Interval::All).build();
assert_eq!(type_set.lanes, num_set![1]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.floats, num_set![16, 32, 64, 128]);
assert!(type_set.ints.is_empty());

let type_set = TypeSetBuilder::new()
Expand All @@ -720,7 +720,7 @@ fn test_typevar_builder() {
.includes_scalars(false)
.build();
assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.floats, num_set![16, 32, 64, 128]);
assert!(type_set.ints.is_empty());

let type_set = TypeSetBuilder::new()
Expand All @@ -729,7 +729,7 @@ fn test_typevar_builder() {
.includes_scalars(true)
.build();
assert_eq!(type_set.lanes, num_set![1, 2, 4, 8, 16, 32, 64, 128, 256]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.floats, num_set![16, 32, 64, 128]);
assert!(type_set.ints.is_empty());

let type_set = TypeSetBuilder::new()
Expand All @@ -738,7 +738,7 @@ fn test_typevar_builder() {
.includes_scalars(false)
.build();
assert_eq!(type_set.lanes, num_set![2, 4, 8, 16, 32, 64, 128, 256]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.floats, num_set![16, 32, 64, 128]);
assert!(type_set.dynamic_lanes.is_empty());
assert!(type_set.ints.is_empty());

Expand All @@ -753,7 +753,7 @@ fn test_typevar_builder() {
num_set![2, 4, 8, 16, 32, 64, 128, 256]
);
assert_eq!(type_set.ints, num_set![8, 16, 32, 64, 128]);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.floats, num_set![16, 32, 64, 128]);
assert_eq!(type_set.lanes, num_set![1]);

let type_set = TypeSetBuilder::new()
Expand All @@ -765,7 +765,7 @@ fn test_typevar_builder() {
type_set.dynamic_lanes,
num_set![2, 4, 8, 16, 32, 64, 128, 256]
);
assert_eq!(type_set.floats, num_set![32, 64]);
assert_eq!(type_set.floats, num_set![16, 32, 64, 128]);
assert_eq!(type_set.lanes, num_set![1]);
assert!(type_set.ints.is_empty());

Expand Down Expand Up @@ -871,12 +871,12 @@ fn test_forward_images() {
TypeSetBuilder::new().ints(8..16).build()
);
assert_eq!(
TypeSetBuilder::new().floats(32..32).build().half_width(),
TypeSetBuilder::new().floats(16..16).build().half_width(),
empty_set
);
assert_eq!(
TypeSetBuilder::new().floats(32..64).build().half_width(),
TypeSetBuilder::new().floats(32..32).build()
TypeSetBuilder::new().floats(32..128).build().half_width(),
TypeSetBuilder::new().floats(16..64).build()
);

// Double width.
Expand All @@ -893,8 +893,8 @@ fn test_forward_images() {
TypeSetBuilder::new().floats(64..64).build()
);
assert_eq!(
TypeSetBuilder::new().floats(32..64).build().double_width(),
TypeSetBuilder::new().floats(64..64).build()
TypeSetBuilder::new().floats(16..64).build().double_width(),
TypeSetBuilder::new().floats(32..128).build()
);
}

Expand Down
10 changes: 8 additions & 2 deletions cranelift/codegen/meta/src/shared/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ impl Iterator for IntIterator {

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub(crate) enum Float {
F16 = 16,
F32 = 32,
F64 = 64,
F128 = 128,
}

/// Iterator through the variants of the Float enum.
Expand All @@ -63,8 +65,10 @@ impl Iterator for FloatIterator {
type Item = Float;
fn next(&mut self) -> Option<Self::Item> {
let res = match self.index {
0 => Some(Float::F32),
1 => Some(Float::F64),
0 => Some(Float::F16),
1 => Some(Float::F32),
2 => Some(Float::F64),
3 => Some(Float::F128),
_ => return None,
};
self.index += 1;
Expand Down Expand Up @@ -122,8 +126,10 @@ mod iter_tests {
#[test]
fn float_iter_works() {
let mut float_iter = FloatIterator::new();
assert_eq!(float_iter.next(), Some(Float::F16));
assert_eq!(float_iter.next(), Some(Float::F32));
assert_eq!(float_iter.next(), Some(Float::F64));
assert_eq!(float_iter.next(), Some(Float::F128));
assert_eq!(float_iter.next(), None);
}

Expand Down
27 changes: 26 additions & 1 deletion cranelift/codegen/src/data_value.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! This module gives users to instantiate values that Cranelift understands. These values are used,
//! for example, during interpretation and for wrapping immediates.
use crate::ir::immediates::{Ieee32, Ieee64, Offset32};
use crate::ir::immediates::{Ieee128, Ieee16, Ieee32, Ieee64, Offset32};
use crate::ir::{types, ConstantData, Type};
use core::cmp::Ordering;
use core::fmt::{self, Display, Formatter};

/// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value
Expand All @@ -16,8 +17,10 @@ pub enum DataValue {
I32(i32),
I64(i64),
I128(i128),
F16(Ieee16),
F32(Ieee32),
F64(Ieee64),
F128(Ieee128),
V128([u8; 16]),
V64([u8; 8]),
}
Expand All @@ -36,10 +39,14 @@ impl PartialEq for DataValue {
(I64(_), _) => false,
(I128(l), I128(r)) => l == r,
(I128(_), _) => false,
(F16(l), F16(r)) => l.partial_cmp(&r) == Some(Ordering::Equal),
(F16(_), _) => false,
(F32(l), F32(r)) => l.as_f32() == r.as_f32(),
(F32(_), _) => false,
(F64(l), F64(r)) => l.as_f64() == r.as_f64(),
(F64(_), _) => false,
(F128(l), F128(r)) => l.partial_cmp(&r) == Some(Ordering::Equal),
(F128(_), _) => false,
(V128(l), V128(r)) => l == r,
(V128(_), _) => false,
(V64(l), V64(r)) => l == r,
Expand Down Expand Up @@ -70,8 +77,10 @@ impl DataValue {
DataValue::I32(_) => types::I32,
DataValue::I64(_) => types::I64,
DataValue::I128(_) => types::I128,
DataValue::F16(_) => types::F16,
DataValue::F32(_) => types::F32,
DataValue::F64(_) => types::F64,
DataValue::F128(_) => types::F128,
DataValue::V128(_) => types::I8X16, // A default type.
DataValue::V64(_) => types::I8X8, // A default type.
}
Expand All @@ -92,8 +101,10 @@ impl DataValue {
DataValue::I32(i) => DataValue::I32(i.swap_bytes()),
DataValue::I64(i) => DataValue::I64(i.swap_bytes()),
DataValue::I128(i) => DataValue::I128(i.swap_bytes()),
DataValue::F16(f) => DataValue::F16(Ieee16::with_bits(f.bits().swap_bytes())),
DataValue::F32(f) => DataValue::F32(Ieee32::with_bits(f.bits().swap_bytes())),
DataValue::F64(f) => DataValue::F64(Ieee64::with_bits(f.bits().swap_bytes())),
DataValue::F128(f) => DataValue::F128(Ieee128::with_bits(f.bits().swap_bytes())),
DataValue::V128(mut v) => {
v.reverse();
DataValue::V128(v)
Expand Down Expand Up @@ -135,8 +146,10 @@ impl DataValue {
DataValue::I32(i) => dst[..4].copy_from_slice(&i.to_ne_bytes()[..]),
DataValue::I64(i) => dst[..8].copy_from_slice(&i.to_ne_bytes()[..]),
DataValue::I128(i) => dst[..16].copy_from_slice(&i.to_ne_bytes()[..]),
DataValue::F16(f) => dst[..2].copy_from_slice(&f.bits().to_ne_bytes()[..]),
DataValue::F32(f) => dst[..4].copy_from_slice(&f.bits().to_ne_bytes()[..]),
DataValue::F64(f) => dst[..8].copy_from_slice(&f.bits().to_ne_bytes()[..]),
DataValue::F128(f) => dst[..16].copy_from_slice(&f.bits().to_ne_bytes()[..]),
DataValue::V128(v) => dst[..16].copy_from_slice(&v[..]),
DataValue::V64(v) => dst[..8].copy_from_slice(&v[..]),
};
Expand Down Expand Up @@ -172,12 +185,18 @@ impl DataValue {
types::I32 => DataValue::I32(i32::from_ne_bytes(src[..4].try_into().unwrap())),
types::I64 => DataValue::I64(i64::from_ne_bytes(src[..8].try_into().unwrap())),
types::I128 => DataValue::I128(i128::from_ne_bytes(src[..16].try_into().unwrap())),
types::F16 => DataValue::F16(Ieee16::with_bits(u16::from_ne_bytes(
src[..2].try_into().unwrap(),
))),
types::F32 => DataValue::F32(Ieee32::with_bits(u32::from_ne_bytes(
src[..4].try_into().unwrap(),
))),
types::F64 => DataValue::F64(Ieee64::with_bits(u64::from_ne_bytes(
src[..8].try_into().unwrap(),
))),
types::F128 => DataValue::F128(Ieee128::with_bits(u128::from_ne_bytes(
src[..16].try_into().unwrap(),
))),
_ if ty.is_vector() => {
if ty.bytes() == 16 {
DataValue::V128(src[..16].try_into().unwrap())
Expand Down Expand Up @@ -233,8 +252,10 @@ impl DataValue {
// We need to bit compare the floats to ensure that we produce the correct values
// on NaN's. The test suite expects to assert the precise bit pattern on NaN's or
// works around it in the tests themselves.
(DataValue::F16(a), DataValue::F16(b)) => a.bits() == b.bits(),
(DataValue::F32(a), DataValue::F32(b)) => a.bits() == b.bits(),
(DataValue::F64(a), DataValue::F64(b)) => a.bits() == b.bits(),
(DataValue::F128(a), DataValue::F128(b)) => a.bits() == b.bits(),

// We don't need to worry about F32x4 / F64x2 Since we compare V128 which is already the
// raw bytes anyway
Expand Down Expand Up @@ -305,8 +326,10 @@ build_conversion_impl!(i16, I16, I16);
build_conversion_impl!(i32, I32, I32);
build_conversion_impl!(i64, I64, I64);
build_conversion_impl!(i128, I128, I128);
build_conversion_impl!(Ieee16, F16, F16);
build_conversion_impl!(Ieee32, F32, F32);
build_conversion_impl!(Ieee64, F64, F64);
build_conversion_impl!(Ieee128, F128, F128);
build_conversion_impl!([u8; 16], V128, I8X16);
build_conversion_impl!([u8; 8], V64, I8X8);
impl From<Offset32> for DataValue {
Expand All @@ -324,8 +347,10 @@ impl Display for DataValue {
DataValue::I64(dv) => write!(f, "{}", dv),
DataValue::I128(dv) => write!(f, "{}", dv),
// The Ieee* wrappers here print the expected syntax.
DataValue::F16(dv) => write!(f, "{}", dv),
DataValue::F32(dv) => write!(f, "{}", dv),
DataValue::F64(dv) => write!(f, "{}", dv),
DataValue::F128(dv) => write!(f, "{}", dv),
// Again, for syntax consistency, use ConstantData, which in this case displays as hex.
DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
DataValue::V64(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
Expand Down
Loading

0 comments on commit 7ac3fda

Please sign in to comment.