Skip to content

Commit 3775f2f

Browse files
authored
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 #124583 r? ``@RalfJung``
2 parents a224902 + e649042 commit 3775f2f

File tree

6 files changed

+89
-112
lines changed

6 files changed

+89
-112
lines changed

compiler/rustc_const_eval/src/interpret/operator.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -362,14 +362,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
362362
let left = left.to_scalar();
363363
let right = right.to_scalar();
364364
Ok(match fty {
365-
FloatTy::F16 => unimplemented!("f16_f128"),
365+
FloatTy::F16 => {
366+
self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?)
367+
}
366368
FloatTy::F32 => {
367369
self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?)
368370
}
369371
FloatTy::F64 => {
370372
self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?)
371373
}
372-
FloatTy::F128 => unimplemented!("f16_f128"),
374+
FloatTy::F128 => {
375+
self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?)
376+
}
373377
})
374378
}
375379
_ if left.layout.ty.is_integral() => {
@@ -429,11 +433,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
429433
}
430434
ty::Float(fty) => {
431435
let val = val.to_scalar();
436+
if un_op != Neg {
437+
span_bug!(self.cur_span(), "Invalid float op {:?}", un_op);
438+
}
439+
432440
// No NaN adjustment here, `-` is a bitwise operation!
433-
let res = match (un_op, fty) {
434-
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
435-
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
436-
_ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op),
441+
let res = match fty {
442+
FloatTy::F16 => Scalar::from_f16(-val.to_f16()?),
443+
FloatTy::F32 => Scalar::from_f32(-val.to_f32()?),
444+
FloatTy::F64 => Scalar::from_f64(-val.to_f64()?),
445+
FloatTy::F128 => Scalar::from_f128(-val.to_f128()?),
437446
};
438447
Ok(ImmTy::from_scalar(res, layout))
439448
}

compiler/rustc_middle/src/mir/interpret/value.rs

+14
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> {
6969
}
7070
}
7171

72+
impl<Prov> From<Half> for Scalar<Prov> {
73+
#[inline(always)]
74+
fn from(f: Half) -> Self {
75+
Scalar::from_f16(f)
76+
}
77+
}
78+
7279
impl<Prov> From<Single> for Scalar<Prov> {
7380
#[inline(always)]
7481
fn from(f: Single) -> Self {
@@ -83,6 +90,13 @@ impl<Prov> From<Double> for Scalar<Prov> {
8390
}
8491
}
8592

93+
impl<Prov> From<Quad> for Scalar<Prov> {
94+
#[inline(always)]
95+
fn from(f: Quad) -> Self {
96+
Scalar::from_f128(f)
97+
}
98+
}
99+
86100
impl<Prov> From<ScalarInt> for Scalar<Prov> {
87101
#[inline(always)]
88102
fn from(ptr: ScalarInt) -> Self {

src/tools/miri/src/helpers.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::time::Duration;
77

88
use rand::RngCore;
99

10-
use rustc_apfloat::ieee::{Double, Single};
10+
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
1111
use rustc_apfloat::Float;
1212
use rustc_hir::{
1313
def::{DefKind, Namespace},
@@ -1201,12 +1201,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
12011201
};
12021202

12031203
let (val, status) = match fty {
1204-
FloatTy::F16 => unimplemented!("f16_f128"),
1204+
FloatTy::F16 =>
1205+
float_to_int_inner::<Half>(this, src.to_scalar().to_f16()?, cast_to, round),
12051206
FloatTy::F32 =>
12061207
float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
12071208
FloatTy::F64 =>
12081209
float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
1209-
FloatTy::F128 => unimplemented!("f16_f128"),
1210+
FloatTy::F128 =>
1211+
float_to_int_inner::<Quad>(this, src.to_scalar().to_f128()?, cast_to, round),
12101212
};
12111213

12121214
if status.intersects(

src/tools/miri/tests/pass/float.rs

+51-98
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![feature(stmt_expr_attributes)]
22
#![feature(float_gamma)]
33
#![feature(core_intrinsics)]
4+
#![feature(f128)]
5+
#![feature(f16)]
46
#![allow(arithmetic_overflow)]
57

68
use std::fmt::Debug;
@@ -41,103 +43,23 @@ trait FloatToInt<Int>: Copy {
4143
unsafe fn cast_unchecked(self) -> Int;
4244
}
4345

44-
impl FloatToInt<i8> for f32 {
45-
fn cast(self) -> i8 {
46-
self as _
47-
}
48-
unsafe fn cast_unchecked(self) -> i8 {
49-
self.to_int_unchecked()
50-
}
51-
}
52-
impl FloatToInt<i32> for f32 {
53-
fn cast(self) -> i32 {
54-
self as _
55-
}
56-
unsafe fn cast_unchecked(self) -> i32 {
57-
self.to_int_unchecked()
58-
}
59-
}
60-
impl FloatToInt<u32> for f32 {
61-
fn cast(self) -> u32 {
62-
self as _
63-
}
64-
unsafe fn cast_unchecked(self) -> u32 {
65-
self.to_int_unchecked()
66-
}
67-
}
68-
impl FloatToInt<i64> for f32 {
69-
fn cast(self) -> i64 {
70-
self as _
71-
}
72-
unsafe fn cast_unchecked(self) -> i64 {
73-
self.to_int_unchecked()
74-
}
75-
}
76-
impl FloatToInt<u64> for f32 {
77-
fn cast(self) -> u64 {
78-
self as _
79-
}
80-
unsafe fn cast_unchecked(self) -> u64 {
81-
self.to_int_unchecked()
82-
}
46+
macro_rules! float_to_int {
47+
($fty:ty => $($ity:ty),+ $(,)?) => {
48+
$(
49+
impl FloatToInt<$ity> for $fty {
50+
fn cast(self) -> $ity {
51+
self as _
52+
}
53+
unsafe fn cast_unchecked(self) -> $ity {
54+
self.to_int_unchecked()
55+
}
56+
}
57+
)*
58+
};
8359
}
8460

85-
impl FloatToInt<i8> for f64 {
86-
fn cast(self) -> i8 {
87-
self as _
88-
}
89-
unsafe fn cast_unchecked(self) -> i8 {
90-
self.to_int_unchecked()
91-
}
92-
}
93-
impl FloatToInt<i32> for f64 {
94-
fn cast(self) -> i32 {
95-
self as _
96-
}
97-
unsafe fn cast_unchecked(self) -> i32 {
98-
self.to_int_unchecked()
99-
}
100-
}
101-
impl FloatToInt<u32> for f64 {
102-
fn cast(self) -> u32 {
103-
self as _
104-
}
105-
unsafe fn cast_unchecked(self) -> u32 {
106-
self.to_int_unchecked()
107-
}
108-
}
109-
impl FloatToInt<i64> for f64 {
110-
fn cast(self) -> i64 {
111-
self as _
112-
}
113-
unsafe fn cast_unchecked(self) -> i64 {
114-
self.to_int_unchecked()
115-
}
116-
}
117-
impl FloatToInt<u64> for f64 {
118-
fn cast(self) -> u64 {
119-
self as _
120-
}
121-
unsafe fn cast_unchecked(self) -> u64 {
122-
self.to_int_unchecked()
123-
}
124-
}
125-
impl FloatToInt<i128> for f64 {
126-
fn cast(self) -> i128 {
127-
self as _
128-
}
129-
unsafe fn cast_unchecked(self) -> i128 {
130-
self.to_int_unchecked()
131-
}
132-
}
133-
impl FloatToInt<u128> for f64 {
134-
fn cast(self) -> u128 {
135-
self as _
136-
}
137-
unsafe fn cast_unchecked(self) -> u128 {
138-
self.to_int_unchecked()
139-
}
140-
}
61+
float_to_int!(f32 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
62+
float_to_int!(f64 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
14163

14264
/// Test this cast both via `as` and via `approx_unchecked` (i.e., it must not saturate).
14365
#[track_caller]
@@ -153,18 +75,29 @@ where
15375

15476
fn basic() {
15577
// basic arithmetic
78+
assert_eq(6.0_f16 * 6.0_f16, 36.0_f16);
15679
assert_eq(6.0_f32 * 6.0_f32, 36.0_f32);
15780
assert_eq(6.0_f64 * 6.0_f64, 36.0_f64);
81+
assert_eq(6.0_f128 * 6.0_f128, 36.0_f128);
82+
assert_eq(-{ 5.0_f16 }, -5.0_f16);
15883
assert_eq(-{ 5.0_f32 }, -5.0_f32);
15984
assert_eq(-{ 5.0_f64 }, -5.0_f64);
85+
assert_eq(-{ 5.0_f128 }, -5.0_f128);
86+
16087
// infinities, NaN
88+
// FIXME(f16_f128): add when constants and `is_infinite` are available
16189
assert!((5.0_f32 / 0.0).is_infinite());
16290
assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 });
16391
assert!((5.0_f64 / 0.0).is_infinite());
16492
assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 });
16593
assert_ne!(f32::NAN, f32::NAN);
16694
assert_ne!(f64::NAN, f64::NAN);
95+
16796
// negative zero
97+
let posz = 0.0f16;
98+
let negz = -0.0f16;
99+
assert_eq(posz, negz);
100+
assert_ne!(posz.to_bits(), negz.to_bits());
168101
let posz = 0.0f32;
169102
let negz = -0.0f32;
170103
assert_eq(posz, negz);
@@ -173,15 +106,30 @@ fn basic() {
173106
let negz = -0.0f64;
174107
assert_eq(posz, negz);
175108
assert_ne!(posz.to_bits(), negz.to_bits());
109+
let posz = 0.0f128;
110+
let negz = -0.0f128;
111+
assert_eq(posz, negz);
112+
assert_ne!(posz.to_bits(), negz.to_bits());
113+
176114
// byte-level transmute
177-
let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
178-
let y: f64 = unsafe { std::mem::transmute(x) };
179-
assert_eq(y, 42.0_f64);
115+
let x: u16 = unsafe { std::mem::transmute(42.0_f16) };
116+
let y: f16 = unsafe { std::mem::transmute(x) };
117+
assert_eq(y, 42.0_f16);
180118
let x: u32 = unsafe { std::mem::transmute(42.0_f32) };
181119
let y: f32 = unsafe { std::mem::transmute(x) };
182120
assert_eq(y, 42.0_f32);
121+
let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
122+
let y: f64 = unsafe { std::mem::transmute(x) };
123+
assert_eq(y, 42.0_f64);
124+
let x: u128 = unsafe { std::mem::transmute(42.0_f128) };
125+
let y: f128 = unsafe { std::mem::transmute(x) };
126+
assert_eq(y, 42.0_f128);
183127

184128
// `%` sign behavior, some of this used to be buggy
129+
assert!((black_box(1.0f16) % 1.0).is_sign_positive());
130+
assert!((black_box(1.0f16) % -1.0).is_sign_positive());
131+
assert!((black_box(-1.0f16) % 1.0).is_sign_negative());
132+
assert!((black_box(-1.0f16) % -1.0).is_sign_negative());
185133
assert!((black_box(1.0f32) % 1.0).is_sign_positive());
186134
assert!((black_box(1.0f32) % -1.0).is_sign_positive());
187135
assert!((black_box(-1.0f32) % 1.0).is_sign_negative());
@@ -190,7 +138,12 @@ fn basic() {
190138
assert!((black_box(1.0f64) % -1.0).is_sign_positive());
191139
assert!((black_box(-1.0f64) % 1.0).is_sign_negative());
192140
assert!((black_box(-1.0f64) % -1.0).is_sign_negative());
141+
assert!((black_box(1.0f128) % 1.0).is_sign_positive());
142+
assert!((black_box(1.0f128) % -1.0).is_sign_positive());
143+
assert!((black_box(-1.0f128) % 1.0).is_sign_negative());
144+
assert!((black_box(-1.0f128) % -1.0).is_sign_negative());
193145

146+
// FIXME(f16_f128): add when `abs` is available
194147
assert_eq!((-1.0f32).abs(), 1.0f32);
195148
assert_eq!(34.2f64.abs(), 34.2f64);
196149
}

tests/crashes/124583.rs

-5
This file was deleted.

tests/ui/numbers-arithmetic/f16-f128-lit.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Make sure negation happens correctly. Also included:
2+
// issue: rust-lang/rust#124583
13
//@ run-pass
24

35
#![feature(f16)]
@@ -8,9 +10,11 @@ fn main() {
810
assert_eq!((-0.0_f16).to_bits(), 0x8000);
911
assert_eq!(10.0_f16.to_bits(), 0x4900);
1012
assert_eq!((-10.0_f16).to_bits(), 0xC900);
13+
assert_eq!((-(-0.0f16)).to_bits(), 0x0000);
1114

1215
assert_eq!(0.0_f128.to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000);
1316
assert_eq!((-0.0_f128).to_bits(), 0x8000_0000_0000_0000_0000_0000_0000_0000);
1417
assert_eq!(10.0_f128.to_bits(), 0x4002_4000_0000_0000_0000_0000_0000_0000);
1518
assert_eq!((-10.0_f128).to_bits(), 0xC002_4000_0000_0000_0000_0000_0000_0000);
19+
assert_eq!((-(-0.0f128)).to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000);
1620
}

0 commit comments

Comments
 (0)