Skip to content

Commit

Permalink
cranelift: Add f16const and f128const instructions (#8893)
Browse files Browse the repository at this point in the history
* cranelift: Add `f16const` and `f128const` instructions

* cranelift: Add constant propagation for `f16` and `f128`
  • Loading branch information
beetrees authored Jul 17, 2024
1 parent 0a296b3 commit 41eca60
Show file tree
Hide file tree
Showing 20 changed files with 437 additions and 36 deletions.
3 changes: 3 additions & 0 deletions cranelift/codegen/meta/src/shared/formats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub(crate) struct Formats {
pub(crate) unary: Rc<InstructionFormat>,
pub(crate) unary_const: Rc<InstructionFormat>,
pub(crate) unary_global_value: Rc<InstructionFormat>,
pub(crate) unary_ieee16: Rc<InstructionFormat>,
pub(crate) unary_ieee32: Rc<InstructionFormat>,
pub(crate) unary_ieee64: Rc<InstructionFormat>,
pub(crate) unary_imm: Rc<InstructionFormat>,
Expand All @@ -48,6 +49,8 @@ impl Formats {

unary_imm: Builder::new("UnaryImm").imm(&imm.imm64).build(),

unary_ieee16: Builder::new("UnaryIeee16").imm(&imm.ieee16).build(),

unary_ieee32: Builder::new("UnaryIeee32").imm(&imm.ieee32).build(),

unary_ieee64: Builder::new("UnaryIeee64").imm(&imm.ieee64).build(),
Expand Down
10 changes: 10 additions & 0 deletions cranelift/codegen/meta/src/shared/immediates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ pub(crate) struct Immediates {
/// This is used to represent an immediate address offset in load/store instructions.
pub offset32: OperandKind,

/// A 16-bit immediate floating point operand.
///
/// IEEE 754-2008 binary16 interchange format.
pub ieee16: OperandKind,

/// A 32-bit immediate floating point operand.
///
/// IEEE 754-2008 binary32 interchange format.
Expand Down Expand Up @@ -119,6 +124,11 @@ impl Immediates {
"ir::immediates::Offset32",
"A 32-bit immediate signed offset.",
),
ieee16: new_imm(
"imm",
"ir::immediates::Ieee16",
"A 16-bit immediate floating point number.",
),
ieee32: new_imm(
"imm",
"ir::immediates::Ieee32",
Expand Down
34 changes: 34 additions & 0 deletions cranelift/codegen/meta/src/shared/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,10 @@ pub(crate) fn define(

// Operand kind shorthands.
let i8: &TypeVar = &ValueType::from(LaneType::from(types::Int::I8)).into();
let f16_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F16)).into();
let f32_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F32)).into();
let f64_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F64)).into();
let f128_: &TypeVar = &ValueType::from(LaneType::from(types::Float::F128)).into();

// Starting definitions.
let Int = &TypeVar::new(
Expand Down Expand Up @@ -1285,6 +1287,22 @@ pub(crate) fn define(
]),
);

ig.push(
Inst::new(
"f16const",
r#"
Floating point constant.
Create a `f16` SSA value with an immediate constant value.
"#,
&formats.unary_ieee16,
)
.operands_in(vec![Operand::new("N", &imm.ieee16)])
.operands_out(vec![
Operand::new("a", f16_).with_doc("A constant f16 scalar value")
]),
);

ig.push(
Inst::new(
"f32const",
Expand Down Expand Up @@ -1317,6 +1335,22 @@ pub(crate) fn define(
]),
);

ig.push(
Inst::new(
"f128const",
r#"
Floating point constant.
Create a `f128` SSA value with an immediate constant value.
"#,
&formats.unary_const,
)
.operands_in(vec![Operand::new("N", &imm.pool_constant)])
.operands_out(vec![
Operand::new("a", f128_).with_doc("A constant f128 scalar value")
]),
);

ig.push(
Inst::new(
"vconst",
Expand Down
1 change: 1 addition & 0 deletions cranelift/codegen/src/inst_predicates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
}
match data {
&InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64),
&InstructionData::UnaryIeee16 { imm, .. } => Some(imm.bits() as u64),
&InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64),
&InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()),
_ => None,
Expand Down
29 changes: 28 additions & 1 deletion cranelift/codegen/src/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! - ensuring alignment of constants within the pool,
//! - bucketing constants by size.
use crate::ir::immediates::{IntoBytes, V128Imm};
use crate::ir::immediates::{Ieee128, IntoBytes, V128Imm};
use crate::ir::Constant;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
Expand Down Expand Up @@ -54,6 +54,22 @@ impl From<V128Imm> for ConstantData {
}
}

impl From<Ieee128> for ConstantData {
fn from(v: Ieee128) -> Self {
Self(v.into_bytes())
}
}

impl TryFrom<&ConstantData> for Ieee128 {
type Error = <[u8; 16] as TryFrom<&'static [u8]>>::Error;

fn try_from(value: &ConstantData) -> Result<Self, Self::Error> {
Ok(Ieee128::with_bits(u128::from_le_bytes(
value.as_slice().try_into()?,
)))
}
}

impl ConstantData {
/// Return the number of bytes in the constant.
pub fn len(&self) -> usize {
Expand Down Expand Up @@ -459,4 +475,15 @@ mod tests {
[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}

#[test]
fn constant_ieee128() {
let value = Ieee128::with_bits(0x000102030405060708090a0b0c0d0e0f);
let constant = ConstantData::from(value);
assert_eq!(
constant.as_slice(),
&[0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0]
);
assert_eq!(Ieee128::try_from(&constant).unwrap().bits(), value.bits());
}
}
2 changes: 2 additions & 0 deletions cranelift/codegen/src/ir/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ impl Block {
/// [`InstBuilder`](super::InstBuilder) instructions:
///
/// - [`iconst`](super::InstBuilder::iconst) for integer constants
/// - [`f16const`](super::InstBuilder::f16const) for 16-bit float constants
/// - [`f32const`](super::InstBuilder::f32const) for 32-bit float constants
/// - [`f64const`](super::InstBuilder::f64const) for 64-bit float constants
/// - [`f128const`](super::InstBuilder::f128const) for 128-bit float constants
/// - [`vconst`](super::InstBuilder::vconst) for vector constants
/// - [`null`](super::InstBuilder::null) for null reference constants
///
Expand Down
90 changes: 70 additions & 20 deletions cranelift/codegen/src/ir/immediates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,9 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result<u128, &'static str> {
impl Ieee16 {
const SIGNIFICAND_BITS: u8 = 10;
const EXPONENT_BITS: u8 = 5;
const SIGN_MASK: u16 = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS);
const SIGNIFICAND_MASK: u16 = u16::MAX >> (Self::EXPONENT_BITS + 1);
const EXPONENT_MASK: u16 = !Self::SIGN_MASK & !Self::SIGNIFICAND_MASK;

/// Create a new `Ieee16` containing the bits of `x`.
pub fn with_bits(x: u16) -> Self {
Expand All @@ -779,6 +782,16 @@ impl Ieee16 {
self.0
}

/// Computes the absolute value of self.
pub fn abs(self) -> Self {
Self::with_bits(self.bits() & !Self::SIGN_MASK)
}

/// Returns a number composed of the magnitude of self and the sign of sign.
pub fn copysign(self, sign: Self) -> Self {
Self::with_bits((self.bits() & !Self::SIGN_MASK) | (sign.bits() & Self::SIGN_MASK))
}

/// Returns true if self is positive or negative zero
pub fn is_zero(&self) -> bool {
self.partial_cmp(&Self::with_bits(0)) == Some(Ordering::Equal)
Expand All @@ -788,14 +801,12 @@ impl Ieee16 {
impl PartialOrd for Ieee16 {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
// FIXME(#8312): Use Rust `f16` comparisons once `f16` support is stabalised.
let significand_mask = u16::MAX >> (Self::EXPONENT_BITS + 1);
let sign_mask = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS);
let exponent_mask = !sign_mask & !significand_mask;

let lhs_abs = self.bits() & !sign_mask;
let rhs_abs = rhs.bits() & !sign_mask;
if (lhs_abs & exponent_mask == exponent_mask && lhs_abs & significand_mask != 0)
&& (rhs_abs & exponent_mask == exponent_mask && rhs_abs & significand_mask != 0)
let lhs_abs = self.bits() & !Self::SIGN_MASK;
let rhs_abs = rhs.bits() & !Self::SIGN_MASK;
if (lhs_abs & Self::EXPONENT_MASK == Self::EXPONENT_MASK
&& lhs_abs & Self::SIGNIFICAND_MASK != 0)
&& (rhs_abs & Self::EXPONENT_MASK == Self::EXPONENT_MASK
&& rhs_abs & Self::SIGNIFICAND_MASK != 0)
{
// One of the floats is a NaN.
return None;
Expand All @@ -804,8 +815,8 @@ impl PartialOrd for Ieee16 {
// Zeros are always equal regardless of sign.
return Some(Ordering::Equal);
}
let lhs_positive = self.bits() & sign_mask == 0;
let rhs_positive = rhs.bits() & sign_mask == 0;
let lhs_positive = self.bits() & Self::SIGN_MASK == 0;
let rhs_positive = rhs.bits() & Self::SIGN_MASK == 0;
if lhs_positive != rhs_positive {
// Different signs: negative < positive
return lhs_positive.partial_cmp(&rhs_positive);
Expand Down Expand Up @@ -843,6 +854,20 @@ impl FromStr for Ieee16 {
}
}

impl IntoBytes for Ieee16 {
fn into_bytes(self) -> Vec<u8> {
self.bits().to_le_bytes().to_vec()
}
}

impl Neg for Ieee16 {
type Output = Self;

fn neg(self) -> Self {
Self::with_bits(self.bits() ^ Self::SIGN_MASK)
}
}

impl Ieee32 {
/// Create a new `Ieee32` containing the bits of `x`.
pub fn with_bits(x: u32) -> Self {
Expand Down Expand Up @@ -1287,6 +1312,9 @@ impl Not for Ieee64 {
impl Ieee128 {
const SIGNIFICAND_BITS: u8 = 112;
const EXPONENT_BITS: u8 = 15;
const SIGN_MASK: u128 = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS);
const SIGNIFICAND_MASK: u128 = u128::MAX >> (Self::EXPONENT_BITS + 1);
const EXPONENT_MASK: u128 = !Self::SIGN_MASK & !Self::SIGNIFICAND_MASK;

/// Create a new `Ieee128` containing the bits of `x`.
pub fn with_bits(x: u128) -> Self {
Expand All @@ -1298,6 +1326,16 @@ impl Ieee128 {
self.0
}

/// Computes the absolute value of self.
pub fn abs(self) -> Self {
Self::with_bits(self.bits() & !Self::SIGN_MASK)
}

/// Returns a number composed of the magnitude of self and the sign of sign.
pub fn copysign(self, sign: Self) -> Self {
Self::with_bits((self.bits() & !Self::SIGN_MASK) | (sign.bits() & Self::SIGN_MASK))
}

/// Returns true if self is positive or negative zero
pub fn is_zero(&self) -> bool {
self.partial_cmp(&Self::with_bits(0)) == Some(Ordering::Equal)
Expand All @@ -1307,14 +1345,12 @@ impl Ieee128 {
impl PartialOrd for Ieee128 {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
// FIXME(#8312): Use Rust `f128` comparisons once `f128` support is stabalised.
let significand_mask = u128::MAX >> (Self::EXPONENT_BITS + 1);
let sign_mask = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS);
let exponent_mask = !sign_mask & !significand_mask;

let lhs_abs = self.bits() & !sign_mask;
let rhs_abs = rhs.bits() & !sign_mask;
if (lhs_abs & exponent_mask == exponent_mask && lhs_abs & significand_mask != 0)
&& (rhs_abs & exponent_mask == exponent_mask && rhs_abs & significand_mask != 0)
let lhs_abs = self.bits() & !Self::SIGN_MASK;
let rhs_abs = rhs.bits() & !Self::SIGN_MASK;
if (lhs_abs & Self::EXPONENT_MASK == Self::EXPONENT_MASK
&& lhs_abs & Self::SIGNIFICAND_MASK != 0)
&& (rhs_abs & Self::EXPONENT_MASK == Self::EXPONENT_MASK
&& rhs_abs & Self::SIGNIFICAND_MASK != 0)
{
// One of the floats is a NaN.
return None;
Expand All @@ -1323,8 +1359,8 @@ impl PartialOrd for Ieee128 {
// Zeros are always equal regardless of sign.
return Some(Ordering::Equal);
}
let lhs_positive = self.bits() & sign_mask == 0;
let rhs_positive = rhs.bits() & sign_mask == 0;
let lhs_positive = self.bits() & Self::SIGN_MASK == 0;
let rhs_positive = rhs.bits() & Self::SIGN_MASK == 0;
if lhs_positive != rhs_positive {
// Different signs: negative < positive
return lhs_positive.partial_cmp(&rhs_positive);
Expand Down Expand Up @@ -1357,6 +1393,20 @@ impl FromStr for Ieee128 {
}
}

impl IntoBytes for Ieee128 {
fn into_bytes(self) -> Vec<u8> {
self.bits().to_le_bytes().to_vec()
}
}

impl Neg for Ieee128 {
type Output = Self;

fn neg(self) -> Self {
Self::with_bits(self.bits() ^ Self::SIGN_MASK)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
24 changes: 24 additions & 0 deletions cranelift/codegen/src/isle_prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,18 @@ macro_rules! isle_common_prelude_methods {
}
}

fn f16_neg(&mut self, n: Ieee16) -> Ieee16 {
-n
}

fn f16_abs(&mut self, n: Ieee16) -> Ieee16 {
n.abs()
}

fn f16_copysign(&mut self, a: Ieee16, b: Ieee16) -> Ieee16 {
a.copysign(b)
}

fn f32_neg(&mut self, n: Ieee32) -> Ieee32 {
n.neg()
}
Expand All @@ -961,5 +973,17 @@ macro_rules! isle_common_prelude_methods {
fn f64_copysign(&mut self, a: Ieee64, b: Ieee64) -> Ieee64 {
a.copysign(b)
}

fn f128_neg(&mut self, n: Ieee128) -> Ieee128 {
-n
}

fn f128_abs(&mut self, n: Ieee128) -> Ieee128 {
n.abs()
}

fn f128_copysign(&mut self, a: Ieee128, b: Ieee128) -> Ieee128 {
a.copysign(b)
}
};
}
9 changes: 8 additions & 1 deletion cranelift/codegen/src/machinst/isle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ macro_rules! isle_lower_prelude_methods {
return self.zero_value(arg);
}
InstructionData::UnaryConst {
opcode: Opcode::Vconst,
opcode: Opcode::Vconst | Opcode::F128const,
constant_handle,
} => {
let constant_data =
Expand All @@ -271,6 +271,13 @@ macro_rules! isle_lower_prelude_methods {
return None;
}
}
InstructionData::UnaryIeee16 { imm, .. } => {
if imm.bits() == 0 {
return Some(value);
} else {
return None;
}
}
InstructionData::UnaryIeee32 { imm, .. } => {
if imm.bits() == 0 {
return Some(value);
Expand Down
Loading

0 comments on commit 41eca60

Please sign in to comment.