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

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

Issue 1046873004: MIPS: Refactor simulator and add selection instructions for r6. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 9 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
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

Powered by Google App Engine
This is Rietveld 408576698