| Index: src/mips64/simulator-mips64.cc
|
| diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc
|
| index 86e8b91464eacbd74a54294a7a14fee232673782..f4cad547bbd8ef23474a92508d2a6fa679564fc6 100644
|
| --- a/src/mips64/simulator-mips64.cc
|
| +++ b/src/mips64/simulator-mips64.cc
|
| @@ -1955,9 +1955,6 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
|
| switch (op) {
|
| case COP1: // Coprocessor instructions.
|
| switch (instr->RsFieldRaw()) {
|
| - case BC1: // Handled in DecodeTypeImmed, should never come here.
|
| - UNREACHABLE();
|
| - break;
|
| case CFC1:
|
| // At the moment only FCSR is supported.
|
| ASSERT(fs_reg == kFCSRRegister);
|
| @@ -1976,8 +1973,6 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
|
| case MTC1:
|
| case DMTC1:
|
| case MTHC1:
|
| - // Do the store in the execution step.
|
| - break;
|
| case S:
|
| case D:
|
| case W:
|
| @@ -1986,7 +1981,8 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
|
| // Do everything in the execution step.
|
| break;
|
| default:
|
| - UNIMPLEMENTED_MIPS();
|
| + // BC1 BC1EQZ BC1NEZ handled in DecodeTypeImmed, should never come here.
|
| + UNREACHABLE();
|
| }
|
| break;
|
| case COP1X:
|
| @@ -2071,13 +2067,23 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
|
| case DSRAV:
|
| *alu_out = rt >> rs;
|
| break;
|
| - case MFHI:
|
| - *alu_out = get_register(HI);
|
| + case MFHI: // MFHI == CLZ on R6.
|
| + if (kArchVariant != kMips64r6) {
|
| + ASSERT(instr->SaValue() == 0);
|
| + *alu_out = get_register(HI);
|
| + } else {
|
| + // MIPS spec: If no bits were set in GPR rs, the result written to
|
| + // GPR rd is 32.
|
| + // GCC __builtin_clz: If input is 0, the result is undefined.
|
| + ASSERT(instr->SaValue() == 1);
|
| + *alu_out =
|
| + rs_u == 0 ? 32 : CompilerIntrinsics::CountLeadingZeros(rs_u);
|
| + }
|
| break;
|
| case MFLO:
|
| *alu_out = get_register(LO);
|
| break;
|
| - case MULT:
|
| + case MULT: // MULT == D_MUL_MUH.
|
| // TODO(plind) - Unify MULT/DMULT with single set of 64-bit HI/Lo
|
| // regs.
|
| // TODO(plind) - make the 32-bit MULT ops conform to spec regarding
|
| @@ -2088,9 +2094,23 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
|
| case MULTU:
|
| *u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
|
| break;
|
| - case DMULT:
|
| - *i128resultH = MultiplyHighSigned(rs, rt);
|
| - *i128resultL = rs * rt;
|
| + case DMULT: // DMULT == D_MUL_MUH.
|
| + if (kArchVariant != kMips64r6) {
|
| + *i128resultH = MultiplyHighSigned(rs, rt);
|
| + *i128resultL = rs * rt;
|
| + } else {
|
| + switch (instr->SaValue()) {
|
| + case MUL_OP:
|
| + *i128resultL = rs * rt;
|
| + break;
|
| + case MUH_OP:
|
| + *i128resultH = MultiplyHighSigned(rs, rt);
|
| + break;
|
| + default:
|
| + UNIMPLEMENTED_MIPS();
|
| + break;
|
| + }
|
| + }
|
| break;
|
| case DMULTU:
|
| UNIMPLEMENTED_MIPS();
|
| @@ -2295,6 +2315,8 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
|
| case COP1:
|
| switch (instr->RsFieldRaw()) {
|
| case BC1: // Branch on coprocessor condition.
|
| + case BC1EQZ:
|
| + case BC1NEZ:
|
| UNREACHABLE();
|
| break;
|
| case CFC1:
|
| @@ -2328,20 +2350,9 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
|
| f = get_fpu_register_float(fs_reg);
|
| set_fpu_register_double(fd_reg, static_cast<double>(f));
|
| break;
|
| - case CVT_W_S:
|
| - case CVT_L_S:
|
| - case TRUNC_W_S:
|
| - case TRUNC_L_S:
|
| - case ROUND_W_S:
|
| - case ROUND_L_S:
|
| - case FLOOR_W_S:
|
| - case FLOOR_L_S:
|
| - case CEIL_W_S:
|
| - case CEIL_L_S:
|
| - case CVT_PS_S:
|
| - UNIMPLEMENTED_MIPS();
|
| - 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;
|
| @@ -2514,25 +2525,77 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
|
| alu_out = get_fpu_register_signed_word(fs_reg);
|
| set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
|
| break;
|
| - default:
|
| + 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_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;
|
| - default:
|
| + 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;
|
| - case PS:
|
| - break;
|
| default:
|
| UNREACHABLE();
|
| }
|
| @@ -2572,32 +2635,91 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
|
| }
|
| // Instructions using HI and LO registers.
|
| case MULT:
|
| - set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
|
| - set_register(HI, static_cast<int32_t>(i64hilo >> 32));
|
| + 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:
|
| - set_register(LO, static_cast<int64_t>(i128resultL));
|
| - set_register(HI, static_cast<int64_t>(i128resultH));
|
| + 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:
|
| - // 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);
|
| + 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:
|
| @@ -2696,6 +2818,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
|
| int16_t imm16 = instr->Imm16Value();
|
|
|
| int32_t ft_reg = instr->FtValue(); // Destination register.
|
| + int64_t ft = get_fpu_register(ft_reg);
|
|
|
| // Zero extended immediate.
|
| uint32_t oe_imm16 = 0xffff & imm16;
|
| @@ -2742,6 +2865,26 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
|
| next_pc = current_pc + kBranchReturnOffset;
|
| }
|
| break;
|
| + case BC1EQZ:
|
| + do_branch = (ft & 0x1) ? false : true;
|
| + execute_branch_delay_instruction = true;
|
| + // Set next_pc.
|
| + if (do_branch) {
|
| + next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
|
| + } else {
|
| + next_pc = current_pc + kBranchReturnOffset;
|
| + }
|
| + break;
|
| + case BC1NEZ:
|
| + do_branch = (ft & 0x1) ? true : false;
|
| + execute_branch_delay_instruction = true;
|
| + // Set next_pc.
|
| + if (do_branch) {
|
| + next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
|
| + } else {
|
| + next_pc = current_pc + kBranchReturnOffset;
|
| + }
|
| + break;
|
| default:
|
| UNREACHABLE();
|
| }
|
|
|