Commit 09f41681 authored by balazs.kilvady's avatar balazs.kilvady Committed by Commit bot

MIPS: Optimize simulator.

The patch decreases the calls of huge switch instructions making the DecodeType*() functions to work in one phase and optimizing Instruction::InstructionType(). Speed gain in release full check is about 33% (6:13 s -> 4:09 s) and in optdebug full test is about 50% (12:29 -> 6:17)

BUG=

Review URL: https://codereview.chromium.org/1310883005

Cr-Commit-Position: refs/heads/master@{#30596}
parent 2fe2258c
...@@ -219,151 +219,6 @@ bool Instruction::IsTrap() const { ...@@ -219,151 +219,6 @@ bool Instruction::IsTrap() const {
} }
Instruction::Type Instruction::InstructionType() const {
switch (OpcodeFieldRaw()) {
case SPECIAL:
switch (FunctionFieldRaw()) {
case JR:
case JALR:
case BREAK:
case SLL:
case SRL:
case SRA:
case SLLV:
case SRLV:
case SRAV:
case MFHI:
case MFLO:
case MULT:
case MULTU:
case DIV:
case DIVU:
case ADD:
case ADDU:
case SUB:
case SUBU:
case AND:
case OR:
case XOR:
case NOR:
case SLT:
case SLTU:
case TGE:
case TGEU:
case TLT:
case TLTU:
case TEQ:
case TNE:
case MOVZ:
case MOVN:
case MOVCI:
case SELEQZ_S:
case SELNEZ_S:
return kRegisterType;
default:
return kUnsupported;
}
break;
case SPECIAL2:
switch (FunctionFieldRaw()) {
case MUL:
case CLZ:
return kRegisterType;
default:
return kUnsupported;
}
break;
case SPECIAL3:
switch (FunctionFieldRaw()) {
case INS:
case EXT:
return kRegisterType;
case BSHFL: {
int sa = SaFieldRaw() >> kSaShift;
switch (sa) {
case BITSWAP:
return kRegisterType;
case WSBH:
case SEB:
case SEH:
return kUnsupported;
}
sa >>= kBp2Bits;
switch (sa) {
case ALIGN:
return kRegisterType;
default:
return kUnsupported;
}
}
default:
return kUnsupported;
}
break;
case COP1: // Coprocessor instructions.
switch (RsFieldRawNoAssert()) {
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
return kImmediateType;
default:
return kRegisterType;
}
break;
case COP1X:
return kRegisterType;
// 16 bits Immediate type instructions. e.g.: addi dest, src, imm16.
case REGIMM:
case BEQ:
case BNE:
case BLEZ:
case BGTZ:
case ADDI:
case DADDI:
case ADDIU:
case SLTI:
case SLTIU:
case ANDI:
case ORI:
case XORI:
case LUI:
case BEQL:
case BNEL:
case BLEZL:
case BGTZL:
case POP66:
case POP76:
case LB:
case LH:
case LWL:
case LW:
case LBU:
case LHU:
case LWR:
case SB:
case SH:
case SWL:
case SW:
case SWR:
case LWC1:
case LDC1:
case SWC1:
case SDC1:
case PCREL:
case BC:
case BALC:
return kImmediateType;
// 26 bits immediate type instructions. e.g.: j imm26.
case J:
case JAL:
return kJumpType;
default:
return kUnsupported;
}
return kUnsupported;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -820,6 +820,7 @@ const Instr rtCallRedirInstr = SPECIAL | BREAK | call_rt_redirected << 6; ...@@ -820,6 +820,7 @@ const Instr rtCallRedirInstr = SPECIAL | BREAK | call_rt_redirected << 6;
// A nop instruction. (Encoding of sll 0 0 0). // A nop instruction. (Encoding of sll 0 0 0).
const Instr nopInstr = 0; const Instr nopInstr = 0;
class Instruction { class Instruction {
public: public:
enum { enum {
...@@ -858,9 +859,57 @@ class Instruction { ...@@ -858,9 +859,57 @@ class Instruction {
kUnsupported = -1 kUnsupported = -1
}; };
// Get the encoding type of the instruction. enum TypeChecks { NORMAL, EXTRA };
Type InstructionType() const;
#define OpcodeToBitNumber(opcode) \
(1ULL << (static_cast<uint32_t>(opcode) >> kOpcodeShift))
static const uint64_t kOpcodeImmediateTypeMask =
OpcodeToBitNumber(REGIMM) | OpcodeToBitNumber(BEQ) |
OpcodeToBitNumber(BNE) | OpcodeToBitNumber(BLEZ) |
OpcodeToBitNumber(BGTZ) | OpcodeToBitNumber(ADDI) |
OpcodeToBitNumber(DADDI) | OpcodeToBitNumber(ADDIU) |
OpcodeToBitNumber(SLTI) | OpcodeToBitNumber(SLTIU) |
OpcodeToBitNumber(ANDI) | OpcodeToBitNumber(ORI) |
OpcodeToBitNumber(XORI) | OpcodeToBitNumber(LUI) |
OpcodeToBitNumber(BEQL) | OpcodeToBitNumber(BNEL) |
OpcodeToBitNumber(BLEZL) | OpcodeToBitNumber(BGTZL) |
OpcodeToBitNumber(POP66) | OpcodeToBitNumber(POP76) |
OpcodeToBitNumber(LB) | OpcodeToBitNumber(LH) | OpcodeToBitNumber(LWL) |
OpcodeToBitNumber(LW) | OpcodeToBitNumber(LBU) | OpcodeToBitNumber(LHU) |
OpcodeToBitNumber(LWR) | OpcodeToBitNumber(SB) | OpcodeToBitNumber(SH) |
OpcodeToBitNumber(SWL) | OpcodeToBitNumber(SW) | OpcodeToBitNumber(SWR) |
OpcodeToBitNumber(LWC1) | OpcodeToBitNumber(LDC1) |
OpcodeToBitNumber(SWC1) | OpcodeToBitNumber(SDC1) |
OpcodeToBitNumber(PCREL) | OpcodeToBitNumber(BC) |
OpcodeToBitNumber(BALC);
#define FunctionFieldToBitNumber(function) (1ULL << function)
static const uint64_t kFunctionFieldRegisterTypeMask =
FunctionFieldToBitNumber(JR) | FunctionFieldToBitNumber(JALR) |
FunctionFieldToBitNumber(BREAK) | FunctionFieldToBitNumber(SLL) |
FunctionFieldToBitNumber(SRL) | FunctionFieldToBitNumber(SRA) |
FunctionFieldToBitNumber(SLLV) | FunctionFieldToBitNumber(SRLV) |
FunctionFieldToBitNumber(SRAV) | FunctionFieldToBitNumber(MFHI) |
FunctionFieldToBitNumber(MFLO) | FunctionFieldToBitNumber(MULT) |
FunctionFieldToBitNumber(MULTU) | FunctionFieldToBitNumber(DIV) |
FunctionFieldToBitNumber(DIVU) | FunctionFieldToBitNumber(ADD) |
FunctionFieldToBitNumber(ADDU) | FunctionFieldToBitNumber(SUB) |
FunctionFieldToBitNumber(SUBU) | FunctionFieldToBitNumber(AND) |
FunctionFieldToBitNumber(OR) | FunctionFieldToBitNumber(XOR) |
FunctionFieldToBitNumber(NOR) | FunctionFieldToBitNumber(SLT) |
FunctionFieldToBitNumber(SLTU) | FunctionFieldToBitNumber(TGE) |
FunctionFieldToBitNumber(TGEU) | FunctionFieldToBitNumber(TLT) |
FunctionFieldToBitNumber(TLTU) | FunctionFieldToBitNumber(TEQ) |
FunctionFieldToBitNumber(TNE) | FunctionFieldToBitNumber(MOVZ) |
FunctionFieldToBitNumber(MOVN) | FunctionFieldToBitNumber(MOVCI) |
FunctionFieldToBitNumber(SELEQZ_S) | FunctionFieldToBitNumber(SELNEZ_S);
// Get the encoding type of the instruction.
inline Type InstructionType(TypeChecks checks = NORMAL) const;
// Accessors for the different named fields used in the MIPS encoding. // Accessors for the different named fields used in the MIPS encoding.
inline Opcode OpcodeValue() const { inline Opcode OpcodeValue() const {
...@@ -1044,6 +1093,91 @@ const int kBArgsSlotsSize = 0 * Instruction::kInstrSize; ...@@ -1044,6 +1093,91 @@ const int kBArgsSlotsSize = 0 * Instruction::kInstrSize;
const int kBranchReturnOffset = 2 * Instruction::kInstrSize; const int kBranchReturnOffset = 2 * Instruction::kInstrSize;
Instruction::Type Instruction::InstructionType(TypeChecks checks) const {
if (checks == EXTRA) {
if (OpcodeToBitNumber(OpcodeFieldRaw()) & kOpcodeImmediateTypeMask) {
return kImmediateType;
}
}
switch (OpcodeFieldRaw()) {
case SPECIAL:
if (checks == EXTRA) {
if (FunctionFieldToBitNumber(FunctionFieldRaw()) &
kFunctionFieldRegisterTypeMask) {
return kRegisterType;
} else {
return kUnsupported;
}
} else {
return kRegisterType;
}
break;
case SPECIAL2:
switch (FunctionFieldRaw()) {
case MUL:
case CLZ:
return kRegisterType;
default:
return kUnsupported;
}
break;
case SPECIAL3:
switch (FunctionFieldRaw()) {
case INS:
case EXT:
return kRegisterType;
case BSHFL: {
int sa = SaFieldRaw() >> kSaShift;
switch (sa) {
case BITSWAP:
return kRegisterType;
case WSBH:
case SEB:
case SEH:
return kUnsupported;
}
sa >>= kBp2Bits;
switch (sa) {
case ALIGN:
return kRegisterType;
default:
return kUnsupported;
}
}
default:
return kUnsupported;
}
break;
case COP1: // Coprocessor instructions.
switch (RsFieldRawNoAssert()) {
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
return kImmediateType;
default:
return kRegisterType;
}
break;
case COP1X:
return kRegisterType;
// 26 bits immediate type instructions. e.g.: j imm26.
case J:
case JAL:
return kJumpType;
default:
if (checks == NORMAL) {
return kImmediateType;
} else {
return kUnsupported;
}
}
}
#undef OpcodeToBitNumber
#undef FunctionFieldToBitNumber
} } // namespace v8::internal } } // namespace v8::internal
#endif // #ifndef V8_MIPS_CONSTANTS_H_ #endif // #ifndef V8_MIPS_CONSTANTS_H_
...@@ -979,10 +979,6 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { ...@@ -979,10 +979,6 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
registers_[pc] = bad_ra; registers_[pc] = bad_ra;
registers_[ra] = bad_ra; registers_[ra] = bad_ra;
InitializeCoverage(); InitializeCoverage();
for (int i = 0; i < kNumExceptions; i++) {
exceptions[i] = 0;
}
last_debugger_input_ = NULL; last_debugger_input_ = NULL;
} }
...@@ -1628,7 +1624,8 @@ void Simulator::TraceRegWr(int32_t value) { ...@@ -1628,7 +1624,8 @@ void Simulator::TraceRegWr(int32_t value) {
// TODO(plind): consider making icount_ printing a flag option. // TODO(plind): consider making icount_ printing a flag option.
void Simulator::TraceMemRd(int32_t addr, int32_t value) { void Simulator::TraceMemRd(int32_t addr, int32_t value) {
if (::v8::internal::FLAG_trace_sim) { if (::v8::internal::FLAG_trace_sim) {
SNPrintF(trace_buf_, "%08x <-- [%08x] (%d)", value, addr, icount_); SNPrintF(trace_buf_, "%08x <-- [%08x] (%" PRIu64 ")", value, addr,
icount_);
} }
} }
...@@ -2101,7 +2098,8 @@ bool Simulator::IsWatchpoint(uint32_t code) { ...@@ -2101,7 +2098,8 @@ bool Simulator::IsWatchpoint(uint32_t code) {
void Simulator::PrintWatchpoint(uint32_t code) { void Simulator::PrintWatchpoint(uint32_t code) {
MipsDebugger dbg(this); MipsDebugger dbg(this);
++break_count_; ++break_count_;
PrintF("\n---- break %d marker: %3d (instr count: %8d) ----------" PrintF("\n---- break %d marker: %3d (instr count: %" PRIu64
") ----------"
"----------------------------------", "----------------------------------",
code, break_count_, icount_); code, break_count_, icount_);
dbg.PrintAllRegs(); // Print registers and continue running. dbg.PrintAllRegs(); // Print registers and continue running.
...@@ -2185,362 +2183,26 @@ void Simulator::PrintStopInfo(uint32_t code) { ...@@ -2185,362 +2183,26 @@ void Simulator::PrintStopInfo(uint32_t code) {
} }
void Simulator::SignalExceptions() { void Simulator::SignalException(Exception e) {
for (int i = 1; i < kNumExceptions; i++) { V8_Fatal(__FILE__, __LINE__, "Error: Exception %i raised.",
if (exceptions[i] != 0) { static_cast<int>(e));
V8_Fatal(__FILE__, __LINE__, "Error: Exception %i raised.", i);
}
}
} }
// Handle execution based on instruction types. void Simulator::DecodeTypeRegisterDRsType() {
void Simulator::ConfigureTypeRegister(Instruction* instr,
int32_t* alu_out,
int64_t* i64hilo,
uint64_t* u64hilo,
int32_t* next_pc,
int32_t* return_addr_reg,
bool* do_interrupt) {
// Every local variable declared here needs to be const.
// This is to make sure that changed values are sent back to
// DecodeTypeRegister correctly.
// Instruction fields.
const Opcode op = instr->OpcodeFieldRaw();
const int32_t rs_reg = instr->RsValue();
const int32_t rs = get_register(rs_reg);
const uint32_t rs_u = static_cast<uint32_t>(rs);
const int32_t rt_reg = instr->RtValue();
const int32_t rt = get_register(rt_reg);
const uint32_t rt_u = static_cast<uint32_t>(rt);
const int32_t rd_reg = instr->RdValue();
const uint32_t sa = instr->SaValue();
const uint8_t bp = instr->Bp2Value();
const int32_t fs_reg = instr->FsValue();
// ---------- Configuration.
switch (op) {
case COP1: // Coprocessor instructions.
switch (instr->RsFieldRaw()) {
case CFC1:
// At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister);
*alu_out = FCSR_;
break;
case MFC1:
*alu_out = get_fpu_register_word(fs_reg);
break;
case MFHC1:
*alu_out = get_fpu_register_hi_word(fs_reg);
break;
case CTC1:
case MTC1:
case MTHC1:
case S:
case D:
case W:
case L:
case PS:
// Do everything in the execution step.
break;
default:
// BC1 BC1EQZ BC1NEZ handled in DecodeTypeImmed, should never come here.
UNREACHABLE();
}
break;
case COP1X:
break;
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case JR:
case JALR:
*next_pc = get_register(instr->RsValue());
*return_addr_reg = instr->RdValue();
break;
case SLL:
*alu_out = rt << sa;
break;
case SRL:
if (rs_reg == 0) {
// Regular logical right shift of a word by a fixed number of
// bits instruction. RS field is always equal to 0.
*alu_out = rt_u >> sa;
} else {
// Logical right-rotate of a word by a fixed number of bits. This
// is special case of SRL instruction, added in MIPS32 Release 2.
// RS field is equal to 00001.
*alu_out = base::bits::RotateRight32(rt_u, sa);
}
break;
case SRA:
*alu_out = rt >> sa;
break;
case SLLV:
*alu_out = rt << rs;
break;
case SRLV:
if (sa == 0) {
// Regular logical right-shift of a word by a variable number of
// bits instruction. SA field is always equal to 0.
*alu_out = rt_u >> rs;
} else {
// Logical right-rotate of a word by a variable number of bits.
// This is special case od SRLV instruction, added in MIPS32
// Release 2. SA field is equal to 00001.
*alu_out = base::bits::RotateRight32(rt_u, rs_u);
}
break;
case SRAV:
*alu_out = rt >> rs;
break;
case MFHI: // MFHI == CLZ on R6.
if (!IsMipsArchVariant(kMips32r6)) {
DCHECK(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.
DCHECK(instr->SaValue() == 1);
*alu_out = base::bits::CountLeadingZeros32(rs_u);
}
break;
case MFLO:
*alu_out = get_register(LO);
break;
case MULT: // MULT == MUL_MUH.
if (!IsMipsArchVariant(kMips32r6)) {
*i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
} else {
switch (instr->SaValue()) {
case MUL_OP:
case MUH_OP:
*i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case MULTU: // MULTU == MUL_MUH_U.
if (!IsMipsArchVariant(kMips32r6)) {
*u64hilo = static_cast<uint64_t>(rs_u) *
static_cast<uint64_t>(rt_u);
} else {
switch (instr->SaValue()) {
case MUL_OP:
case MUH_OP:
*u64hilo = static_cast<uint64_t>(rs_u) *
static_cast<uint64_t>(rt_u);
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case ADD:
if (HaveSameSign(rs, rt)) {
if (rs > 0) {
exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue - rt);
} else if (rs < 0) {
exceptions[kIntegerUnderflow] = rs < (Registers::kMinValue - rt);
}
}
*alu_out = rs + rt;
break;
case ADDU:
*alu_out = rs + rt;
break;
case SUB:
if (!HaveSameSign(rs, rt)) {
if (rs > 0) {
exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue + rt);
} else if (rs < 0) {
exceptions[kIntegerUnderflow] = rs < (Registers::kMinValue + rt);
}
}
*alu_out = rs - rt;
break;
case SUBU:
*alu_out = rs - rt;
break;
case AND:
*alu_out = rs & rt;
break;
case OR:
*alu_out = rs | rt;
break;
case XOR:
*alu_out = rs ^ rt;
break;
case NOR:
*alu_out = ~(rs | rt);
break;
case SLT:
*alu_out = rs < rt ? 1 : 0;
break;
case SLTU:
*alu_out = rs_u < rt_u ? 1 : 0;
break;
// Break and trap instructions.
case BREAK:
*do_interrupt = true;
break;
case TGE:
*do_interrupt = rs >= rt;
break;
case TGEU:
*do_interrupt = rs_u >= rt_u;
break;
case TLT:
*do_interrupt = rs < rt;
break;
case TLTU:
*do_interrupt = rs_u < rt_u;
break;
case TEQ:
*do_interrupt = rs == rt;
break;
case TNE:
*do_interrupt = rs != rt;
break;
case MOVN:
case MOVZ:
case MOVCI:
// No action taken on decode.
break;
case DIV:
case DIVU:
// div and divu never raise exceptions.
case SELEQZ_S:
case SELNEZ_S:
break;
default:
UNREACHABLE();
}
break;
case SPECIAL2:
switch (instr->FunctionFieldRaw()) {
case MUL:
*alu_out = rs_u * rt_u; // Only the lower 32 bits are kept.
break;
case CLZ:
// MIPS32 spec: If no bits were set in GPR rs, the result written to
// GPR rd is 32.
*alu_out = base::bits::CountLeadingZeros32(rs_u);
break;
default:
UNREACHABLE();
}
break;
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS: { // Mips32r2 instruction.
// Interpret rd field as 5-bit msb of insert.
uint16_t msb = rd_reg;
// Interpret sa field as 5-bit lsb of insert.
uint16_t lsb = sa;
uint16_t size = msb - lsb + 1;
uint32_t mask = (1 << size) - 1;
*alu_out = (rt_u & ~(mask << lsb)) | ((rs_u & mask) << lsb);
break;
}
case EXT: { // Mips32r2 instruction.
// Interpret rd field as 5-bit msb of extract.
uint16_t msb = rd_reg;
// Interpret sa field as 5-bit lsb of extract.
uint16_t lsb = sa;
uint16_t size = msb + 1;
uint32_t mask = (1 << size) - 1;
*alu_out = (rs_u & (mask << lsb)) >> lsb;
break;
}
case BSHFL: {
int sa = instr->SaFieldRaw() >> kSaShift;
switch (sa) {
case BITSWAP: {
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<int32_t>(output);
break;
}
case SEB:
case SEH:
case WSBH:
UNREACHABLE();
break;
default: {
sa >>= kBp2Bits;
switch (sa) {
case ALIGN: {
if (bp == 0) {
*alu_out = static_cast<int32_t>(rt);
} else {
uint32_t rt_hi = rt << (8 * bp);
uint32_t rs_lo = rs >> (8 * (4 - bp));
*alu_out = static_cast<int32_t>(rt_hi | rs_lo);
}
break;
}
default:
UNREACHABLE();
break;
}
}
}
break;
}
default:
UNREACHABLE();
}
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
const int32_t& fr_reg,
const int32_t& fs_reg,
const int32_t& ft_reg,
const int32_t& fd_reg) {
double ft, fs, fd; double ft, fs, fd;
uint32_t cc, fcsr_cc; uint32_t cc, fcsr_cc;
int64_t i64; int64_t i64;
fs = get_fpu_register_double(fs_reg); fs = get_fpu_register_double(fs_reg());
ft = (instr->FunctionFieldRaw() != MOVF) ? get_fpu_register_double(ft_reg) ft = (get_instr()->FunctionFieldRaw() != MOVF)
: 0.0; ? get_fpu_register_double(ft_reg())
fd = get_fpu_register_double(fd_reg); : 0.0;
fd = get_fpu_register_double(fd_reg());
int64_t ft_int = bit_cast<int64_t>(ft); int64_t ft_int = bit_cast<int64_t>(ft);
int64_t fd_int = bit_cast<int64_t>(fd); int64_t fd_int = bit_cast<int64_t>(fd);
cc = instr->FCccValue(); cc = get_instr()->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc); fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) { switch (get_instr()->FunctionFieldRaw()) {
case RINT: { case RINT: {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
double result, temp, temp_result; double result, temp, temp_result;
...@@ -2572,7 +2234,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2572,7 +2234,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
result = lower; result = lower;
break; break;
} }
set_fpu_register_double(fd_reg, result); set_fpu_register_double(fd_reg(), result);
if (result != fs) { if (result != fs) {
set_fcsr_bit(kFCSRInexactFlagBit, true); set_fcsr_bit(kFCSRInexactFlagBit, true);
} }
...@@ -2580,69 +2242,67 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2580,69 +2242,67 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
} }
case SEL: case SEL:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (fd_int & 0x1) == 0 ? fs : ft); set_fpu_register_double(fd_reg(), (fd_int & 0x1) == 0 ? fs : ft);
break; break;
case SELEQZ_C: case SELEQZ_C:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0); set_fpu_register_double(fd_reg(), (ft_int & 0x1) == 0 ? fs : 0.0);
break; break;
case SELNEZ_C: case SELNEZ_C:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0); set_fpu_register_double(fd_reg(), (ft_int & 0x1) != 0 ? fs : 0.0);
break; break;
case MOVZ_C: { case MOVZ_C: {
DCHECK(IsMipsArchVariant(kMips32r2)); DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue(); if (rt() == 0) {
int32_t rt = get_register(rt_reg); set_fpu_register_double(fd_reg(), fs);
if (rt == 0) {
set_fpu_register_double(fd_reg, fs);
} }
break; break;
} }
case MOVN_C: { case MOVN_C: {
DCHECK(IsMipsArchVariant(kMips32r2)); DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue(); int32_t rt_reg = get_instr()->RtValue();
int32_t rt = get_register(rt_reg); int32_t rt = get_register(rt_reg);
if (rt != 0) { if (rt != 0) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} }
break; break;
} }
case MOVF: { case MOVF: {
// Same function field for MOVT.D and MOVF.D // Same function field for MOVT.D and MOVF.D
uint32_t ft_cc = (ft_reg >> 2) & 0x7; uint32_t ft_cc = (ft_reg() >> 2) & 0x7;
ft_cc = get_fcsr_condition_bit(ft_cc); ft_cc = get_fcsr_condition_bit(ft_cc);
if (instr->Bit(16)) { // Read Tf bit. if (get_instr()->Bit(16)) { // Read Tf bit.
// MOVT.D // MOVT.D
if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs); if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg(), fs);
} else { } else {
// MOVF.D // MOVF.D
if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs); if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg(), fs);
} }
break; break;
} }
case MIN: case MIN:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg); fs = get_fpu_register_double(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft); set_fpu_register_double(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else { } else {
set_fpu_register_double(fd_reg, (fs >= ft) ? ft : fs); set_fpu_register_double(fd_reg(), (fs >= ft) ? ft : fs);
} }
break; break;
case MINA: case MINA:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg); fs = get_fpu_register_double(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft); set_fpu_register_double(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else { } else {
double result; double result;
if (fabs(fs) > fabs(ft)) { if (fabs(fs) > fabs(ft)) {
...@@ -2652,18 +2312,18 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2652,18 +2312,18 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
} else { } else {
result = (fs > ft ? fs : ft); result = (fs > ft ? fs : ft);
} }
set_fpu_register_double(fd_reg, result); set_fpu_register_double(fd_reg(), result);
} }
break; break;
case MAXA: case MAXA:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg); fs = get_fpu_register_double(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft); set_fpu_register_double(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else { } else {
double result; double result;
if (fabs(fs) < fabs(ft)) { if (fabs(fs) < fabs(ft)) {
...@@ -2673,57 +2333,57 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2673,57 +2333,57 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
} else { } else {
result = (fs > ft ? fs : ft); result = (fs > ft ? fs : ft);
} }
set_fpu_register_double(fd_reg, result); set_fpu_register_double(fd_reg(), result);
} }
break; break;
case MAX: case MAX:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg); fs = get_fpu_register_double(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft); set_fpu_register_double(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
} else { } else {
set_fpu_register_double(fd_reg, (fs <= ft) ? ft : fs); set_fpu_register_double(fd_reg(), (fs <= ft) ? ft : fs);
} }
break; break;
break; break;
case ADD_D: case ADD_D:
set_fpu_register_double(fd_reg, fs + ft); set_fpu_register_double(fd_reg(), fs + ft);
break; break;
case SUB_D: case SUB_D:
set_fpu_register_double(fd_reg, fs - ft); set_fpu_register_double(fd_reg(), fs - ft);
break; break;
case MUL_D: case MUL_D:
set_fpu_register_double(fd_reg, fs * ft); set_fpu_register_double(fd_reg(), fs * ft);
break; break;
case DIV_D: case DIV_D:
set_fpu_register_double(fd_reg, fs / ft); set_fpu_register_double(fd_reg(), fs / ft);
break; break;
case ABS_D: case ABS_D:
set_fpu_register_double(fd_reg, fabs(fs)); set_fpu_register_double(fd_reg(), fabs(fs));
break; break;
case MOV_D: case MOV_D:
set_fpu_register_double(fd_reg, fs); set_fpu_register_double(fd_reg(), fs);
break; break;
case NEG_D: case NEG_D:
set_fpu_register_double(fd_reg, -fs); set_fpu_register_double(fd_reg(), -fs);
break; break;
case SQRT_D: case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs)); set_fpu_register_double(fd_reg(), fast_sqrt(fs));
break; break;
case RSQRT_D: { case RSQRT_D: {
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
double result = 1.0 / fast_sqrt(fs); double result = 1.0 / fast_sqrt(fs);
set_fpu_register_double(fd_reg, result); set_fpu_register_double(fd_reg(), result);
break; break;
} }
case RECIP_D: { case RECIP_D: {
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
double result = 1.0 / fs; double result = 1.0 / fs;
set_fpu_register_double(fd_reg, result); set_fpu_register_double(fd_reg(), result);
break; break;
} }
case C_UN_D: case C_UN_D:
...@@ -2751,9 +2411,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2751,9 +2411,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
double rounded; double rounded;
int32_t result; int32_t result;
round_according_to_fcsr(fs, rounded, result, fs); round_according_to_fcsr(fs, rounded, result, fs);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case ROUND_W_D: // Round double to word (round half to even). case ROUND_W_D: // Round double to word (round half to even).
...@@ -2765,49 +2425,49 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2765,49 +2425,49 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
// round to the even one. // round to the even one.
result--; result--;
} }
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case TRUNC_W_D: // Truncate double to word (round towards 0). case TRUNC_W_D: // Truncate double to word (round towards 0).
{ {
double rounded = trunc(fs); double rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded); int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case FLOOR_W_D: // Round double to word towards negative infinity. case FLOOR_W_D: // Round double to word towards negative infinity.
{ {
double rounded = std::floor(fs); double rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded); int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case CEIL_W_D: // Round double to word towards positive infinity. case CEIL_W_D: // Round double to word towards positive infinity.
{ {
double rounded = std::ceil(fs); double rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded); int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case CVT_S_D: // Convert double to float (single). case CVT_S_D: // Convert double to float (single).
set_fpu_register_float(fd_reg, static_cast<float>(fs)); set_fpu_register_float(fd_reg(), static_cast<float>(fs));
break; break;
case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word. case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
if (IsFp64Mode()) { if (IsFp64Mode()) {
int64_t result; int64_t result;
double rounded; double rounded;
round64_according_to_fcsr(fs, rounded, result, fs); round64_according_to_fcsr(fs, rounded, result, fs);
set_fpu_register(fd_reg, result); set_fpu_register(fd_reg(), result);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -2820,9 +2480,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2820,9 +2480,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
double rounded = trunc(fs); double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded); i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -2840,9 +2500,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2840,9 +2500,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
} }
int64_t i64 = static_cast<int64_t>(result); int64_t i64 = static_cast<int64_t>(result);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -2854,9 +2514,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2854,9 +2514,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
double rounded = std::floor(fs); double rounded = std::floor(fs);
int64_t i64 = static_cast<int64_t>(rounded); int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -2868,9 +2528,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2868,9 +2528,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
double rounded = std::ceil(fs); double rounded = std::ceil(fs);
int64_t i64 = static_cast<int64_t>(rounded); int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -2938,7 +2598,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2938,7 +2598,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
DCHECK(result != 0); DCHECK(result != 0);
dResult = bit_cast<double>(result); dResult = bit_cast<double>(result);
set_fpu_register_double(fd_reg, dResult); set_fpu_register_double(fd_reg(), dResult);
break; break;
} }
...@@ -2952,92 +2612,90 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, ...@@ -2952,92 +2612,90 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
} }
void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out, void Simulator::DecodeTypeRegisterWRsType() {
const int32_t& fd_reg, float fs = get_fpu_register_float(fs_reg());
const int32_t& fs_reg, float ft = get_fpu_register_float(ft_reg());
const int32_t& ft_reg) { int32_t alu_out = 0x12345678;
float fs = get_fpu_register_float(fs_reg); switch (get_instr()->FunctionFieldRaw()) {
float ft = get_fpu_register_float(ft_reg);
switch (instr->FunctionFieldRaw()) {
case CVT_S_W: // Convert word to float (single). case CVT_S_W: // Convert word to float (single).
alu_out = get_fpu_register_signed_word(fs_reg); alu_out = get_fpu_register_signed_word(fs_reg());
set_fpu_register_float(fd_reg, static_cast<float>(alu_out)); set_fpu_register_float(fd_reg(), static_cast<float>(alu_out));
break; break;
case CVT_D_W: // Convert word to double. case CVT_D_W: // Convert word to double.
alu_out = get_fpu_register_signed_word(fs_reg); alu_out = get_fpu_register_signed_word(fs_reg());
set_fpu_register_double(fd_reg, static_cast<double>(alu_out)); set_fpu_register_double(fd_reg(), static_cast<double>(alu_out));
break; break;
case CMP_AF: case CMP_AF:
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
break; break;
case CMP_UN: case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) { if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_EQ: case CMP_EQ:
if (fs == ft) { if (fs == ft) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_UEQ: case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_LT: case CMP_LT:
if (fs < ft) { if (fs < ft) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_ULT: case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_LE: case CMP_LE:
if (fs <= ft) { if (fs <= ft) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_ULE: case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_OR: case CMP_OR:
if (!std::isnan(fs) && !std::isnan(ft)) { if (!std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_UNE: case CMP_UNE:
if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
case CMP_NE: case CMP_NE:
if (fs != ft) { if (fs != ft) {
set_fpu_register_word(fd_reg, -1); set_fpu_register_word(fd_reg(), -1);
} else { } else {
set_fpu_register_word(fd_reg, 0); set_fpu_register_word(fd_reg(), 0);
} }
break; break;
default: default:
...@@ -3046,20 +2704,17 @@ void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out, ...@@ -3046,20 +2704,17 @@ void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out,
} }
void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, void Simulator::DecodeTypeRegisterSRsType() {
const int32_t& ft_reg,
const int32_t& fs_reg,
const int32_t& fd_reg) {
float fs, ft, fd; float fs, ft, fd;
fs = get_fpu_register_float(fs_reg); fs = get_fpu_register_float(fs_reg());
ft = get_fpu_register_float(ft_reg); ft = get_fpu_register_float(ft_reg());
fd = get_fpu_register_float(fd_reg); fd = get_fpu_register_float(fd_reg());
int32_t ft_int = bit_cast<int32_t>(ft); int32_t ft_int = bit_cast<int32_t>(ft);
int32_t fd_int = bit_cast<int32_t>(fd); int32_t fd_int = bit_cast<int32_t>(fd);
uint32_t cc, fcsr_cc; uint32_t cc, fcsr_cc;
cc = instr->FCccValue(); cc = get_instr()->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc); fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) { switch (get_instr()->FunctionFieldRaw()) {
case RINT: { case RINT: {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
float result, temp_result; float result, temp_result;
...@@ -3092,46 +2747,46 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3092,46 +2747,46 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
result = lower; result = lower;
break; break;
} }
set_fpu_register_float(fd_reg, result); set_fpu_register_float(fd_reg(), result);
if (result != fs) { if (result != fs) {
set_fcsr_bit(kFCSRInexactFlagBit, true); set_fcsr_bit(kFCSRInexactFlagBit, true);
} }
break; break;
} }
case ADD_S: case ADD_S:
set_fpu_register_float(fd_reg, fs + ft); set_fpu_register_float(fd_reg(), fs + ft);
break; break;
case SUB_S: case SUB_S:
set_fpu_register_float(fd_reg, fs - ft); set_fpu_register_float(fd_reg(), fs - ft);
break; break;
case MUL_S: case MUL_S:
set_fpu_register_float(fd_reg, fs * ft); set_fpu_register_float(fd_reg(), fs * ft);
break; break;
case DIV_S: case DIV_S:
set_fpu_register_float(fd_reg, fs / ft); set_fpu_register_float(fd_reg(), fs / ft);
break; break;
case ABS_S: case ABS_S:
set_fpu_register_float(fd_reg, fabs(fs)); set_fpu_register_float(fd_reg(), fabs(fs));
break; break;
case MOV_S: case MOV_S:
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
break; break;
case NEG_S: case NEG_S:
set_fpu_register_float(fd_reg, -fs); set_fpu_register_float(fd_reg(), -fs);
break; break;
case SQRT_S: case SQRT_S:
set_fpu_register_float(fd_reg, fast_sqrt(fs)); set_fpu_register_float(fd_reg(), fast_sqrt(fs));
break; break;
case RSQRT_S: { case RSQRT_S: {
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
float result = 1.0 / fast_sqrt(fs); float result = 1.0 / fast_sqrt(fs);
set_fpu_register_float(fd_reg, result); set_fpu_register_float(fd_reg(), result);
break; break;
} }
case RECIP_S: { case RECIP_S: {
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
float result = 1.0 / fs; float result = 1.0 / fs;
set_fpu_register_float(fd_reg, result); set_fpu_register_float(fd_reg(), result);
break; break;
} }
case C_F_D: case C_F_D:
...@@ -3159,15 +2814,15 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3159,15 +2814,15 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break; break;
case CVT_D_S: case CVT_D_S:
set_fpu_register_double(fd_reg, static_cast<double>(fs)); set_fpu_register_double(fd_reg(), static_cast<double>(fs));
break; break;
case SEL: case SEL:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_float(fd_reg, (fd_int & 0x1) == 0 ? fs : ft); set_fpu_register_float(fd_reg(), (fd_int & 0x1) == 0 ? fs : ft);
break; break;
case CLASS_S: { // Mips32r6 instruction case CLASS_S: { // Mips32r6 instruction
// Convert float input to uint32_t for easier bit manipulation // Convert float input to uint32_t for easier bit manipulation
float fs = get_fpu_register_float(fs_reg); float fs = get_fpu_register_float(fs_reg());
uint32_t classed = bit_cast<uint32_t>(fs); uint32_t classed = bit_cast<uint32_t>(fs);
// Extracting sign, exponent and mantissa from the input float // Extracting sign, exponent and mantissa from the input float
...@@ -3227,58 +2882,56 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3227,58 +2882,56 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
DCHECK(result != 0); DCHECK(result != 0);
fResult = bit_cast<float>(result); fResult = bit_cast<float>(result);
set_fpu_register_float(fd_reg, fResult); set_fpu_register_float(fd_reg(), fResult);
break; break;
} }
case SELEQZ_C: case SELEQZ_C:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_float( set_fpu_register_float(fd_reg(), (ft_int & 0x1) == 0
fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_float(fs_reg) : 0.0); ? get_fpu_register_float(fs_reg())
: 0.0);
break; break;
case SELNEZ_C: case SELNEZ_C:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_float( set_fpu_register_float(fd_reg(), (ft_int & 0x1) != 0
fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_float(fs_reg) : 0.0); ? get_fpu_register_float(fs_reg())
: 0.0);
break; break;
case MOVZ_C: { case MOVZ_C: {
DCHECK(IsMipsArchVariant(kMips32r2)); DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue(); if (rt() == 0) {
int32_t rt = get_register(rt_reg); set_fpu_register_float(fd_reg(), fs);
if (rt == 0) {
set_fpu_register_float(fd_reg, fs);
} }
break; break;
} }
case MOVN_C: { case MOVN_C: {
DCHECK(IsMipsArchVariant(kMips32r2)); DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue(); if (rt() != 0) {
int32_t rt = get_register(rt_reg); set_fpu_register_float(fd_reg(), fs);
if (rt != 0) {
set_fpu_register_float(fd_reg, fs);
} }
break; break;
} }
case MOVF: { case MOVF: {
// Same function field for MOVT.D and MOVF.D // Same function field for MOVT.D and MOVF.D
uint32_t ft_cc = (ft_reg >> 2) & 0x7; uint32_t ft_cc = (ft_reg() >> 2) & 0x7;
ft_cc = get_fcsr_condition_bit(ft_cc); ft_cc = get_fcsr_condition_bit(ft_cc);
if (instr->Bit(16)) { // Read Tf bit. if (get_instr()->Bit(16)) { // Read Tf bit.
// MOVT.D // MOVT.D
if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs); if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg(), fs);
} else { } else {
// MOVF.D // MOVF.D
if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs); if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg(), fs);
} }
break; break;
} }
case TRUNC_W_S: { // Truncate single to word (round towards 0). case TRUNC_W_S: { // Truncate single to word (round towards 0).
float rounded = trunc(fs); float rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded); int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case TRUNC_L_S: { // Mips32r2 instruction. case TRUNC_L_S: { // Mips32r2 instruction.
...@@ -3286,9 +2939,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3286,9 +2939,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
float rounded = trunc(fs); float rounded = trunc(fs);
int64_t i64 = static_cast<int64_t>(rounded); int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -3299,9 +2952,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3299,9 +2952,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
{ {
float rounded = std::floor(fs); float rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded); int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case FLOOR_L_S: { // Mips32r2 instruction. case FLOOR_L_S: { // Mips32r2 instruction.
...@@ -3309,9 +2962,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3309,9 +2962,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
float rounded = std::floor(fs); float rounded = std::floor(fs);
int64_t i64 = static_cast<int64_t>(rounded); int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -3326,9 +2979,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3326,9 +2979,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
// round to the even one. // round to the even one.
result--; result--;
} }
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
break; break;
} }
...@@ -3343,9 +2996,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3343,9 +2996,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
} }
int64_t i64 = static_cast<int64_t>(result); int64_t i64 = static_cast<int64_t>(result);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -3356,9 +3009,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3356,9 +3009,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
{ {
float rounded = std::ceil(fs); float rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded); int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
} break; } break;
case CEIL_L_S: { // Mips32r2 instruction. case CEIL_L_S: { // Mips32r2 instruction.
...@@ -3366,9 +3019,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3366,9 +3019,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
float rounded = std::ceil(fs); float rounded = std::ceil(fs);
int64_t i64 = static_cast<int64_t>(rounded); int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) { if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64); set_fpu_register(fd_reg(), i64);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -3377,39 +3030,39 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3377,39 +3030,39 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
} }
case MIN: case MIN:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg); fs = get_fpu_register_float(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft); set_fpu_register_float(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else { } else {
set_fpu_register_float(fd_reg, (fs >= ft) ? ft : fs); set_fpu_register_float(fd_reg(), (fs >= ft) ? ft : fs);
} }
break; break;
case MAX: case MAX:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg); fs = get_fpu_register_float(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft); set_fpu_register_float(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else { } else {
set_fpu_register_float(fd_reg, (fs <= ft) ? ft : fs); set_fpu_register_float(fd_reg(), (fs <= ft) ? ft : fs);
} }
break; break;
case MINA: case MINA:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg); fs = get_fpu_register_float(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft); set_fpu_register_float(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else { } else {
float result; float result;
if (fabs(fs) > fabs(ft)) { if (fabs(fs) > fabs(ft)) {
...@@ -3419,18 +3072,18 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3419,18 +3072,18 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
} else { } else {
result = (fs > ft ? fs : ft); result = (fs > ft ? fs : ft);
} }
set_fpu_register_float(fd_reg, result); set_fpu_register_float(fd_reg(), result);
} }
break; break;
case MAXA: case MAXA:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg); fs = get_fpu_register_float(fs_reg());
if (std::isnan(fs) && std::isnan(ft)) { if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else if (std::isnan(fs) && !std::isnan(ft)) { } else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft); set_fpu_register_float(fd_reg(), ft);
} else if (!std::isnan(fs) && std::isnan(ft)) { } else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs); set_fpu_register_float(fd_reg(), fs);
} else { } else {
float result; float result;
if (fabs(fs) < fabs(ft)) { if (fabs(fs) < fabs(ft)) {
...@@ -3440,7 +3093,7 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3440,7 +3093,7 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
} else { } else {
result = (fs > ft ? fs : ft); result = (fs > ft ? fs : ft);
} }
set_fpu_register_float(fd_reg, result); set_fpu_register_float(fd_reg(), result);
} }
break; break;
case CVT_L_S: { case CVT_L_S: {
...@@ -3448,9 +3101,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3448,9 +3101,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
int64_t result; int64_t result;
float rounded; float rounded;
round64_according_to_fcsr(fs, rounded, result, fs); round64_according_to_fcsr(fs, rounded, result, fs);
set_fpu_register(fd_reg, result); set_fpu_register(fd_reg(), result);
if (set_fcsr_round64_error(fs, rounded)) { if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult); set_fpu_register(fd_reg(), kFPU64InvalidResult);
} }
} else { } else {
UNSUPPORTED(); UNSUPPORTED();
...@@ -3461,9 +3114,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3461,9 +3114,9 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
float rounded; float rounded;
int32_t result; int32_t result;
round_according_to_fcsr(fs, rounded, result, fs); round_according_to_fcsr(fs, rounded, result, fs);
set_fpu_register_word(fd_reg, result); set_fpu_register_word(fd_reg(), result);
if (set_fcsr_round_error(fs, rounded)) { if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult); set_fpu_register_word(fd_reg(), kFPUInvalidResult);
} }
break; break;
} }
...@@ -3475,105 +3128,102 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, ...@@ -3475,105 +3128,102 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
} }
void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, void Simulator::DecodeTypeRegisterLRsType() {
const int32_t& ft_reg, double fs = get_fpu_register_double(fs_reg());
const int32_t& fs_reg, double ft = get_fpu_register_double(ft_reg());
const int32_t& fd_reg) { switch (get_instr()->FunctionFieldRaw()) {
double fs = get_fpu_register_double(fs_reg);
double ft = get_fpu_register_double(ft_reg);
switch (instr->FunctionFieldRaw()) {
case CVT_D_L: // Mips32r2 instruction. case CVT_D_L: // Mips32r2 instruction.
// Watch the signs here, we want 2 32-bit vals // Watch the signs here, we want 2 32-bit vals
// to make a sign-64. // to make a sign-64.
int64_t i64; int64_t i64;
if (IsFp64Mode()) { if (IsFp64Mode()) {
i64 = get_fpu_register(fs_reg); i64 = get_fpu_register(fs_reg());
} else { } else {
i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg)); i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg()));
i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg + 1)) << 32; i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg() + 1)) << 32;
} }
set_fpu_register_double(fd_reg, static_cast<double>(i64)); set_fpu_register_double(fd_reg(), static_cast<double>(i64));
break; break;
case CVT_S_L: case CVT_S_L:
if (IsFp64Mode()) { if (IsFp64Mode()) {
i64 = get_fpu_register(fs_reg); i64 = get_fpu_register(fs_reg());
} else { } else {
i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg)); i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg()));
i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg + 1)) << 32; i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg() + 1)) << 32;
} }
set_fpu_register_float(fd_reg, static_cast<float>(i64)); set_fpu_register_float(fd_reg(), static_cast<float>(i64));
break; break;
case CMP_AF: // Mips64r6 CMP.D instructions. case CMP_AF: // Mips64r6 CMP.D instructions.
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
break; break;
case CMP_UN: case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) { if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_EQ: case CMP_EQ:
if (fs == ft) { if (fs == ft) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_UEQ: case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_LT: case CMP_LT:
if (fs < ft) { if (fs < ft) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_ULT: case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_LE: case CMP_LE:
if (fs <= ft) { if (fs <= ft) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_ULE: case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_OR: case CMP_OR:
if (!std::isnan(fs) && !std::isnan(ft)) { if (!std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_UNE: case CMP_UNE:
if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) { if ((fs != ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
case CMP_NE: case CMP_NE:
if (fs != ft && (!std::isnan(fs) && !std::isnan(ft))) { if (fs != ft && (!std::isnan(fs) && !std::isnan(ft))) {
set_fpu_register(fd_reg, -1); set_fpu_register(fd_reg(), -1);
} else { } else {
set_fpu_register(fd_reg, 0); set_fpu_register(fd_reg(), 0);
} }
break; break;
default: default:
...@@ -3582,67 +3232,62 @@ void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, ...@@ -3582,67 +3232,62 @@ void Simulator::DecodeTypeRegisterLRsType(Instruction* instr,
} }
void Simulator::DecodeTypeRegisterCOP1( void Simulator::DecodeTypeRegisterCOP1() {
Instruction* instr, const int32_t& rs_reg, const int32_t& rs, switch (get_instr()->RsFieldRaw()) {
const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt,
const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg,
int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt,
int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg) {
switch (instr->RsFieldRaw()) {
case CFC1: case CFC1:
set_register(rt_reg, alu_out); // At the moment only FCSR is supported.
DCHECK(fs_reg() == kFCSRRegister);
set_register(rt_reg(), FCSR_);
break; break;
case MFC1: case MFC1:
set_register(rt_reg, alu_out); set_register(rt_reg(), get_fpu_register_word(fs_reg()));
break; break;
case MFHC1: case MFHC1:
set_register(rt_reg, alu_out); set_register(rt_reg(), get_fpu_register_hi_word(fs_reg()));
break; break;
case CTC1: case CTC1:
// At the moment only FCSR is supported. // At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister); DCHECK(fs_reg() == kFCSRRegister);
FCSR_ = registers_[rt_reg]; FCSR_ = registers_[rt_reg()];
break; break;
case MTC1: case MTC1:
// Hardware writes upper 32-bits to zero on mtc1. // Hardware writes upper 32-bits to zero on mtc1.
set_fpu_register_hi_word(fs_reg, 0); set_fpu_register_hi_word(fs_reg(), 0);
set_fpu_register_word(fs_reg, registers_[rt_reg]); set_fpu_register_word(fs_reg(), registers_[rt_reg()]);
break; break;
case MTHC1: case MTHC1:
set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); set_fpu_register_hi_word(fs_reg(), registers_[rt_reg()]);
break; break;
case S: { case S: {
DecodeTypeRegisterSRsType(instr, ft_reg, fs_reg, fd_reg); DecodeTypeRegisterSRsType();
break; break;
} }
case D: case D:
DecodeTypeRegisterDRsType(instr, fr_reg, fs_reg, ft_reg, fd_reg); DecodeTypeRegisterDRsType();
break; break;
case W: case W:
DecodeTypeRegisterWRsType(instr, alu_out, fd_reg, fs_reg, ft_reg); DecodeTypeRegisterWRsType();
break; break;
case L: case L:
DecodeTypeRegisterLRsType(instr, ft_reg, fs_reg, fd_reg); DecodeTypeRegisterLRsType();
break; break;
case PS:
// Not implemented.
UNREACHABLE();
default: default:
UNREACHABLE(); UNREACHABLE();
} }
} }
void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr, void Simulator::DecodeTypeRegisterCOP1X() {
const int32_t& fr_reg, switch (get_instr()->FunctionFieldRaw()) {
const int32_t& fs_reg,
const int32_t& ft_reg,
const int32_t& fd_reg) {
switch (instr->FunctionFieldRaw()) {
case MADD_D: case MADD_D:
double fr, ft, fs; double fr, ft, fs;
fr = get_fpu_register_double(fr_reg); fr = get_fpu_register_double(fr_reg());
fs = get_fpu_register_double(fs_reg); fs = get_fpu_register_double(fs_reg());
ft = get_fpu_register_double(ft_reg); ft = get_fpu_register_double(ft_reg());
set_fpu_register_double(fd_reg, fs * ft + fr); set_fpu_register_double(fd_reg(), fs * ft + fr);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -3650,216 +3295,411 @@ void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr, ...@@ -3650,216 +3295,411 @@ void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr,
} }
void Simulator::DecodeTypeRegisterSPECIAL( void Simulator::DecodeTypeRegisterSPECIAL() {
Instruction* instr, const int32_t& rs_reg, const int32_t& rs, int64_t alu_out = 0x12345678;
const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt, int64_t i64hilo = 0;
const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg, uint64_t u64hilo = 0;
const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg, bool do_interrupt = false;
int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt,
int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg) { switch (get_instr()->FunctionFieldRaw()) {
switch (instr->FunctionFieldRaw()) {
case SELEQZ_S: case SELEQZ_S:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_register(rd_reg, rt == 0 ? rs : 0); set_register(rd_reg(), rt() == 0 ? rs() : 0);
break; break;
case SELNEZ_S: case SELNEZ_S:
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
set_register(rd_reg, rt != 0 ? rs : 0); set_register(rd_reg(), rt() != 0 ? rs() : 0);
break; break;
case JR: { case JR: {
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>( int32_t next_pc = rs();
current_pc+Instruction::kInstrSize); int32_t current_pc = get_pc();
BranchDelayInstructionDecode(branch_delay_instr); Instruction* branch_delay_instr =
set_pc(next_pc); reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
pc_modified_ = true; BranchDelayInstructionDecode(branch_delay_instr);
break; set_pc(next_pc);
pc_modified_ = true;
break;
}
case JALR: {
int32_t next_pc = rs();
int32_t return_addr_reg = rd_reg();
int32_t current_pc = get_pc();
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;
}
case SLL:
alu_out = rt() << sa();
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case SRL:
if (rs_reg() == 0) {
// Regular logical right shift of a word by a fixed number of
// bits instruction. RS field is always equal to 0.
alu_out = rt_u() >> sa();
} else {
// Logical right-rotate of a word by a fixed number of bits. This
// is special case of SRL instruction, added in MIPS32 Release 2.
// RS field is equal to 00001.
alu_out = base::bits::RotateRight32(rt_u(), sa());
}
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case SRA:
alu_out = rt() >> sa();
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case SLLV:
alu_out = rt() << rs();
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case SRLV:
if (sa() == 0) {
// Regular logical right-shift of a word by a variable number of
// bits instruction. SA field is always equal to 0.
alu_out = rt_u() >> rs();
} else {
// Logical right-rotate of a word by a variable number of bits.
// This is special case od SRLV instruction, added in MIPS32
// Release 2. SA field is equal to 00001.
alu_out = base::bits::RotateRight32(rt_u(), rs_u());
}
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case SRAV:
alu_out = rt() >> rs();
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case MFHI: // MFHI == CLZ on R6.
if (!IsMipsArchVariant(kMips32r6)) {
DCHECK(sa() == 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.
DCHECK(sa() == 1);
alu_out = base::bits::CountLeadingZeros32(rs_u());
}
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
case MFLO:
alu_out = get_register(LO);
SetResult(rd_reg(), static_cast<int32_t>(alu_out));
break;
// Instructions using HI and LO registers.
case MULT:
i64hilo = static_cast<int64_t>(rs()) * static_cast<int64_t>(rt());
if (!IsMipsArchVariant(kMips32r6)) {
set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(i64hilo >> 32));
} else {
switch (sa()) {
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;
} }
case JALR: { }
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>( break;
current_pc+Instruction::kInstrSize); case MULTU:
BranchDelayInstructionDecode(branch_delay_instr); u64hilo = static_cast<uint64_t>(rs_u()) * static_cast<uint64_t>(rt_u());
set_register(return_addr_reg, if (!IsMipsArchVariant(kMips32r6)) {
current_pc + 2 * Instruction::kInstrSize); set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
set_pc(next_pc); set_register(HI, static_cast<int32_t>(u64hilo >> 32));
pc_modified_ = true; } else {
break; switch (sa()) {
case MUL_OP:
set_register(rd_reg(), static_cast<int32_t>(u64hilo & 0xffffffff));
break;
case MUH_OP:
set_register(rd_reg(), static_cast<int32_t>(u64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
} }
// Instructions using HI and LO registers. }
case MULT: break;
if (!IsMipsArchVariant(kMips32r6)) { case DIV:
set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff)); if (IsMipsArchVariant(kMips32r6)) {
set_register(HI, static_cast<int32_t>(i64hilo >> 32)); switch (get_instr()->SaValue()) {
} else { case DIV_OP:
switch (instr->SaValue()) { if (rs() == INT_MIN && rt() == -1) {
case MUL_OP: set_register(rd_reg(), INT_MIN);
set_register(rd_reg, } else if (rt() != 0) {
static_cast<int32_t>(i64hilo & 0xffffffff)); set_register(rd_reg(), rs() / rt());
break;
case MUH_OP:
set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case MULTU:
if (!IsMipsArchVariant(kMips32r6)) {
set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(u64hilo >> 32));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg,
static_cast<int32_t>(u64hilo & 0xffffffff));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int32_t>(u64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
} }
} break;
break; case MOD_OP:
case DIV: if (rs() == INT_MIN && rt() == -1) {
if (IsMipsArchVariant(kMips32r6)) { set_register(rd_reg(), 0);
switch (instr->SaValue()) { } else if (rt() != 0) {
case DIV_OP: set_register(rd_reg(), rs() % rt());
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;
} }
} else { break;
// Divide by zero and overflow was not checked in the default:
// configuration step - div and divu do not raise exceptions. On UNIMPLEMENTED_MIPS();
// division by 0 the result will be UNPREDICTABLE. On overflow break;
// (INT_MIN/-1), return INT_MIN which is what the hardware does. }
if (rs == INT_MIN && rt == -1) { } else {
set_register(LO, INT_MIN); // Divide by zero and overflow was not checked in the
set_register(HI, 0); // configuration step - div and divu do not raise exceptions. On
} else if (rt != 0) { // division by 0 the result will be UNPREDICTABLE. On overflow
set_register(LO, rs / rt); // (INT_MIN/-1), return INT_MIN which is what the hardware does.
set_register(HI, rs % rt); 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 DIVU:
if (IsMipsArchVariant(kMips32r6)) {
switch (get_instr()->SaValue()) {
case DIV_OP:
if (rt_u() != 0) {
set_register(rd_reg(), rs_u() / rt_u());
} }
} break;
break; case MOD_OP:
case DIVU: if (rt_u() != 0) {
if (IsMipsArchVariant(kMips32r6)) { set_register(rd_reg(), rs_u() % rt_u());
switch (instr->SaValue()) {
case DIV_OP:
if (rt_u != 0) {
set_register(rd_reg, rs_u / rt_u);
}
break;
case MOD_OP:
if (rt_u != 0) {
set_register(rd_reg, rs_u % rt_u);
}
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
} else {
if (rt_u != 0) {
set_register(LO, rs_u / rt_u);
set_register(HI, rs_u % rt_u);
} }
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
} else {
if (rt_u() != 0) {
set_register(LO, rs_u() / rt_u());
set_register(HI, rs_u() % rt_u());
}
}
break;
case ADD:
if (HaveSameSign(rs(), rt())) {
if (rs() > 0) {
if (rs() <= (Registers::kMaxValue - rt())) {
SignalException(kIntegerOverflow);
} }
break; } else if (rs() < 0) {
// Break and trap instructions. if (rs() >= (Registers::kMinValue - rt())) {
case BREAK: SignalException(kIntegerUnderflow);
case TGE:
case TGEU:
case TLT:
case TLTU:
case TEQ:
case TNE:
if (do_interrupt) {
SoftwareInterrupt(instr);
} }
break; }
// Conditional moves. }
case MOVN: SetResult(rd_reg(), rs() + rt());
if (rt) { break;
set_register(rd_reg, rs); case ADDU:
TraceRegWr(rs); SetResult(rd_reg(), rs() + rt());
break;
case SUB:
if (!HaveSameSign(rs(), rt())) {
if (rs() > 0) {
if (rs() <= (Registers::kMaxValue + rt())) {
SignalException(kIntegerOverflow);
} }
break; } else if (rs() < 0) {
case MOVCI: { if (rs() >= (Registers::kMinValue + rt())) {
uint32_t cc = instr->FBccValue(); SignalException(kIntegerUnderflow);
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);
} }
SetResult(rd_reg(), rs() - rt());
break;
case SUBU:
SetResult(rd_reg(), rs() - rt());
break;
case AND:
SetResult(rd_reg(), rs() & rt());
break;
case OR:
SetResult(rd_reg(), rs() | rt());
break;
case XOR:
SetResult(rd_reg(), rs() ^ rt());
break;
case NOR:
SetResult(rd_reg(), ~(rs() | rt()));
break;
case SLT:
SetResult(rd_reg(), rs() < rt() ? 1 : 0);
break;
case SLTU:
SetResult(rd_reg(), rs_u() < rt_u() ? 1 : 0);
break;
// Break and trap instructions.
case BREAK:
do_interrupt = true;
break;
case TGE:
do_interrupt = rs() >= rt();
break;
case TGEU:
do_interrupt = rs_u() >= rt_u();
break;
case TLT:
do_interrupt = rs() < rt();
break;
case TLTU:
do_interrupt = rs_u() < rt_u();
break;
case TEQ:
do_interrupt = rs() == rt();
break;
case TNE:
do_interrupt = rs() != rt();
break;
// Conditional moves.
case MOVN:
if (rt()) {
set_register(rd_reg(), rs());
TraceRegWr(rs());
}
break;
case MOVCI: {
uint32_t cc = get_instr()->FBccValue();
uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
if (get_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:
UNREACHABLE();
}
if (do_interrupt) {
SoftwareInterrupt(get_instr());
}
} }
void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr, void Simulator::DecodeTypeRegisterSPECIAL2() {
const int32_t& rd_reg, int32_t alu_out;
int32_t& alu_out) { switch (get_instr()->FunctionFieldRaw()) {
switch (instr->FunctionFieldRaw()) {
case MUL: case MUL:
set_register(rd_reg, alu_out); // Only the lower 32 bits are kept.
TraceRegWr(alu_out); alu_out = rs_u() * rt_u();
// HI and LO are UNPREDICTABLE after the operation. // HI and LO are UNPREDICTABLE after the operation.
set_register(LO, Unpredictable); set_register(LO, Unpredictable);
set_register(HI, Unpredictable); set_register(HI, Unpredictable);
break; break;
default: // For other special2 opcodes we do the default operation. case CLZ:
set_register(rd_reg, alu_out); // MIPS32 spec: If no bits were set in GPR rs, the result written to
TraceRegWr(alu_out); // GPR rd is 32.
alu_out = base::bits::CountLeadingZeros32(rs_u());
break;
default:
alu_out = 0x12345678;
UNREACHABLE();
} }
SetResult(rd_reg(), alu_out);
} }
void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, void Simulator::DecodeTypeRegisterSPECIAL3() {
const int32_t& rt_reg, int32_t alu_out;
const int32_t& rd_reg, switch (get_instr()->FunctionFieldRaw()) {
int32_t& alu_out) { case INS: { // Mips32r2 instruction.
switch (instr->FunctionFieldRaw()) { // Interpret rd field as 5-bit msb of insert.
case INS: uint16_t msb = rd_reg();
// Interpret sa field as 5-bit lsb of insert.
uint16_t lsb = sa();
uint16_t size = msb - lsb + 1;
uint32_t mask = (1 << size) - 1;
alu_out = (rt_u() & ~(mask << lsb)) | ((rs_u() & mask) << lsb);
// Ins instr leaves result in Rt, rather than Rd. // Ins instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out); SetResult(rt_reg(), alu_out);
TraceRegWr(alu_out);
break; break;
case EXT: }
set_register(rt_reg, alu_out); case EXT: { // Mips32r2 instruction.
TraceRegWr(alu_out); // Interpret rd field as 5-bit msb of extract.
uint16_t msb = rd_reg();
// Interpret sa field as 5-bit lsb of extract.
uint16_t lsb = sa();
uint16_t size = msb + 1;
uint32_t mask = (1 << size) - 1;
alu_out = (rs_u() & (mask << lsb)) >> lsb;
SetResult(rt_reg(), alu_out);
break; break;
case BSHFL: }
set_register(rd_reg, alu_out); case BSHFL: {
TraceRegWr(alu_out); int sa = get_instr()->SaFieldRaw() >> kSaShift;
switch (sa) {
case BITSWAP: {
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<int32_t>(output);
break;
}
case SEB:
case SEH:
case WSBH:
alu_out = 0x12345678;
UNREACHABLE();
break;
default: {
const uint8_t bp = get_instr()->Bp2Value();
sa >>= kBp2Bits;
switch (sa) {
case ALIGN: {
if (bp == 0) {
alu_out = static_cast<int32_t>(rt());
} else {
uint32_t rt_hi = rt() << (8 * bp);
uint32_t rs_lo = rs() >> (8 * (4 - bp));
alu_out = static_cast<int32_t>(rt_hi | rs_lo);
}
break;
}
default:
alu_out = 0x12345678;
UNREACHABLE();
break;
}
}
}
SetResult(rd_reg(), alu_out);
break; break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -3867,134 +3707,101 @@ void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, ...@@ -3867,134 +3707,101 @@ void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr,
void Simulator::DecodeTypeRegister(Instruction* instr) { void Simulator::DecodeTypeRegister(Instruction* instr) {
// Instruction fields.
const Opcode op = instr->OpcodeFieldRaw(); const Opcode op = instr->OpcodeFieldRaw();
const int32_t rs_reg = instr->RsValue();
const int32_t rs = get_register(rs_reg);
const uint32_t rs_u = static_cast<uint32_t>(rs);
const int32_t rt_reg = instr->RtValue();
const int32_t rt = get_register(rt_reg);
const uint32_t rt_u = static_cast<uint32_t>(rt);
const int32_t rd_reg = instr->RdValue();
const int32_t fr_reg = instr->FrValue();
const int32_t fs_reg = instr->FsValue();
const int32_t ft_reg = instr->FtValue();
const int32_t fd_reg = instr->FdValue();
int64_t i64hilo = 0;
uint64_t u64hilo = 0;
// ALU output.
// It should not be used as is. Instructions using it should always
// initialize it first.
int32_t alu_out = 0x12345678;
// For break and trap instructions.
bool do_interrupt = false;
// For jr and jalr.
// Get current pc.
int32_t current_pc = get_pc();
// Next pc
int32_t next_pc = 0;
int32_t return_addr_reg = 31;
// Set up the variables if needed before executing the instruction. // Set up the variables if needed before executing the instruction.
ConfigureTypeRegister(instr, &alu_out, &i64hilo, &u64hilo, &next_pc, // ConfigureTypeRegister(instr);
&return_addr_reg, &do_interrupt); set_instr(instr);
// ---------- Raise exceptions triggered.
SignalExceptions();
// ---------- Execution. // ---------- Execution.
switch (op) { switch (op) {
case COP1: case COP1:
DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, DecodeTypeRegisterCOP1();
fr_reg, fs_reg, ft_reg, fd_reg, i64hilo, u64hilo,
alu_out, do_interrupt, current_pc, next_pc,
return_addr_reg);
break; break;
case COP1X: case COP1X:
DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg); DecodeTypeRegisterCOP1X();
break; break;
case SPECIAL: case SPECIAL:
DecodeTypeRegisterSPECIAL(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, DecodeTypeRegisterSPECIAL();
rd_reg, fr_reg, fs_reg, ft_reg, fd_reg, i64hilo,
u64hilo, alu_out, do_interrupt, current_pc,
next_pc, return_addr_reg);
break; break;
case SPECIAL2: case SPECIAL2:
DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out); DecodeTypeRegisterSPECIAL2();
break; break;
case SPECIAL3: case SPECIAL3:
DecodeTypeRegisterSPECIAL3(instr, rt_reg, rd_reg, alu_out); DecodeTypeRegisterSPECIAL3();
break; 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
// cases.
default: default:
set_register(rd_reg, alu_out); UNREACHABLE();
} }
} }
// Branch instructions common part.
#define BranchAndLinkHelper(do_branch) \
execute_branch_delay_instruction = true; \
if (do_branch) { \
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; \
set_register(31, current_pc + 2 * Instruction::kInstrSize); \
} else { \
next_pc = current_pc + 2 * Instruction::kInstrSize; \
}
#define BranchHelper(do_branch) \
execute_branch_delay_instruction = true; \
if (do_branch) { \
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; \
} else { \
next_pc = current_pc + 2 * Instruction::kInstrSize; \
}
// Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq). // Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq).
void Simulator::DecodeTypeImmediate(Instruction* instr) { void Simulator::DecodeTypeImmediate(Instruction* instr) {
// Instruction fields. // Instruction fields.
Opcode op = instr->OpcodeFieldRaw(); Opcode op = instr->OpcodeFieldRaw();
int32_t rs_reg = instr->RsValue(); int32_t rs_reg = instr->RsValue();
int32_t rs = get_register(instr->RsValue()); int32_t rs = get_register(instr->RsValue());
uint32_t rs_u = static_cast<uint32_t>(rs); uint32_t rs_u = static_cast<uint32_t>(rs);
int32_t rt_reg = instr->RtValue(); // Destination register. int32_t rt_reg = instr->RtValue(); // Destination register.
int32_t rt = get_register(rt_reg); int32_t rt = get_register(rt_reg);
int16_t imm16 = instr->Imm16Value(); int16_t imm16 = instr->Imm16Value();
int32_t imm19 = instr->Imm19Value();
int32_t imm21 = instr->Imm21Value(); int32_t imm21 = instr->Imm21Value();
int32_t imm26 = instr->Imm26Value(); int32_t imm26 = instr->Imm26Value();
int32_t ft_reg = instr->FtValue(); // Destination register. int32_t ft_reg = instr->FtValue(); // Destination register.
int64_t ft; int64_t ft;
// Zero extended immediate. // Zero extended immediate.
uint32_t oe_imm16 = 0xffff & imm16; uint32_t oe_imm16 = 0xffff & imm16;
// Sign extended immediate. // Sign extended immediate.
int32_t se_imm16 = imm16; int32_t se_imm16 = imm16;
int32_t se_imm19 = imm19 | ((imm19 & 0x40000) ? 0xfff80000 : 0);
int32_t se_imm26 = imm26 | ((imm26 & 0x2000000) ? 0xfc000000 : 0); int32_t se_imm26 = imm26 | ((imm26 & 0x2000000) ? 0xfc000000 : 0);
// Get current pc. // Get current pc.
int32_t current_pc = get_pc(); int32_t current_pc = get_pc();
// Next pc. // Next pc.
int32_t next_pc = bad_ra; int32_t next_pc = bad_ra;
// pc increment
int16_t pc_increment;
// Used for conditional branch instructions. // Used for conditional branch instructions.
bool do_branch = false;
bool execute_branch_delay_instruction = false; bool execute_branch_delay_instruction = false;
// Used for arithmetic instructions. // Used for arithmetic instructions.
int32_t alu_out = 0; int32_t alu_out = 0;
// Floating point.
double fp_out = 0.0;
uint32_t cc, cc_value, fcsr_cc;
// Used for memory instructions. // Used for memory instructions.
int32_t addr = 0x0; int32_t addr = 0x0;
// Value to be written in memory.
uint32_t mem_value = 0x0;
// ---------- Configuration (and execution for REGIMM). // ---------- Configuration (and execution for REGIMM).
switch (op) { switch (op) {
// ------------- COP1. Coprocessor instructions. // ------------- COP1. Coprocessor instructions.
case COP1: case COP1:
switch (instr->RsFieldRaw()) { switch (instr->RsFieldRaw()) {
case BC1: // Branch on coprocessor condition. case BC1: { // Branch on coprocessor condition.
cc = instr->FBccValue(); // Floating point.
fcsr_cc = get_fcsr_condition_bit(cc); uint32_t cc = instr->FBccValue();
cc_value = test_fcsr_bit(fcsr_cc); uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value; uint32_t cc_value = test_fcsr_bit(fcsr_cc);
bool do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value;
execute_branch_delay_instruction = true; execute_branch_delay_instruction = true;
// Set next_pc. // Set next_pc.
if (do_branch) { if (do_branch) {
...@@ -4003,12 +3810,12 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4003,12 +3810,12 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
next_pc = current_pc + kBranchReturnOffset; next_pc = current_pc + kBranchReturnOffset;
} }
break; break;
}
case BC1EQZ: case BC1EQZ:
ft = get_fpu_register(ft_reg); ft = get_fpu_register(ft_reg);
do_branch = (ft & 0x1) ? false : true;
execute_branch_delay_instruction = true; execute_branch_delay_instruction = true;
// Set next_pc. // Set next_pc.
if (do_branch) { if (!(ft & 0x1)) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else { } else {
next_pc = current_pc + kBranchReturnOffset; next_pc = current_pc + kBranchReturnOffset;
...@@ -4016,10 +3823,9 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4016,10 +3823,9 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
break; break;
case BC1NEZ: case BC1NEZ:
ft = get_fpu_register(ft_reg); ft = get_fpu_register(ft_reg);
do_branch = (ft & 0x1) ? true : false;
execute_branch_delay_instruction = true; execute_branch_delay_instruction = true;
// Set next_pc. // Set next_pc.
if (do_branch) { if (ft & 0x1) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize; next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else { } else {
next_pc = current_pc + kBranchReturnOffset; next_pc = current_pc + kBranchReturnOffset;
...@@ -4033,54 +3839,35 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4033,54 +3839,35 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case REGIMM: case REGIMM:
switch (instr->RtFieldRaw()) { switch (instr->RtFieldRaw()) {
case BLTZ: case BLTZ:
do_branch = (rs < 0); BranchHelper(rs < 0);
break;
case BLTZAL:
do_branch = rs < 0;
break; break;
case BGEZ: case BGEZ:
do_branch = rs >= 0; BranchHelper(rs >= 0);
break;
case BLTZAL:
BranchAndLinkHelper(rs < 0);
break; break;
case BGEZAL: case BGEZAL:
do_branch = rs >= 0; BranchAndLinkHelper(rs >= 0);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
switch (instr->RtFieldRaw()) { break; // case REGIMM.
case BLTZ:
case BLTZAL:
case BGEZ:
case BGEZAL:
// Branch instructions common part.
execute_branch_delay_instruction = true;
// Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
if (instr->IsLinkingInstruction()) {
set_register(31, current_pc + kBranchReturnOffset);
}
} else {
next_pc = current_pc + kBranchReturnOffset;
}
default:
break;
}
break; // case REGIMM.
// ------------- Branch instructions. // ------------- Branch instructions.
// When comparing to zero, the encoding of rt field is always 0, so we don't // When comparing to zero, the encoding of rt field is always 0, so we don't
// need to replace rt with zero. // need to replace rt with zero.
case BEQ: case BEQ:
do_branch = (rs == rt); BranchHelper(rs == rt);
break; break;
case BNE: case BNE:
do_branch = rs != rt; BranchHelper(rs != rt);
break; break;
case BLEZ: case BLEZ:
do_branch = rs <= 0; BranchHelper(rs <= 0);
break; break;
case BGTZ: case BGTZ:
do_branch = rs > 0; BranchHelper(rs > 0);
break; break;
case POP66: { case POP66: {
if (rs_reg) { // BEQZC if (rs_reg) { // BEQZC
...@@ -4113,43 +3900,44 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4113,43 +3900,44 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case ADDI: case ADDI:
if (HaveSameSign(rs, se_imm16)) { if (HaveSameSign(rs, se_imm16)) {
if (rs > 0) { if (rs > 0) {
exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue - se_imm16); if (rs <= (Registers::kMaxValue - se_imm16)) {
SignalException(kIntegerOverflow);
}
} else if (rs < 0) { } else if (rs < 0) {
exceptions[kIntegerUnderflow] = if (rs >= (Registers::kMinValue - se_imm16)) {
rs < (Registers::kMinValue - se_imm16); SignalException(kIntegerUnderflow);
}
} }
} }
alu_out = rs + se_imm16; SetResult(rt_reg, rs + se_imm16);
break; break;
case ADDIU: case ADDIU:
alu_out = rs + se_imm16; SetResult(rt_reg, rs + se_imm16);
break; break;
case SLTI: case SLTI:
alu_out = (rs < se_imm16) ? 1 : 0; SetResult(rt_reg, rs < se_imm16 ? 1 : 0);
break; break;
case SLTIU: case SLTIU:
alu_out = (rs_u < static_cast<uint32_t>(se_imm16)) ? 1 : 0; SetResult(rt_reg, rs_u < static_cast<uint32_t>(se_imm16) ? 1 : 0);
break; break;
case ANDI: case ANDI:
alu_out = rs & oe_imm16; SetResult(rt_reg, rs & oe_imm16);
break; break;
case ORI: case ORI:
alu_out = rs | oe_imm16; SetResult(rt_reg, rs | oe_imm16);
break; break;
case XORI: case XORI:
alu_out = rs ^ oe_imm16; SetResult(rt_reg, rs ^ oe_imm16);
break; break;
case LUI: case LUI:
alu_out = (oe_imm16 << 16); SetResult(rt_reg, oe_imm16 << 16);
break; break;
// ------------- Memory instructions. // ------------- Memory instructions.
case LB: case LB:
addr = rs + se_imm16; set_register(rt_reg, ReadB(rs + se_imm16));
alu_out = ReadB(addr);
break; break;
case LH: case LH:
addr = rs + se_imm16; set_register(rt_reg, ReadH(rs + se_imm16, instr));
alu_out = ReadH(addr, instr);
break; break;
case LWL: { case LWL: {
// al_offset is offset of the effective address within an aligned word. // al_offset is offset of the effective address within an aligned word.
...@@ -4160,19 +3948,17 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4160,19 +3948,17 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
alu_out = ReadW(addr, instr); alu_out = ReadW(addr, instr);
alu_out <<= byte_shift * 8; alu_out <<= byte_shift * 8;
alu_out |= rt & mask; alu_out |= rt & mask;
set_register(rt_reg, alu_out);
break; break;
} }
case LW: case LW:
addr = rs + se_imm16; set_register(rt_reg, ReadW(rs + se_imm16, instr));
alu_out = ReadW(addr, instr);
break; break;
case LBU: case LBU:
addr = rs + se_imm16; set_register(rt_reg, ReadBU(rs + se_imm16));
alu_out = ReadBU(addr);
break; break;
case LHU: case LHU:
addr = rs + se_imm16; set_register(rt_reg, ReadHU(rs + se_imm16, instr));
alu_out = ReadHU(addr, instr);
break; break;
case LWR: { case LWR: {
// al_offset is offset of the effective address within an aligned word. // al_offset is offset of the effective address within an aligned word.
...@@ -4183,58 +3969,64 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4183,58 +3969,64 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
alu_out = ReadW(addr, instr); alu_out = ReadW(addr, instr);
alu_out = static_cast<uint32_t> (alu_out) >> al_offset * 8; alu_out = static_cast<uint32_t> (alu_out) >> al_offset * 8;
alu_out |= rt & mask; alu_out |= rt & mask;
set_register(rt_reg, alu_out);
break; break;
} }
case SB: case SB:
addr = rs + se_imm16; WriteB(rs + se_imm16, static_cast<int8_t>(rt));
break; break;
case SH: case SH:
addr = rs + se_imm16; WriteH(rs + se_imm16, static_cast<uint16_t>(rt), instr);
break; break;
case SWL: { case SWL: {
uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask; uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
uint8_t byte_shift = kPointerAlignmentMask - al_offset; uint8_t byte_shift = kPointerAlignmentMask - al_offset;
uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0; uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0;
addr = rs + se_imm16 - al_offset; addr = rs + se_imm16 - al_offset;
mem_value = ReadW(addr, instr) & mask; // Value to be written in memory.
uint32_t mem_value = ReadW(addr, instr) & mask;
mem_value |= static_cast<uint32_t>(rt) >> byte_shift * 8; mem_value |= static_cast<uint32_t>(rt) >> byte_shift * 8;
WriteW(addr, mem_value, instr);
break; break;
} }
case SW: case SW:
addr = rs + se_imm16; WriteW(rs + se_imm16, rt, instr);
break; break;
case SWR: { case SWR: {
uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask; uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
uint32_t mask = (1 << al_offset * 8) - 1; uint32_t mask = (1 << al_offset * 8) - 1;
addr = rs + se_imm16 - al_offset; addr = rs + se_imm16 - al_offset;
mem_value = ReadW(addr, instr); uint32_t mem_value = ReadW(addr, instr);
mem_value = (rt << al_offset * 8) | (mem_value & mask); mem_value = (rt << al_offset * 8) | (mem_value & mask);
WriteW(addr, mem_value, instr);
break; break;
} }
case LWC1: case LWC1:
addr = rs + se_imm16; set_fpu_register_hi_word(ft_reg, 0);
alu_out = ReadW(addr, instr); set_fpu_register_word(ft_reg, ReadW(rs + se_imm16, instr));
break; break;
case LDC1: case LDC1:
addr = rs + se_imm16; set_fpu_register_double(ft_reg, ReadD(rs + se_imm16, instr));
fp_out = ReadD(addr, instr);
break; break;
case SWC1: case SWC1:
WriteW(rs + se_imm16, get_fpu_register_word(ft_reg), instr);
break;
case SDC1: case SDC1:
addr = rs + se_imm16; WriteD(rs + se_imm16, get_fpu_register_double(ft_reg), instr);
break; break;
// ------------- JIALC and BNEZC instructions. // ------------- JIALC and BNEZC instructions.
case POP76: case POP76: {
// Next pc. // Next pc.
next_pc = rt + se_imm16; next_pc = rt + se_imm16;
// The instruction after the jump is NOT executed. // The instruction after the jump is NOT executed.
pc_increment = Instruction::kInstrSize; int16_t pc_increment = Instruction::kInstrSize;
if (instr->IsLinkingInstruction()) { if (instr->IsLinkingInstruction()) {
set_register(31, current_pc + pc_increment); set_register(31, current_pc + pc_increment);
} }
set_pc(next_pc); set_pc(next_pc);
pc_modified_ = true; pc_modified_ = true;
break; break;
}
// ------------- PC-Relative instructions. // ------------- PC-Relative instructions.
case PCREL: { case PCREL: {
// rt field: checking 5-bits. // rt field: checking 5-bits.
...@@ -4248,115 +4040,37 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4248,115 +4040,37 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
alu_out = current_pc + (se_imm16 << 16); alu_out = current_pc + (se_imm16 << 16);
break; break;
default: { default: {
int32_t imm19 = instr->Imm19Value();
// rt field: checking the most significant 2-bits. // rt field: checking the most significant 2-bits.
rt = (imm21 >> kImm19Bits); rt = (imm21 >> kImm19Bits);
switch (rt) { switch (rt) {
case LWPC: { case LWPC: {
int32_t offset = imm19;
// Set sign. // Set sign.
offset <<= (kOpcodeBits + kRsBits + 2); imm19 <<= (kOpcodeBits + kRsBits + 2);
offset >>= (kOpcodeBits + kRsBits + 2); imm19 >>= (kOpcodeBits + kRsBits + 2);
addr = current_pc + (offset << 2); addr = current_pc + (imm19 << 2);
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr); uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
alu_out = *ptr; alu_out = *ptr;
break; break;
} }
case ADDIUPC: case ADDIUPC: {
int32_t se_imm19 = imm19 | ((imm19 & 0x40000) ? 0xfff80000 : 0);
alu_out = current_pc + (se_imm19 << 2); alu_out = current_pc + (se_imm19 << 2);
break; break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
} }
} }
} }
set_register(rs_reg, alu_out);
break; break;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
} }
// ---------- Raise exceptions triggered.
SignalExceptions();
// ---------- Execution.
switch (op) {
// ------------- Branch instructions.
case BEQ:
case BNE:
case BLEZ:
case BGTZ:
// Branch instructions common part.
execute_branch_delay_instruction = true;
// Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
if (instr->IsLinkingInstruction()) {
set_register(31, current_pc + 2* Instruction::kInstrSize);
}
} else {
next_pc = current_pc + 2 * Instruction::kInstrSize;
}
break;
// ------------- Arithmetic instructions.
case ADDI:
case ADDIU:
case SLTI:
case SLTIU:
case ANDI:
case ORI:
case XORI:
case LUI:
set_register(rt_reg, alu_out);
TraceRegWr(alu_out);
break;
// ------------- Memory instructions.
case LB:
case LH:
case LWL:
case LW:
case LBU:
case LHU:
case LWR:
set_register(rt_reg, alu_out);
break;
case SB:
WriteB(addr, static_cast<int8_t>(rt));
break;
case SH:
WriteH(addr, static_cast<uint16_t>(rt), instr);
break;
case SWL:
WriteW(addr, mem_value, instr);
break;
case SW:
WriteW(addr, rt, instr);
break;
case SWR:
WriteW(addr, mem_value, instr);
break;
case LWC1:
set_fpu_register_hi_word(ft_reg, 0);
set_fpu_register_word(ft_reg, alu_out);
break;
case LDC1:
set_fpu_register_double(ft_reg, fp_out);
break;
case SWC1:
addr = rs + se_imm16;
WriteW(addr, get_fpu_register_word(ft_reg), instr);
break;
case SDC1:
addr = rs + se_imm16;
WriteD(addr, get_fpu_register_double(ft_reg), instr);
break;
case PCREL:
set_register(rs_reg, alu_out);
default:
break;
}
if (execute_branch_delay_instruction) { if (execute_branch_delay_instruction) {
// Execute branch delay slot // Execute branch delay slot
// We don't check for end_sim_pc. First it should not be met as the current // We don't check for end_sim_pc. First it should not be met as the current
...@@ -4372,6 +4086,9 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) { ...@@ -4372,6 +4086,9 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
} }
} }
#undef BranchHelper
#undef BranchAndLinkHelper
// Type 3: instructions using a 26 bytes immediate. (e.g. j, jal). // Type 3: instructions using a 26 bytes immediate. (e.g. j, jal).
void Simulator::DecodeTypeJump(Instruction* instr) { void Simulator::DecodeTypeJump(Instruction* instr) {
...@@ -4413,7 +4130,7 @@ void Simulator::InstructionDecode(Instruction* instr) { ...@@ -4413,7 +4130,7 @@ void Simulator::InstructionDecode(Instruction* instr) {
dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr)); dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr));
} }
switch (instr->InstructionType()) { switch (instr->InstructionType(Instruction::TypeChecks::EXTRA)) {
case Instruction::kRegisterType: case Instruction::kRegisterType:
DecodeTypeRegister(instr); DecodeTypeRegister(instr);
break; break;
......
...@@ -293,56 +293,51 @@ class Simulator { ...@@ -293,56 +293,51 @@ class Simulator {
// Executing is handled based on the instruction type. // Executing is handled based on the instruction type.
void DecodeTypeRegister(Instruction* instr); void DecodeTypeRegister(Instruction* instr);
// Called from DecodeTypeRegisterCOP1 // Functions called from DecodeTypeRegister.
void DecodeTypeRegisterDRsType(Instruction* instr, const int32_t& fr_reg, void DecodeTypeRegisterCOP1();
const int32_t& fs_reg, const int32_t& ft_reg,
const int32_t& fd_reg); void DecodeTypeRegisterCOP1X();
void DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out,
const int32_t& fd_reg, const int32_t& fs_reg, void DecodeTypeRegisterSPECIAL();
const int32_t& ft_reg);
void DecodeTypeRegisterSRsType(Instruction* instr, const int32_t& ft_reg, void DecodeTypeRegisterSPECIAL2();
const int32_t& fs_reg, const int32_t& fd_reg);
void DecodeTypeRegisterLRsType(Instruction* instr, const int32_t& ft_reg, void DecodeTypeRegisterSPECIAL3();
const int32_t& fs_reg, const int32_t& fd_reg);
// Called from DecodeTypeRegisterCOP1.
// Functions called from DeocodeTypeRegister void DecodeTypeRegisterSRsType();
void DecodeTypeRegisterCOP1(
Instruction* instr, const int32_t& rs_reg, const int32_t& rs, void DecodeTypeRegisterDRsType();
const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt,
const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg, void DecodeTypeRegisterWRsType();
const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg,
int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt, void DecodeTypeRegisterLRsType();
int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg);
Instruction* currentInstr_;
inline Instruction* get_instr() const { return currentInstr_; }
void DecodeTypeRegisterCOP1X(Instruction* instr, const int32_t& fr_reg, inline void set_instr(Instruction* instr) { currentInstr_ = instr; }
const int32_t& fs_reg, const int32_t& ft_reg,
const int32_t& fd_reg); inline int32_t rs_reg() const { return currentInstr_->RsValue(); }
inline int32_t rs() const { return get_register(rs_reg()); }
inline uint32_t rs_u() const {
void DecodeTypeRegisterSPECIAL( return static_cast<uint32_t>(get_register(rs_reg()));
Instruction* instr, const int32_t& rs_reg, const int32_t& rs, }
const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt, inline int32_t rt_reg() const { return currentInstr_->RtValue(); }
const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg, inline int32_t rt() const { return get_register(rt_reg()); }
const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg, inline uint32_t rt_u() const {
int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt, return static_cast<uint32_t>(get_register(rt_reg()));
int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg); }
inline int32_t rd_reg() const { return currentInstr_->RdValue(); }
inline int32_t fr_reg() const { return currentInstr_->FrValue(); }
void DecodeTypeRegisterSPECIAL2(Instruction* instr, const int32_t& rd_reg, inline int32_t fs_reg() const { return currentInstr_->FsValue(); }
int32_t& alu_out); inline int32_t ft_reg() const { return currentInstr_->FtValue(); }
inline int32_t fd_reg() const { return currentInstr_->FdValue(); }
void DecodeTypeRegisterSPECIAL3(Instruction* instr, const int32_t& rt_reg, inline int32_t sa() const { return currentInstr_->SaValue(); }
const int32_t& rd_reg, int32_t& alu_out);
inline void SetResult(int32_t rd_reg, int32_t alu_out) {
// Helper function for DecodeTypeRegister. set_register(rd_reg, alu_out);
void ConfigureTypeRegister(Instruction* instr, TraceRegWr(alu_out);
int32_t* alu_out, }
int64_t* i64hilo,
uint64_t* u64hilo,
int32_t* next_pc,
int32_t* return_addr_reg,
bool* do_interrupt);
void DecodeTypeImmediate(Instruction* instr); void DecodeTypeImmediate(Instruction* instr);
void DecodeTypeJump(Instruction* instr); void DecodeTypeJump(Instruction* instr);
...@@ -394,10 +389,9 @@ class Simulator { ...@@ -394,10 +389,9 @@ class Simulator {
kDivideByZero, kDivideByZero,
kNumExceptions kNumExceptions
}; };
int16_t exceptions[kNumExceptions];
// Exceptions. // Exceptions.
void SignalExceptions(); void SignalException(Exception e);
// Runtime call support. // Runtime call support.
static void* RedirectExternalReference(void* external_function, static void* RedirectExternalReference(void* external_function,
...@@ -424,7 +418,7 @@ class Simulator { ...@@ -424,7 +418,7 @@ class Simulator {
static const size_t stack_size_ = 1 * 1024*1024; static const size_t stack_size_ = 1 * 1024*1024;
char* stack_; char* stack_;
bool pc_modified_; bool pc_modified_;
int icount_; uint64_t icount_;
int break_count_; int break_count_;
// Debugger input. // Debugger input.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment