Index: src/mips64/simulator-mips64.cc |
diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc |
index 9ca57f6948743c6a2442b85f6e46a4ac763b8e35..bf4c2066c31b445898700fb127394ee6066624d1 100644 |
--- a/src/mips64/simulator-mips64.cc |
+++ b/src/mips64/simulator-mips64.cc |
@@ -2206,6 +2206,9 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, |
case DDIVU: |
// div and divu never raise exceptions. |
break; |
+ case SELEQZ_S: |
+ case SELNEZ_S: |
+ break; |
default: |
UNREACHABLE(); |
} |
@@ -2267,6 +2270,561 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, |
} |
+void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
+ const int32_t& fs_reg, |
+ const int64_t& fd_reg) { |
+ float f; |
+ switch (instr->FunctionFieldRaw()) { |
+ case CVT_D_S: |
+ f = get_fpu_register_float(fs_reg); |
+ set_fpu_register_double(fd_reg, static_cast<double>(f)); |
+ 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. |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
+ const int32_t& fs_reg, |
+ const int64_t& ft_reg, |
+ const int32_t& fd_reg) { |
+ double ft, fs; |
+ uint32_t cc, fcsr_cc; |
+ fs = get_fpu_register_double(fs_reg); |
+ ft = get_fpu_register_double(ft_reg); |
+ cc = instr->FCccValue(); |
+ fcsr_cc = get_fcsr_condition_bit(cc); |
+ int64_t ft_int = static_cast<int64_t>(ft); |
+ switch (instr->FunctionFieldRaw()) { |
+ case SELEQZ_C: |
+ DCHECK(kArchVariant == kMips64r6); |
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0); |
+ break; |
+ case SELNEZ_C: |
+ DCHECK(kArchVariant == kMips64r6); |
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0); |
+ break; |
+ case ADD_D: |
+ set_fpu_register_double(fd_reg, fs + ft); |
+ break; |
+ case SUB_D: |
+ set_fpu_register_double(fd_reg, fs - ft); |
+ break; |
+ case MUL_D: |
+ set_fpu_register_double(fd_reg, fs * ft); |
+ break; |
+ case DIV_D: |
+ set_fpu_register_double(fd_reg, fs / ft); |
+ break; |
+ case ABS_D: |
+ set_fpu_register_double(fd_reg, fabs(fs)); |
+ break; |
+ case MOV_D: |
+ set_fpu_register_double(fd_reg, fs); |
+ break; |
+ case NEG_D: |
+ set_fpu_register_double(fd_reg, -fs); |
+ break; |
+ case SQRT_D: |
+ set_fpu_register_double(fd_reg, fast_sqrt(fs)); |
+ break; |
+ case C_UN_D: |
+ set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
+ break; |
+ case C_EQ_D: |
+ set_fcsr_bit(fcsr_cc, (fs == ft)); |
+ break; |
+ case C_UEQ_D: |
+ set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft))); |
+ break; |
+ case C_OLT_D: |
+ set_fcsr_bit(fcsr_cc, (fs < ft)); |
+ break; |
+ case C_ULT_D: |
+ set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft))); |
+ break; |
+ case C_OLE_D: |
+ set_fcsr_bit(fcsr_cc, (fs <= ft)); |
+ break; |
+ case C_ULE_D: |
+ set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); |
+ break; |
+ case CVT_W_D: // Convert double to word. |
+ // Rounding modes are not yet supported. |
+ DCHECK((FCSR_ & 3) == 0); |
+ // In rounding mode 0 it should behave like ROUND. |
+ // No break. |
+ case ROUND_W_D: // Round double to word (round half to even). |
+ { |
+ double 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(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case TRUNC_W_D: // Truncate double to word (round towards 0). |
+ { |
+ double 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(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case FLOOR_W_D: // Round double to word towards negative infinity. |
+ { |
+ double 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(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case CEIL_W_D: // Round double to word towards positive infinity. |
+ { |
+ double 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 CVT_S_D: // Convert double to float (single). |
+ set_fpu_register_float(fd_reg, static_cast<float>(fs)); |
+ break; |
+ case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word. |
+ // Rounding modes are not yet supported. |
+ DCHECK((FCSR_ & 3) == 0); |
+ // In rounding mode 0 it should behave like ROUND. |
+ // No break. |
+ case ROUND_L_D: { // Mips64r2 instruction. |
paul.l...
2015/03/31 04:02:01
As mentioned in simulator-mips.cc: this is for mip
|
+ // check error cases |
+ double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5); |
+ 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 TRUNC_L_D: { // Mips64r2 instruction. |
+ double 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 FLOOR_L_D: { // Mips64r2 instruction. |
+ double 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 CEIL_L_D: { // Mips64r2 instruction. |
+ double 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 C_F_D: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, |
+ const int32_t& fs_reg, |
+ const int32_t& fd_reg, |
+ int64_t& alu_out) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case CVT_S_W: // Convert word to float (single). |
+ alu_out = get_fpu_register_signed_word(fs_reg); |
+ set_fpu_register_float(fd_reg, static_cast<float>(alu_out)); |
+ break; |
+ case CVT_D_W: // Convert word to double. |
+ 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. |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, |
+ const int32_t& fs_reg, |
+ const int32_t& fd_reg, |
+ const int32_t& ft_reg) { |
+ double fs = get_fpu_register_double(fs_reg); |
+ double ft = get_fpu_register_double(ft_reg); |
+ int64_t i64; |
+ switch (instr->FunctionFieldRaw()) { |
+ case CVT_D_L: // Mips32r2 instruction. |
+ i64 = get_fpu_register(fs_reg); |
+ set_fpu_register_double(fd_reg, static_cast<double>(i64)); |
+ break; |
+ case CVT_S_L: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ case CMP_AF: // Mips64r6 CMP.D instructions. |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ case CMP_UN: |
+ if (std::isnan(fs) || std::isnan(ft)) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_EQ: |
+ if (fs == ft) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_UEQ: |
+ 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_LT: |
+ if (fs < ft) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_ULT: |
+ 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_LE: |
+ if (fs <= ft) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_ULE: |
+ if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterCOP1( |
+ Instruction* instr, const int64_t& rs_reg, const int64_t& rs, |
+ const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt, |
+ const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg, |
+ const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg, |
+ int64_t& alu_out) { |
+ switch (instr->RsFieldRaw()) { |
+ case BC1: // Branch on coprocessor condition. |
+ case BC1EQZ: |
+ case BC1NEZ: |
+ UNREACHABLE(); |
+ break; |
+ case CFC1: |
+ set_register(rt_reg, alu_out); |
+ break; |
+ case MFC1: |
+ case DMFC1: |
+ case MFHC1: |
+ set_register(rt_reg, alu_out); |
+ break; |
+ case CTC1: |
+ // At the moment only FCSR is supported. |
+ DCHECK(fs_reg == kFCSRRegister); |
+ FCSR_ = registers_[rt_reg]; |
+ break; |
+ case MTC1: |
+ // Hardware writes upper 32-bits to zero on mtc1. |
+ set_fpu_register_hi_word(fs_reg, 0); |
+ set_fpu_register_word(fs_reg, registers_[rt_reg]); |
+ break; |
+ case DMTC1: |
+ set_fpu_register(fs_reg, registers_[rt_reg]); |
+ break; |
+ case MTHC1: |
+ set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); |
+ break; |
+ case S: |
+ DecodeTypeRegisterSRsType(instr, fs_reg, fd_reg); |
+ break; |
+ case D: |
+ DecodeTypeRegisterDRsType(instr, fs_reg, ft_reg, fd_reg); |
+ break; |
+ case W: |
+ DecodeTypeRegisterWRsType(instr, fs_reg, fd_reg, alu_out); |
+ break; |
+ case L: |
+ DecodeTypeRegisterLRsType(instr, fs_reg, fd_reg, ft_reg); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr, |
+ const int32_t& fr_reg, |
+ const int32_t& fs_reg, |
+ const int32_t& ft_reg, |
+ const int64_t& fd_reg) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case MADD_D: |
+ double fr, ft, fs; |
+ fr = get_fpu_register_double(fr_reg); |
+ fs = get_fpu_register_double(fs_reg); |
+ ft = get_fpu_register_double(ft_reg); |
+ set_fpu_register_double(fd_reg, fs * ft + fr); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterSPECIAL( |
+ Instruction* instr, const int64_t& rs_reg, const int64_t& rs, |
+ const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt, |
+ const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg, |
+ const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg, |
+ int64_t& i64hilo, uint64_t& u64hilo, int64_t& alu_out, bool& do_interrupt, |
+ int64_t& current_pc, int64_t& next_pc, int64_t& return_addr_reg, |
+ int64_t& i128resultH, int64_t& i128resultL) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case SELEQZ_S: |
+ DCHECK(kArchVariant == kMips64r6); |
+ set_register(rd_reg, rt == 0 ? rs : 0); |
+ break; |
+ case SELNEZ_S: |
+ DCHECK(kArchVariant == kMips64r6); |
+ set_register(rd_reg, rt != 0 ? rs : 0); |
+ break; |
+ case JR: { |
+ Instruction* branch_delay_instr = |
+ reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); |
+ BranchDelayInstructionDecode(branch_delay_instr); |
+ set_pc(next_pc); |
+ pc_modified_ = true; |
+ break; |
+ } |
+ case JALR: { |
+ Instruction* branch_delay_instr = |
+ reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize); |
+ BranchDelayInstructionDecode(branch_delay_instr); |
+ set_register(return_addr_reg, current_pc + 2 * Instruction::kInstrSize); |
+ set_pc(next_pc); |
+ pc_modified_ = true; |
+ break; |
+ } |
+ // Instructions using HI and LO registers. |
+ case MULT: |
+ if (kArchVariant != kMips64r6) { |
+ set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff)); |
+ set_register(HI, static_cast<int32_t>(i64hilo >> 32)); |
+ } else { |
+ switch (instr->SaValue()) { |
+ case MUL_OP: |
+ set_register(rd_reg, static_cast<int32_t>(i64hilo & 0xffffffff)); |
+ break; |
+ case MUH_OP: |
+ set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32)); |
+ break; |
+ default: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ } |
+ } |
+ break; |
+ case MULTU: |
+ set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff)); |
+ set_register(HI, static_cast<int32_t>(u64hilo >> 32)); |
+ break; |
+ case DMULT: // DMULT == D_MUL_MUH. |
+ if (kArchVariant != kMips64r6) { |
+ set_register(LO, static_cast<int64_t>(i128resultL)); |
+ set_register(HI, static_cast<int64_t>(i128resultH)); |
+ } else { |
+ switch (instr->SaValue()) { |
+ case MUL_OP: |
+ set_register(rd_reg, static_cast<int64_t>(i128resultL)); |
+ break; |
+ case MUH_OP: |
+ set_register(rd_reg, static_cast<int64_t>(i128resultH)); |
+ break; |
+ default: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ } |
+ } |
+ break; |
+ case DMULTU: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ case DSLL: |
+ set_register(rd_reg, alu_out); |
+ break; |
+ case DIV: |
+ case DDIV: |
+ switch (kArchVariant) { |
+ case kMips64r2: |
+ // Divide by zero and overflow was not checked in the |
+ // configuration step - div and divu do not raise exceptions. On |
+ // division by 0 the result will be UNPREDICTABLE. On overflow |
+ // (INT_MIN/-1), return INT_MIN which is what the hardware does. |
+ if (rs == INT_MIN && rt == -1) { |
+ set_register(LO, INT_MIN); |
+ set_register(HI, 0); |
+ } else if (rt != 0) { |
+ set_register(LO, rs / rt); |
+ set_register(HI, rs % rt); |
+ } |
+ break; |
+ case kMips64r6: |
+ switch (instr->SaValue()) { |
+ case DIV_OP: |
+ if (rs == INT_MIN && rt == -1) { |
+ set_register(rd_reg, INT_MIN); |
+ } else if (rt != 0) { |
+ set_register(rd_reg, rs / rt); |
+ } |
+ break; |
+ case MOD_OP: |
+ if (rs == INT_MIN && rt == -1) { |
+ set_register(rd_reg, 0); |
+ } else if (rt != 0) { |
+ set_register(rd_reg, rs % rt); |
+ } |
+ break; |
+ default: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ break; |
+ case DIVU: |
+ if (rt_u != 0) { |
+ set_register(LO, rs_u / rt_u); |
+ set_register(HI, rs_u % rt_u); |
+ } |
+ break; |
+ // Break and trap instructions. |
+ case BREAK: |
+ case TGE: |
+ case TGEU: |
+ case TLT: |
+ case TLTU: |
+ case TEQ: |
+ case TNE: |
+ if (do_interrupt) { |
+ SoftwareInterrupt(instr); |
+ } |
+ break; |
+ // Conditional moves. |
+ case MOVN: |
+ if (rt) { |
+ set_register(rd_reg, rs); |
+ TraceRegWr(rs); |
+ } |
+ break; |
+ case MOVCI: { |
+ uint32_t cc = instr->FBccValue(); |
+ uint32_t fcsr_cc = get_fcsr_condition_bit(cc); |
+ if (instr->Bit(16)) { // Read Tf bit. |
+ if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); |
+ } else { |
+ if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); |
+ } |
+ break; |
+ } |
+ case MOVZ: |
+ if (!rt) { |
+ set_register(rd_reg, rs); |
+ TraceRegWr(rs); |
+ } |
+ break; |
+ default: // For other special opcodes we do the default operation. |
+ set_register(rd_reg, alu_out); |
+ TraceRegWr(alu_out); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr, |
+ const int64_t& rd_reg, |
+ int64_t& alu_out) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case MUL: |
+ set_register(rd_reg, alu_out); |
+ TraceRegWr(alu_out); |
+ // HI and LO are UNPREDICTABLE after the operation. |
+ set_register(LO, Unpredictable); |
+ set_register(HI, Unpredictable); |
+ break; |
+ default: // For other special2 opcodes we do the default operation. |
+ set_register(rd_reg, alu_out); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, |
+ const int64_t& rt_reg, |
+ int64_t& alu_out) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case INS: |
+ // Ins instr leaves result in Rt, rather than Rd. |
+ set_register(rt_reg, alu_out); |
+ TraceRegWr(alu_out); |
+ break; |
+ case EXT: |
+ case DEXT: |
+ // Dext/Ext instr leaves result in Rt, rather than Rd. |
+ set_register(rt_reg, alu_out); |
+ TraceRegWr(alu_out); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
void Simulator::DecodeTypeRegister(Instruction* instr) { |
// Instruction fields. |
const Opcode op = instr->OpcodeFieldRaw(); |
@@ -2320,490 +2878,23 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { |
// ---------- Execution. |
switch (op) { |
case COP1: |
- switch (instr->RsFieldRaw()) { |
- case BC1: // Branch on coprocessor condition. |
- case BC1EQZ: |
- case BC1NEZ: |
- UNREACHABLE(); |
- break; |
- case CFC1: |
- set_register(rt_reg, alu_out); |
- break; |
- case MFC1: |
- case DMFC1: |
- case MFHC1: |
- set_register(rt_reg, alu_out); |
- break; |
- case CTC1: |
- // At the moment only FCSR is supported. |
- DCHECK(fs_reg == kFCSRRegister); |
- FCSR_ = registers_[rt_reg]; |
- break; |
- case MTC1: |
- // Hardware writes upper 32-bits to zero on mtc1. |
- set_fpu_register_hi_word(fs_reg, 0); |
- set_fpu_register_word(fs_reg, registers_[rt_reg]); |
- break; |
- case DMTC1: |
- set_fpu_register(fs_reg, registers_[rt_reg]); |
- break; |
- case MTHC1: |
- set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); |
- break; |
- case S: |
- float f; |
- switch (instr->FunctionFieldRaw()) { |
- case CVT_D_S: |
- f = get_fpu_register_float(fs_reg); |
- set_fpu_register_double(fd_reg, static_cast<double>(f)); |
- 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. |
- UNREACHABLE(); |
- } |
- break; |
- case D: |
- double ft, fs; |
- uint32_t cc, fcsr_cc; |
- int64_t i64; |
- fs = get_fpu_register_double(fs_reg); |
- ft = get_fpu_register_double(ft_reg); |
- cc = instr->FCccValue(); |
- fcsr_cc = get_fcsr_condition_bit(cc); |
- switch (instr->FunctionFieldRaw()) { |
- case ADD_D: |
- set_fpu_register_double(fd_reg, fs + ft); |
- break; |
- case SUB_D: |
- set_fpu_register_double(fd_reg, fs - ft); |
- break; |
- case MUL_D: |
- set_fpu_register_double(fd_reg, fs * ft); |
- break; |
- case DIV_D: |
- set_fpu_register_double(fd_reg, fs / ft); |
- break; |
- case ABS_D: |
- set_fpu_register_double(fd_reg, fabs(fs)); |
- break; |
- case MOV_D: |
- set_fpu_register_double(fd_reg, fs); |
- break; |
- case NEG_D: |
- set_fpu_register_double(fd_reg, -fs); |
- break; |
- case SQRT_D: |
- set_fpu_register_double(fd_reg, fast_sqrt(fs)); |
- break; |
- case C_UN_D: |
- set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
- break; |
- case C_EQ_D: |
- set_fcsr_bit(fcsr_cc, (fs == ft)); |
- break; |
- case C_UEQ_D: |
- set_fcsr_bit(fcsr_cc, |
- (fs == ft) || (std::isnan(fs) || std::isnan(ft))); |
- break; |
- case C_OLT_D: |
- set_fcsr_bit(fcsr_cc, (fs < ft)); |
- break; |
- case C_ULT_D: |
- set_fcsr_bit(fcsr_cc, |
- (fs < ft) || (std::isnan(fs) || std::isnan(ft))); |
- break; |
- case C_OLE_D: |
- set_fcsr_bit(fcsr_cc, (fs <= ft)); |
- break; |
- case C_ULE_D: |
- set_fcsr_bit(fcsr_cc, |
- (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); |
- break; |
- case CVT_W_D: // Convert double to word. |
- // Rounding modes are not yet supported. |
- DCHECK((FCSR_ & 3) == 0); |
- // In rounding mode 0 it should behave like ROUND. |
- // No break. |
- case ROUND_W_D: // Round double to word (round half to even). |
- { |
- double 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(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case TRUNC_W_D: // Truncate double to word (round towards 0). |
- { |
- double 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(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case FLOOR_W_D: // Round double to word towards negative infinity. |
- { |
- double 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(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case CEIL_W_D: // Round double to word towards positive infinity. |
- { |
- double 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 CVT_S_D: // Convert double to float (single). |
- set_fpu_register_float(fd_reg, static_cast<float>(fs)); |
- break; |
- case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word. |
- // Rounding modes are not yet supported. |
- DCHECK((FCSR_ & 3) == 0); |
- // In rounding mode 0 it should behave like ROUND. |
- // No break. |
- case ROUND_L_D: { // Mips64r2 instruction. |
- // check error cases |
- double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5); |
- 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 TRUNC_L_D: { // Mips64r2 instruction. |
- double 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 FLOOR_L_D: { // Mips64r2 instruction. |
- double 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 CEIL_L_D: { // Mips64r2 instruction. |
- double 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 C_F_D: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
- break; |
- case W: |
- switch (instr->FunctionFieldRaw()) { |
- case CVT_S_W: // Convert word to float (single). |
- alu_out = get_fpu_register_signed_word(fs_reg); |
- set_fpu_register_float(fd_reg, static_cast<float>(alu_out)); |
- break; |
- case CVT_D_W: // Convert word to double. |
- 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. |
- UNREACHABLE(); |
- } |
- break; |
- case L: |
- fs = get_fpu_register_double(fs_reg); |
- ft = get_fpu_register_double(ft_reg); |
- switch (instr->FunctionFieldRaw()) { |
- case CVT_D_L: // Mips32r2 instruction. |
- i64 = get_fpu_register(fs_reg); |
- set_fpu_register_double(fd_reg, static_cast<double>(i64)); |
- break; |
- case CVT_S_L: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- case CMP_AF: // Mips64r6 CMP.D instructions. |
- UNIMPLEMENTED_MIPS(); |
- break; |
- case CMP_UN: |
- if (std::isnan(fs) || std::isnan(ft)) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_EQ: |
- if (fs == ft) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_UEQ: |
- 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_LT: |
- if (fs < ft) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_ULT: |
- 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_LE: |
- if (fs <= ft) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_ULE: |
- if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED |
- UNREACHABLE(); |
- } |
- break; |
- default: |
- UNREACHABLE(); |
- } |
+ DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, |
+ fr_reg, fs_reg, ft_reg, fd_reg, alu_out); |
break; |
case COP1X: |
- switch (instr->FunctionFieldRaw()) { |
- case MADD_D: |
- double fr, ft, fs; |
- fr = get_fpu_register_double(fr_reg); |
- fs = get_fpu_register_double(fs_reg); |
- ft = get_fpu_register_double(ft_reg); |
- set_fpu_register_double(fd_reg, fs * ft + fr); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
+ DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg); |
break; |
case SPECIAL: |
- switch (instr->FunctionFieldRaw()) { |
- case JR: { |
- Instruction* branch_delay_instr = reinterpret_cast<Instruction*>( |
- current_pc+Instruction::kInstrSize); |
- BranchDelayInstructionDecode(branch_delay_instr); |
- set_pc(next_pc); |
- pc_modified_ = true; |
- break; |
- } |
- case JALR: { |
- Instruction* branch_delay_instr = reinterpret_cast<Instruction*>( |
- current_pc+Instruction::kInstrSize); |
- BranchDelayInstructionDecode(branch_delay_instr); |
- set_register(return_addr_reg, |
- current_pc + 2 * Instruction::kInstrSize); |
- set_pc(next_pc); |
- pc_modified_ = true; |
- break; |
- } |
- // Instructions using HI and LO registers. |
- case MULT: |
- if (kArchVariant != kMips64r6) { |
- set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff)); |
- set_register(HI, static_cast<int32_t>(i64hilo >> 32)); |
- } else { |
- switch (instr->SaValue()) { |
- case MUL_OP: |
- set_register(rd_reg, |
- static_cast<int32_t>(i64hilo & 0xffffffff)); |
- break; |
- case MUH_OP: |
- set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32)); |
- break; |
- default: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- } |
- } |
- break; |
- case MULTU: |
- set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff)); |
- set_register(HI, static_cast<int32_t>(u64hilo >> 32)); |
- break; |
- case DMULT: // DMULT == D_MUL_MUH. |
- if (kArchVariant != kMips64r6) { |
- set_register(LO, static_cast<int64_t>(i128resultL)); |
- set_register(HI, static_cast<int64_t>(i128resultH)); |
- } else { |
- switch (instr->SaValue()) { |
- case MUL_OP: |
- set_register(rd_reg, static_cast<int64_t>(i128resultL)); |
- break; |
- case MUH_OP: |
- set_register(rd_reg, static_cast<int64_t>(i128resultH)); |
- break; |
- default: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- } |
- } |
- break; |
- case DMULTU: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- case DSLL: |
- set_register(rd_reg, alu_out); |
- break; |
- case DIV: |
- case DDIV: |
- switch (kArchVariant) { |
- case kMips64r2: |
- // Divide by zero and overflow was not checked in the |
- // configuration step - div and divu do not raise exceptions. On |
- // division by 0 the result will be UNPREDICTABLE. On overflow |
- // (INT_MIN/-1), return INT_MIN which is what the hardware does. |
- if (rs == INT_MIN && rt == -1) { |
- set_register(LO, INT_MIN); |
- set_register(HI, 0); |
- } else if (rt != 0) { |
- set_register(LO, rs / rt); |
- set_register(HI, rs % rt); |
- } |
- break; |
- case kMips64r6: |
- switch (instr->SaValue()) { |
- case DIV_OP: |
- if (rs == INT_MIN && rt == -1) { |
- set_register(rd_reg, INT_MIN); |
- } else if (rt != 0) { |
- set_register(rd_reg, rs / rt); |
- } |
- break; |
- case MOD_OP: |
- if (rs == INT_MIN && rt == -1) { |
- set_register(rd_reg, 0); |
- } else if (rt != 0) { |
- set_register(rd_reg, rs % rt); |
- } |
- break; |
- default: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- } |
- break; |
- default: |
- break; |
- } |
- break; |
- case DIVU: |
- if (rt_u != 0) { |
- set_register(LO, rs_u / rt_u); |
- set_register(HI, rs_u % rt_u); |
- } |
- break; |
- // Break and trap instructions. |
- case BREAK: |
- case TGE: |
- case TGEU: |
- case TLT: |
- case TLTU: |
- case TEQ: |
- case TNE: |
- if (do_interrupt) { |
- SoftwareInterrupt(instr); |
- } |
- break; |
- // Conditional moves. |
- case MOVN: |
- if (rt) { |
- set_register(rd_reg, rs); |
- TraceRegWr(rs); |
- } |
- break; |
- case MOVCI: { |
- uint32_t cc = instr->FBccValue(); |
- uint32_t fcsr_cc = get_fcsr_condition_bit(cc); |
- if (instr->Bit(16)) { // Read Tf bit. |
- if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); |
- } else { |
- if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); |
- } |
- break; |
- } |
- case MOVZ: |
- if (!rt) { |
- set_register(rd_reg, rs); |
- TraceRegWr(rs); |
- } |
- break; |
- default: // For other special opcodes we do the default operation. |
- set_register(rd_reg, alu_out); |
- TraceRegWr(alu_out); |
- } |
+ DecodeTypeRegisterSPECIAL( |
+ instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, fr_reg, fs_reg, |
+ ft_reg, fd_reg, i64hilo, u64hilo, alu_out, do_interrupt, current_pc, |
+ next_pc, return_addr_reg, i128resultH, i128resultL); |
break; |
case SPECIAL2: |
- switch (instr->FunctionFieldRaw()) { |
- case MUL: |
- set_register(rd_reg, alu_out); |
- TraceRegWr(alu_out); |
- // HI and LO are UNPREDICTABLE after the operation. |
- set_register(LO, Unpredictable); |
- set_register(HI, Unpredictable); |
- break; |
- default: // For other special2 opcodes we do the default operation. |
- set_register(rd_reg, alu_out); |
- } |
+ DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out); |
break; |
case SPECIAL3: |
- switch (instr->FunctionFieldRaw()) { |
- case INS: |
- // Ins instr leaves result in Rt, rather than Rd. |
- set_register(rt_reg, alu_out); |
- TraceRegWr(alu_out); |
- break; |
- case EXT: |
- case DEXT: |
- // Dext/Ext instr leaves result in Rt, rather than Rd. |
- set_register(rt_reg, alu_out); |
- TraceRegWr(alu_out); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
+ DecodeTypeRegisterSPECIAL3(instr, rt_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 |