Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(180)

Unified Diff: src/mips64/simulator-mips64.cc

Issue 1147493002: Reland "MIPS: Add float instructions and test coverage, part one" (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Bug fixed Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/mips64/simulator-mips64.h ('k') | test/cctest/test-assembler-mips.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/mips64/simulator-mips64.cc
diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc
index 29fccd0b5974b8d5b81f2002f0d4fd6656d65866..39391d62cefd6763cbc4d63a867b9567c8822dca 100644
--- a/src/mips64/simulator-mips64.cc
+++ b/src/mips64/simulator-mips64.cc
@@ -1188,6 +1188,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.
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round_error(double original, double rounded) {
@@ -1252,6 +1262,68 @@ bool Simulator::set_fcsr_round64_error(double original, double rounded) {
}
+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);
+ ret = true;
+ }
+
+ return ret;
+}
+
+
// for cvt instructions only
void Simulator::round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs) {
@@ -2356,37 +2428,88 @@ 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 ADD_D:
+ 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_S:
set_fpu_register_float(fd_reg, fs + ft);
break;
- case SUB_D:
+ case SUB_S:
set_fpu_register_float(fd_reg, fs - ft);
break;
- case MUL_D:
+ case MUL_S:
set_fpu_register_float(fd_reg, fs * ft);
break;
- case DIV_D:
+ case DIV_S:
set_fpu_register_float(fd_reg, fs / ft);
break;
- case ABS_D:
+ case ABS_S:
set_fpu_register_float(fd_reg, fabs(fs));
break;
- case MOV_D:
+ case MOV_S:
set_fpu_register_float(fd_reg, fs);
break;
- case NEG_D:
+ case NEG_S:
set_fpu_register_float(fd_reg, -fs);
break;
- case SQRT_D:
+ case SQRT_S:
set_fpu_register_float(fd_reg, fast_sqrt(fs));
break;
+ case RSQRT_S: {
+ float result = 1.0 / fast_sqrt(fs);
+ set_fpu_register_float(fd_reg, result);
+ break;
+ }
+ case RECIP_S: {
+ 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 +2534,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: { // Mips64r2 instruction.
+ 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: { // Mips64r2 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 +2745,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 +2759,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 +2803,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 +2926,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_D: {
+ 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 +3022,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);
}
« no previous file with comments | « src/mips64/simulator-mips64.h ('k') | test/cctest/test-assembler-mips.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698