From 5992c2f0ba1e10f62df10f9d902ef285e4042ad1 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 13 Jul 2023 10:03:28 +0300 Subject: [PATCH] Advance the port to https://github.com/llvm/llvm-project/commit/6dabc38cce73549ed747c537f81f6f4dd79eba39 (last APFloat-related LLVM commit from 2020). --- Cargo.toml | 2 +- fuzz/build.rs | 2 +- src/ieee.rs | 16 +++++----------- src/lib.rs | 2 +- tests/ieee.rs | 43 +++++++++++++++++++++++++++++++++++++------ 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 67b9702..ed38794 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = ["fuzz"] [workspace.package] -version = "0.0.5+llvm-a2588948febc" +version = "0.0.6+llvm-6dabc38cce73" edition = "2021" license = "Apache-2.0 WITH LLVM-exception" diff --git a/fuzz/build.rs b/fuzz/build.rs index fbce73a..6403516 100644 --- a/fuzz/build.rs +++ b/fuzz/build.rs @@ -91,7 +91,7 @@ echo | clang++ -x c++ - -std=c++17 \ $clang_codegen_flags \ -I "$llvm"/include \ -I "$OUT_DIR"/fake-config \ - -DNDEBUG -DHAVE_UNISTD_H \ + -DNDEBUG -DHAVE_UNISTD_H -DLLVM_ON_UNIX \ --include="$llvm"/lib/Support/{APInt,APFloat,SmallVector,ErrorHandling}.cpp \ --include="$OUT_DIR"/cxx_apf_fuzz.cpp \ -c -emit-llvm -o "$OUT_DIR"/cxx_apf_fuzz.bc diff --git a/src/ieee.rs b/src/ieee.rs index 783e1fa..0276516 100644 --- a/src/ieee.rs +++ b/src/ieee.rs @@ -100,13 +100,10 @@ pub trait Semantics: Sized { }; if r.exp == Self::MIN_EXP - 1 && r.sig == [0] { - // Exponent, significand meaningless. r.category = Category::Zero; } else if r.exp == Self::MAX_EXP + 1 && r.sig == [0] { - // Exponent, significand meaningless. r.category = Category::Infinity; } else if r.exp == Self::MAX_EXP + 1 && r.sig != [0] { - // Sign, exponent, significand meaningless. r.category = Category::NaN; } else { r.category = Category::Normal; @@ -220,16 +217,14 @@ impl Semantics for X87DoubleExtendedS { let integer_bit = r.sig[0] >> (Self::PRECISION - 1); if r.exp == Self::MIN_EXP - 1 && r.sig == [0] { - // Exponent, significand meaningless. r.category = Category::Zero; } else if r.exp == Self::MAX_EXP + 1 && r.sig == [1 << (Self::PRECISION - 1)] { - // Exponent, significand meaningless. r.category = Category::Infinity; } else if r.exp == Self::MAX_EXP + 1 && r.sig != [1 << (Self::PRECISION - 1)] || r.exp != Self::MAX_EXP + 1 && r.exp != Self::MIN_EXP - 1 && integer_bit == 0 { - // Sign, exponent, significand meaningless. r.category = Category::NaN; + r.exp = Self::MAX_EXP + 1; } else { r.category = Category::Normal; if r.exp == Self::MIN_EXP - 1 { @@ -1739,11 +1734,10 @@ impl FloatConvert> for IeeeFloat { sig::set_bit(&mut r.sig, T::PRECISION - 1); } - // gcc forces the Quiet bit on, which means (float)(double)(float_sNan) - // does not give you back the same bits. This is dubious, and we - // don't currently do it. You're really supposed to get - // an invalid operation signal at runtime, but nobody does that. - status = Status::OK; + // Convert of sNaN creates qNaN and raises an exception (invalid op). + // This also guarantees that a sNaN does not become Inf on a truncation + // that loses all payload bits. + r = unpack!(status=, IeeeDefaultExceptionHandling::result_from_nan(r)); } else { *loses_info = false; status = Status::OK; diff --git a/src/lib.rs b/src/lib.rs index fbf4fc6..4fd757f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! Port of LLVM's APFloat software floating-point implementation from the //! following C++ sources (please update commit hash when backporting): -//! https://github.com/llvm/llvm-project/commit/a2588948febccfed5ba074fc32dcb093484fa5c8 +//! https://github.com/llvm/llvm-project/commit/6dabc38cce73549ed747c537f81f6f4dd79eba39 //! * `llvm/include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits //! * `llvm/lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules //! * `llvm/unittests/ADT/APFloatTest.cpp` -> `tests` directory diff --git a/tests/ieee.rs b/tests/ieee.rs index 4aee378..81a2e52 100644 --- a/tests/ieee.rs +++ b/tests/ieee.rs @@ -1743,6 +1743,8 @@ fn copy_sign() { #[test] fn convert() { let mut loses_info = false; + let mut status; + let test = "1.0".parse::().unwrap(); let test: Single = test.convert(&mut loses_info).value; assert_eq!(1.0, test.to_f32()); @@ -1768,10 +1770,11 @@ fn convert() { assert!(!loses_info); let test = Single::snan(None); - let x87_snan = X87DoubleExtended::snan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_snan)); + let test: X87DoubleExtended = unpack!(status=, test.convert(&mut loses_info)); + // Conversion quiets the SNAN, so now 2 bits of the 64-bit significand should be set. + assert!(test.bitwise_eq(X87DoubleExtended::qnan(Some(0x6000000000000000)))); assert!(!loses_info); + assert_eq!(status, Status::INVALID_OP); let test = Single::qnan(None); let x87_qnan = X87DoubleExtended::qnan(None); @@ -1779,15 +1782,33 @@ fn convert() { assert!(test.bitwise_eq(x87_qnan)); assert!(!loses_info); - let test = X87DoubleExtended::snan(None); + // NOTE(eddyb) these were mistakenly noops upstream, here they're already + // fixed (by instead converting from `Double` to `X87DoubleExtended`), + // see also upstream issue https://github.com/llvm/llvm-project/issues/63842. + let test = Double::snan(None); let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_snan)); + // Conversion quiets the SNAN, so now 2 bits of the 64-bit significand should be set. + assert!(test.bitwise_eq(X87DoubleExtended::qnan(Some(0x6000000000000000)))); assert!(!loses_info); - let test = X87DoubleExtended::qnan(None); + let test = Double::qnan(None); let test: X87DoubleExtended = test.convert(&mut loses_info).value; assert!(test.bitwise_eq(x87_qnan)); assert!(!loses_info); + + // The payload is lost in truncation, but we retain NaN by setting the quiet bit. + let test = Double::snan(Some(1)); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0x7fc00000, test.to_bits()); + assert!(loses_info); + assert_eq!(status, Status::INVALID_OP); + + // The payload is lost in truncation. QNaN remains QNaN. + let test = Double::qnan(Some(1)); + let test: Single = unpack!(status=, test.convert(&mut loses_info)); + assert_eq!(0x7fc00000, test.to_bits()); + assert!(loses_info); + assert_eq!(status, Status::OK); } #[test] @@ -3978,3 +3999,13 @@ fn remainder() { assert_eq!(status, Status::OK); } } + +#[test] +fn x87_largest() { + assert!(X87DoubleExtended::largest().is_largest()); +} + +#[test] +fn x87_next() { + assert_eq!("-1.0".parse::().unwrap().next_up().value.ilogb(), -1); +}