Index: src/mips64/simulator-mips64.cc |
diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc |
index 662b873965d4d9bc5294d0cce33e8f6c0d8cb217..483f5e21a0616a83a199b1048b1b9db3558155a5 100644 |
--- a/src/mips64/simulator-mips64.cc |
+++ b/src/mips64/simulator-mips64.cc |
@@ -2364,6 +2364,63 @@ static T FPUMaxA(T a, T b) { |
return result; |
} |
+enum class KeepSign : bool { no = false, yes }; |
+ |
+template <typename T> |
+T FPUCanonalizeNaNArg(T result, T arg, KeepSign keepSign = KeepSign::no); |
+ |
+template <> |
+double FPUCanonalizeNaNArg(double result, double arg, KeepSign keepSign) { |
+ DCHECK(std::isnan(arg)); |
+ double qNaN = std::numeric_limits<double>::quiet_NaN(); |
+ if (keepSign == KeepSign::yes) { |
+ uint64_t resBits = bit_cast<uint64_t>(result); |
+ return resBits & Double::kSignMask ? -qNaN : qNaN; |
+ } |
+ return qNaN; |
+} |
+ |
+template <> |
+float FPUCanonalizeNaNArg(float result, float arg, KeepSign keepSign) { |
+ DCHECK(std::isnan(arg)); |
+ float qNaN = std::numeric_limits<float>::quiet_NaN(); |
+ if (keepSign == KeepSign::yes) { |
+ uint32_t resBits = bit_cast<uint32_t>(result); |
+ return resBits & kBinary32SignMask ? -qNaN : qNaN; |
+ } |
+ return qNaN; |
+} |
+ |
+template <typename T> |
+T FPUCanonalizeNaNArgs(T result, KeepSign keepSign, T first) { |
+ if (std::isnan(first)) { |
+ return FPUCanonalizeNaNArg(result, first, keepSign); |
+ } |
+ return result; |
+} |
+ |
+template <typename T, typename... Args> |
+T FPUCanonalizeNaNArgs(T result, KeepSign keepSign, T first, Args... args) { |
+ if (std::isnan(first)) { |
+ return FPUCanonalizeNaNArg(result, first, keepSign); |
+ } |
+ return FPUCanonalizeNaNArgs(result, keepSign, args...); |
+} |
+ |
+template <typename Func, typename T, typename... Args> |
+T FPUCanonalizeOperation(Func f, T first, Args... args) { |
+ return FPUCanonalizeOperation(f, KeepSign::no, first, args...); |
+} |
+ |
+template <typename Func, typename T, typename... Args> |
+T FPUCanonalizeOperation(Func f, KeepSign keepSign, T first, Args... args) { |
+ T result = f(first, args...); |
+ if (std::isnan(result)) { |
+ result = FPUCanonalizeNaNArgs(result, keepSign, first, args...); |
+ } |
+ return result; |
+} |
+ |
// Handle execution based on instruction types. |
void Simulator::DecodeTypeRegisterSRsType() { |
@@ -2416,10 +2473,16 @@ void Simulator::DecodeTypeRegisterSRsType() { |
break; |
} |
case ADD_S: |
- set_fpu_register_float(fd_reg(), fs + ft); |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float lhs, float rhs) { return lhs + rhs; }, |
+ fs, ft)); |
break; |
case SUB_S: |
- set_fpu_register_float(fd_reg(), fs - ft); |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float lhs, float rhs) { return lhs - rhs; }, |
+ fs, ft)); |
break; |
case MADDF_S: |
DCHECK(kArchVariant == kMips64r6); |
@@ -2430,35 +2493,45 @@ void Simulator::DecodeTypeRegisterSRsType() { |
set_fpu_register_float(fd_reg(), fd - (fs * ft)); |
break; |
case MUL_S: |
- set_fpu_register_float(fd_reg(), fs * ft); |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float lhs, float rhs) { return lhs * rhs; }, |
+ fs, ft)); |
break; |
case DIV_S: |
- set_fpu_register_float(fd_reg(), fs / ft); |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float lhs, float rhs) { return lhs / rhs; }, |
+ fs, ft)); |
break; |
case ABS_S: |
- set_fpu_register_float(fd_reg(), fabs(fs)); |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float fs) { return FPAbs(fs); }, fs)); |
break; |
case MOV_S: |
set_fpu_register_float(fd_reg(), fs); |
break; |
case NEG_S: |
- set_fpu_register_float(fd_reg(), -fs); |
+ set_fpu_register_float( |
+ fd_reg(), FPUCanonalizeOperation([](float src) { return -src; }, |
+ KeepSign::yes, fs)); |
break; |
case SQRT_S: |
- lazily_initialize_fast_sqrt(isolate_); |
- set_fpu_register_float(fd_reg(), fast_sqrt(fs, isolate_)); |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float src) { return std::sqrt(src); }, fs)); |
break; |
- case RSQRT_S: { |
- lazily_initialize_fast_sqrt(isolate_); |
- float result = 1.0 / fast_sqrt(fs, isolate_); |
- set_fpu_register_float(fd_reg(), result); |
+ case RSQRT_S: |
+ set_fpu_register_float( |
+ fd_reg(), FPUCanonalizeOperation( |
+ [](float src) { return 1.0 / std::sqrt(src); }, fs)); |
break; |
- } |
- case RECIP_S: { |
- float result = 1.0 / fs; |
- set_fpu_register_float(fd_reg(), result); |
+ case RECIP_S: |
+ set_fpu_register_float( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](float src) { return 1.0 / src; }, fs)); |
break; |
- } |
case C_F_D: |
set_fcsr_bit(fcsr_cc, false); |
break; |
@@ -2827,10 +2900,16 @@ void Simulator::DecodeTypeRegisterDRsType() { |
set_fpu_register_double(fd_reg(), FPUMax(ft, fs)); |
break; |
case ADD_D: |
- set_fpu_register_double(fd_reg(), fs + ft); |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation( |
+ [](double lhs, double rhs) { return lhs + rhs; }, fs, ft)); |
break; |
case SUB_D: |
- set_fpu_register_double(fd_reg(), fs - ft); |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation( |
+ [](double lhs, double rhs) { return lhs - rhs; }, fs, ft)); |
break; |
case MADDF_D: |
DCHECK(kArchVariant == kMips64r6); |
@@ -2841,35 +2920,45 @@ void Simulator::DecodeTypeRegisterDRsType() { |
set_fpu_register_double(fd_reg(), fd - (fs * ft)); |
break; |
case MUL_D: |
- set_fpu_register_double(fd_reg(), fs * ft); |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation( |
+ [](double lhs, double rhs) { return lhs * rhs; }, fs, ft)); |
break; |
case DIV_D: |
- set_fpu_register_double(fd_reg(), fs / ft); |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation( |
+ [](double lhs, double rhs) { return lhs / rhs; }, fs, ft)); |
break; |
case ABS_D: |
- set_fpu_register_double(fd_reg(), fabs(fs)); |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](double fs) { return FPAbs(fs); }, fs)); |
break; |
case MOV_D: |
set_fpu_register_double(fd_reg(), fs); |
break; |
case NEG_D: |
- set_fpu_register_double(fd_reg(), -fs); |
+ set_fpu_register_double( |
+ fd_reg(), FPUCanonalizeOperation([](double src) { return -src; }, |
+ KeepSign::yes, fs)); |
break; |
case SQRT_D: |
- lazily_initialize_fast_sqrt(isolate_); |
- set_fpu_register_double(fd_reg(), fast_sqrt(fs, isolate_)); |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](double fs) { return std::sqrt(fs); }, fs)); |
break; |
- case RSQRT_D: { |
- lazily_initialize_fast_sqrt(isolate_); |
- double result = 1.0 / fast_sqrt(fs, isolate_); |
- set_fpu_register_double(fd_reg(), result); |
+ case RSQRT_D: |
+ set_fpu_register_double( |
+ fd_reg(), FPUCanonalizeOperation( |
+ [](double fs) { return 1.0 / std::sqrt(fs); }, fs)); |
break; |
- } |
- case RECIP_D: { |
- double result = 1.0 / fs; |
- set_fpu_register_double(fd_reg(), result); |
+ case RECIP_D: |
+ set_fpu_register_double( |
+ fd_reg(), |
+ FPUCanonalizeOperation([](double fs) { return 1.0 / fs; }, fs)); |
break; |
- } |
case C_UN_D: |
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
break; |