Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cranelift: Add f16const and f128const instructions #8893

Merged
merged 2 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading