Skip to content

Commit 875ab8d

Browse files
tgross35eduardosmeddyb
committed
Add TF32
This floating point type is used by some accelerators (e.g., AMD and NVIDIA GPUs). Upstream LLVM commit: llvm/llvm-project@55c2211a233e ("[APFloat] Add APFloat semantic support for TF32") Mostly authored by Eddy and Eduardo, I just updated some of the denormal tests. Created from two commits (not in tree): * 70fe4d8 ("Add TF32") * 07807bf ("Port TF32 tests") Co-authored-by: Eduardo Sánchez Muñoz <eduardosm-dev@e64.io> Co-authored-by: Eduard-Mihai Burtescu <eddyb@lyken.rs>
1 parent 80a5e1d commit 875ab8d

4 files changed

Lines changed: 118 additions & 4 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
members = ["fuzz"]
33

44
[workspace.package]
5-
version = "0.2.3+llvm-687bd77e2c26"
5+
version = "0.2.3+llvm-55c2211a233e"
66
edition = "2021"
77
license = "Apache-2.0 WITH LLVM-exception"
88

src/ieee.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,11 @@ ieee_semantics! {
424424
const NAN_ENCODING: NanEncoding = NanEncoding::NegativeZero;
425425
const MIN_EXP: ExpInt = Self::IEEE_MIN_EXP - 4;
426426
},
427+
428+
/// Floating point number that occupies 32 bits or less of storage, providing
429+
/// improved range compared to half (16-bit) formats, at (potentially)
430+
/// greater throughput than single precision (32-bit) formats.
431+
FloatTF32 = FloatTF32S(19:8),
427432
}
428433

429434
// FIXME(eddyb) consider moving X87-specific logic to a "has explicit integer bit"

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Port of LLVM's APFloat software floating-point implementation from the
22
//! following C++ sources (please update commit hash when backporting):
3-
//! <https://github.com/llvm/llvm-project/commit/687bd77e2c26487cba727aacfa7067dd01286be0>
3+
//! <https://github.com/llvm/llvm-project/commit/55c2211a233e11179048cf58778f40e5a62f444a>
44
//! * `llvm/include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits
55
//! * `llvm/lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules
66
//! * `llvm/unittests/ADT/APFloatTest.cpp` -> `tests` directory

tests/ieee.rs

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ extern crate rustc_apfloat;
33

44
use core::cmp::Ordering;
55
use rustc_apfloat::ieee::{
6-
BFloat, Double, Float8E4M3B11FNUZ, Float8E4M3FN, Float8E4M3FNUZ, Float8E5M2, Float8E5M2FNUZ, Half, Quad, Single,
7-
X87DoubleExtended,
6+
BFloat, Double, Float8E4M3B11FNUZ, Float8E4M3FN, Float8E4M3FNUZ, Float8E5M2, Float8E5M2FNUZ, FloatTF32, Half, Quad,
7+
Single, X87DoubleExtended,
88
};
99
use rustc_apfloat::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO};
1010
use rustc_apfloat::{Float, FloatConvert, Round, Status, StatusAnd};
@@ -35,6 +35,7 @@ define_for_each_float_type! {
3535
Float8E4M3FNUZ,
3636
Float8E5M2FNUZ,
3737
Float8E4M3B11FNUZ,
38+
FloatTF32,
3839
X87DoubleExtended,
3940

4041
// NOTE(eddyb) tests for this are usually in `ppc.rs` but this works too.
@@ -95,6 +96,7 @@ impl ToF32LosslessViaConvertToSingle for Float8E4M3FN {}
9596
impl ToF32LosslessViaConvertToSingle for Float8E4M3FNUZ {}
9697
impl ToF32LosslessViaConvertToSingle for Float8E5M2FNUZ {}
9798
impl ToF32LosslessViaConvertToSingle for Float8E4M3B11FNUZ {}
99+
impl ToF32LosslessViaConvertToSingle for FloatTF32 {}
98100

99101
trait ToF64LosslessViaConvertToDouble: FloatConvert<Double> {
100102
fn to_f64(self) -> f64 {
@@ -849,6 +851,24 @@ fn denormal() {
849851
t /= Quad::from_u128(2).value;
850852
assert!(t.is_denormal());
851853
}
854+
855+
// Test TF32
856+
{
857+
let mut t = "1.17549435082228750797e-38".parse::<FloatTF32>().unwrap();
858+
assert!(!t.is_denormal());
859+
assert!(!FloatTF32::from_u128(0).value.is_denormal());
860+
861+
t /= FloatTF32::from_u128(2).value;
862+
assert!(t.is_denormal());
863+
assert!(t.is_sign_positive());
864+
865+
let mut t = "-1.17549435082228750797e-38".parse::<FloatTF32>().unwrap();
866+
assert!(!t.is_denormal());
867+
868+
t /= FloatTF32::from_u128(2).value;
869+
assert!(t.is_denormal());
870+
assert!(t.is_sign_negative());
871+
}
852872
}
853873

854874
#[test]
@@ -1516,6 +1536,19 @@ fn nan() {
15161536
(0x80, true, false, 0xaa),
15171537
(0x80, true, true, 0xaa),
15181538
];
1539+
let test_tf32 = [
1540+
// ex. SNaN Neg payload
1541+
(0x3fe00, false, false, 0x00000000),
1542+
(0x7fe00, false, true, 0x00000000),
1543+
(0x3feaa, false, false, 0xaa),
1544+
(0x3ffaa, false, false, 0xdaa),
1545+
(0x3ffaa, false, false, 0xfdaa),
1546+
(0x3fd00, true, false, 0x00000000),
1547+
(0x7fd00, true, true, 0x00000000),
1548+
(0x3fcaa, true, false, 0xaa),
1549+
(0x3fdaa, true, false, 0xfaa),
1550+
(0x3fdaa, true, false, 0x1aa),
1551+
];
15191552
for (expected, signaling, negative, payload) in tests_single {
15201553
assert_eq!(expected, nanbits_from_u128::<Single>(signaling, negative, payload));
15211554
}
@@ -1531,6 +1564,9 @@ fn nan() {
15311564
for (expected, signaling, negative, payload) in tests_8e4m3b11fnuz {
15321565
assert_eq!(expected, nanbits_from_u128::<Float8E4M3B11FNUZ>(signaling, negative, payload));
15331566
}
1567+
for (expected, signaling, negative, payload) in test_tf32 {
1568+
assert_eq!(expected, nanbits_from_u128::<FloatTF32>(signaling, negative, payload));
1569+
}
15341570
}
15351571

15361572
#[test]
@@ -1888,6 +1924,7 @@ fn largest() {
18881924
assert_eq!(240.0, Float8E4M3FNUZ::largest().to_f64());
18891925
assert_eq!(57344.0, Float8E5M2FNUZ::largest().to_f64());
18901926
assert_eq!(30.0, Float8E4M3B11FNUZ::largest().to_f64());
1927+
assert_eq!(3.40116213421e+38, FloatTF32::largest().to_f32());
18911928
}
18921929

18931930
#[test]
@@ -1940,6 +1977,13 @@ fn smallest() {
19401977
assert!(test.is_finite_non_zero());
19411978
assert!(test.is_denormal());
19421979
assert!(test.bitwise_eq(expected));
1980+
1981+
let test = -FloatTF32::SMALLEST;
1982+
let expected = "-0x0.004p-126".parse::<FloatTF32>().unwrap();
1983+
assert!(test.is_negative());
1984+
assert!(test.is_finite_non_zero());
1985+
assert!(test.is_denormal());
1986+
assert!(test.bitwise_eq(expected));
19431987
}
19441988

19451989
#[test]
@@ -2015,6 +2059,14 @@ fn smallest_normalized() {
20152059
assert!(!test.is_denormal());
20162060
assert!(test.bitwise_eq(expected));
20172061
assert!(test.is_smallest_normalized());
2062+
2063+
let test = FloatTF32::smallest_normalized();
2064+
let expected = "0x1p-126".parse::<FloatTF32>().unwrap();
2065+
assert!(!test.is_negative());
2066+
assert!(test.is_finite_non_zero());
2067+
assert!(!test.is_denormal());
2068+
assert!(test.bitwise_eq(expected));
2069+
assert!(test.is_smallest_normalized());
20182070
}
20192071

20202072
#[test]
@@ -2056,6 +2108,8 @@ fn zero() {
20562108
test::<Float8E4M3FNUZ>(true, false, 0);
20572109
test::<Float8E4M3B11FNUZ>(false, false, 0);
20582110
test::<Float8E4M3B11FNUZ>(true, false, 0);
2111+
test::<FloatTF32>(false, true, 0);
2112+
test::<FloatTF32>(true, true, 0x40000);
20592113
}
20602114

20612115
#[test]
@@ -5573,6 +5627,32 @@ fn float8e4m3fnuz_to_f64() {
55735627
assert!(qnan.to_f64().is_nan());
55745628
}
55755629

5630+
#[test]
5631+
fn float_tf32_to_f64() {
5632+
let one = "1.0".parse::<FloatTF32>().unwrap();
5633+
assert_eq!(1.0, one.to_f64());
5634+
let pos_largest = FloatTF32::largest();
5635+
assert_eq!(3.401162134214653489792616e+38, pos_largest.to_f64());
5636+
let neg_largest = -FloatTF32::largest();
5637+
assert_eq!(-3.401162134214653489792616e+38, neg_largest.to_f64());
5638+
let pos_smallest = FloatTF32::smallest_normalized();
5639+
assert_eq!(1.1754943508222875079687e-38, pos_smallest.to_f64());
5640+
let neg_smallest = -FloatTF32::smallest_normalized();
5641+
assert_eq!(-1.1754943508222875079687e-38, neg_smallest.to_f64());
5642+
5643+
let smallest_denorm = FloatTF32::SMALLEST;
5644+
assert_eq!(1.1479437019748901445007e-41, smallest_denorm.to_f64());
5645+
let largest_denorm = "0x1.FF8p-127".parse::<FloatTF32>().unwrap();
5646+
assert_eq!(1.1743464071203126178242e-38, largest_denorm.to_f64());
5647+
5648+
let pos_inf = FloatTF32::INFINITY;
5649+
assert_eq!(f64::INFINITY, pos_inf.to_f64());
5650+
let neg_inf = -FloatTF32::INFINITY;
5651+
assert_eq!(f64::NEG_INFINITY, neg_inf.to_f64());
5652+
let qnan = FloatTF32::NAN;
5653+
assert!(qnan.to_f64().is_nan());
5654+
}
5655+
55765656
#[test]
55775657
fn float8e5m2fnuz_to_f32() {
55785658
let pos_zero = Float8E5M2FNUZ::ZERO;
@@ -5791,3 +5871,32 @@ fn float8e4m3fn_to_f32() {
57915871
let qnan = Float8E4M3FN::qnan(None);
57925872
assert!(qnan.to_f32().is_nan());
57935873
}
5874+
5875+
#[test]
5876+
fn float_tf32_to_f32() {
5877+
let pos_zero = FloatTF32::ZERO;
5878+
assert!(Single::from_f32(pos_zero.to_f32()).is_pos_zero());
5879+
let neg_zero = -FloatTF32::ZERO;
5880+
assert!(Single::from_f32(neg_zero.to_f32()).is_neg_zero());
5881+
5882+
let one = "1.0".parse::<FloatTF32>().unwrap();
5883+
assert_eq!(1.0, one.to_f32());
5884+
let two = "2.0".parse::<FloatTF32>().unwrap();
5885+
assert_eq!(2.0, two.to_f32());
5886+
5887+
let pos_largest = FloatTF32::largest();
5888+
assert_eq!(3.40116213421e+38, pos_largest.to_f32());
5889+
let neg_largest = -FloatTF32::largest();
5890+
assert_eq!(-3.40116213421e+38, neg_largest.to_f32());
5891+
let pos_smallest = FloatTF32::smallest_normalized();
5892+
assert_eq!(/*0x1.p-126*/ 1.1754943508222875e-38, pos_smallest.to_f32());
5893+
let neg_smallest = -FloatTF32::smallest_normalized();
5894+
assert_eq!(/*-0x1.p-126*/ -1.1754943508222875e-38, neg_smallest.to_f32());
5895+
5896+
let smallest_denorm = FloatTF32::SMALLEST;
5897+
assert!(smallest_denorm.is_denormal());
5898+
assert_eq!(/*0x0.004p-126*/ 1.148e-41, smallest_denorm.to_f32());
5899+
5900+
let qnan = FloatTF32::qnan(None);
5901+
assert!(qnan.to_f32().is_nan());
5902+
}

0 commit comments

Comments
 (0)