Skip to content

Commit

Permalink
Add integer to f16 conversions
Browse files Browse the repository at this point in the history
These are not present in LLVM's `compiler-rt` but LLVM does emit them in
some cases [1].

[1]: rust-lang/rust#132614 (comment)
  • Loading branch information
tgross35 committed Nov 5, 2024
1 parent e344295 commit 7230b09
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 26 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
149 changes: 125 additions & 24 deletions src/float/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,25 @@ mod int_to_float {
F::from_bits(conv(i.unsigned_abs()) | sign_bit)
}

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::<u32, f16>()) 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::<f16>(m_base, adj);
let e = if i == 0 { 0 } else { exp::<u32, f16>(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::<f16>(e, m)
}
}

pub fn u32_to_f32_bits(i: u32) -> u32 {
if i == 0 {
return 0;
Expand Down Expand Up @@ -122,6 +141,32 @@ mod int_to_float {
(h as u128) << 64
}

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::<u64, f16>()) 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::<f16>(m_base, adj);
let e = if i == 0 { 0 } else { exp::<u64, f16>(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::<f16>(e, m)
}
}

pub fn u64_to_f32_bits(i: u64) -> u32 {
let n = i.leading_zeros();
let i_m = i.wrapping_shl(n);
Expand Down Expand Up @@ -160,6 +205,32 @@ mod int_to_float {
repr::<f128>(e, m)
}

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::<u128, f16>()) 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::<f16>(m_base, adj);
let e = if i == 0 { 0 } else { exp::<u128, f16>(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::<f16>(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
Expand Down Expand Up @@ -210,6 +281,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))
Expand All @@ -220,6 +296,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))
Expand All @@ -230,6 +317,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))
Expand All @@ -240,18 +338,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 {
Expand All @@ -261,6 +347,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)
Expand All @@ -271,6 +362,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)
Expand All @@ -281,6 +383,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)
Expand All @@ -291,18 +404,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 {
Expand Down
3 changes: 2 additions & 1 deletion testcrate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
6 changes: 6 additions & 0 deletions testcrate/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum Feature {
NoSysF128,
NoSysF128IntConvert,
NoSysF16,
NoSysF16IntConvert,
NoSysF16F128Convert,
}

Expand Down Expand Up @@ -40,6 +41,7 @@ fn main() {
{
features.insert(Feature::NoSysF128);
features.insert(Feature::NoSysF128IntConvert);
features.insert(Feature::NoSysF16IntConvert);
features.insert(Feature::NoSysF16F128Convert);
}

Expand Down Expand Up @@ -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",
Expand Down
12 changes: 11 additions & 1 deletion testcrate/tests/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 7230b09

Please sign in to comment.