Rollup merge of #126429 - tgross35:f16-f128-const-eval, r=RalfJung
Add `f16` and `f128` const eval for binary and unary operationations Add const evaluation and Miri support for f16 and f128, including unary and binary operations. Casts are not yet included. Fixes https://github.com/rust-lang/rust/issues/124583 r? ``@RalfJung``
This commit is contained in:
commit
3775f2f5d0
6 changed files with 89 additions and 112 deletions
|
@ -362,14 +362,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let left = left.to_scalar();
|
||||
let right = right.to_scalar();
|
||||
Ok(match fty {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F16 => {
|
||||
self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?)
|
||||
}
|
||||
FloatTy::F32 => {
|
||||
self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?)
|
||||
}
|
||||
FloatTy::F64 => {
|
||||
self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?)
|
||||
}
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
FloatTy::F128 => {
|
||||
self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?)
|
||||
}
|
||||
})
|
||||
}
|
||||
_ if left.layout.ty.is_integral() => {
|
||||
|
@ -429,11 +433,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
ty::Float(fty) => {
|
||||
let val = val.to_scalar();
|
||||
if un_op != Neg {
|
||||
span_bug!(self.cur_span(), "Invalid float op {:?}", un_op);
|
||||
}
|
||||
|
||||
// No NaN adjustment here, `-` is a bitwise operation!
|
||||
let res = match (un_op, fty) {
|
||||
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
|
||||
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
|
||||
_ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op),
|
||||
let res = match fty {
|
||||
FloatTy::F16 => Scalar::from_f16(-val.to_f16()?),
|
||||
FloatTy::F32 => Scalar::from_f32(-val.to_f32()?),
|
||||
FloatTy::F64 => Scalar::from_f64(-val.to_f64()?),
|
||||
FloatTy::F128 => Scalar::from_f128(-val.to_f128()?),
|
||||
};
|
||||
Ok(ImmTy::from_scalar(res, layout))
|
||||
}
|
||||
|
|
|
@ -69,6 +69,13 @@ impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Prov> From<Half> for Scalar<Prov> {
|
||||
#[inline(always)]
|
||||
fn from(f: Half) -> Self {
|
||||
Scalar::from_f16(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prov> From<Single> for Scalar<Prov> {
|
||||
#[inline(always)]
|
||||
fn from(f: Single) -> Self {
|
||||
|
@ -83,6 +90,13 @@ impl<Prov> From<Double> for Scalar<Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Prov> From<Quad> for Scalar<Prov> {
|
||||
#[inline(always)]
|
||||
fn from(f: Quad) -> Self {
|
||||
Scalar::from_f128(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prov> From<ScalarInt> for Scalar<Prov> {
|
||||
#[inline(always)]
|
||||
fn from(ptr: ScalarInt) -> Self {
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::time::Duration;
|
|||
|
||||
use rand::RngCore;
|
||||
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_hir::{
|
||||
def::{DefKind, Namespace},
|
||||
|
@ -1201,12 +1201,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
};
|
||||
|
||||
let (val, status) = match fty {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F16 =>
|
||||
float_to_int_inner::<Half>(this, src.to_scalar().to_f16()?, cast_to, round),
|
||||
FloatTy::F32 =>
|
||||
float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
|
||||
FloatTy::F64 =>
|
||||
float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
FloatTy::F128 =>
|
||||
float_to_int_inner::<Quad>(this, src.to_scalar().to_f128()?, cast_to, round),
|
||||
};
|
||||
|
||||
if status.intersects(
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(float_gamma)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![allow(arithmetic_overflow)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
@ -41,103 +43,23 @@ trait FloatToInt<Int>: Copy {
|
|||
unsafe fn cast_unchecked(self) -> Int;
|
||||
}
|
||||
|
||||
impl FloatToInt<i8> for f32 {
|
||||
fn cast(self) -> i8 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i8 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<i32> for f32 {
|
||||
fn cast(self) -> i32 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i32 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<u32> for f32 {
|
||||
fn cast(self) -> u32 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> u32 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<i64> for f32 {
|
||||
fn cast(self) -> i64 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i64 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<u64> for f32 {
|
||||
fn cast(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> u64 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
macro_rules! float_to_int {
|
||||
($fty:ty => $($ity:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl FloatToInt<$ity> for $fty {
|
||||
fn cast(self) -> $ity {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> $ity {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl FloatToInt<i8> for f64 {
|
||||
fn cast(self) -> i8 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i8 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<i32> for f64 {
|
||||
fn cast(self) -> i32 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i32 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<u32> for f64 {
|
||||
fn cast(self) -> u32 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> u32 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<i64> for f64 {
|
||||
fn cast(self) -> i64 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i64 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<u64> for f64 {
|
||||
fn cast(self) -> u64 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> u64 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<i128> for f64 {
|
||||
fn cast(self) -> i128 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> i128 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
impl FloatToInt<u128> for f64 {
|
||||
fn cast(self) -> u128 {
|
||||
self as _
|
||||
}
|
||||
unsafe fn cast_unchecked(self) -> u128 {
|
||||
self.to_int_unchecked()
|
||||
}
|
||||
}
|
||||
float_to_int!(f32 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
|
||||
float_to_int!(f64 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
|
||||
|
||||
/// Test this cast both via `as` and via `approx_unchecked` (i.e., it must not saturate).
|
||||
#[track_caller]
|
||||
|
@ -153,18 +75,29 @@ where
|
|||
|
||||
fn basic() {
|
||||
// basic arithmetic
|
||||
assert_eq(6.0_f16 * 6.0_f16, 36.0_f16);
|
||||
assert_eq(6.0_f32 * 6.0_f32, 36.0_f32);
|
||||
assert_eq(6.0_f64 * 6.0_f64, 36.0_f64);
|
||||
assert_eq(6.0_f128 * 6.0_f128, 36.0_f128);
|
||||
assert_eq(-{ 5.0_f16 }, -5.0_f16);
|
||||
assert_eq(-{ 5.0_f32 }, -5.0_f32);
|
||||
assert_eq(-{ 5.0_f64 }, -5.0_f64);
|
||||
assert_eq(-{ 5.0_f128 }, -5.0_f128);
|
||||
|
||||
// infinities, NaN
|
||||
// FIXME(f16_f128): add when constants and `is_infinite` are available
|
||||
assert!((5.0_f32 / 0.0).is_infinite());
|
||||
assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 });
|
||||
assert!((5.0_f64 / 0.0).is_infinite());
|
||||
assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 });
|
||||
assert_ne!(f32::NAN, f32::NAN);
|
||||
assert_ne!(f64::NAN, f64::NAN);
|
||||
|
||||
// negative zero
|
||||
let posz = 0.0f16;
|
||||
let negz = -0.0f16;
|
||||
assert_eq(posz, negz);
|
||||
assert_ne!(posz.to_bits(), negz.to_bits());
|
||||
let posz = 0.0f32;
|
||||
let negz = -0.0f32;
|
||||
assert_eq(posz, negz);
|
||||
|
@ -173,15 +106,30 @@ fn basic() {
|
|||
let negz = -0.0f64;
|
||||
assert_eq(posz, negz);
|
||||
assert_ne!(posz.to_bits(), negz.to_bits());
|
||||
let posz = 0.0f128;
|
||||
let negz = -0.0f128;
|
||||
assert_eq(posz, negz);
|
||||
assert_ne!(posz.to_bits(), negz.to_bits());
|
||||
|
||||
// byte-level transmute
|
||||
let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
|
||||
let y: f64 = unsafe { std::mem::transmute(x) };
|
||||
assert_eq(y, 42.0_f64);
|
||||
let x: u16 = unsafe { std::mem::transmute(42.0_f16) };
|
||||
let y: f16 = unsafe { std::mem::transmute(x) };
|
||||
assert_eq(y, 42.0_f16);
|
||||
let x: u32 = unsafe { std::mem::transmute(42.0_f32) };
|
||||
let y: f32 = unsafe { std::mem::transmute(x) };
|
||||
assert_eq(y, 42.0_f32);
|
||||
let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
|
||||
let y: f64 = unsafe { std::mem::transmute(x) };
|
||||
assert_eq(y, 42.0_f64);
|
||||
let x: u128 = unsafe { std::mem::transmute(42.0_f128) };
|
||||
let y: f128 = unsafe { std::mem::transmute(x) };
|
||||
assert_eq(y, 42.0_f128);
|
||||
|
||||
// `%` sign behavior, some of this used to be buggy
|
||||
assert!((black_box(1.0f16) % 1.0).is_sign_positive());
|
||||
assert!((black_box(1.0f16) % -1.0).is_sign_positive());
|
||||
assert!((black_box(-1.0f16) % 1.0).is_sign_negative());
|
||||
assert!((black_box(-1.0f16) % -1.0).is_sign_negative());
|
||||
assert!((black_box(1.0f32) % 1.0).is_sign_positive());
|
||||
assert!((black_box(1.0f32) % -1.0).is_sign_positive());
|
||||
assert!((black_box(-1.0f32) % 1.0).is_sign_negative());
|
||||
|
@ -190,7 +138,12 @@ fn basic() {
|
|||
assert!((black_box(1.0f64) % -1.0).is_sign_positive());
|
||||
assert!((black_box(-1.0f64) % 1.0).is_sign_negative());
|
||||
assert!((black_box(-1.0f64) % -1.0).is_sign_negative());
|
||||
assert!((black_box(1.0f128) % 1.0).is_sign_positive());
|
||||
assert!((black_box(1.0f128) % -1.0).is_sign_positive());
|
||||
assert!((black_box(-1.0f128) % 1.0).is_sign_negative());
|
||||
assert!((black_box(-1.0f128) % -1.0).is_sign_negative());
|
||||
|
||||
// FIXME(f16_f128): add when `abs` is available
|
||||
assert_eq!((-1.0f32).abs(), 1.0f32);
|
||||
assert_eq!(34.2f64.abs(), 34.2f64);
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
//@ known-bug: rust-lang/rust#124583
|
||||
|
||||
fn main() {
|
||||
let _ = -(-0.0f16);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
// Make sure negation happens correctly. Also included:
|
||||
// issue: rust-lang/rust#124583
|
||||
//@ run-pass
|
||||
|
||||
#![feature(f16)]
|
||||
|
@ -8,9 +10,11 @@ fn main() {
|
|||
assert_eq!((-0.0_f16).to_bits(), 0x8000);
|
||||
assert_eq!(10.0_f16.to_bits(), 0x4900);
|
||||
assert_eq!((-10.0_f16).to_bits(), 0xC900);
|
||||
assert_eq!((-(-0.0f16)).to_bits(), 0x0000);
|
||||
|
||||
assert_eq!(0.0_f128.to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000);
|
||||
assert_eq!((-0.0_f128).to_bits(), 0x8000_0000_0000_0000_0000_0000_0000_0000);
|
||||
assert_eq!(10.0_f128.to_bits(), 0x4002_4000_0000_0000_0000_0000_0000_0000);
|
||||
assert_eq!((-10.0_f128).to_bits(), 0xC002_4000_0000_0000_0000_0000_0000_0000);
|
||||
assert_eq!((-(-0.0f128)).to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue