| 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
|
|
|