Skip to content
Open
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
42 changes: 42 additions & 0 deletions builtins-test-intrinsics/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,11 @@ mod intrinsics {
x as f128
}

#[cfg(f16_enabled)]
pub fn floatsihf(x: i32) -> f16 {
x as f16
}

pub fn aeabi_idiv(a: i32, b: i32) -> i32 {
a.wrapping_div(b)
}
Expand All @@ -319,6 +324,11 @@ mod intrinsics {
x as f128
}

#[cfg(f16_enabled)]
pub fn floatdihf(x: i64) -> f16 {
x as f16
}

pub fn mulodi4(a: i64, b: i64) -> i64 {
a * b
}
Expand Down Expand Up @@ -352,6 +362,11 @@ mod intrinsics {
x as f128
}

#[cfg(f16_enabled)]
pub fn floattihf(x: i128) -> f16 {
x as f16
}

pub fn lshrti3(a: i128, b: usize) -> i128 {
a >> b
}
Expand Down Expand Up @@ -381,6 +396,11 @@ mod intrinsics {
x as f128
}

#[cfg(f16_enabled)]
pub fn floatunsihf(x: u32) -> f16 {
x as f16
}

pub fn aeabi_uidiv(a: u32, b: u32) -> u32 {
a / b
}
Expand All @@ -406,6 +426,11 @@ mod intrinsics {
x as f128
}

#[cfg(f16_enabled)]
pub fn floatundihf(x: u64) -> f16 {
x as f16
}

// udivdi3
pub fn aeabi_uldivmod(a: u64, b: u64) -> u64 {
a * b
Expand All @@ -430,6 +455,11 @@ mod intrinsics {
x as f128
}

#[cfg(f16_enabled)]
pub fn floatuntihf(x: u128) -> f16 {
x as f16
}

pub fn muloti4(a: u128, b: u128) -> Option<u128> {
a.checked_mul(b)
}
Expand Down Expand Up @@ -550,6 +580,18 @@ fn run() {
bb(floatuntisf(bb(2)));
#[cfg(f128_enabled)]
bb(floatuntitf(bb(2)));
#[cfg(f16_enabled)]
bb(floatsihf(bb(2)));
#[cfg(f16_enabled)]
bb(floatdihf(bb(2)));
#[cfg(f16_enabled)]
bb(floattihf(bb(2)));
#[cfg(f16_enabled)]
bb(floatunsihf(bb(2)));
#[cfg(f16_enabled)]
bb(floatundihf(bb(2)));
#[cfg(f16_enabled)]
bb(floatuntihf(bb(2)));
#[cfg(f128_enabled)]
bb(gttf(bb(2.), bb(2.)));
bb(lshrti3(bb(2), bb(2)));
Expand Down
17 changes: 15 additions & 2 deletions builtins-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum SetCfg {
NoSysF16,
NoSysF16F64Convert,
NoSysF16F128Convert,
NoSysF16IntConvert,
}

impl SetCfg {
Expand All @@ -22,15 +23,22 @@ impl SetCfg {
Self::NoSysF16,
Self::NoSysF16F64Convert,
Self::NoSysF16F128Convert,
Self::NoSysF16IntConvert,
];

fn implies(self) -> &'static [Self] {
match self {
Self::NoSysF128 => [Self::NoSysF128IntConvert, Self::NoSysF16F128Convert].as_slice(),
Self::NoSysF128IntConvert => [].as_slice(),
Self::NoSysF16 => [Self::NoSysF16F64Convert, Self::NoSysF16F128Convert].as_slice(),
Self::NoSysF16 => [
Self::NoSysF16F64Convert,
Self::NoSysF16F128Convert,
Self::NoSysF16IntConvert,
]
.as_slice(),
Self::NoSysF16F64Convert => [].as_slice(),
Self::NoSysF16F128Convert => [].as_slice(),
Self::NoSysF16IntConvert => [].as_slice(),
}
}

Expand All @@ -41,6 +49,7 @@ impl SetCfg {
Self::NoSysF16F64Convert => "no_sys_f16_f64_convert",
Self::NoSysF16F128Convert => "no_sys_f16_f128_convert",
Self::NoSysF16 => "no_sys_f16",
Self::NoSysF16IntConvert => "no_sys_f16_int_convert",
}
}
}
Expand Down Expand Up @@ -74,6 +83,8 @@ fn main() {
to_set.insert(SetCfg::NoSysF128IntConvert);
// FIXME: 32-bit x86 has a bug in `f128 -> f16` system libraries
to_set.insert(SetCfg::NoSysF16F128Convert);
// 32-bit x86 libgcc does not provide the 128-bit integer to `f16` routines
to_set.insert(SetCfg::NoSysF16IntConvert);
}

// These platforms do not have f16 symbols available in their system libraries, so
Expand All @@ -95,9 +106,11 @@ fn main() {
to_set.insert(SetCfg::NoSysF16);
}

// These platforms are missing either `__extendhfdf2` or `__truncdfhf2`.
// These platforms are missing either `__extendhfdf2` or `__truncdfhf2`, and also
// lack the integer-to-`f16` conversion routines in their system libraries.
if cfg.target_vendor == "apple" || cfg.target_os == "windows" {
to_set.insert(SetCfg::NoSysF16F64Convert);
to_set.insert(SetCfg::NoSysF16IntConvert);
}

// Add implied features. Collection is required for borrows.
Expand Down
110 changes: 76 additions & 34 deletions builtins-test/tests/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod i_to_f {
#[test]
fn $fn() {
use compiler_builtins::float::conv::$fn;
use compiler_builtins::support::Int;
use compiler_builtins::support::{Int, MinInt};

fuzz(N, |x: $i_ty| {
let f0 = apfloat_fallback!(
Expand All @@ -27,51 +27,83 @@ mod i_to_f {
// When the builtin is not available, we need to use a different conversion
// method (since apfloat doesn't support `as` casting).
|x: $i_ty| {
use compiler_builtins::support::MinInt;

let apf = if <$i_ty>::SIGNED {
FloatTy::from_i128(x.try_into().unwrap()).value
} else {
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
);
let f1: $f_ty = $fn(x);

// This makes sure that the conversion produced the best rounding possible,
// and does this independent of `x as $into` rounding correctly. It also
// exercises the `f -> i` cast, which catches ABI and platform bugs that a
// comparison against arbitrary-precision math would not.
//
#[cfg($sys_available)] {
// This makes sure that the conversion produced the best rounding possible, and does
// this independent of `x as $into` rounding correctly.
// This assumes that float to integer conversion is correct.
let y_minus_ulp = <$f_ty>::from_bits(f1.to_bits().wrapping_sub(1)) as $i_ty;
let y = f1 as $i_ty;
let y_plus_ulp = <$f_ty>::from_bits(f1.to_bits().wrapping_add(1)) as $i_ty;
let error_minus = <$i_ty as Int>::abs_diff(y_minus_ulp, x);
let error = <$i_ty as Int>::abs_diff(y, x);
let error_plus = <$i_ty as Int>::abs_diff(y_plus_ulp, x);

// The first two conditions check that none of the two closest float values are
// strictly closer in representation to `x`. The second makes sure that rounding is
// towards even significand if two float values are equally close to the integer.
if error_minus < error
|| error_plus < error
|| ((error_minus == error || error_plus == error)
&& ((f0.to_bits() & 1) != 0))
{
panic!(
"incorrect rounding by {}({}): {}, ({}, {}, {}), errors ({}, {}, {})",
stringify!($fn),
x,
f1.to_bits(),
y_minus_ulp,
y,
y_plus_ulp,
error_minus,
error,
error_plus,
);
if f1.is_infinite() {
// Integer-to-float overflow is rounded as though the exponent were
// unbounded. Recover the overflow threshold from the two largest
// finite values, avoiding the meaningless `inf`/NaN-to-int casts.
let y_toward_zero =
<$f_ty>::from_bits(f1.to_bits().wrapping_sub(1)) as $i_ty;
let y_next_toward_zero =
<$f_ty>::from_bits(f1.to_bits().wrapping_sub(2)) as $i_ty;
let half_ulp = <$i_ty as Int>::abs_diff(
y_toward_zero,
y_next_toward_zero,
) >> 1;
let error = <$i_ty as Int>::abs_diff(y_toward_zero, x);

// Equality is the tie case: the unbounded power of two has an even
// significand, so it rounds outward and then saturates to infinity.
let x_is_negative = <$i_ty>::SIGNED && x.signed() < 0;
if f1.is_sign_negative() != x_is_negative || error < half_ulp
{
panic!(
"incorrect overflow by {}({}): {}, threshold error {} (half ULP {})",
stringify!($fn),
x,
f1.to_bits(),
error,
half_ulp,
);
}
} else {
let y_minus_ulp =
<$f_ty>::from_bits(f1.to_bits().wrapping_sub(1)) as $i_ty;
let y = f1 as $i_ty;
let y_plus_ulp =
<$f_ty>::from_bits(f1.to_bits().wrapping_add(1)) as $i_ty;
let error_minus = <$i_ty as Int>::abs_diff(y_minus_ulp, x);
let error = <$i_ty as Int>::abs_diff(y, x);
let error_plus = <$i_ty as Int>::abs_diff(y_plus_ulp, x);

// The first two conditions check that none of the two closest float values are
// strictly closer in representation to `x`. The second makes sure that rounding is
// towards even significand if two float values are equally close to the integer.
if error_minus < error
|| error_plus < error
|| ((error_minus == error || error_plus == error)
&& ((f0.to_bits() & 1) != 0))
{
panic!(
"incorrect rounding by {}({}): {}, ({}, {}, {}), errors ({}, {}, {})",
stringify!($fn),
x,
f1.to_bits(),
y_minus_ulp,
y,
y_plus_ulp,
error_minus,
error,
error_plus,
);
}
}
}

Expand All @@ -97,6 +129,16 @@ mod i_to_f {
};
}

#[cfg(f16_enabled)]
i_to_f! { f16, Half, not(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
6 changes: 6 additions & 0 deletions compiler-builtins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,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
Loading
Loading