Index: src/a64/simulator-a64.cc |
diff --git a/src/a64/simulator-a64.cc b/src/a64/simulator-a64.cc |
index c80266bea69cfb229e8c7b304e1f8ba356281abc..ca171058ba9e98abf7840560b6c187876ee6da3b 100644 |
--- a/src/a64/simulator-a64.cc |
+++ b/src/a64/simulator-a64.cc |
@@ -731,6 +731,16 @@ int64_t Simulator::ExtendValue(unsigned reg_size, |
} |
+template<> double Simulator::FPDefaultNaN<double>() const { |
+ return kFP64DefaultNaN; |
+} |
+ |
+ |
+template<> float Simulator::FPDefaultNaN<float>() const { |
+ return kFP32DefaultNaN; |
+} |
+ |
+ |
void Simulator::FPCompare(double val0, double val1) { |
AssertSupportedFPCR(); |
@@ -2100,8 +2110,8 @@ uint64_t Simulator::FPToUInt64(double value, FPRounding rmode) { |
void Simulator::VisitFPCompare(Instruction* instr) { |
AssertSupportedFPCR(); |
- unsigned reg_size = instr->FPType() == FP32 ? kSRegSizeInBits |
- : kDRegSizeInBits; |
+ unsigned reg_size = (instr->Mask(FP64) == FP64) ? kDRegSizeInBits |
+ : kSRegSizeInBits; |
double fn_val = fpreg(reg_size, instr->Rn()); |
switch (instr->Mask(FPCompareMask)) { |
@@ -2123,8 +2133,8 @@ void Simulator::VisitFPConditionalCompare(Instruction* instr) { |
if (ConditionPassed(static_cast<Condition>(instr->Condition()))) { |
// If the condition passes, set the status flags to the result of |
// comparing the operands. |
- unsigned reg_size = instr->FPType() == FP32 ? kSRegSizeInBits |
- : kDRegSizeInBits; |
+ unsigned reg_size = (instr->Mask(FP64) == FP64) ? kDRegSizeInBits |
+ : kSRegSizeInBits; |
FPCompare(fpreg(reg_size, instr->Rn()), fpreg(reg_size, instr->Rm())); |
} else { |
// If the condition fails, set the status flags to the nzcv immediate. |
@@ -2168,8 +2178,8 @@ void Simulator::VisitFPDataProcessing1Source(Instruction* instr) { |
case FABS_d: set_dreg(fd, std::fabs(dreg(fn))); break; |
case FNEG_s: set_sreg(fd, -sreg(fn)); break; |
case FNEG_d: set_dreg(fd, -dreg(fn)); break; |
- case FSQRT_s: set_sreg(fd, std::sqrt(sreg(fn))); break; |
- case FSQRT_d: set_dreg(fd, std::sqrt(dreg(fn))); break; |
+ case FSQRT_s: set_sreg(fd, FPSqrt(sreg(fn))); break; |
+ case FSQRT_d: set_dreg(fd, FPSqrt(dreg(fn))); break; |
case FRINTA_s: set_sreg(fd, FPRoundInt(sreg(fn), FPTieAway)); break; |
case FRINTA_d: set_dreg(fd, FPRoundInt(dreg(fn), FPTieAway)); break; |
case FRINTN_s: set_sreg(fd, FPRoundInt(sreg(fn), FPTieEven)); break; |
@@ -2428,8 +2438,10 @@ float Simulator::UFixedToFloat(uint64_t src, int fbits, FPRounding round) { |
double Simulator::FPRoundInt(double value, FPRounding round_mode) { |
if ((value == 0.0) || (value == kFP64PositiveInfinity) || |
- (value == kFP64NegativeInfinity) || std::isnan(value)) { |
+ (value == kFP64NegativeInfinity)) { |
return value; |
+ } else if (std::isnan(value)) { |
+ return FPProcessNaN(value); |
} |
double int_result = floor(value); |
@@ -2473,8 +2485,9 @@ double Simulator::FPRoundInt(double value, FPRounding round_mode) { |
double Simulator::FPToDouble(float value) { |
switch (std::fpclassify(value)) { |
case FP_NAN: { |
- // Convert NaNs as the processor would, assuming that FPCR.DN (default |
- // NaN) is not set: |
+ if (DN()) return kFP64DefaultNaN; |
+ |
+ // Convert NaNs as the processor would: |
// - The sign is propagated. |
// - The payload (mantissa) is transferred entirely, except that the top |
// bit is forced to '1', making the result a quiet NaN. The unused |
@@ -2513,8 +2526,9 @@ float Simulator::FPToFloat(double value, FPRounding round_mode) { |
switch (std::fpclassify(value)) { |
case FP_NAN: { |
- // Convert NaNs as the processor would, assuming that FPCR.DN (default |
- // NaN) is not set: |
+ if (DN()) return kFP32DefaultNaN; |
+ |
+ // Convert NaNs as the processor would: |
// - The sign is propagated. |
// - The payload (mantissa) is transferred as much as possible, except |
// that the top bit is forced to '1', making the result a quiet NaN. |
@@ -2565,23 +2579,37 @@ void Simulator::VisitFPDataProcessing2Source(Instruction* instr) { |
unsigned fn = instr->Rn(); |
unsigned fm = instr->Rm(); |
+ // Fmaxnm and Fminnm have special NaN handling. |
+ switch (instr->Mask(FPDataProcessing2SourceMask)) { |
+ case FMAXNM_s: set_sreg(fd, FPMaxNM(sreg(fn), sreg(fm))); return; |
+ case FMAXNM_d: set_dreg(fd, FPMaxNM(dreg(fn), dreg(fm))); return; |
+ case FMINNM_s: set_sreg(fd, FPMinNM(sreg(fn), sreg(fm))); return; |
+ case FMINNM_d: set_dreg(fd, FPMinNM(dreg(fn), dreg(fm))); return; |
+ default: |
+ break; // Fall through. |
+ } |
+ |
+ if (FPProcessNaNs(instr)) return; |
+ |
switch (instr->Mask(FPDataProcessing2SourceMask)) { |
- case FADD_s: set_sreg(fd, sreg(fn) + sreg(fm)); break; |
- case FADD_d: set_dreg(fd, dreg(fn) + dreg(fm)); break; |
- case FSUB_s: set_sreg(fd, sreg(fn) - sreg(fm)); break; |
- case FSUB_d: set_dreg(fd, dreg(fn) - dreg(fm)); break; |
- case FMUL_s: set_sreg(fd, sreg(fn) * sreg(fm)); break; |
- case FMUL_d: set_dreg(fd, dreg(fn) * dreg(fm)); break; |
- case FDIV_s: set_sreg(fd, sreg(fn) / sreg(fm)); break; |
- case FDIV_d: set_dreg(fd, dreg(fn) / dreg(fm)); break; |
+ case FADD_s: set_sreg(fd, FPAdd(sreg(fn), sreg(fm))); break; |
+ case FADD_d: set_dreg(fd, FPAdd(dreg(fn), dreg(fm))); break; |
+ case FSUB_s: set_sreg(fd, FPSub(sreg(fn), sreg(fm))); break; |
+ case FSUB_d: set_dreg(fd, FPSub(dreg(fn), dreg(fm))); break; |
+ case FMUL_s: set_sreg(fd, FPMul(sreg(fn), sreg(fm))); break; |
+ case FMUL_d: set_dreg(fd, FPMul(dreg(fn), dreg(fm))); break; |
+ case FDIV_s: set_sreg(fd, FPDiv(sreg(fn), sreg(fm))); break; |
+ case FDIV_d: set_dreg(fd, FPDiv(dreg(fn), dreg(fm))); break; |
case FMAX_s: set_sreg(fd, FPMax(sreg(fn), sreg(fm))); break; |
case FMAX_d: set_dreg(fd, FPMax(dreg(fn), dreg(fm))); break; |
case FMIN_s: set_sreg(fd, FPMin(sreg(fn), sreg(fm))); break; |
case FMIN_d: set_dreg(fd, FPMin(dreg(fn), dreg(fm))); break; |
- case FMAXNM_s: set_sreg(fd, FPMaxNM(sreg(fn), sreg(fm))); break; |
- case FMAXNM_d: set_dreg(fd, FPMaxNM(dreg(fn), dreg(fm))); break; |
- case FMINNM_s: set_sreg(fd, FPMinNM(sreg(fn), sreg(fm))); break; |
- case FMINNM_d: set_dreg(fd, FPMinNM(dreg(fn), dreg(fm))); break; |
+ case FMAXNM_s: |
+ case FMAXNM_d: |
+ case FMINNM_s: |
+ case FMINNM_d: |
+ // These were handled before the standard FPProcessNaNs() stage. |
+ UNREACHABLE(); |
default: UNIMPLEMENTED(); |
} |
} |
@@ -2598,33 +2626,62 @@ void Simulator::VisitFPDataProcessing3Source(Instruction* instr) { |
// The C99 (and C++11) fma function performs a fused multiply-accumulate. |
switch (instr->Mask(FPDataProcessing3SourceMask)) { |
// fd = fa +/- (fn * fm) |
- case FMADD_s: set_sreg(fd, fmaf(sreg(fn), sreg(fm), sreg(fa))); break; |
- case FMSUB_s: set_sreg(fd, fmaf(-sreg(fn), sreg(fm), sreg(fa))); break; |
- case FMADD_d: set_dreg(fd, fma(dreg(fn), dreg(fm), dreg(fa))); break; |
- case FMSUB_d: set_dreg(fd, fma(-dreg(fn), dreg(fm), dreg(fa))); break; |
- // Variants of the above where the result is negated. |
- case FNMADD_s: set_sreg(fd, -fmaf(sreg(fn), sreg(fm), sreg(fa))); break; |
- case FNMSUB_s: set_sreg(fd, -fmaf(-sreg(fn), sreg(fm), sreg(fa))); break; |
- case FNMADD_d: set_dreg(fd, -fma(dreg(fn), dreg(fm), dreg(fa))); break; |
- case FNMSUB_d: set_dreg(fd, -fma(-dreg(fn), dreg(fm), dreg(fa))); break; |
+ case FMADD_s: set_sreg(fd, FPMulAdd(sreg(fa), sreg(fn), sreg(fm))); break; |
+ case FMSUB_s: set_sreg(fd, FPMulAdd(sreg(fa), -sreg(fn), sreg(fm))); break; |
+ case FMADD_d: set_dreg(fd, FPMulAdd(dreg(fa), dreg(fn), dreg(fm))); break; |
+ case FMSUB_d: set_dreg(fd, FPMulAdd(dreg(fa), -dreg(fn), dreg(fm))); break; |
+ // Negated variants of the above. |
+ case FNMADD_s: |
+ set_sreg(fd, FPMulAdd(-sreg(fa), -sreg(fn), sreg(fm))); |
+ break; |
+ case FNMSUB_s: |
+ set_sreg(fd, FPMulAdd(-sreg(fa), sreg(fn), sreg(fm))); |
+ break; |
+ case FNMADD_d: |
+ set_dreg(fd, FPMulAdd(-dreg(fa), -dreg(fn), dreg(fm))); |
+ break; |
+ case FNMSUB_d: |
+ set_dreg(fd, FPMulAdd(-dreg(fa), dreg(fn), dreg(fm))); |
+ break; |
default: UNIMPLEMENTED(); |
} |
} |
template <typename T> |
-T Simulator::FPMax(T a, T b) { |
- if (IsSignallingNaN(a)) { |
- return a; |
- } else if (IsSignallingNaN(b)) { |
- return b; |
- } else if (std::isnan(a)) { |
- ASSERT(IsQuietNaN(a)); |
- return a; |
- } else if (std::isnan(b)) { |
- ASSERT(IsQuietNaN(b)); |
- return b; |
+T Simulator::FPAdd(T op1, T op2) { |
+ // NaNs should be handled elsewhere. |
+ ASSERT(!std::isnan(op1) && !std::isnan(op2)); |
+ |
+ if (isinf(op1) && isinf(op2) && (op1 != op2)) { |
+ // inf + -inf returns the default NaN. |
+ return FPDefaultNaN<T>(); |
+ } else { |
+ // Other cases should be handled by standard arithmetic. |
+ return op1 + op2; |
+ } |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPDiv(T op1, T op2) { |
+ // NaNs should be handled elsewhere. |
+ ASSERT(!std::isnan(op1) && !std::isnan(op2)); |
+ |
+ if ((isinf(op1) && isinf(op2)) || ((op1 == 0.0) && (op2 == 0.0))) { |
+ // inf / inf and 0.0 / 0.0 return the default NaN. |
+ return FPDefaultNaN<T>(); |
+ } else { |
+ // Other cases should be handled by standard arithmetic. |
+ return op1 / op2; |
} |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPMax(T a, T b) { |
+ // NaNs should be handled elsewhere. |
+ ASSERT(!std::isnan(a) && !std::isnan(b)); |
if ((a == 0.0) && (b == 0.0) && |
(copysign(1.0, a) != copysign(1.0, b))) { |
@@ -2643,22 +2700,15 @@ T Simulator::FPMaxNM(T a, T b) { |
} else if (!IsQuietNaN(a) && IsQuietNaN(b)) { |
b = kFP64NegativeInfinity; |
} |
- return FPMax(a, b); |
+ |
+ T result = FPProcessNaNs(a, b); |
+ return std::isnan(result) ? result : FPMax(a, b); |
} |
template <typename T> |
T Simulator::FPMin(T a, T b) { |
- if (IsSignallingNaN(a)) { |
- return a; |
- } else if (IsSignallingNaN(b)) { |
- return b; |
- } else if (std::isnan(a)) { |
- ASSERT(IsQuietNaN(a)); |
- return a; |
- } else if (std::isnan(b)) { |
- ASSERT(IsQuietNaN(b)); |
- return b; |
- } |
+ // NaNs should be handled elsewhere. |
+ ASSERT(!isnan(a) && !isnan(b)); |
if ((a == 0.0) && (b == 0.0) && |
(copysign(1.0, a) != copysign(1.0, b))) { |
@@ -2677,7 +2727,168 @@ T Simulator::FPMinNM(T a, T b) { |
} else if (!IsQuietNaN(a) && IsQuietNaN(b)) { |
b = kFP64PositiveInfinity; |
} |
- return FPMin(a, b); |
+ |
+ T result = FPProcessNaNs(a, b); |
+ return isnan(result) ? result : FPMin(a, b); |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPMul(T op1, T op2) { |
+ // NaNs should be handled elsewhere. |
+ ASSERT(!std::isnan(op1) && !std::isnan(op2)); |
+ |
+ if ((isinf(op1) && (op2 == 0.0)) || (isinf(op2) && (op1 == 0.0))) { |
+ // inf * 0.0 returns the default NaN. |
+ return FPDefaultNaN<T>(); |
+ } else { |
+ // Other cases should be handled by standard arithmetic. |
+ return op1 * op2; |
+ } |
+} |
+ |
+ |
+template<typename T> |
+T Simulator::FPMulAdd(T a, T op1, T op2) { |
+ T result = FPProcessNaNs3(a, op1, op2); |
+ |
+ T sign_a = copysign(1.0, a); |
+ T sign_prod = copysign(1.0, op1) * copysign(1.0, op2); |
+ bool isinf_prod = std::isinf(op1) || std::isinf(op2); |
+ bool operation_generates_nan = |
+ (std::isinf(op1) && (op2 == 0.0)) || // inf * 0.0 |
+ (std::isinf(op2) && (op1 == 0.0)) || // 0.0 * inf |
+ (std::isinf(a) && isinf_prod && (sign_a != sign_prod)); // inf - inf |
+ |
+ if (std::isnan(result)) { |
+ // Generated NaNs override quiet NaNs propagated from a. |
+ if (operation_generates_nan && IsQuietNaN(a)) { |
+ return FPDefaultNaN<T>(); |
+ } else { |
+ return result; |
+ } |
+ } |
+ |
+ // If the operation would produce a NaN, return the default NaN. |
+ if (operation_generates_nan) { |
+ return FPDefaultNaN<T>(); |
+ } |
+ |
+ // Work around broken fma implementations for exact zero results: The sign of |
+ // exact 0.0 results is positive unless both a and op1 * op2 are negative. |
+ if (((op1 == 0.0) || (op2 == 0.0)) && (a == 0.0)) { |
+ return ((sign_a < 0) && (sign_prod < 0)) ? -0.0 : 0.0; |
+ } |
+ |
+ result = FusedMultiplyAdd(op1, op2, a); |
+ ASSERT(!std::isnan(result)); |
+ |
+ // Work around broken fma implementations for rounded zero results: If a is |
+ // 0.0, the sign of the result is the sign of op1 * op2 before rounding. |
+ if ((a == 0.0) && (result == 0.0)) { |
+ return copysign(0.0, sign_prod); |
+ } |
+ |
+ return result; |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPSqrt(T op) { |
+ if (std::isnan(op)) { |
+ return FPProcessNaN(op); |
+ } else if (op < 0.0) { |
+ return FPDefaultNaN<T>(); |
+ } else { |
+ return std::sqrt(op); |
+ } |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPSub(T op1, T op2) { |
+ // NaNs should be handled elsewhere. |
+ ASSERT(!std::isnan(op1) && !std::isnan(op2)); |
+ |
+ if (isinf(op1) && isinf(op2) && (op1 == op2)) { |
+ // inf - inf returns the default NaN. |
+ return FPDefaultNaN<T>(); |
+ } else { |
+ // Other cases should be handled by standard arithmetic. |
+ return op1 - op2; |
+ } |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPProcessNaN(T op) { |
+ ASSERT(std::isnan(op)); |
+ return DN() ? FPDefaultNaN<T>() : ToQuietNaN(op); |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPProcessNaNs(T op1, T op2) { |
+ if (IsSignallingNaN(op1)) { |
+ return FPProcessNaN(op1); |
+ } else if (IsSignallingNaN(op2)) { |
+ return FPProcessNaN(op2); |
+ } else if (std::isnan(op1)) { |
+ ASSERT(IsQuietNaN(op1)); |
+ return FPProcessNaN(op1); |
+ } else if (std::isnan(op2)) { |
+ ASSERT(IsQuietNaN(op2)); |
+ return FPProcessNaN(op2); |
+ } else { |
+ return 0.0; |
+ } |
+} |
+ |
+ |
+template <typename T> |
+T Simulator::FPProcessNaNs3(T op1, T op2, T op3) { |
+ if (IsSignallingNaN(op1)) { |
+ return FPProcessNaN(op1); |
+ } else if (IsSignallingNaN(op2)) { |
+ return FPProcessNaN(op2); |
+ } else if (IsSignallingNaN(op3)) { |
+ return FPProcessNaN(op3); |
+ } else if (std::isnan(op1)) { |
+ ASSERT(IsQuietNaN(op1)); |
+ return FPProcessNaN(op1); |
+ } else if (std::isnan(op2)) { |
+ ASSERT(IsQuietNaN(op2)); |
+ return FPProcessNaN(op2); |
+ } else if (std::isnan(op3)) { |
+ ASSERT(IsQuietNaN(op3)); |
+ return FPProcessNaN(op3); |
+ } else { |
+ return 0.0; |
+ } |
+} |
+ |
+ |
+bool Simulator::FPProcessNaNs(Instruction* instr) { |
+ unsigned fd = instr->Rd(); |
+ unsigned fn = instr->Rn(); |
+ unsigned fm = instr->Rm(); |
+ bool done = false; |
+ |
+ if (instr->Mask(FP64) == FP64) { |
+ double result = FPProcessNaNs(dreg(fn), dreg(fm)); |
+ if (std::isnan(result)) { |
+ set_dreg(fd, result); |
+ done = true; |
+ } |
+ } else { |
+ float result = FPProcessNaNs(sreg(fn), sreg(fm)); |
+ if (std::isnan(result)) { |
+ set_sreg(fd, result); |
+ done = true; |
+ } |
+ } |
+ |
+ return done; |
} |