about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-10-17 15:24:26 +0000
committerbors <bors@rust-lang.org>2023-10-17 15:24:26 +0000
commit2366a90d3f4f39c5f241176d88c57f1ee4bd5c05 (patch)
tree7e9788ab0aab0b8d11e7ef2132c927f2ef413bb1
parentf40849235404d47abc0ec49c8b5aa252fa00dbbc (diff)
parenta8aa303cf0107afaabaf551a6c7c00835244e70f (diff)
downloadrust-2366a90d3f4f39c5f241176d88c57f1ee4bd5c05.tar.gz
rust-2366a90d3f4f39c5f241176d88c57f1ee4bd5c05.zip
Auto merge of #3124 - eduardosm:fix-sse41-round, r=RalfJung
Fix rounding mode check in SSE4.1 round functions

Now it masks out the correct bit and adds some explanatory comments. Also extends the tests.
-rw-r--r--src/tools/miri/src/shims/x86/sse41.rs19
-rw-r--r--src/tools/miri/tests/pass/intrinsics-x86-sse41.rs50
2 files changed, 64 insertions, 5 deletions
diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs
index 1c8100ecc65..cfa06ded6e6 100644
--- a/src/tools/miri/src/shims/x86/sse41.rs
+++ b/src/tools/miri/src/shims/x86/sse41.rs
@@ -283,11 +283,20 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
-    let rounding = match this.read_scalar(rounding)?.to_i32()? & !0x80 {
-        0x00 => rustc_apfloat::Round::NearestTiesToEven,
-        0x01 => rustc_apfloat::Round::TowardNegative,
-        0x02 => rustc_apfloat::Round::TowardPositive,
-        0x03 => rustc_apfloat::Round::TowardZero,
+    // The fourth bit of `rounding` only affects the SSE status
+    // register, which cannot be accessed from Miri (or from Rust,
+    // for that matter), so we can ignore it.
+    let rounding = match this.read_scalar(rounding)?.to_i32()? & !0b1000 {
+        // When the third bit is 0, the rounding mode is determined by the
+        // first two bits.
+        0b000 => rustc_apfloat::Round::NearestTiesToEven,
+        0b001 => rustc_apfloat::Round::TowardNegative,
+        0b010 => rustc_apfloat::Round::TowardPositive,
+        0b011 => rustc_apfloat::Round::TowardZero,
+        // When the third bit is 1, the rounding mode is determined by the
+        // SSE status register. Since we do not support modifying it from
+        // Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
+        0b100..=0b111 => rustc_apfloat::Round::NearestTiesToEven,
         rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
     };
 
diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs
index 01b915f1810..d5489ffaf4b 100644
--- a/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs
+++ b/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs
@@ -119,6 +119,31 @@ unsafe fn test_sse41() {
         let r = _mm_round_sd::<_MM_FROUND_TO_NEAREST_INT>(a, b);
         let e = _mm_setr_pd(-2.0, 3.5);
         assert_eq_m128d(r, e);
+
+        let a = _mm_setr_pd(1.5, 3.5);
+        let b = _mm_setr_pd(-2.5, -4.5);
+        let r = _mm_round_sd::<_MM_FROUND_TO_NEG_INF>(a, b);
+        let e = _mm_setr_pd(-3.0, 3.5);
+        assert_eq_m128d(r, e);
+
+        let a = _mm_setr_pd(1.5, 3.5);
+        let b = _mm_setr_pd(-2.5, -4.5);
+        let r = _mm_round_sd::<_MM_FROUND_TO_POS_INF>(a, b);
+        let e = _mm_setr_pd(-2.0, 3.5);
+        assert_eq_m128d(r, e);
+
+        let a = _mm_setr_pd(1.5, 3.5);
+        let b = _mm_setr_pd(-2.5, -4.5);
+        let r = _mm_round_sd::<_MM_FROUND_TO_ZERO>(a, b);
+        let e = _mm_setr_pd(-2.0, 3.5);
+        assert_eq_m128d(r, e);
+
+        // Assume round-to-nearest by default
+        let a = _mm_setr_pd(1.5, 3.5);
+        let b = _mm_setr_pd(-2.5, -4.5);
+        let r = _mm_round_sd::<_MM_FROUND_CUR_DIRECTION>(a, b);
+        let e = _mm_setr_pd(-2.0, 3.5);
+        assert_eq_m128d(r, e);
     }
     test_mm_round_sd();
 
@@ -129,6 +154,31 @@ unsafe fn test_sse41() {
         let r = _mm_round_ss::<_MM_FROUND_TO_NEAREST_INT>(a, b);
         let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5);
         assert_eq_m128(r, e);
+
+        let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
+        let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
+        let r = _mm_round_ss::<_MM_FROUND_TO_NEG_INF>(a, b);
+        let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5);
+        assert_eq_m128(r, e);
+
+        let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
+        let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
+        let r = _mm_round_ss::<_MM_FROUND_TO_POS_INF>(a, b);
+        let e = _mm_setr_ps(-1.0, 3.5, 7.5, 15.5);
+        assert_eq_m128(r, e);
+
+        let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
+        let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
+        let r = _mm_round_ss::<_MM_FROUND_TO_ZERO>(a, b);
+        let e = _mm_setr_ps(-1.0, 3.5, 7.5, 15.5);
+        assert_eq_m128(r, e);
+
+        // Assume round-to-nearest by default
+        let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
+        let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
+        let r = _mm_round_ss::<_MM_FROUND_CUR_DIRECTION>(a, b);
+        let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5);
+        assert_eq_m128(r, e);
     }
     test_mm_round_ss();