Chromium Code Reviews| Index: src/mips64/simulator-mips64.cc |
| diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc |
| index 29fccd0b5974b8d5b81f2002f0d4fd6656d65866..074d4f553143d56a926c49407067dddc194ebd1e 100644 |
| --- a/src/mips64/simulator-mips64.cc |
| +++ b/src/mips64/simulator-mips64.cc |
| @@ -1188,6 +1188,12 @@ bool Simulator::test_fcsr_bit(uint32_t cc) { |
| } |
| +void Simulator::set_fcsr_rounding_mode(FPURoundingMode mode) { FCSR_ |= mode; } |
|
paul.l...
2015/05/09 01:07:48
As you did in previous CL for mips32, please use t
Djordje.Pesic
2015/05/14 13:22:31
Done.
|
| + |
| + |
| +unsigned int Simulator::get_fcsr_rounding_mode() { return FCSR_ & 0x3; } |
| + |
| + |
| // Sets the rounding error codes in FCSR based on the result of the rounding. |
| // Returns true if the operation was invalid. |
| bool Simulator::set_fcsr_round_error(double original, double rounded) { |
| @@ -1209,7 +1215,7 @@ bool Simulator::set_fcsr_round_error(double original, double rounded) { |
| ret = true; |
| } |
| - if (rounded > max_int32 || rounded < min_int32) { |
| + if (rounded >= max_int32 || rounded <= min_int32) { |
| set_fcsr_bit(kFCSROverflowFlagBit, true); |
| // The reference is not really clear but it seems this is required: |
| set_fcsr_bit(kFCSRInvalidOpFlagBit, true); |
| @@ -1241,7 +1247,69 @@ bool Simulator::set_fcsr_round64_error(double original, double rounded) { |
| ret = true; |
| } |
| - if (rounded > max_int64 || rounded < min_int64) { |
| + if (rounded >= max_int64 || rounded <= min_int64) { |
| + set_fcsr_bit(kFCSROverflowFlagBit, true); |
| + // The reference is not really clear but it seems this is required: |
| + set_fcsr_bit(kFCSRInvalidOpFlagBit, true); |
| + ret = true; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| + |
|
paul.l...
2015/05/09 01:07:48
I like the comments that you have on the set_fcsr_
Djordje.Pesic
2015/05/14 13:22:31
Done.
|
| +bool Simulator::set_fcsr_round_error(float original, float rounded) { |
| + bool ret = false; |
| + double max_int32 = std::numeric_limits<int32_t>::max(); |
| + double min_int32 = std::numeric_limits<int32_t>::min(); |
| + |
| + if (!std::isfinite(original) || !std::isfinite(rounded)) { |
| + set_fcsr_bit(kFCSRInvalidOpFlagBit, true); |
| + ret = true; |
| + } |
| + |
| + if (original != rounded) { |
| + set_fcsr_bit(kFCSRInexactFlagBit, true); |
| + } |
| + |
| + if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) { |
| + set_fcsr_bit(kFCSRUnderflowFlagBit, true); |
| + ret = true; |
| + } |
| + |
| + if (rounded >= max_int32 || rounded <= min_int32) { |
| + set_fcsr_bit(kFCSROverflowFlagBit, true); |
| + // The reference is not really clear but it seems this is required: |
| + set_fcsr_bit(kFCSRInvalidOpFlagBit, true); |
| + ret = true; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| + |
| +// Sets the rounding error codes in FCSR based on the result of the rounding. |
| +// Returns true if the operation was invalid. |
| +bool Simulator::set_fcsr_round64_error(float original, float rounded) { |
| + bool ret = false; |
| + double max_int64 = std::numeric_limits<int64_t>::max(); |
| + double min_int64 = std::numeric_limits<int64_t>::min(); |
| + |
| + if (!std::isfinite(original) || !std::isfinite(rounded)) { |
| + set_fcsr_bit(kFCSRInvalidOpFlagBit, true); |
| + ret = true; |
| + } |
| + |
| + if (original != rounded) { |
| + set_fcsr_bit(kFCSRInexactFlagBit, true); |
| + } |
| + |
| + if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) { |
| + set_fcsr_bit(kFCSRUnderflowFlagBit, true); |
| + ret = true; |
| + } |
| + |
| + if (rounded >= max_int64 || rounded <= min_int64) { |
| set_fcsr_bit(kFCSROverflowFlagBit, true); |
| // The reference is not really clear but it seems this is required: |
| set_fcsr_bit(kFCSRInvalidOpFlagBit, true); |
| @@ -2356,13 +2424,54 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
| const int32_t& fs_reg, |
| const int32_t& ft_reg, |
| const int32_t& fd_reg) { |
| - float fs, ft; |
| + float fs, ft, fd; |
| fs = get_fpu_register_float(fs_reg); |
| ft = get_fpu_register_float(ft_reg); |
| + fd = get_fpu_register_float(fd_reg); |
| + int32_t ft_int = bit_cast<int32_t>(ft); |
| + int32_t fd_int = bit_cast<int32_t>(fd); |
| uint32_t cc, fcsr_cc; |
| cc = instr->FCccValue(); |
| fcsr_cc = get_fcsr_condition_bit(cc); |
| switch (instr->FunctionFieldRaw()) { |
| + case RINT: { |
| + DCHECK(kArchVariant == kMips64r6); |
| + float result, temp_result; |
| + double temp; |
| + float upper = std::ceil(fs); |
| + float lower = std::floor(fs); |
| + switch (get_fcsr_rounding_mode()) { |
| + case kRoundToNearest: |
| + if (upper - fs < fs - lower) { |
| + result = upper; |
| + } else if (upper - fs > fs - lower) { |
| + result = lower; |
| + } else { |
| + temp_result = upper / 2; |
| + float reminder = modf(temp_result, &temp); |
| + if (reminder == 0) { |
| + result = upper; |
| + } else { |
| + result = lower; |
| + } |
| + } |
| + break; |
| + case kRoundToZero: |
| + result = (fs > 0 ? lower : upper); |
| + break; |
| + case kRoundToPlusInf: |
| + result = upper; |
| + break; |
| + case kRoundToMinusInf: |
| + result = lower; |
| + break; |
| + } |
| + set_fpu_register_float(fd_reg, result); |
| + if (result != fs) { |
| + set_fcsr_bit(kFCSRInexactFlagBit, true); |
| + } |
| + break; |
| + } |
| case ADD_D: |
| set_fpu_register_float(fd_reg, fs + ft); |
| break; |
| @@ -2387,6 +2496,16 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
| case SQRT_D: |
| set_fpu_register_float(fd_reg, fast_sqrt(fs)); |
| break; |
| + case RSQRT_D: { |
| + float result = 1.0 / fast_sqrt(fs); |
| + set_fpu_register_float(fd_reg, result); |
| + break; |
| + } |
| + case RECIP: { |
| + float result = 1.0 / fs; |
| + set_fpu_register_float(fd_reg, result); |
| + break; |
| + } |
| case C_UN_D: |
| set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
| break; |
| @@ -2411,6 +2530,202 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
| case CVT_D_S: |
| set_fpu_register_double(fd_reg, static_cast<double>(fs)); |
| break; |
| + case TRUNC_W_S: { // Truncate single to word (round towards 0). |
| + float rounded = trunc(fs); |
| + int32_t result = static_cast<int32_t>(rounded); |
| + set_fpu_register_word(fd_reg, result); |
| + if (set_fcsr_round_error(fs, rounded)) { |
| + set_fpu_register_word(fd_reg, kFPUInvalidResult); |
| + } |
| + } break; |
| + case TRUNC_L_S: { |
| + float rounded = trunc(fs); |
| + int64_t result = static_cast<int64_t>(rounded); |
| + set_fpu_register(fd_reg, result); |
| + if (set_fcsr_round64_error(fs, rounded)) { |
| + set_fpu_register(fd_reg, kFPU64InvalidResult); |
| + } |
| + break; |
| + } |
| + case ROUND_W_S: { |
| + float rounded = std::floor(fs + 0.5); |
| + int32_t result = static_cast<int32_t>(rounded); |
| + if ((result & 1) != 0 && result - fs == 0.5) { |
| + // If the number is halfway between two integers, |
| + // round to the even one. |
| + result--; |
| + } |
| + set_fpu_register_word(fd_reg, result); |
| + if (set_fcsr_round_error(fs, rounded)) { |
| + set_fpu_register_word(fd_reg, kFPUInvalidResult); |
| + } |
| + break; |
| + } |
| + case ROUND_L_S: { // Mips32r2 instruction. |
|
paul.l...
2015/05/09 01:07:48
mips64r2.
We don't support earlier than mips64r2,
Djordje.Pesic
2015/05/14 13:22:31
Done.
|
| + float rounded = std::floor(fs + 0.5); |
| + int64_t result = static_cast<int64_t>(rounded); |
| + if ((result & 1) != 0 && result - fs == 0.5) { |
| + // If the number is halfway between two integers, |
| + // round to the even one. |
| + result--; |
| + } |
| + int64_t i64 = static_cast<int64_t>(result); |
| + set_fpu_register(fd_reg, i64); |
| + if (set_fcsr_round64_error(fs, rounded)) { |
| + set_fpu_register(fd_reg, kFPU64InvalidResult); |
| + } |
| + break; |
| + } |
| + case FLOOR_L_S: { // Mips64r2 instruction. |
| + float rounded = floor(fs); |
| + int64_t result = static_cast<int64_t>(rounded); |
| + set_fpu_register(fd_reg, result); |
| + if (set_fcsr_round64_error(fs, rounded)) { |
| + set_fpu_register(fd_reg, kFPU64InvalidResult); |
| + } |
| + break; |
| + } |
| + case FLOOR_W_S: // Round double to word towards negative infinity. |
| + { |
| + float rounded = std::floor(fs); |
| + int32_t result = static_cast<int32_t>(rounded); |
| + set_fpu_register_word(fd_reg, result); |
| + if (set_fcsr_round_error(fs, rounded)) { |
| + set_fpu_register_word(fd_reg, kFPUInvalidResult); |
| + } |
| + } break; |
| + case CEIL_W_S: // Round double to word towards positive infinity. |
| + { |
| + float rounded = std::ceil(fs); |
| + int32_t result = static_cast<int32_t>(rounded); |
| + set_fpu_register_word(fd_reg, result); |
| + if (set_fcsr_round_error(fs, rounded)) { |
| + set_fpu_register(fd_reg, kFPUInvalidResult); |
| + } |
| + } break; |
| + case CEIL_L_S: { // Mips64r2 instruction. |
| + float rounded = ceil(fs); |
| + int64_t result = static_cast<int64_t>(rounded); |
| + set_fpu_register(fd_reg, result); |
| + if (set_fcsr_round64_error(fs, rounded)) { |
| + set_fpu_register(fd_reg, kFPU64InvalidResult); |
| + } |
| + break; |
| + } |
| + case MINA: |
| + DCHECK(kArchVariant == kMips64r6); |
| + fs = get_fpu_register_float(fs_reg); |
| + if (std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else if (std::isnan(fs) && !std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, ft); |
| + } else if (!std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else { |
| + float result; |
| + if (fabs(fs) > fabs(ft)) { |
| + result = ft; |
| + } else if (fabs(fs) < fabs(ft)) { |
| + result = fs; |
| + } else { |
| + result = (fs > ft ? fs : ft); |
| + } |
| + set_fpu_register_float(fd_reg, result); |
| + } |
| + break; |
| + case MAXA: |
| + DCHECK(kArchVariant == kMips64r6); |
| + fs = get_fpu_register_float(fs_reg); |
| + if (std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else if (std::isnan(fs) && !std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, ft); |
| + } else if (!std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else { |
| + float result; |
| + if (fabs(fs) < fabs(ft)) { |
| + result = ft; |
| + } else if (fabs(fs) > fabs(ft)) { |
| + result = fs; |
| + } else { |
| + result = (fs > ft ? fs : ft); |
| + } |
| + set_fpu_register_float(fd_reg, result); |
| + } |
| + break; |
| + case MIN: |
| + DCHECK(kArchVariant == kMips64r6); |
| + fs = get_fpu_register_float(fs_reg); |
| + if (std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else if (std::isnan(fs) && !std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, ft); |
| + } else if (!std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else { |
| + set_fpu_register_float(fd_reg, (fs >= ft) ? ft : fs); |
| + } |
| + break; |
| + case MAX: |
| + DCHECK(kArchVariant == kMips64r6); |
| + fs = get_fpu_register_float(fs_reg); |
| + if (std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else if (std::isnan(fs) && !std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, ft); |
| + } else if (!std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } else { |
| + set_fpu_register_float(fd_reg, (fs <= ft) ? ft : fs); |
| + } |
| + break; |
| + case SEL: |
| + DCHECK(kArchVariant == kMips64r6); |
| + set_fpu_register_float(fd_reg, (fd_int & 0x1) == 0 ? fs : ft); |
| + break; |
| + case SELEQZ_C: |
| + DCHECK(kArchVariant == kMips64r6); |
| + set_fpu_register_float( |
| + fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_float(fs_reg) : 0.0); |
| + break; |
| + case SELNEZ_C: |
| + DCHECK(kArchVariant == kMips64r6); |
| + set_fpu_register_float( |
| + fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_float(fs_reg) : 0.0); |
| + break; |
| + case MOVZ_C: { |
| + DCHECK(kArchVariant == kMips64r2); |
| + int32_t rt_reg = instr->RtValue(); |
| + int64_t rt = get_register(rt_reg); |
| + if (rt == 0) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } |
| + break; |
| + } |
| + case MOVN_C: { |
| + DCHECK(kArchVariant == kMips64r2); |
| + int32_t rt_reg = instr->RtValue(); |
| + int64_t rt = get_register(rt_reg); |
| + if (rt != 0) { |
| + set_fpu_register_float(fd_reg, fs); |
| + } |
| + break; |
| + } |
| + case MOVF: { |
| + // Same function field for MOVT.D and MOVF.D |
| + uint32_t ft_cc = (ft_reg >> 2) & 0x7; |
| + ft_cc = get_fcsr_condition_bit(ft_cc); |
| + |
| + if (instr->Bit(16)) { // Read Tf bit. |
| + // MOVT.D |
| + if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs); |
| + } else { |
| + // MOVF.D |
| + if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs); |
| + } |
| + break; |
| + } |
| default: |
| // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S |
| // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented. |
| @@ -2426,7 +2741,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
| double ft, fs, fd; |
| uint32_t cc, fcsr_cc; |
| fs = get_fpu_register_double(fs_reg); |
| - ft = get_fpu_register_double(ft_reg); |
| + if (instr->FunctionFieldRaw() != MOVF) { |
| + ft = get_fpu_register_double(ft_reg); |
| + } |
| fd = get_fpu_register_double(fd_reg); |
| cc = instr->FCccValue(); |
| fcsr_cc = get_fcsr_condition_bit(cc); |
| @@ -2438,7 +2755,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
| double result, temp, temp_result; |
| double upper = std::ceil(fs); |
| double lower = std::floor(fs); |
| - switch (FCSR_ & 0x3) { |
| + switch (get_fcsr_rounding_mode()) { |
| case kRoundToNearest: |
| if (upper - fs < fs - lower) { |
| result = upper; |
| @@ -2482,6 +2799,79 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
| DCHECK(kArchVariant == kMips64r6); |
| set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0); |
| break; |
| + case MOVZ_C: { |
| + DCHECK(kArchVariant == kMips64r2); |
| + int32_t rt_reg = instr->RtValue(); |
| + int64_t rt = get_register(rt_reg); |
| + if (rt == 0) { |
| + set_fpu_register_double(fd_reg, fs); |
| + } |
| + break; |
| + } |
| + case MOVN_C: { |
| + DCHECK(kArchVariant == kMips64r2); |
| + int32_t rt_reg = instr->RtValue(); |
| + int64_t rt = get_register(rt_reg); |
| + if (rt != 0) { |
| + set_fpu_register_double(fd_reg, fs); |
| + } |
| + break; |
| + } |
| + case MOVF: { |
| + // Same function field for MOVT.D and MOVF.D |
| + uint32_t ft_cc = (ft_reg >> 2) & 0x7; |
| + ft_cc = get_fcsr_condition_bit(ft_cc); |
| + if (instr->Bit(16)) { // Read Tf bit. |
| + // MOVT.D |
| + if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs); |
| + } else { |
| + // MOVF.D |
| + if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs); |
| + } |
| + break; |
| + } |
| + case MINA: |
| + DCHECK(kArchVariant == kMips64r6); |
| + fs = get_fpu_register_double(fs_reg); |
| + if (std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_double(fd_reg, fs); |
| + } else if (std::isnan(fs) && !std::isnan(ft)) { |
| + set_fpu_register_double(fd_reg, ft); |
| + } else if (!std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_double(fd_reg, fs); |
| + } else { |
| + double result; |
| + if (fabs(fs) > fabs(ft)) { |
| + result = ft; |
| + } else if (fabs(fs) < fabs(ft)) { |
| + result = fs; |
| + } else { |
| + result = (fs > ft ? fs : ft); |
| + } |
| + set_fpu_register_double(fd_reg, result); |
| + } |
| + break; |
| + case MAXA: |
| + DCHECK(kArchVariant == kMips64r6); |
| + fs = get_fpu_register_double(fs_reg); |
| + if (std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_double(fd_reg, fs); |
| + } else if (std::isnan(fs) && !std::isnan(ft)) { |
| + set_fpu_register_double(fd_reg, ft); |
| + } else if (!std::isnan(fs) && std::isnan(ft)) { |
| + set_fpu_register_double(fd_reg, fs); |
| + } else { |
| + double result; |
| + if (fabs(fs) < fabs(ft)) { |
| + result = ft; |
| + } else if (fabs(fs) > fabs(ft)) { |
| + result = fs; |
| + } else { |
| + result = (fs > ft ? fs : ft); |
| + } |
| + set_fpu_register_double(fd_reg, result); |
| + } |
| + break; |
| case MIN: |
| DCHECK(kArchVariant == kMips64r6); |
| fs = get_fpu_register_double(fs_reg); |
| @@ -2532,6 +2922,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
| case SQRT_D: |
| set_fpu_register_double(fd_reg, fast_sqrt(fs)); |
| break; |
| + case RSQRT_D: { |
| + double result = 1.0 / fast_sqrt(fs); |
| + set_fpu_register_double(fd_reg, result); |
| + break; |
| + } |
| + case RECIP: { |
| + double result = 1.0 / fs; |
| + set_fpu_register_double(fd_reg, result); |
| + break; |
| + } |
| case C_UN_D: |
| set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
| break; |
| @@ -2618,10 +3018,15 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
| break; |
| } |
| case ROUND_L_D: { // Mips64r2 instruction. |
| - // check error cases |
| - double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5); |
| + double rounded = std::floor(fs + 0.5); |
| int64_t result = static_cast<int64_t>(rounded); |
| - set_fpu_register(fd_reg, result); |
| + if ((result & 1) != 0 && result - fs == 0.5) { |
| + // If the number is halfway between two integers, |
| + // round to the even one. |
| + result--; |
| + } |
| + int64_t i64 = static_cast<int64_t>(result); |
| + set_fpu_register(fd_reg, i64); |
| if (set_fcsr_round64_error(fs, rounded)) { |
| set_fpu_register(fd_reg, kFPU64InvalidResult); |
| } |