Commit 9da34c56 authored by Djordje.Pesic's avatar Djordje.Pesic Committed by Commit bot

MIPS: Add rounding support in simulator and RINT instruction.

Added rounding according to fcsr, CVT_W_D and RINT.D instruction in assembler, dissasembler and simulator and wrote appropiate tests.

BUG=

Review URL: https://codereview.chromium.org/1108583003

Cr-Commit-Position: refs/heads/master@{#28143}
parent 4b8bb5ed
...@@ -2246,6 +2246,18 @@ void Assembler::ceil_w_d(FPURegister fd, FPURegister fs) { ...@@ -2246,6 +2246,18 @@ void Assembler::ceil_w_d(FPURegister fd, FPURegister fs) {
} }
void Assembler::rint_s(FPURegister fd, FPURegister fs) { rint(S, fd, fs); }
void Assembler::rint(SecondaryField fmt, FPURegister fd, FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(COP1, fmt, f0, fs, fd, RINT);
}
void Assembler::rint_d(FPURegister fd, FPURegister fs) { rint(D, fd, fs); }
void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) { void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r2)); DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S); GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S);
......
...@@ -912,6 +912,9 @@ class Assembler : public AssemblerBase { ...@@ -912,6 +912,9 @@ class Assembler : public AssemblerBase {
void floor_w_d(FPURegister fd, FPURegister fs); void floor_w_d(FPURegister fd, FPURegister fs);
void ceil_w_s(FPURegister fd, FPURegister fs); void ceil_w_s(FPURegister fd, FPURegister fs);
void ceil_w_d(FPURegister fd, FPURegister fs); void ceil_w_d(FPURegister fd, FPURegister fs);
void rint_s(FPURegister fd, FPURegister fs);
void rint_d(FPURegister fd, FPURegister fs);
void rint(SecondaryField fmt, FPURegister fd, FPURegister fs);
void cvt_l_s(FPURegister fd, FPURegister fs); void cvt_l_s(FPURegister fd, FPURegister fs);
void cvt_l_d(FPURegister fd, FPURegister fs); void cvt_l_d(FPURegister fd, FPURegister fs);
......
This diff is collapsed.
...@@ -487,6 +487,9 @@ void Decoder::Unknown(Instruction* instr) { ...@@ -487,6 +487,9 @@ void Decoder::Unknown(Instruction* instr) {
bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) { bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
switch (instr->FunctionFieldRaw()) { switch (instr->FunctionFieldRaw()) {
case RINT:
Format(instr, "rint.'t 'fd, 'fs");
break;
case MIN: case MIN:
Format(instr, "min.'t 'fd, 'fs, 'ft"); Format(instr, "min.'t 'fd, 'fs, 'ft");
break; break;
......
...@@ -1272,6 +1272,16 @@ bool Simulator::test_fcsr_bit(uint32_t cc) { ...@@ -1272,6 +1272,16 @@ bool Simulator::test_fcsr_bit(uint32_t cc) {
} }
void Simulator::set_fcsr_rounding_mode(FPURoundingMode mode) {
FCSR_ |= mode & kFPURoundingModeMask;
}
unsigned int Simulator::get_fcsr_rounding_mode() {
return FCSR_ & kFPURoundingModeMask;
}
// Sets the rounding error codes in FCSR based on the result of the rounding. // Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid. // Returns true if the operation was invalid.
bool Simulator::set_fcsr_round_error(double original, double rounded) { bool Simulator::set_fcsr_round_error(double original, double rounded) {
...@@ -1304,6 +1314,47 @@ bool Simulator::set_fcsr_round_error(double original, double rounded) { ...@@ -1304,6 +1314,47 @@ bool Simulator::set_fcsr_round_error(double original, double rounded) {
} }
void Simulator::round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs) {
// 0 RN (round to nearest): Round a result to the nearest
// representable value; if the result is exactly halfway between
// two representable values, round to zero. Behave like round_w_d.
// 1 RZ (round toward zero): Round a result to the closest
// representable value whose absolute value is less than or
// equal to the infinitely accurate result. Behave like trunc_w_d.
// 2 RP (round up, or toward infinity): Round a result to the
// next representable value up. Behave like ceil_w_d.
// 3 RD (round down, or toward −infinity): Round a result to
// the next representable value down. Behave like floor_w_d.
switch (get_fcsr_rounding_mode()) {
case kRoundToNearest:
rounded = std::floor(fs + 0.5);
rounded_int = static_cast<int32_t>(rounded);
if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
rounded_int--;
}
break;
case kRoundToZero:
rounded = trunc(fs);
rounded_int = static_cast<int32_t>(rounded);
break;
case kRoundToPlusInf:
rounded = std::ceil(fs);
rounded_int = static_cast<int32_t>(rounded);
break;
case kRoundToMinusInf:
rounded = std::floor(fs);
rounded_int = static_cast<int32_t>(rounded);
break;
}
}
// Raw access to the PC register. // Raw access to the PC register.
void Simulator::set_pc(int32_t value) { void Simulator::set_pc(int32_t value) {
pc_modified_ = true; pc_modified_ = true;
...@@ -2146,6 +2197,43 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2146,6 +2197,43 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
cc = instr->FCccValue(); cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc); fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) { switch (instr->FunctionFieldRaw()) {
case RINT: {
DCHECK(IsMipsArchVariant(kMips32r6));
double result, temp, temp_result;
double upper = std::ceil(fs);
double 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;
double 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_double(fd_reg, result);
if (result != fs) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
break;
}
case SEL: case SEL:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (fd_int & 0x1) == 0 ? fs : ft); set_fpu_register_double(fd_reg, (fd_int & 0x1) == 0 ? fs : ft);
...@@ -2230,10 +2318,15 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2230,10 +2318,15 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
case C_ULE_D: case C_ULE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break; break;
case CVT_W_D: // Convert double to word. case CVT_W_D: { // Convert double to word.
// Rounding modes are not yet supported. double rounded;
DCHECK((FCSR_ & 3) == 0); int32_t result;
// In rounding mode 0 it should behave like ROUND. round_according_to_fcsr(fs, rounded, result, fs);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case ROUND_W_D: // Round double to word (round half to even). case ROUND_W_D: // Round double to word (round half to even).
{ {
double rounded = std::floor(fs + 0.5); double rounded = std::floor(fs + 0.5);
......
...@@ -175,8 +175,11 @@ class Simulator { ...@@ -175,8 +175,11 @@ class Simulator {
double get_fpu_register_double(int fpureg) const; double get_fpu_register_double(int fpureg) const;
void set_fcsr_bit(uint32_t cc, bool value); void set_fcsr_bit(uint32_t cc, bool value);
bool test_fcsr_bit(uint32_t cc); bool test_fcsr_bit(uint32_t cc);
void set_fcsr_rounding_mode(FPURoundingMode mode);
unsigned int get_fcsr_rounding_mode();
bool set_fcsr_round_error(double original, double rounded); bool set_fcsr_round_error(double original, double rounded);
void round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs);
// Special case of set_register and get_register to access the raw PC value. // Special case of set_register and get_register to access the raw PC value.
void set_pc(int32_t value); void set_pc(int32_t value);
int32_t get_pc() const; int32_t get_pc() const;
......
...@@ -2428,6 +2428,18 @@ void Assembler::ceil_w_d(FPURegister fd, FPURegister fs) { ...@@ -2428,6 +2428,18 @@ void Assembler::ceil_w_d(FPURegister fd, FPURegister fs) {
} }
void Assembler::rint_s(FPURegister fd, FPURegister fs) { rint(S, fd, fs); }
void Assembler::rint_d(FPURegister fd, FPURegister fs) { rint(D, fd, fs); }
void Assembler::rint(SecondaryField fmt, FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r6);
GenInstrRegister(COP1, D, f0, fs, fd, RINT);
}
void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) { void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r2); DCHECK(kArchVariant == kMips64r2);
GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S); GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S);
......
...@@ -942,6 +942,10 @@ class Assembler : public AssemblerBase { ...@@ -942,6 +942,10 @@ class Assembler : public AssemblerBase {
void floor_w_d(FPURegister fd, FPURegister fs); void floor_w_d(FPURegister fd, FPURegister fs);
void ceil_w_s(FPURegister fd, FPURegister fs); void ceil_w_s(FPURegister fd, FPURegister fs);
void ceil_w_d(FPURegister fd, FPURegister fs); void ceil_w_d(FPURegister fd, FPURegister fs);
void rint_s(FPURegister fd, FPURegister fs);
void rint_d(FPURegister fd, FPURegister fs);
void rint(SecondaryField fmt, FPURegister fd, FPURegister fs);
void cvt_l_s(FPURegister fd, FPURegister fs); void cvt_l_s(FPURegister fd, FPURegister fs);
void cvt_l_d(FPURegister fd, FPURegister fs); void cvt_l_d(FPURegister fd, FPURegister fs);
......
This diff is collapsed.
...@@ -517,6 +517,9 @@ int Decoder::DecodeBreakInstr(Instruction* instr) { ...@@ -517,6 +517,9 @@ int Decoder::DecodeBreakInstr(Instruction* instr) {
bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) { bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
switch (instr->FunctionFieldRaw()) { switch (instr->FunctionFieldRaw()) {
case RINT:
Format(instr, "rint.'t 'fd, 'fs");
break;
case SELEQZ_C: case SELEQZ_C:
Format(instr, "seleqz.'t 'fd, 'fs, 'ft"); Format(instr, "seleqz.'t 'fd, 'fs, 'ft");
break; break;
......
...@@ -1252,6 +1252,89 @@ bool Simulator::set_fcsr_round64_error(double original, double rounded) { ...@@ -1252,6 +1252,89 @@ bool Simulator::set_fcsr_round64_error(double original, double rounded) {
} }
// for cvt instructions only
void Simulator::round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs) {
// 0 RN (round to nearest): Round a result to the nearest
// representable value; if the result is exactly halfway between
// two representable values, round to zero. Behave like round_w_d.
// 1 RZ (round toward zero): Round a result to the closest
// representable value whose absolute value is less than or
// equal to the infinitely accurate result. Behave like trunc_w_d.
// 2 RP (round up, or toward +infinity): Round a result to the
// next representable value up. Behave like ceil_w_d.
// 3 RN (round down, or toward −infinity): Round a result to
// the next representable value down. Behave like floor_w_d.
switch (FCSR_ & 3) {
case kRoundToNearest:
rounded = std::floor(fs + 0.5);
rounded_int = static_cast<int32_t>(rounded);
if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
rounded_int--;
}
break;
case kRoundToZero:
rounded = trunc(fs);
rounded_int = static_cast<int32_t>(rounded);
break;
case kRoundToPlusInf:
rounded = std::ceil(fs);
rounded_int = static_cast<int32_t>(rounded);
break;
case kRoundToMinusInf:
rounded = std::floor(fs);
rounded_int = static_cast<int32_t>(rounded);
break;
}
}
void Simulator::round64_according_to_fcsr(double toRound, double& rounded,
int64_t& rounded_int, double fs) {
// 0 RN (round to nearest): Round a result to the nearest
// representable value; if the result is exactly halfway between
// two representable values, round to zero. Behave like round_w_d.
// 1 RZ (round toward zero): Round a result to the closest
// representable value whose absolute value is less than or.
// equal to the infinitely accurate result. Behave like trunc_w_d.
// 2 RP (round up, or toward +infinity): Round a result to the
// next representable value up. Behave like ceil_w_d.
// 3 RN (round down, or toward −infinity): Round a result to
// the next representable value down. Behave like floor_w_d.
switch (FCSR_ & 3) {
case kRoundToNearest:
rounded = std::floor(fs + 0.5);
rounded_int = static_cast<int64_t>(rounded);
if ((rounded_int & 1) != 0 && rounded_int - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
rounded_int--;
}
break;
case kRoundToZero:
rounded = trunc(fs);
rounded_int = static_cast<int64_t>(rounded);
break;
case kRoundToPlusInf:
rounded = std::ceil(fs);
rounded_int = static_cast<int64_t>(rounded);
break;
case kRoundToMinusInf:
rounded = std::floor(fs);
rounded_int = static_cast<int64_t>(rounded);
break;
}
}
// Raw access to the PC register. // Raw access to the PC register.
void Simulator::set_pc(int64_t value) { void Simulator::set_pc(int64_t value) {
pc_modified_ = true; pc_modified_ = true;
...@@ -2350,6 +2433,43 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2350,6 +2433,43 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
int64_t ft_int = bit_cast<int64_t>(ft); int64_t ft_int = bit_cast<int64_t>(ft);
int64_t fd_int = bit_cast<int64_t>(fd); int64_t fd_int = bit_cast<int64_t>(fd);
switch (instr->FunctionFieldRaw()) { switch (instr->FunctionFieldRaw()) {
case RINT: {
DCHECK(kArchVariant == kMips64r6);
double result, temp, temp_result;
double upper = std::ceil(fs);
double lower = std::floor(fs);
switch (FCSR_ & 0x3) {
case kRoundToNearest:
if (upper - fs < fs - lower) {
result = upper;
} else if (upper - fs > fs - lower) {
result = lower;
} else {
temp_result = upper / 2;
double 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_double(fd_reg, result);
if (result != fs) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
break;
}
case SEL: case SEL:
DCHECK(kArchVariant == kMips64r6); DCHECK(kArchVariant == kMips64r6);
set_fpu_register_double(fd_reg, (fd_int & 0x1) == 0 ? fs : ft); set_fpu_register_double(fd_reg, (fd_int & 0x1) == 0 ? fs : ft);
...@@ -2433,11 +2553,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2433,11 +2553,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
case C_ULE_D: case C_ULE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break; break;
case CVT_W_D: // Convert double to word. case CVT_W_D: { // Convert double to word.
// Rounding modes are not yet supported. double rounded;
DCHECK((FCSR_ & 3) == 0); int32_t result;
// In rounding mode 0 it should behave like ROUND. round_according_to_fcsr(fs, rounded, result, fs);
// No break. set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
break;
}
case ROUND_W_D: // Round double to word (round half to even). case ROUND_W_D: // Round double to word (round half to even).
{ {
double rounded = std::floor(fs + 0.5); double rounded = std::floor(fs + 0.5);
...@@ -2482,11 +2607,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2482,11 +2607,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
case CVT_S_D: // Convert double to float (single). case CVT_S_D: // Convert double to float (single).
set_fpu_register_float(fd_reg, static_cast<float>(fs)); set_fpu_register_float(fd_reg, static_cast<float>(fs));
break; break;
case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word. case CVT_L_D: { // Mips64r2: Truncate double to 64-bit long-word.
// Rounding modes are not yet supported. double rounded;
DCHECK((FCSR_ & 3) == 0); int64_t result;
// In rounding mode 0 it should behave like ROUND. round64_according_to_fcsr(fs, rounded, result, fs);
// No break. set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
break;
}
case ROUND_L_D: { // Mips64r2 instruction. case ROUND_L_D: { // Mips64r2 instruction.
// check error cases // check error cases
double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5); double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
...@@ -3623,7 +3753,6 @@ uintptr_t Simulator::PopAddress() { ...@@ -3623,7 +3753,6 @@ uintptr_t Simulator::PopAddress() {
#undef UNSUPPORTED #undef UNSUPPORTED
} } // namespace v8::internal } } // namespace v8::internal
#endif // USE_SIMULATOR #endif // USE_SIMULATOR
......
...@@ -207,6 +207,10 @@ class Simulator { ...@@ -207,6 +207,10 @@ class Simulator {
bool test_fcsr_bit(uint32_t cc); bool test_fcsr_bit(uint32_t cc);
bool set_fcsr_round_error(double original, double rounded); bool set_fcsr_round_error(double original, double rounded);
bool set_fcsr_round64_error(double original, double rounded); bool set_fcsr_round64_error(double original, double rounded);
void round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs);
void round64_according_to_fcsr(double toRound, double& rounded,
int64_t& rounded_int, double fs);
// Special case of set_register and get_register to access the raw PC value. // Special case of set_register and get_register to access the raw PC value.
void set_pc(int64_t value); void set_pc(int64_t value);
......
This diff is collapsed.
This diff is collapsed.
...@@ -538,6 +538,8 @@ TEST(Type1) { ...@@ -538,6 +538,8 @@ TEST(Type1) {
COMPARE(min(D, f3, f4, f5), "462520dc min.d f3, f4, f5"); COMPARE(min(D, f3, f4, f5), "462520dc min.d f3, f4, f5");
COMPARE(max(D, f3, f4, f5), "462520de max.d f3, f4, f5"); COMPARE(max(D, f3, f4, f5), "462520de max.d f3, f4, f5");
COMPARE(rint_d(f8, f6), "4620321a rint.d f8, f6");
VERIFY_RUN(); VERIFY_RUN();
} }
} }
...@@ -685,6 +685,7 @@ TEST(Type1) { ...@@ -685,6 +685,7 @@ TEST(Type1) {
COMPARE(min(D, f3, f4, f5), "462520dc min.d f3, f4, f5"); COMPARE(min(D, f3, f4, f5), "462520dc min.d f3, f4, f5");
COMPARE(max(D, f3, f4, f5), "462520de max.d f3, f4, f5"); COMPARE(max(D, f3, f4, f5), "462520de max.d f3, f4, f5");
COMPARE(rint_d(f8, f6), "4620321a rint.d f8, f6");
VERIFY_RUN(); VERIFY_RUN();
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment