| 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; }
|
| +
|
| +
|
| +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;
|
| +}
|
| +
|
| +
|
| +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.
|
| + 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);
|
| }
|
|
|