Index: src/mips64/simulator-mips64.cc |
diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc |
index 4298ca0e16cda9f61e8a10293278a910c2f0cea8..4b251f2b2fa037cbcbebe72d658bfde9ec66e18e 100644 |
--- a/src/mips64/simulator-mips64.cc |
+++ b/src/mips64/simulator-mips64.cc |
@@ -1282,6 +1282,8 @@ bool Simulator::set_fcsr_round64_error(double original, double rounded) { |
} |
+// 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(float original, float rounded) { |
bool ret = false; |
double max_int32 = std::numeric_limits<int32_t>::max(); |
@@ -1344,7 +1346,7 @@ bool Simulator::set_fcsr_round64_error(float original, float rounded) { |
} |
-// for cvt instructions only |
+// 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 |
@@ -1427,6 +1429,89 @@ void Simulator::round64_according_to_fcsr(double toRound, double& rounded, |
} |
+// for cvt instructions only |
+void Simulator::round_according_to_fcsr(float toRound, float& rounded, |
+ int32_t& rounded_int, float 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(float toRound, float& rounded, |
+ int64_t& rounded_int, float 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. |
void Simulator::set_pc(int64_t value) { |
pc_modified_ = true; |
@@ -2434,6 +2519,62 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, |
*alu_out = static_cast<int64_t>((rs_u & (mask << lsb)) >> lsb); |
break; |
} |
+ case BITSWAP: { // Mips32r6 instruction |
+ uint32_t input = static_cast<uint32_t>(rt); |
+ uint32_t output = 0; |
+ uint8_t i_byte, o_byte; |
+ |
+ // Reverse the bit in byte for each individual byte |
+ for (int i = 0; i < 4; i++) { |
+ output = output >> 8; |
+ i_byte = input & 0xff; |
+ |
+ // Fast way to reverse bits in byte |
+ // Devised by Sean Anderson, July 13, 2001 |
+ o_byte = static_cast<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) | |
+ (i_byte * 0x8020LU & 0x88440LU)) * |
+ 0x10101LU >> |
+ 16); |
+ |
+ output = output | (static_cast<uint32_t>(o_byte << 24)); |
+ input = input >> 8; |
+ } |
+ |
+ *alu_out = static_cast<int64_t>(static_cast<int32_t>(output)); |
+ break; |
+ } |
+ case DBITSWAP: { |
+ switch (instr->SaFieldRaw()) { |
+ case DBITSWAP_SA: { // Mips64r6 |
+ uint64_t input = static_cast<uint64_t>(rt); |
+ uint64_t output = 0; |
+ uint8_t i_byte, o_byte; |
+ |
+ // Reverse the bit in byte for each individual byte |
+ for (int i = 0; i < 8; i++) { |
+ output = output >> 8; |
+ i_byte = input & 0xff; |
+ |
+ // Fast way to reverse bits in byte |
+ // Devised by Sean Anderson, July 13, 2001 |
+ o_byte = |
+ static_cast<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) | |
+ (i_byte * 0x8020LU & 0x88440LU)) * |
+ 0x10101LU >> |
+ 16); |
+ |
+ output = output | ((static_cast<uint64_t>(o_byte) << 56)); |
+ input = input >> 8; |
+ } |
+ |
+ *alu_out = static_cast<int64_t>(output); |
+ break; |
+ } |
+ default: |
+ UNREACHABLE(); |
+ } |
+ break; |
+ } |
default: |
UNREACHABLE(); |
} |
@@ -2530,6 +2671,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
set_fpu_register_float(fd_reg, result); |
break; |
} |
+ case C_F_D: |
+ set_fcsr_bit(fcsr_cc, false); |
+ break; |
case C_UN_D: |
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
break; |
@@ -2554,6 +2698,91 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
case CVT_D_S: |
set_fpu_register_double(fd_reg, static_cast<double>(fs)); |
break; |
+ case CLASS_S: { // Mips64r6 instruction |
+ // Convert float input to uint32_t for easier bit manipulation |
+ uint32_t classed = bit_cast<uint32_t>(fs); |
+ |
+ // Extracting sign, exponent and mantissa from the input float |
+ uint32_t sign = (classed >> 31) & 1; |
+ uint32_t exponent = (classed >> 23) & 0x000000ff; |
+ uint32_t mantissa = classed & 0x007fffff; |
+ uint32_t result; |
+ float fResult; |
+ |
+ // Setting flags if input float is negative infinity, |
+ // positive infinity, negative zero or positive zero |
+ bool negInf = (classed == 0xFF800000); |
+ bool posInf = (classed == 0x7F800000); |
+ bool negZero = (classed == 0x80000000); |
+ bool posZero = (classed == 0x00000000); |
+ |
+ bool signalingNan; |
+ bool quietNan; |
+ bool negSubnorm; |
+ bool posSubnorm; |
+ bool negNorm; |
+ bool posNorm; |
+ |
+ // Setting flags if float is NaN |
+ signalingNan = false; |
+ quietNan = false; |
+ if (!negInf && !posInf && (exponent == 0xff)) { |
+ quietNan = ((mantissa & 0x00200000) == 0) && |
+ ((mantissa & (0x00200000 - 1)) == 0); |
+ signalingNan = !quietNan; |
+ } |
+ |
+ // Setting flags if float is subnormal number |
+ posSubnorm = false; |
+ negSubnorm = false; |
+ if ((exponent == 0) && (mantissa != 0)) { |
+ DCHECK(sign == 0 || sign == 1); |
+ posSubnorm = (sign == 0); |
+ negSubnorm = (sign == 1); |
+ } |
+ |
+ // Setting flags if float is normal number |
+ posNorm = false; |
+ negNorm = false; |
+ if (!posSubnorm && !negSubnorm && !posInf && !negInf && !signalingNan && |
+ !quietNan && !negZero && !posZero) { |
+ DCHECK(sign == 0 || sign == 1); |
+ posNorm = (sign == 0); |
+ negNorm = (sign == 1); |
+ } |
+ |
+ // Calculating result according to description of CLASS.S instruction |
+ result = (posZero << 9) | (posSubnorm << 8) | (posNorm << 7) | |
+ (posInf << 6) | (negZero << 5) | (negSubnorm << 4) | |
+ (negNorm << 3) | (negInf << 2) | (quietNan << 1) | signalingNan; |
+ |
+ DCHECK(result != 0); |
+ |
+ fResult = bit_cast<float>(result); |
+ set_fpu_register_float(fd_reg, fResult); |
+ |
+ break; |
+ } |
+ case CVT_L_S: { |
+ float rounded; |
+ int64_t result; |
+ round64_according_to_fcsr(fs, rounded, result, fs); |
+ set_fpu_register(fd_reg, result); |
+ if (set_fcsr_round64_error(fs, rounded)) { |
+ set_fpu_register(fd_reg, kFPU64InvalidResult); |
+ } |
+ break; |
+ } |
+ case CVT_W_S: { |
+ float rounded; |
+ int32_t result; |
+ 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 TRUNC_W_S: { // Truncate single to word (round towards 0). |
float rounded = trunc(fs); |
int32_t result = static_cast<int32_t>(rounded); |
@@ -2751,7 +2980,7 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
break; |
} |
default: |
- // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_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. |
UNREACHABLE(); |
} |
@@ -3037,7 +3266,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
round64_according_to_fcsr(fs, rounded, result, fs); |
set_fpu_register(fd_reg, result); |
if (set_fcsr_round64_error(fs, rounded)) { |
- set_fpu_register(fd_reg, kFPUInvalidResult); |
+ set_fpu_register(fd_reg, kFPU64InvalidResult); |
} |
break; |
} |
@@ -3083,9 +3312,75 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
} |
break; |
} |
- case C_F_D: |
- UNIMPLEMENTED_MIPS(); |
+ case CLASS_D: { // Mips64r6 instruction |
+ // Convert double input to uint64_t for easier bit manipulation |
+ uint64_t classed = bit_cast<uint64_t>(fs); |
+ |
+ // Extracting sign, exponent and mantissa from the input double |
+ uint32_t sign = (classed >> 63) & 1; |
+ uint32_t exponent = (classed >> 52) & 0x00000000000007ff; |
+ uint64_t mantissa = classed & 0x000fffffffffffff; |
+ uint64_t result; |
+ double dResult; |
+ |
+ // Setting flags if input double is negative infinity, |
+ // positive infinity, negative zero or positive zero |
+ bool negInf = (classed == 0xFFF0000000000000); |
+ bool posInf = (classed == 0x7FF0000000000000); |
+ bool negZero = (classed == 0x8000000000000000); |
+ bool posZero = (classed == 0x0000000000000000); |
+ |
+ bool signalingNan; |
+ bool quietNan; |
+ bool negSubnorm; |
+ bool posSubnorm; |
+ bool negNorm; |
+ bool posNorm; |
+ |
+ // Setting flags if double is NaN |
+ signalingNan = false; |
+ quietNan = false; |
+ if (!negInf && !posInf && exponent == 0x7ff) { |
+ quietNan = ((mantissa & 0x0008000000000000) != 0) && |
+ ((mantissa & (0x0008000000000000 - 1)) == 0); |
+ signalingNan = !quietNan; |
+ } |
+ |
+ // Setting flags if double is subnormal number |
+ posSubnorm = false; |
+ negSubnorm = false; |
+ if ((exponent == 0) && (mantissa != 0)) { |
+ DCHECK(sign == 0 || sign == 1); |
+ posSubnorm = (sign == 0); |
+ negSubnorm = (sign == 1); |
+ } |
+ |
+ // Setting flags if double is normal number |
+ posNorm = false; |
+ negNorm = false; |
+ if (!posSubnorm && !negSubnorm && !posInf && !negInf && !signalingNan && |
+ !quietNan && !negZero && !posZero) { |
+ DCHECK(sign == 0 || sign == 1); |
+ posNorm = (sign == 0); |
+ negNorm = (sign == 1); |
+ } |
+ |
+ // Calculating result according to description of CLASS.D instruction |
+ result = (posZero << 9) | (posSubnorm << 8) | (posNorm << 7) | |
+ (posInf << 6) | (negZero << 5) | (negSubnorm << 4) | |
+ (negNorm << 3) | (negInf << 2) | (quietNan << 1) | signalingNan; |
+ |
+ DCHECK(result != 0); |
+ |
+ dResult = bit_cast<double>(result); |
+ set_fpu_register_double(fd_reg, dResult); |
+ |
+ break; |
+ } |
+ case C_F_D: { |
+ set_fcsr_bit(fcsr_cc, false); |
break; |
+ } |
default: |
UNREACHABLE(); |
} |
@@ -3095,7 +3390,10 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, |
const int32_t& fs_reg, |
const int32_t& fd_reg, |
+ const int32_t& ft_reg, |
int64_t& alu_out) { |
+ float fs = get_fpu_register_float(fs_reg); |
+ float ft = get_fpu_register_float(ft_reg); |
switch (instr->FunctionFieldRaw()) { |
case CVT_S_W: // Convert word to float (single). |
alu_out = get_fpu_register_signed_word(fs_reg); |
@@ -3105,7 +3403,80 @@ void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, |
alu_out = get_fpu_register_signed_word(fs_reg); |
set_fpu_register_double(fd_reg, static_cast<double>(alu_out)); |
break; |
- default: // Mips64r6 CMP.S instructions unimplemented. |
+ case CMP_AF: |
+ set_fpu_register_word(fd_reg, 0); |
+ break; |
+ case CMP_UN: |
+ if (std::isnan(fs) || std::isnan(ft)) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_EQ: |
+ if (fs == ft) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_UEQ: |
+ if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_LT: |
+ if (fs < ft) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_ULT: |
+ if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_LE: |
+ if (fs <= ft) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_ULE: |
+ if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_OR: |
+ if (!std::isnan(fs) && !std::isnan(ft)) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_UNE: |
+ if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_NE: |
+ if (fs != ft) { |
+ set_fpu_register_word(fd_reg, -1); |
+ } else { |
+ set_fpu_register_word(fd_reg, 0); |
+ } |
+ break; |
+ default: |
UNREACHABLE(); |
} |
} |
@@ -3124,10 +3495,11 @@ void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, |
set_fpu_register_double(fd_reg, static_cast<double>(i64)); |
break; |
case CVT_S_L: |
- UNIMPLEMENTED_MIPS(); |
+ i64 = get_fpu_register(fs_reg); |
+ set_fpu_register_float(fd_reg, static_cast<float>(i64)); |
break; |
- case CMP_AF: // Mips64r6 CMP.D instructions. |
- UNIMPLEMENTED_MIPS(); |
+ case CMP_AF: |
+ set_fpu_register(fd_reg, 0); |
break; |
case CMP_UN: |
if (std::isnan(fs) || std::isnan(ft)) { |
@@ -3178,7 +3550,28 @@ void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, |
set_fpu_register(fd_reg, 0); |
} |
break; |
- default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED |
+ case CMP_OR: |
+ if (!std::isnan(fs) && !std::isnan(ft)) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_UNE: |
+ if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_NE: |
+ if (fs != ft && (!std::isnan(fs) && !std::isnan(ft))) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ default: |
UNREACHABLE(); |
} |
} |
@@ -3227,7 +3620,7 @@ void Simulator::DecodeTypeRegisterCOP1( |
DecodeTypeRegisterDRsType(instr, fs_reg, ft_reg, fd_reg); |
break; |
case W: |
- DecodeTypeRegisterWRsType(instr, fs_reg, fd_reg, alu_out); |
+ DecodeTypeRegisterWRsType(instr, fs_reg, fd_reg, ft_reg, alu_out); |
break; |
case L: |
DecodeTypeRegisterLRsType(instr, fs_reg, fd_reg, ft_reg); |
@@ -3446,6 +3839,7 @@ void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr, |
void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, |
const int64_t& rt_reg, |
+ const int64_t& rd_reg, |
int64_t& alu_out) { |
switch (instr->FunctionFieldRaw()) { |
case INS: |
@@ -3459,6 +3853,11 @@ void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, |
set_register(rt_reg, alu_out); |
TraceRegWr(alu_out); |
break; |
+ case BITSWAP: |
+ case DBITSWAP: |
+ set_register(rd_reg, alu_out); |
+ TraceRegWr(alu_out); |
+ break; |
default: |
UNREACHABLE(); |
} |
@@ -3534,7 +3933,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { |
DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out); |
break; |
case SPECIAL3: |
- DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out); |
+ DecodeTypeRegisterSPECIAL3(instr, rt_reg, rd_reg, alu_out); |
break; |
// Unimplemented opcodes raised an error in the configuration step before, |
// so we can use the default here to set the destination register in common |