Index: src/arm/simulator-arm.cc |
=================================================================== |
--- src/arm/simulator-arm.cc (revision 6620) |
+++ src/arm/simulator-arm.cc (working copy) |
@@ -2539,7 +2539,7 @@ |
(overflow_vfp_flag_ << 2) | |
(div_zero_vfp_flag_ << 1) | |
(inv_op_vfp_flag_ << 0) | |
- (FPSCR_rounding_mode_ << 22); |
+ (FPSCR_rounding_mode_); |
set_register(rt, fpscr); |
} |
} else if ((instr->VLValue() == 0x0) && |
@@ -2562,7 +2562,7 @@ |
div_zero_vfp_flag_ = (rt_value >> 1) & 1; |
inv_op_vfp_flag_ = (rt_value >> 0) & 1; |
FPSCR_rounding_mode_ = |
- static_cast<FPSCRRoundingModes>((rt_value >> 22) & 3); |
+ static_cast<VFPRoundingMode>((rt_value) & kVFPRoundingModeMask); |
} |
} else { |
UNIMPLEMENTED(); // Not used by V8. |
@@ -2651,87 +2651,135 @@ |
} |
} |
+bool get_inv_op_vfp_flag(VFPRoundingMode mode, |
+ double val, |
+ bool unsigned_) { |
+ ASSERT((mode == RN) || (mode == RM) || (mode == RZ)); |
+ double max_uint = static_cast<double>(0xffffffffu); |
+ double max_int = static_cast<double>(kMaxInt); |
+ double min_int = static_cast<double>(kMinInt); |
+ // Check for NaN. |
+ if (val != val) { |
+ return true; |
+ } |
+ |
+ // Check for overflow. This code works because 32bit integers can be |
+ // exactly represented by ieee-754 64bit floating-point values. |
+ switch (mode) { |
+ case RN: |
+ return unsigned_ ? (val >= (max_uint + 0.5)) || |
+ (val < -0.5) |
+ : (val >= (max_int + 0.5)) || |
+ (val < (min_int - 0.5)); |
+ |
+ case RM: |
+ return unsigned_ ? (val >= (max_uint + 1.0)) || |
+ (val < 0) |
+ : (val >= (max_int + 1.0)) || |
+ (val < min_int); |
+ |
+ case RZ: |
+ return unsigned_ ? (val >= (max_uint + 1.0)) || |
+ (val <= -1) |
+ : (val >= (max_int + 1.0)) || |
+ (val <= (min_int - 1.0)); |
+ default: |
+ UNREACHABLE(); |
+ return true; |
+ } |
+} |
+ |
+ |
+// We call this function only if we had a vfp invalid exception. |
+// It returns the correct saturated value. |
+int VFPConversionSaturate(double val, bool unsigned_res) { |
+ if (val != val) { |
+ return 0; |
+ } else { |
+ if (unsigned_res) { |
+ return (val < 0) ? 0 : 0xffffffffu; |
+ } else { |
+ return (val < 0) ? kMinInt : kMaxInt; |
+ } |
+ } |
+} |
+ |
+ |
void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { |
- ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); |
+ ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) && |
+ (instr->Bits(27, 23) == 0x1D)); |
ASSERT(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || |
(((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1))); |
// Conversion between floating-point and integer. |
bool to_integer = (instr->Bit(18) == 1); |
- VFPRegPrecision src_precision = kSinglePrecision; |
- if (instr->SzValue() == 1) { |
- src_precision = kDoublePrecision; |
- } |
+ VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision |
+ : kSinglePrecision; |
if (to_integer) { |
- bool unsigned_integer = (instr->Bit(16) == 0); |
- FPSCRRoundingModes mode; |
- if (instr->Bit(7) != 1) { |
- // Use FPSCR defined rounding mode. |
- mode = FPSCR_rounding_mode_; |
- // Only RZ and RM modes are supported. |
- ASSERT((mode == RM) || (mode == RZ)); |
- } else { |
- // VFP uses round towards zero by default. |
- mode = RZ; |
- } |
+ // We are playing with code close to the C++ standard's limits below, |
+ // hence the very simple code and heavy checks. |
+ // |
+ // Note: |
+ // C++ defines default type casting from floating point to integer as |
+ // (close to) rounding toward zero ("fractional part discarded"). |
int dst = instr->VFPDRegValue(kSinglePrecision); |
int src = instr->VFPMRegValue(src_precision); |
- int32_t kMaxInt = v8::internal::kMaxInt; |
- int32_t kMinInt = v8::internal::kMinInt; |
- switch (mode) { |
- case RM: |
- if (src_precision == kDoublePrecision) { |
- double val = get_double_from_d_register(src); |
- inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); |
+ // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding |
+ // mode or the default Round to Zero mode. |
+ VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_ |
+ : RZ; |
+ ASSERT((mode == RM) || (mode == RZ) || (mode == RN)); |
- int sint = unsigned_integer ? static_cast<uint32_t>(val) : |
- static_cast<int32_t>(val); |
- sint = sint > val ? sint - 1 : sint; |
+ bool unsigned_integer = (instr->Bit(16) == 0); |
+ bool double_precision = (src_precision == kDoublePrecision); |
- set_s_register_from_sinteger(dst, sint); |
- } else { |
- float val = get_float_from_s_register(src); |
+ double val = double_precision ? get_double_from_d_register(src) |
+ : get_float_from_s_register(src); |
- inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); |
+ int temp = unsigned_integer ? static_cast<uint32_t>(val) |
+ : static_cast<int32_t>(val); |
- int sint = unsigned_integer ? static_cast<uint32_t>(val) : |
- static_cast<int32_t>(val); |
- sint = sint > val ? sint - 1 : sint; |
+ inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); |
- set_s_register_from_sinteger(dst, sint); |
+ if (inv_op_vfp_flag_) { |
+ temp = VFPConversionSaturate(val, unsigned_integer); |
+ } else { |
+ switch (mode) { |
+ case RN: { |
+ double abs_diff = |
+ unsigned_integer ? fabs(val - static_cast<uint32_t>(temp)) |
+ : fabs(val - temp); |
+ int val_sign = (val > 0) ? 1 : -1; |
+ if (abs_diff > 0.5) { |
+ temp += val_sign; |
+ } else if (abs_diff == 0.5) { |
+ // Round to even if exactly halfway. |
+ temp = ((temp % 2) == 0) ? temp : temp + val_sign; |
+ } |
+ break; |
} |
- break; |
- case RZ: |
- if (src_precision == kDoublePrecision) { |
- double val = get_double_from_d_register(src); |
- inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); |
+ case RM: |
+ temp = temp > val ? temp - 1 : temp; |
+ break; |
- int sint = unsigned_integer ? static_cast<uint32_t>(val) : |
- static_cast<int32_t>(val); |
+ case RZ: |
+ // Nothing to do. |
+ break; |
- set_s_register_from_sinteger(dst, sint); |
- } else { |
- float val = get_float_from_s_register(src); |
+ default: |
+ UNREACHABLE(); |
+ } |
+ } |
- inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); |
+ // Update the destination register. |
+ set_s_register_from_sinteger(dst, temp); |
- int sint = unsigned_integer ? static_cast<uint32_t>(val) : |
- static_cast<int32_t>(val); |
- |
- set_s_register_from_sinteger(dst, sint); |
- } |
- break; |
- |
- default: |
- UNREACHABLE(); |
- } |
- |
} else { |
bool unsigned_integer = (instr->Bit(7) == 0); |