about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/compiler-builtins/libm/README.md1
-rw-r--r--library/compiler-builtins/libm/src/fabs.rs5
-rw-r--r--library/compiler-builtins/libm/src/lib.rs7
-rw-r--r--library/compiler-builtins/libm/test-generator/src/main.rs228
4 files changed, 230 insertions, 11 deletions
diff --git a/library/compiler-builtins/libm/README.md b/library/compiler-builtins/libm/README.md
index 17c4ee94df5..a0b131733d2 100644
--- a/library/compiler-builtins/libm/README.md
+++ b/library/compiler-builtins/libm/README.md
@@ -96,6 +96,7 @@ cf. [rustwasm/team#84](https://github.com/rustwasm/team/issues/84).
 
 ### Other functions
 
+- [x] fabs
 - [x] fabsf
 - [x] scalbnf
 - [x] sqrtf
diff --git a/library/compiler-builtins/libm/src/fabs.rs b/library/compiler-builtins/libm/src/fabs.rs
new file mode 100644
index 00000000000..993918efcfb
--- /dev/null
+++ b/library/compiler-builtins/libm/src/fabs.rs
@@ -0,0 +1,5 @@
+use core::u64;
+
+pub fn fabs(x: f64) -> f64 {
+    f64::from_bits(x.to_bits() & (u64::MAX / 2))
+}
diff --git a/library/compiler-builtins/libm/src/lib.rs b/library/compiler-builtins/libm/src/lib.rs
index 41c20b2dae5..3f71fe876c6 100644
--- a/library/compiler-builtins/libm/src/lib.rs
+++ b/library/compiler-builtins/libm/src/lib.rs
@@ -1,12 +1,14 @@
 #![deny(warnings)]
 #![no_std]
 
+mod fabs;
 mod fabsf;
 mod fmodf;
 mod powf;
 mod scalbnf;
 mod sqrtf;
 
+pub use fabs::fabs;
 pub use fabsf::fabsf;
 pub use fmodf::fmodf;
 pub use powf::powf;
@@ -19,6 +21,11 @@ pub fn _eqf(a: u32, b: u32) -> bool {
     (a as i32).wrapping_sub(b as i32).abs() <= 1
 }
 
+#[doc(hidden)]
+pub fn _eq(a: u64, b: u64) -> bool {
+    (a as i64).wrapping_sub(b as i64).abs() <= 1
+}
+
 fn isnanf(x: f32) -> bool {
     x.to_bits() & 0x7fffffff > 0x7f800000
 }
diff --git a/library/compiler-builtins/libm/test-generator/src/main.rs b/library/compiler-builtins/libm/test-generator/src/main.rs
index 3e58f7e1a08..fb9154aecc6 100644
--- a/library/compiler-builtins/libm/test-generator/src/main.rs
+++ b/library/compiler-builtins/libm/test-generator/src/main.rs
@@ -10,14 +10,14 @@ use std::error::Error;
 use std::fmt::Write as _0;
 use std::fs::{self, File};
 use std::io::Write as _1;
-use std::{i16, u32, u8};
+use std::{i16, u16, u32, u64, u8};
 
 use rand::{Rng, SeedableRng, XorShiftRng};
 
 // Number of test cases to generate
 const NTESTS: usize = 10_000;
 
-// TODO tweak this function to generate edge cases (zero, infinity, NaN) more often
+// TODO tweak these functions to generate edge cases (zero, infinity, NaN) more often
 fn f32(rng: &mut XorShiftRng) -> f32 {
     let sign = if rng.gen_bool(0.5) { 1 << 31 } else { 0 };
     let exponent = (rng.gen_range(0, u8::MAX) as u32) << 23;
@@ -26,13 +26,21 @@ fn f32(rng: &mut XorShiftRng) -> f32 {
     f32::from_bits(sign + exponent + mantissa)
 }
 
+fn f64(rng: &mut XorShiftRng) -> f64 {
+    let sign = if rng.gen_bool(0.5) { 1 << 63 } else { 0 };
+    let exponent = (rng.gen_range(0, u16::MAX) as u64 & ((1 << 11) - 1)) << 52;
+    let mantissa = rng.gen_range(0, u64::MAX) & ((1 << 52) - 1);
+
+    f64::from_bits(sign + exponent + mantissa)
+}
+
 // fn(f32) -> f32
 macro_rules! f32_f32 {
-    ($($intr:ident,)+) => {
+    ($($intr:ident,)*) => {
         fn f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
             // MUSL C implementation of the function to test
             extern "C" {
-                $(fn $intr(_: f32) -> f32;)+
+                $(fn $intr(_: f32) -> f32;)*
             }
 
             $(
@@ -78,18 +86,19 @@ macro_rules! f32_f32 {
 ",
                        stringify!($intr),
                        cases)?;
-            )+
+            )*
 
             Ok(())
         }
     }
 }
 
+// fn(f32, f32) -> f32
 macro_rules! f32f32_f32 {
-    ($($intr:ident,)+) => {
+    ($($intr:ident,)*) => {
         fn f32f32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
             extern "C" {
-                $(fn $intr(_: f32, _: f32) -> f32;)+
+                $(fn $intr(_: f32, _: f32) -> f32;)*
             }
 
             $(
@@ -137,18 +146,19 @@ macro_rules! f32f32_f32 {
 ",
                        stringify!($intr),
                        cases)?;
-            )+
+            )*
 
             Ok(())
         }
     };
 }
 
+// fn(f32, i32) -> f32
 macro_rules! f32i32_f32 {
-    ($($intr:ident,)+) => {
+    ($($intr:ident,)*) => {
         fn f32i32_f32(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
             extern "C" {
-                $(fn $intr(_: f32, _: i32) -> f32;)+
+                $(fn $intr(_: f32, _: i32) -> f32;)*
             }
 
             $(
@@ -195,7 +205,185 @@ macro_rules! f32i32_f32 {
 ",
                        stringify!($intr),
                        cases)?;
-            )+
+            )*
+
+            Ok(())
+        }
+    };
+}
+
+// fn(f64) -> f64
+macro_rules! f64_f64 {
+    ($($intr:ident,)*) => {
+        fn f64_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
+            // MUSL C implementation of the function to test
+            extern "C" {
+                $(fn $intr(_: f64) -> f64;)*
+            }
+
+            $(
+                let mut cases = String::new();
+                for _ in 0..NTESTS {
+                    let inp = f64(rng);
+                    let out = unsafe { $intr(inp) };
+
+                    let inp = inp.to_bits();
+                    let out = out.to_bits();
+
+                    write!(cases, "({}, {})", inp, out).unwrap();
+                    cases.push(',');
+                }
+
+                let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
+                write!(f, "
+                    extern crate libm;
+
+                    #[test]
+                    fn {0}() {{
+                        const CASES: &[(u64, u64)] = &[
+                            {1}
+                        ];
+
+                        for case in CASES {{
+                            let (inp, expected) = *case;
+
+                            let outf = libm::{0}(f64::from_bits(inp));
+                            let outi = outf.to_bits();
+
+                            if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
+                                 libm::_eq(outi, expected)) {{
+                                panic!(
+                                    \"input: {{}}, output: {{}}, expected: {{}}\",
+                                    inp,
+                                    outi,
+                                    expected,
+                                );
+                            }}
+                        }}
+                    }}
+",
+                       stringify!($intr),
+                       cases)?;
+            )*
+
+            Ok(())
+        }
+    }
+}
+
+// fn(f64, f64) -> f64
+macro_rules! f64f64_f64 {
+    ($($intr:ident,)*) => {
+        fn f64f64_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
+            extern "C" {
+                $(fn $intr(_: f64, _: f64) -> f64;)*
+            }
+
+            $(
+                let mut cases = String::new();
+                for _ in 0..NTESTS {
+                    let i1 = f64(rng);
+                    let i2 = f64(rng);
+                    let out = unsafe { $intr(i1, i2) };
+
+                    let i1 = i1.to_bits();
+                    let i2 = i2.to_bits();
+                    let out = out.to_bits();
+
+                    write!(cases, "(({}, {}), {})", i1, i2, out).unwrap();
+                    cases.push(',');
+                }
+
+                let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
+                write!(f, "
+                    extern crate libm;
+
+                    #[test]
+                    fn {0}() {{
+                        const CASES: &[((u64, u64), u64)] = &[
+                            {1}
+                        ];
+
+                        for case in CASES {{
+                            let ((i1, i2), expected) = *case;
+
+                            let outf = libm::{0}(f64::from_bits(i1), f64::from_bits(i2));
+                            let outi = outf.to_bits();
+
+                            if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
+                                 libm::_eq(outi, expected)) {{
+                                panic!(
+                                    \"input: {{:?}}, output: {{}}, expected: {{}}\",
+                                    (i1, i2),
+                                    outi,
+                                    expected,
+                                );
+                            }}
+                        }}
+                    }}
+",
+                       stringify!($intr),
+                       cases)?;
+            )*
+
+            Ok(())
+        }
+    };
+}
+
+// fn(f64, i32) -> f64
+macro_rules! f64i32_f64 {
+    ($($intr:ident,)*) => {
+        fn f64i32_f64(rng: &mut XorShiftRng) -> Result<(), Box<Error>> {
+            extern "C" {
+                $(fn $intr(_: f64, _: i32) -> f64;)*
+            }
+
+            $(
+                let mut cases = String::new();
+                for _ in 0..NTESTS {
+                    let i1 = f64(rng);
+                    let i2 = rng.gen_range(i16::MIN, i16::MAX);
+                    let out = unsafe { $intr(i1, i2 as i32) };
+
+                    let i1 = i1.to_bits();
+                    let out = out.to_bits();
+
+                    write!(cases, "(({}, {}), {})", i1, i2, out).unwrap();
+                    cases.push(',');
+                }
+
+                let mut f = File::create(concat!("tests/", stringify!($intr), ".rs"))?;
+                write!(f, "
+                    extern crate libm;
+
+                    #[test]
+                    fn {0}() {{
+                        const CASES: &[((u64, i16), u64)] = &[
+                            {1}
+                        ];
+
+                        for case in CASES {{
+                            let ((i1, i2), expected) = *case;
+
+                            let outf = libm::{0}(f64::from_bits(i1), i2 as i32);
+                            let outi = outf.to_bits();
+
+                            if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
+                                 libm::_eq(outi, expected)) {{
+                                panic!(
+                                    \"input: {{:?}}, output: {{}}, expected: {{}}\",
+                                    (i1, i2),
+                                    outi,
+                                    expected,
+                                );
+                            }}
+                        }}
+                    }}
+",
+                       stringify!($intr),
+                       cases)?;
+            )*
 
             Ok(())
         }
@@ -211,6 +399,9 @@ fn main() -> Result<(), Box<Error>> {
     f32_f32(&mut rng)?;
     f32f32_f32(&mut rng)?;
     f32i32_f32(&mut rng)?;
+    f64_f64(&mut rng)?;
+    f64f64_f64(&mut rng)?;
+    f64i32_f64(&mut rng)?;
 
     Ok(())
 }
@@ -233,3 +424,18 @@ f32f32_f32! {
 f32i32_f32! {
     scalbnf,
 }
+
+// With signature `fn(f64) -> f64`
+f64_f64! {
+    fabs,
+}
+
+// With signature `fn(f64, f64) -> f64`
+f64f64_f64! {
+    // fmod,
+}
+
+// With signature `fn(f64, i32) -> f64`
+f64i32_f64! {
+    // scalbn,
+}