arch-riscv: Fix floating-point conversion bugs
authorAlec Roelke <ar4jc@virginia.edu>
Tue, 5 Dec 2017 03:54:35 +0000 (22:54 -0500)
committerAlec Roelke <ar4jc@virginia.edu>
Tue, 16 Jan 2018 16:23:31 +0000 (16:23 +0000)
Using the fetestexcept function to check for specific types of floating
point exceptions is unreliable for some kinds of
floating-point-to-integer conversion operations. RISC-V code used to
make use of them to check for some exceptional cases like overflow and
underflow, which caused incorrect output when compiler optimization is
turned on. This patch changes the use of fetestexcept to explicit checks
for those exceptional cases.

Change-Id: Id983906ea0664dc246e115a9e470d9ab7733bde1
Reviewed-on: https://gem5-review.googlesource.com/6402
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Alec Roelke <ar4jc@virginia.edu>

src/arch/riscv/isa/decoder.isa

index 435266c4f86abdcbcd73844cbb5817c83dae5495..baae5818e8a3a1180796ac1c72e48cf03cc5c156 100644 (file)
@@ -1338,31 +1338,31 @@ decode QUADRANT default Unknown::unknown() {
                         if (std::isnan(fs1)) {
                             Rd_sd = numeric_limits<int32_t>::max();
                             FFLAGS |= FloatInvalid;
+                        } else if (fs1 >= numeric_limits<int32_t>::max()) {
+                            Rd_sd = numeric_limits<int32_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (fs1 <= numeric_limits<int32_t>::min()) {
+                            Rd_sd = numeric_limits<int32_t>::min();
+                            FFLAGS |= FloatInvalid;
                         } else {
                             Rd_sd = (int32_t)fs1;
-                            if (fetestexcept(FE_INVALID)) {
-                                if (signbit(fs1)) {
-                                    Rd_sd = numeric_limits<int32_t>::min();
-                                } else {
-                                    Rd_sd = numeric_limits<int32_t>::max();
-                                }
-                                feclearexcept(FE_INEXACT);
-                            }
                         }
                     }}, FloatCvtOp);
                     0x1: fcvt_wu_s({{
                         uint32_t temp;
                         float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
 
-                        if (fs1 < 0.0) {
+                        if (std::isnan(fs1)) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (fs1 < 0.0) {
                             Rd = 0;
                             FFLAGS |= FloatInvalid;
+                        } else if (fs1 > numeric_limits<uint32_t>::max()) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
                         } else {
                             Rd = (uint32_t)fs1;
-                            if (fetestexcept(FE_INVALID)) {
-                                Rd = numeric_limits<uint64_t>::max();
-                                feclearexcept(FE_INEXACT);
-                            }
                         }
                     }}, FloatCvtOp);
                     0x2: fcvt_l_s({{
@@ -1372,79 +1372,89 @@ decode QUADRANT default Unknown::unknown() {
                         if (std::isnan(fs1)) {
                             Rd_sd = numeric_limits<int64_t>::max();
                             FFLAGS |= FloatInvalid;
+                        } else if (fs1 > numeric_limits<int64_t>::max()) {
+                            Rd_sd = numeric_limits<int64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (fs1 < numeric_limits<int64_t>::min()) {
+                            Rd_sd = numeric_limits<int64_t>::min();
+                            FFLAGS |= FloatInvalid;
                         } else {
                             Rd_sd = (int64_t)fs1;
-                            if (fetestexcept(FE_INVALID)) {
-                                if (signbit(fs1)) {
-                                    Rd_sd = numeric_limits<int64_t>::min();
-                                } else {
-                                    Rd_sd = numeric_limits<int64_t>::max();
-                                }
-                                feclearexcept(FE_INEXACT);
-                            }
                         }
                     }}, FloatCvtOp);
                     0x3: fcvt_lu_s({{
                         uint32_t temp;
                         float fs1 = reinterpret_cast<float&>(temp = Fs1_bits);
 
-                        if (fs1 < 0.0) {
+                        if (std::isnan(fs1)) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (fs1 < 0.0) {
                             Rd = 0;
                             FFLAGS |= FloatInvalid;
+                        } else if (fs1 > numeric_limits<uint64_t>::max()) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
                         } else {
                             Rd = (uint64_t)fs1;
-                            if (fetestexcept(FE_INVALID)) {
-                                Rd = numeric_limits<uint64_t>::max();
-                                feclearexcept(FE_INEXACT);
-                            }
                         }
                     }}, FloatCvtOp);
                 }
                 0x61: decode CONV_SGN {
                     0x0: fcvt_w_d({{
-                        Rd_sd = (int32_t)Fs1;
-                        if (fetestexcept(FE_INVALID)) {
-                            if (Fs1 < 0.0) {
-                                Rd_sd = numeric_limits<int32_t>::min();
-                            } else {
-                                Rd_sd = numeric_limits<int32_t>::max();
-                            }
-                            feclearexcept(FE_INEXACT);
+                        if (std::isnan(Fs1)) {
+                            Rd_sd = numeric_limits<int32_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (Fs1 > numeric_limits<int32_t>::max()) {
+                            Rd_sd = numeric_limits<int32_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (Fs1 < numeric_limits<int32_t>::min()) {
+                            Rd_sd = numeric_limits<int32_t>::min();
+                            FFLAGS |= FloatInvalid;
+                        } else {
+                            Rd_sd = (int32_t)Fs1;
                         }
                     }}, FloatCvtOp);
                     0x1: fcvt_wu_d({{
-                        if (Fs1 < 0.0) {
+                        if (std::isnan(Fs1)) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (Fs1 < 0) {
                             Rd = 0;
                             FFLAGS |= FloatInvalid;
+                        } else if (Fs1 > numeric_limits<uint32_t>::max()) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
                         } else {
                             Rd = (uint32_t)Fs1;
-                            if (fetestexcept(FE_INVALID)) {
-                                Rd = numeric_limits<uint64_t>::max();
-                                feclearexcept(FE_INEXACT);
-                            }
                         }
                     }}, FloatCvtOp);
                     0x2: fcvt_l_d({{
-                        Rd_sd = Fs1;
-                        if (fetestexcept(FE_INVALID)) {
-                            if (Fs1 < 0.0) {
-                                Rd_sd = numeric_limits<int64_t>::min();
-                            } else {
-                                Rd_sd = numeric_limits<int64_t>::max();
-                            }
-                            feclearexcept(FE_INEXACT);
+                        if (std::isnan(Fs1)) {
+                            Rd_sd = numeric_limits<int64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (Fs1 > numeric_limits<int64_t>::max()) {
+                            Rd_sd = numeric_limits<int64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (Fs1 < numeric_limits<int64_t>::min()) {
+                            Rd_sd = numeric_limits<int64_t>::min();
+                            FFLAGS |= FloatInvalid;
+                        } else {
+                            Rd_sd = Fs1;
                         }
                     }}, FloatCvtOp);
                     0x3: fcvt_lu_d({{
-                        if (Fs1 < 0.0) {
+                        if (std::isnan(Fs1)) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
+                        } else if (Fs1 < 0) {
                             Rd = 0;
                             FFLAGS |= FloatInvalid;
+                        } else if (Fs1 > numeric_limits<uint64_t>::max()) {
+                            Rd = numeric_limits<uint64_t>::max();
+                            FFLAGS |= FloatInvalid;
                         } else {
-                            Rd = (uint64_t)Fs1;
-                            if (fetestexcept(FE_INVALID)) {
-                                Rd = numeric_limits<uint64_t>::max();
-                                feclearexcept(FE_INEXACT);
-                            }
+                            Rd = Fs1;
                         }
                     }}, FloatCvtOp);
                 }