Ver código fonte

Merge pull request #222 from whitequark/master

Implement comparesf2/comparedf2 intrinsics
Alex Crichton 7 anos atrás
pai
commit
bf912e607e
8 arquivos alterados com 544 adições e 14 exclusões
  1. 12 8
      README.md
  2. 318 3
      build.rs
  3. 172 0
      src/float/cmp.rs
  4. 14 3
      src/float/mod.rs
  5. 7 0
      tests/gedf2.rs
  6. 7 0
      tests/gesf2.rs
  7. 7 0
      tests/ledf2.rs
  8. 7 0
      tests/lesf2.rs

+ 12 - 8
README.md

@@ -52,16 +52,20 @@ features = ["c"]
 ## Contributing
 
 1. Pick one or more intrinsics from the [pending list](#progress).
-2. Fork this repository
-3. Port the intrinsic(s) and their corresponding [unit tests][1] from their [C implementation][2] to
-   Rust.
-4. Send a Pull Request (PR)
-5. Once the PR passes our extensive [testing infrastructure][3], we'll merge it!
+2. Fork this repository.
+3. Port the intrinsic(s) and their corresponding [unit tests][1] from their
+   [C implementation][2] to Rust.
+4. Implement a [test generator][3] to compare the behavior of the ported intrinsic(s)
+   with their implementation on the testing host. Note that randomized compiler-builtin tests
+   should be run using `cargo test --features gen-tests`.
+4. Send a Pull Request (PR).
+5. Once the PR passes our extensive [testing infrastructure][4], we'll merge it!
 6. Celebrate :tada:
 
 [1]: https://github.com/rust-lang/compiler-rt/tree/8598065bd965d9713bfafb6c1e766d63a7b17b89/test/builtins/Unit
 [2]: https://github.com/rust-lang/compiler-rt/tree/8598065bd965d9713bfafb6c1e766d63a7b17b89/lib/builtins
-[3]: https://travis-ci.org/rust-lang-nursery/compiler-builtins
+[3]: https://github.com/rust-lang-nursery/compiler-builtins/blob/0ba07e49264a54cb5bbd4856fcea083bb3fbec15/build.rs#L180-L265
+[4]: https://travis-ci.org/rust-lang-nursery/compiler-builtins
 
 ### Porting Reminders
 
@@ -133,6 +137,8 @@ features = ["c"]
 - [ ] arm/unordsf2vfp.S
 - [x] ashldi3.c
 - [x] ashrdi3.c
+- [x] comparedf2.c
+- [x] comparesf2.c
 - [x] divdf3.c
 - [x] divdi3.c
 - [x] divmoddi4.c
@@ -301,8 +307,6 @@ These builtins are never called by LLVM.
 - ~~clzti2.c~~
 - ~~cmpdi2.c~~
 - ~~cmpti2.c~~
-- ~~comparedf2.c~~
-- ~~comparesf2.c~~
 - ~~ctzdi2.c~~
 - ~~ctzsi2.c~~
 - ~~ctzti2.c~~

+ 318 - 3
build.rs

@@ -89,6 +89,12 @@ mod tests {
             Adddf3,
             Addsf3,
 
+            // float/cmp.rs
+            Gedf2,
+            Gesf2,
+            Ledf2,
+            Lesf2,
+
             // float/conv.rs
             Fixdfdi,
             Fixdfsi,
@@ -2529,6 +2535,318 @@ fn floatuntidf() {
         }
     }
 
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Gedf2 {
+        a: u64,
+        b: u64,
+        c: i32,
+    }
+
+    impl TestCase for Gedf2 {
+        fn name() -> &'static str {
+            "gedf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f64(rng);
+            let b = gen_f64(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = -1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Gedf2 { a: to_u64(a), b: to_u64(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__gedf2;
+
+fn to_f64(x: u64) -> f64 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u64, u64), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn gedf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __gedf2(to_f64(a), to_f64(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Gesf2 {
+        a: u32,
+        b: u32,
+        c: i32,
+    }
+
+    impl TestCase for Gesf2 {
+        fn name() -> &'static str {
+            "gesf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f32(rng);
+            let b = gen_f32(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = -1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Gesf2 { a: to_u32(a), b: to_u32(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__gesf2;
+
+fn to_f32(x: u32) -> f32 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u32, u32), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn gesf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __gesf2(to_f32(a), to_f32(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Ledf2 {
+        a: u64,
+        b: u64,
+        c: i32,
+    }
+
+    impl TestCase for Ledf2 {
+        fn name() -> &'static str {
+            "ledf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f64(rng);
+            let b = gen_f64(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = 1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Ledf2 { a: to_u64(a), b: to_u64(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__ledf2;
+
+fn to_f64(x: u64) -> f64 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u64, u64), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn ledf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __ledf2(to_f64(a), to_f64(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Lesf2 {
+        a: u32,
+        b: u32,
+        c: i32,
+    }
+
+    impl TestCase for Lesf2 {
+        fn name() -> &'static str {
+            "lesf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f32(rng);
+            let b = gen_f32(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = 1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Lesf2 { a: to_u32(a), b: to_u32(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__lesf2;
+
+fn to_f32(x: u32) -> f32 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u32, u32), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn lesf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __lesf2(to_f32(a), to_f32(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
     #[derive(Eq, Hash, PartialEq)]
     pub struct Moddi3 {
         a: i64,
@@ -4982,8 +5300,6 @@ mod c {
                 "clzdi2.c",
                 "clzsi2.c",
                 "cmpdi2.c",
-                "comparedf2.c",
-                "comparesf2.c",
                 "ctzdi2.c",
                 "ctzsi2.c",
                 "divdc3.c",
@@ -5127,7 +5443,6 @@ mod c {
                     "arm/bswapsi2.S",
                     "arm/clzdi2.S",
                     "arm/clzsi2.S",
-                    "arm/comparesf2.S",
                     "arm/divmodsi4.S",
                     "arm/modsi3.S",
                     "arm/switch16.S",

+ 172 - 0
src/float/cmp.rs

@@ -0,0 +1,172 @@
+#![allow(unreachable_code)]
+
+use int::{Int, CastInto};
+use float::Float;
+
+#[derive(Clone, Copy)]
+enum Result {
+    Less,
+    Equal,
+    Greater,
+    Unordered
+}
+
+impl Result {
+    fn to_le_abi(self) -> i32 {
+        match self {
+            Result::Less      => -1,
+            Result::Equal     => 0,
+            Result::Greater   => 1,
+            Result::Unordered => 1
+        }
+    }
+
+    fn to_ge_abi(self) -> i32 {
+        match self {
+            Result::Less      => -1,
+            Result::Equal     => 0,
+            Result::Greater   => 1,
+            Result::Unordered => -1
+        }
+    }
+}
+
+fn cmp<F: Float>(a: F, b: F) -> Result where
+    u32: CastInto<F::Int>,
+    F::Int: CastInto<u32>,
+    i32: CastInto<F::Int>,
+    F::Int: CastInto<i32>,
+{
+    let one   = F::Int::ONE;
+    let zero  = F::Int::ZERO;
+    let szero = F::SignedInt::ZERO;
+
+    let sign_bit =      F::SIGN_MASK as F::Int;
+    let abs_mask =      sign_bit - one;
+    let exponent_mask = F::EXPONENT_MASK;
+    let inf_rep =       exponent_mask;
+
+    let a_rep  = a.repr();
+    let b_rep  = b.repr();
+    let a_abs  = a_rep & abs_mask;
+    let b_abs  = b_rep & abs_mask;
+
+    // If either a or b is NaN, they are unordered.
+    if a_abs > inf_rep || b_abs > inf_rep {
+        return Result::Unordered
+    }
+
+    // If a and b are both zeros, they are equal.
+    if a_abs | b_abs == zero {
+        return Result::Equal
+    }
+
+    let a_srep = a.signed_repr();
+    let b_srep = b.signed_repr();
+
+    // If at least one of a and b is positive, we get the same result comparing
+    // a and b as signed integers as we would with a fp_ting-point compare.
+    if a_srep & b_srep >= szero {
+        if a_srep < b_srep {
+            return Result::Less
+        } else if a_srep == b_srep {
+            return Result::Equal
+        } else {
+            return Result::Greater
+        }
+    }
+
+    // Otherwise, both are negative, so we need to flip the sense of the
+    // comparison to get the correct result.  (This assumes a twos- or ones-
+    // complement integer representation; if integers are represented in a
+    // sign-magnitude representation, then this flip is incorrect).
+    else {
+        if a_srep > b_srep {
+            return Result::Less
+        } else if a_srep == b_srep {
+            return Result::Equal
+        } else {
+            return Result::Greater
+        }
+    }
+}
+fn unord<F: Float>(a: F, b: F) -> bool where
+    u32: CastInto<F::Int>,
+    F::Int: CastInto<u32>,
+    i32: CastInto<F::Int>,
+    F::Int: CastInto<i32>,
+{
+    let one = F::Int::ONE;
+
+    let sign_bit =      F::SIGN_MASK as F::Int;
+    let abs_mask =      sign_bit - one;
+    let exponent_mask = F::EXPONENT_MASK;
+    let inf_rep =       exponent_mask;
+
+    let a_rep = a.repr();
+    let b_rep = b.repr();
+    let a_abs = a_rep & abs_mask;
+    let b_abs = b_rep & abs_mask;
+
+    a_abs > inf_rep || b_abs > inf_rep
+}
+
+intrinsics! {
+    pub extern "C" fn __lesf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __gesf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_ge_abi()
+    }
+
+    #[arm_aeabi_alias = fcmpun]
+    pub extern "C" fn __unordsf2(a: f32, b: f32) -> i32 {
+        unord(a, b) as i32
+    }
+
+    pub extern "C" fn __eqsf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __ltsf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __nesf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __gtsf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_ge_abi()
+    }
+
+    pub extern "C" fn __ledf2(a: f64, b: f64) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __gedf2(a: f64, b: f64) -> i32 {
+        cmp(a, b).to_ge_abi()
+    }
+
+    #[arm_aeabi_alias = dcmpun]
+    pub extern "C" fn __unorddf2(a: f64, b: f64) -> i32 {
+        unord(a, b) as i32
+    }
+
+    pub extern "C" fn __eqdf2(a: f64, b: f64) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __ltdf2(a: f64, b: f64) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __nedf2(a: f64, b: f64) -> i32 {
+        cmp(a, b).to_le_abi()
+    }
+
+    pub extern "C" fn __gtdf2(a: f32, b: f32) -> i32 {
+        cmp(a, b).to_ge_abi()
+    }
+}

+ 14 - 3
src/float/mod.rs

@@ -4,6 +4,7 @@ use core::ops;
 use super::int::Int;
 
 pub mod conv;
+pub mod cmp;
 pub mod add;
 pub mod pow;
 pub mod sub;
@@ -25,6 +26,9 @@ pub trait Float:
     /// A uint of the same with as the float
     type Int: Int;
 
+    /// A int of the same with as the float
+    type SignedInt: Int;
+
     const ZERO: Self;
     const ONE: Self;
 
@@ -58,6 +62,9 @@ pub trait Float:
     /// Returns `self` transmuted to `Self::Int`
     fn repr(self) -> Self::Int;
 
+    /// Returns `self` transmuted to `Self::SignedInt`
+    fn signed_repr(self) -> Self::SignedInt;
+
     #[cfg(test)]
     /// Checks if two floats have the same bit representation. *Except* for NaNs! NaN can be
     /// represented in multiple different ways. This method returns `true` if two NaNs are
@@ -77,9 +84,10 @@ pub trait Float:
 // FIXME: Some of this can be removed if RFC Issue #1424 is resolved
 //        https://github.com/rust-lang/rfcs/issues/1424
 macro_rules! float_impl {
-    ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {
+    ($ty:ident, $ity:ident, $sity:ident, $bits:expr, $significand_bits:expr) => {
         impl Float for $ty {
             type Int = $ity;
+            type SignedInt = $sity;
             const ZERO: Self = 0.0;
             const ONE: Self = 1.0;
 
@@ -94,6 +102,9 @@ macro_rules! float_impl {
             fn repr(self) -> Self::Int {
                 unsafe { mem::transmute(self) }
             }
+            fn signed_repr(self) -> Self::SignedInt {
+                unsafe { mem::transmute(self) }
+            }
             #[cfg(test)]
             fn eq_repr(self, rhs: Self) -> bool {
                 if self.is_nan() && rhs.is_nan() {
@@ -119,5 +130,5 @@ macro_rules! float_impl {
     }
 }
 
-float_impl!(f32, u32, 32, 23);
-float_impl!(f64, u64, 64, 52);
+float_impl!(f32, u32, i32, 32, 23);
+float_impl!(f64, u64, i64, 64, 52);

+ 7 - 0
tests/gedf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/gedf2.rs"));

+ 7 - 0
tests/gesf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/gesf2.rs"));

+ 7 - 0
tests/ledf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/ledf2.rs"));

+ 7 - 0
tests/lesf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/lesf2.rs"));