From d2d4f22e81311a02821ad025f96916d5e5f5e4cc Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 4 Nov 2024 18:19:49 -0600 Subject: [PATCH] Add integer to `f16` conversions These are not present in LLVM's `compiler-rt` but LLVM does emit them in some cases [1]. [1]: https://github.com/rust-lang/rust/issues/132614#issuecomment-2455910165 --- README.md | 6 ++ src/float/conv.rs | 152 +++++++++++++++++++++++++++++++++------- testcrate/Cargo.toml | 3 +- testcrate/build.rs | 6 ++ testcrate/tests/conv.rs | 12 +++- 5 files changed, 153 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a2b38cce..4c064a22 100644 --- a/README.md +++ b/README.md @@ -233,11 +233,17 @@ of being added to Rust. - [x] fixunstfdi.c - [x] fixunstfsi.c - [x] fixunstfti.c +- [x] floatdihf.c (not yet in `compiler-rt` but emitted by LLVM) - [x] floatditf.c +- [x] floatsihf.c (not yet in `compiler-rt` but emitted by LLVM) - [x] floatsitf.c +- [x] floattihf.c (not yet in `compiler-rt` but emitted by LLVM) - [x] floattitf.c +- [x] floatundihf.c (not yet in `compiler-rt` but emitted by LLVM) - [x] floatunditf.c +- [x] floatunsihf.c (not yet in `compiler-rt` but emitted by LLVM) - [x] floatunsitf.c +- [x] floatuntihf.c (not yet in `compiler-rt` but emitted by LLVM) - [x] floatuntitf.c - [x] multf3.c - [x] powitf2.c diff --git a/src/float/conv.rs b/src/float/conv.rs index 4aea67c9..5e9b995f 100644 --- a/src/float/conv.rs +++ b/src/float/conv.rs @@ -79,6 +79,26 @@ mod int_to_float { F::from_bits(conv(i.unsigned_abs()) | sign_bit) } + #[cfg(f16_enabled)] + pub fn u32_to_f16_bits(i: u32) -> u16 { + let n = i.leading_zeros(); + let i_m = i.wrapping_shl(n); + // Mantissa with implicit bit set + let m_base: u16 = (i_m >> shift_f_lt_i::()) as u16; + // The entire lower half of `i` will be truncated (masked portion), plus the + // next `EXPONENT_BITS` bits. + let adj = (i_m >> f16::EXPONENT_BITS | i_m & 0xFF) as u16; + let m = m_adj::(m_base, adj); + let e = if i == 0 { 0 } else { exp::(n) - 1 }; + // Any int can have an exponent out of range for `f16`, unlike other float types. + // Clamp this. + if e >= f16::EXPONENT_MAX as u16 - 1 { + f16::INFINITY.to_bits() + } else { + repr::(e, m) + } + } + pub fn u32_to_f32_bits(i: u32) -> u32 { if i == 0 { return 0; @@ -122,6 +142,33 @@ mod int_to_float { (h as u128) << 64 } + #[cfg(f16_enabled)] + pub fn u64_to_f16_bits(i: u64) -> u16 { + let n = i.leading_zeros(); + let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero + let m_base: u16 = (i_m >> shift_f_lt_i::()) as u16; + + // Within the upper `F::BITS`, everything except for the signifcand + // gets truncated + let d1: u16 = (i_m >> (u64::BITS - f16::BITS - f16::SIGNIFICAND_BITS - 1)).cast(); + + // The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just + // check if it is nonzero. + let d2: u16 = (i_m << f16::BITS >> f16::BITS != 0).into(); + let adj = d1 | d2; + + // Mantissa with implicit bit set + let m = m_adj::(m_base, adj); + let e = if i == 0 { 0 } else { exp::(n) - 1 }; + + // Clamp to infinity if the exponent is out of range + if e >= f16::EXPONENT_MAX as u16 - 1 { + f16::INFINITY.to_bits() + } else { + repr::(e, m) + } + } + pub fn u64_to_f32_bits(i: u64) -> u32 { let n = i.leading_zeros(); let i_m = i.wrapping_shl(n); @@ -160,6 +207,33 @@ mod int_to_float { repr::(e, m) } + #[cfg(f16_enabled)] + pub fn u128_to_f16_bits(i: u128) -> u16 { + let n = i.leading_zeros(); + let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero + let m_base: u16 = (i_m >> shift_f_lt_i::()) as u16; + + // Within the upper `F::BITS`, everything except for the signifcand + // gets truncated + let d1: u16 = (i_m >> (u128::BITS - f16::BITS - f16::SIGNIFICAND_BITS - 1)).cast(); + + // The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just + // check if it is nonzero. + let d2: u16 = (i_m << f16::BITS >> f16::BITS != 0).into(); + let adj = d1 | d2; + + // Mantissa with implicit bit set + let m = m_adj::(m_base, adj); + let e = if i == 0 { 0 } else { exp::(n) - 1 }; + + // Clamp to infinity if the exponent is out of range + if e >= f16::EXPONENT_MAX as u16 - 1 { + f16::INFINITY.to_bits() + } else { + repr::(e, m) + } + } + pub fn u128_to_f32_bits(i: u128) -> u32 { let n = i.leading_zeros(); let i_m = i.wrapping_shl(n); // Mantissa, shifted so the first bit is nonzero @@ -210,6 +284,11 @@ mod int_to_float { // Conversions from unsigned integers to floats. intrinsics! { + #[cfg(f16_enabled)] + pub extern "C" fn __floatunsihf(i: u32) -> f16 { + f16::from_bits(int_to_float::u32_to_f16_bits(i)) + } + #[arm_aeabi_alias = __aeabi_ui2f] pub extern "C" fn __floatunsisf(i: u32) -> f32 { f32::from_bits(int_to_float::u32_to_f32_bits(i)) @@ -220,6 +299,17 @@ intrinsics! { f64::from_bits(int_to_float::u32_to_f64_bits(i)) } + #[ppc_alias = __floatunsikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatunsitf(i: u32) -> f128 { + f128::from_bits(int_to_float::u32_to_f128_bits(i)) + } + + #[cfg(f16_enabled)] + pub extern "C" fn __floatundihf(i: u64) -> f16 { + f16::from_bits(int_to_float::u64_to_f16_bits(i)) + } + #[arm_aeabi_alias = __aeabi_ul2f] pub extern "C" fn __floatundisf(i: u64) -> f32 { f32::from_bits(int_to_float::u64_to_f32_bits(i)) @@ -230,6 +320,17 @@ intrinsics! { f64::from_bits(int_to_float::u64_to_f64_bits(i)) } + #[ppc_alias = __floatundikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatunditf(i: u64) -> f128 { + f128::from_bits(int_to_float::u64_to_f128_bits(i)) + } + + #[cfg(f16_enabled)] + pub extern "C" fn __floatuntihf(i: u128) -> f16 { + f16::from_bits(int_to_float::u128_to_f16_bits(i)) + } + #[cfg_attr(target_os = "uefi", unadjusted_on_win64)] pub extern "C" fn __floatuntisf(i: u128) -> f32 { f32::from_bits(int_to_float::u128_to_f32_bits(i)) @@ -240,18 +341,6 @@ intrinsics! { f64::from_bits(int_to_float::u128_to_f64_bits(i)) } - #[ppc_alias = __floatunsikf] - #[cfg(f128_enabled)] - pub extern "C" fn __floatunsitf(i: u32) -> f128 { - f128::from_bits(int_to_float::u32_to_f128_bits(i)) - } - - #[ppc_alias = __floatundikf] - #[cfg(f128_enabled)] - pub extern "C" fn __floatunditf(i: u64) -> f128 { - f128::from_bits(int_to_float::u64_to_f128_bits(i)) - } - #[ppc_alias = __floatuntikf] #[cfg(f128_enabled)] pub extern "C" fn __floatuntitf(i: u128) -> f128 { @@ -261,6 +350,11 @@ intrinsics! { // Conversions from signed integers to floats. intrinsics! { + #[cfg(f16_enabled)] + pub extern "C" fn __floatsihf(i: i32) -> f16 { + int_to_float::signed(i, int_to_float::u32_to_f16_bits) + } + #[arm_aeabi_alias = __aeabi_i2f] pub extern "C" fn __floatsisf(i: i32) -> f32 { int_to_float::signed(i, int_to_float::u32_to_f32_bits) @@ -271,6 +365,17 @@ intrinsics! { int_to_float::signed(i, int_to_float::u32_to_f64_bits) } + #[ppc_alias = __floatsikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatsitf(i: i32) -> f128 { + int_to_float::signed(i, int_to_float::u32_to_f128_bits) + } + + #[cfg(f16_enabled)] + pub extern "C" fn __floatdihf(i: i64) -> f16 { + int_to_float::signed(i, int_to_float::u64_to_f16_bits) + } + #[arm_aeabi_alias = __aeabi_l2f] pub extern "C" fn __floatdisf(i: i64) -> f32 { int_to_float::signed(i, int_to_float::u64_to_f32_bits) @@ -281,6 +386,17 @@ intrinsics! { int_to_float::signed(i, int_to_float::u64_to_f64_bits) } + #[ppc_alias = __floatdikf] + #[cfg(f128_enabled)] + pub extern "C" fn __floatditf(i: i64) -> f128 { + int_to_float::signed(i, int_to_float::u64_to_f128_bits) + } + + #[cfg(f16_enabled)] + pub extern "C" fn __floattihf(i: i128) -> f16 { + int_to_float::signed(i, int_to_float::u128_to_f16_bits) + } + #[cfg_attr(target_os = "uefi", unadjusted_on_win64)] pub extern "C" fn __floattisf(i: i128) -> f32 { int_to_float::signed(i, int_to_float::u128_to_f32_bits) @@ -291,18 +407,6 @@ intrinsics! { int_to_float::signed(i, int_to_float::u128_to_f64_bits) } - #[ppc_alias = __floatsikf] - #[cfg(f128_enabled)] - pub extern "C" fn __floatsitf(i: i32) -> f128 { - int_to_float::signed(i, int_to_float::u32_to_f128_bits) - } - - #[ppc_alias = __floatdikf] - #[cfg(f128_enabled)] - pub extern "C" fn __floatditf(i: i64) -> f128 { - int_to_float::signed(i, int_to_float::u64_to_f128_bits) - } - #[ppc_alias = __floattikf] #[cfg(f128_enabled)] pub extern "C" fn __floattitf(i: i128) -> f128 { diff --git a/testcrate/Cargo.toml b/testcrate/Cargo.toml index 21cec170..2299eda8 100644 --- a/testcrate/Cargo.toml +++ b/testcrate/Cargo.toml @@ -39,9 +39,10 @@ no-f16-f128 = ["compiler_builtins/no-f16-f128"] mem = ["compiler_builtins/mem"] mangled-names = ["compiler_builtins/mangled-names"] # Skip tests that rely on f128 symbols being available on the system -no-sys-f128 = ["no-sys-f128-int-convert", "no-sys-f16-f128-convert"] +no-sys-f128 = ["no-sys-f16-int-convert", "no-sys-f128-int-convert", "no-sys-f16-f128-convert"] # Some platforms have some f128 functions but everything except integer conversions no-sys-f128-int-convert = [] +no-sys-f16-int-convert = [] no-sys-f16-f128-convert = [] # Skip tests that rely on f16 symbols being available on the system no-sys-f16 = [] diff --git a/testcrate/build.rs b/testcrate/build.rs index 6205c7ac..8da4d559 100644 --- a/testcrate/build.rs +++ b/testcrate/build.rs @@ -6,6 +6,7 @@ enum Feature { NoSysF128, NoSysF128IntConvert, NoSysF16, + NoSysF16IntConvert, NoSysF16F128Convert, } @@ -40,6 +41,7 @@ fn main() { { features.insert(Feature::NoSysF128); features.insert(Feature::NoSysF128IntConvert); + features.insert(Feature::NoSysF16IntConvert); features.insert(Feature::NoSysF16F128Convert); } @@ -76,6 +78,10 @@ fn main() { "no-sys-f128-int-convert", "using apfloat fallback for f128 <-> int conversions", ), + Feature::NoSysF16IntConvert => ( + "no-sys-f16-int-convert", + "using apfloat fallback for f16 <-> int conversions", + ), Feature::NoSysF16F128Convert => ( "no-sys-f16-f128-convert", "using apfloat fallback for f16 <-> f128 conversions", diff --git a/testcrate/tests/conv.rs b/testcrate/tests/conv.rs index a08748af..0b8c85f8 100644 --- a/testcrate/tests/conv.rs +++ b/testcrate/tests/conv.rs @@ -34,7 +34,7 @@ mod i_to_f { FloatTy::from_u128(x.try_into().unwrap()).value }; - <$f_ty>::from_bits(apf.to_bits()) + <$f_ty>::from_bits(apf.to_bits().try_into().unwrap()) }, x ); @@ -100,6 +100,16 @@ mod i_to_f { }; } + #[cfg(f16_enabled)] + i_to_f! { f16, Half, not(feature = "no-sys-f16-int-convert"), + u32, __floatunsihf; + i32, __floatsihf; + u64, __floatundihf; + i64, __floatdihf; + u128, __floatuntihf; + i128, __floattihf; + } + i_to_f! { f32, Single, all(), u32, __floatunsisf; i32, __floatsisf;