Commit f00b4e94 authored by dusan.milosavljevic's avatar dusan.milosavljevic Committed by Commit bot

MIPS: Refactor simulator and add selection instructions for r6.

TEST=
BUG=

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

Cr-Commit-Position: refs/heads/master@{#27530}
parent 00477a5d
......@@ -1920,6 +1920,34 @@ void Assembler::movf(Register rd, Register rs, uint16_t cc) {
}
void Assembler::seleqz(Register rs, Register rt, Register rd) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELEQZ_S);
}
void Assembler::seleqz(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
void Assembler::selnez(Register rs, Register rt, Register rd) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELNEZ_S);
}
void Assembler::selnez(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
}
// Bit twiddling.
void Assembler::clz(Register rd, Register rs) {
if (!IsMipsArchVariant(kMips32r6)) {
......
......@@ -252,6 +252,8 @@ Instruction::Type Instruction::InstructionType() const {
case MOVZ:
case MOVN:
case MOVCI:
case SELEQZ_S:
case SELNEZ_S:
return kRegisterType;
default:
return kUnsupported;
......@@ -280,6 +282,8 @@ Instruction::Type Instruction::InstructionType() const {
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
case SELEQZ_C:
case SELNEZ_C:
return kImmediateType;
default:
return kRegisterType;
......
......@@ -99,7 +99,13 @@ class Decoder {
void Format(Instruction* instr, const char* format);
void Unknown(Instruction* instr);
// Each of these functions decodes one particular instruction type.
void DecodeTypeRegisterDRsType(Instruction* instr);
void DecodeTypeRegisterLRsType(Instruction* instr);
void DecodeTypeRegisterSPECIAL(Instruction* instr);
void DecodeTypeRegisterSPECIAL2(Instruction* instr);
void DecodeTypeRegisterSPECIAL3(Instruction* instr);
void DecodeTypeRegister(Instruction* instr);
void DecodeTypeImmediate(Instruction* instr);
void DecodeTypeJump(Instruction* instr);
......@@ -449,6 +455,357 @@ void Decoder::Unknown(Instruction* instr) {
}
void Decoder::DecodeTypeRegisterDRsType(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case ADD_D:
Format(instr, "add.d 'fd, 'fs, 'ft");
break;
case SUB_D:
Format(instr, "sub.d 'fd, 'fs, 'ft");
break;
case MUL_D:
Format(instr, "mul.d 'fd, 'fs, 'ft");
break;
case DIV_D:
Format(instr, "div.d 'fd, 'fs, 'ft");
break;
case ABS_D:
Format(instr, "abs.d 'fd, 'fs");
break;
case MOV_D:
Format(instr, "mov.d 'fd, 'fs");
break;
case NEG_D:
Format(instr, "neg.d 'fd, 'fs");
break;
case SQRT_D:
Format(instr, "sqrt.d 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.d 'fd, 'fs");
break;
case CVT_L_D:
Format(instr, "cvt.l.d 'fd, 'fs");
break;
case TRUNC_W_D:
Format(instr, "trunc.w.d 'fd, 'fs");
break;
case TRUNC_L_D:
Format(instr, "trunc.l.d 'fd, 'fs");
break;
case ROUND_W_D:
Format(instr, "round.w.d 'fd, 'fs");
break;
case FLOOR_W_D:
Format(instr, "floor.w.d 'fd, 'fs");
break;
case CEIL_W_D:
Format(instr, "ceil.w.d 'fd, 'fs");
break;
case CVT_S_D:
Format(instr, "cvt.s.d 'fd, 'fs");
break;
case C_F_D:
Format(instr, "c.f.d 'fs, 'ft, 'Cc");
break;
case C_UN_D:
Format(instr, "c.un.d 'fs, 'ft, 'Cc");
break;
case C_EQ_D:
Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
break;
case C_UEQ_D:
Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
break;
case C_OLT_D:
Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
break;
case C_ULT_D:
Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
break;
case C_OLE_D:
Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
break;
case C_ULE_D:
Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
break;
default:
Format(instr, "unknown.cop1.d");
break;
}
}
void Decoder::DecodeTypeRegisterLRsType(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case CVT_D_L:
Format(instr, "cvt.d.l 'fd, 'fs");
break;
case CVT_S_L:
Format(instr, "cvt.s.l 'fd, 'fs");
break;
case CMP_UN:
Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
break;
case CMP_EQ:
Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
break;
case CMP_LT:
Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
break;
case CMP_ULT:
Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
break;
case CMP_LE:
Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
break;
case CMP_ULE:
Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
break;
case CMP_OR:
Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
break;
case CMP_UNE:
Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
break;
case CMP_NE:
Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterSPECIAL(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case JR:
Format(instr, "jr 'rs");
break;
case JALR:
Format(instr, "jalr 'rs");
break;
case SLL:
if (0x0 == static_cast<int>(instr->InstructionBits()))
Format(instr, "nop");
else
Format(instr, "sll 'rd, 'rt, 'sa");
break;
case SRL:
if (instr->RsValue() == 0) {
Format(instr, "srl 'rd, 'rt, 'sa");
} else {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "rotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
}
break;
case SRA:
Format(instr, "sra 'rd, 'rt, 'sa");
break;
case SLLV:
Format(instr, "sllv 'rd, 'rt, 'rs");
break;
case SRLV:
if (instr->SaValue() == 0) {
Format(instr, "srlv 'rd, 'rt, 'rs");
} else {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "rotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
}
break;
case SRAV:
Format(instr, "srav 'rd, 'rt, 'rs");
break;
case MFHI:
if (instr->Bits(25, 16) == 0) {
Format(instr, "mfhi 'rd");
} else {
if ((instr->FunctionFieldRaw() == CLZ_R6) && (instr->FdValue() == 1)) {
Format(instr, "clz 'rd, 'rs");
} else if ((instr->FunctionFieldRaw() == CLO_R6) &&
(instr->FdValue() == 1)) {
Format(instr, "clo 'rd, 'rs");
}
}
break;
case MFLO:
Format(instr, "mflo 'rd");
break;
case MULT: // @Mips32r6 == MUL_MUH.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "mult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mul 'rd, 'rs, 'rt");
} else {
Format(instr, "muh 'rd, 'rs, 'rt");
}
}
break;
case MULTU: // @Mips32r6 == MUL_MUH_U.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "multu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mulu 'rd, 'rs, 'rt");
} else {
Format(instr, "muhu 'rd, 'rs, 'rt");
}
}
break;
case DIV: // @Mips32r6 == DIV_MOD.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "div 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "div 'rd, 'rs, 'rt");
} else {
Format(instr, "mod 'rd, 'rs, 'rt");
}
}
break;
case DIVU: // @Mips32r6 == DIV_MOD_U.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "divu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "divu 'rd, 'rs, 'rt");
} else {
Format(instr, "modu 'rd, 'rs, 'rt");
}
}
break;
case ADD:
Format(instr, "add 'rd, 'rs, 'rt");
break;
case ADDU:
Format(instr, "addu 'rd, 'rs, 'rt");
break;
case SUB:
Format(instr, "sub 'rd, 'rs, 'rt");
break;
case SUBU:
Format(instr, "subu 'rd, 'rs, 'rt");
break;
case AND:
Format(instr, "and 'rd, 'rs, 'rt");
break;
case OR:
if (0 == instr->RsValue()) {
Format(instr, "mov 'rd, 'rt");
} else if (0 == instr->RtValue()) {
Format(instr, "mov 'rd, 'rs");
} else {
Format(instr, "or 'rd, 'rs, 'rt");
}
break;
case XOR:
Format(instr, "xor 'rd, 'rs, 'rt");
break;
case NOR:
Format(instr, "nor 'rd, 'rs, 'rt");
break;
case SLT:
Format(instr, "slt 'rd, 'rs, 'rt");
break;
case SLTU:
Format(instr, "sltu 'rd, 'rs, 'rt");
break;
case BREAK:
Format(instr, "break, code: 'code");
break;
case TGE:
Format(instr, "tge 'rs, 'rt, code: 'code");
break;
case TGEU:
Format(instr, "tgeu 'rs, 'rt, code: 'code");
break;
case TLT:
Format(instr, "tlt 'rs, 'rt, code: 'code");
break;
case TLTU:
Format(instr, "tltu 'rs, 'rt, code: 'code");
break;
case TEQ:
Format(instr, "teq 'rs, 'rt, code: 'code");
break;
case TNE:
Format(instr, "tne 'rs, 'rt, code: 'code");
break;
case MOVZ:
Format(instr, "movz 'rd, 'rs, 'rt");
break;
case MOVN:
Format(instr, "movn 'rd, 'rs, 'rt");
break;
case MOVCI:
if (instr->Bit(16)) {
Format(instr, "movt 'rd, 'rs, 'bc");
} else {
Format(instr, "movf 'rd, 'rs, 'bc");
}
break;
case SELEQZ_S:
Format(instr, "seleqz 'rs, 'rt, 'rd");
break;
case SELNEZ_S:
Format(instr, "selnez 'rs, 'rt, 'rd");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterSPECIAL2(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case MUL:
Format(instr, "mul 'rd, 'rs, 'rt");
break;
case CLZ:
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "clz 'rd, 'rs");
}
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterSPECIAL3(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case INS: {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
} else {
Unknown(instr);
}
break;
}
case EXT: {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
} else {
Unknown(instr);
}
break;
}
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegister(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case COP1: // Coprocessor instructions.
......@@ -476,83 +833,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
Format(instr, "mthc1 'rt, 'fs");
break;
case D:
switch (instr->FunctionFieldRaw()) {
case ADD_D:
Format(instr, "add.d 'fd, 'fs, 'ft");
break;
case SUB_D:
Format(instr, "sub.d 'fd, 'fs, 'ft");
break;
case MUL_D:
Format(instr, "mul.d 'fd, 'fs, 'ft");
break;
case DIV_D:
Format(instr, "div.d 'fd, 'fs, 'ft");
break;
case ABS_D:
Format(instr, "abs.d 'fd, 'fs");
break;
case MOV_D:
Format(instr, "mov.d 'fd, 'fs");
break;
case NEG_D:
Format(instr, "neg.d 'fd, 'fs");
break;
case SQRT_D:
Format(instr, "sqrt.d 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.d 'fd, 'fs");
break;
case CVT_L_D:
Format(instr, "cvt.l.d 'fd, 'fs");
break;
case TRUNC_W_D:
Format(instr, "trunc.w.d 'fd, 'fs");
break;
case TRUNC_L_D:
Format(instr, "trunc.l.d 'fd, 'fs");
break;
case ROUND_W_D:
Format(instr, "round.w.d 'fd, 'fs");
break;
case FLOOR_W_D:
Format(instr, "floor.w.d 'fd, 'fs");
break;
case CEIL_W_D:
Format(instr, "ceil.w.d 'fd, 'fs");
break;
case CVT_S_D:
Format(instr, "cvt.s.d 'fd, 'fs");
break;
case C_F_D:
Format(instr, "c.f.d 'fs, 'ft, 'Cc");
break;
case C_UN_D:
Format(instr, "c.un.d 'fs, 'ft, 'Cc");
break;
case C_EQ_D:
Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
break;
case C_UEQ_D:
Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
break;
case C_OLT_D:
Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
break;
case C_ULT_D:
Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
break;
case C_OLE_D:
Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
break;
case C_ULE_D:
Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
break;
default:
Format(instr, "unknown.cop1.d");
break;
}
DecodeTypeRegisterDRsType(instr);
break;
case S:
switch (instr->FunctionFieldRaw()) {
......@@ -576,46 +857,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
}
break;
case L:
switch (instr->FunctionFieldRaw()) {
case CVT_D_L:
Format(instr, "cvt.d.l 'fd, 'fs");
break;
case CVT_S_L:
Format(instr, "cvt.s.l 'fd, 'fs");
break;
case CMP_UN:
Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
break;
case CMP_EQ:
Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
break;
case CMP_LT:
Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
break;
case CMP_ULT:
Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
break;
case CMP_LE:
Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
break;
case CMP_ULE:
Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
break;
case CMP_OR:
Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
break;
case CMP_UNE:
Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
break;
case CMP_NE:
Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterLRsType(instr);
break;
case PS:
UNIMPLEMENTED_MIPS();
......@@ -634,225 +876,13 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
}
break;
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case JR:
Format(instr, "jr 'rs");
break;
case JALR:
Format(instr, "jalr 'rs");
break;
case SLL:
if ( 0x0 == static_cast<int>(instr->InstructionBits()))
Format(instr, "nop");
else
Format(instr, "sll 'rd, 'rt, 'sa");
break;
case SRL:
if (instr->RsValue() == 0) {
Format(instr, "srl 'rd, 'rt, 'sa");
} else {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "rotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
}
break;
case SRA:
Format(instr, "sra 'rd, 'rt, 'sa");
break;
case SLLV:
Format(instr, "sllv 'rd, 'rt, 'rs");
break;
case SRLV:
if (instr->SaValue() == 0) {
Format(instr, "srlv 'rd, 'rt, 'rs");
} else {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "rotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
}
break;
case SRAV:
Format(instr, "srav 'rd, 'rt, 'rs");
break;
case MFHI:
if (instr->Bits(25, 16) == 0) {
Format(instr, "mfhi 'rd");
} else {
if ((instr->FunctionFieldRaw() == CLZ_R6)
&& (instr->FdValue() == 1)) {
Format(instr, "clz 'rd, 'rs");
} else if ((instr->FunctionFieldRaw() == CLO_R6)
&& (instr->FdValue() == 1)) {
Format(instr, "clo 'rd, 'rs");
}
}
break;
case MFLO:
Format(instr, "mflo 'rd");
break;
case MULT: // @Mips32r6 == MUL_MUH.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "mult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mul 'rd, 'rs, 'rt");
} else {
Format(instr, "muh 'rd, 'rs, 'rt");
}
}
break;
case MULTU: // @Mips32r6 == MUL_MUH_U.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "multu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mulu 'rd, 'rs, 'rt");
} else {
Format(instr, "muhu 'rd, 'rs, 'rt");
}
}
break;
case DIV: // @Mips32r6 == DIV_MOD.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "div 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "div 'rd, 'rs, 'rt");
} else {
Format(instr, "mod 'rd, 'rs, 'rt");
}
}
break;
case DIVU: // @Mips32r6 == DIV_MOD_U.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "divu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "divu 'rd, 'rs, 'rt");
} else {
Format(instr, "modu 'rd, 'rs, 'rt");
}
}
break;
case ADD:
Format(instr, "add 'rd, 'rs, 'rt");
break;
case ADDU:
Format(instr, "addu 'rd, 'rs, 'rt");
break;
case SUB:
Format(instr, "sub 'rd, 'rs, 'rt");
break;
case SUBU:
Format(instr, "subu 'rd, 'rs, 'rt");
break;
case AND:
Format(instr, "and 'rd, 'rs, 'rt");
break;
case OR:
if (0 == instr->RsValue()) {
Format(instr, "mov 'rd, 'rt");
} else if (0 == instr->RtValue()) {
Format(instr, "mov 'rd, 'rs");
} else {
Format(instr, "or 'rd, 'rs, 'rt");
}
break;
case XOR:
Format(instr, "xor 'rd, 'rs, 'rt");
break;
case NOR:
Format(instr, "nor 'rd, 'rs, 'rt");
break;
case SLT:
Format(instr, "slt 'rd, 'rs, 'rt");
break;
case SLTU:
Format(instr, "sltu 'rd, 'rs, 'rt");
break;
case BREAK:
Format(instr, "break, code: 'code");
break;
case TGE:
Format(instr, "tge 'rs, 'rt, code: 'code");
break;
case TGEU:
Format(instr, "tgeu 'rs, 'rt, code: 'code");
break;
case TLT:
Format(instr, "tlt 'rs, 'rt, code: 'code");
break;
case TLTU:
Format(instr, "tltu 'rs, 'rt, code: 'code");
break;
case TEQ:
Format(instr, "teq 'rs, 'rt, code: 'code");
break;
case TNE:
Format(instr, "tne 'rs, 'rt, code: 'code");
break;
case MOVZ:
Format(instr, "movz 'rd, 'rs, 'rt");
break;
case MOVN:
Format(instr, "movn 'rd, 'rs, 'rt");
break;
case MOVCI:
if (instr->Bit(16)) {
Format(instr, "movt 'rd, 'rs, 'bc");
} else {
Format(instr, "movf 'rd, 'rs, 'bc");
}
break;
case SELEQZ_S:
Format(instr, "seleqz 'rd, 'rs, 'rt");
break;
case SELNEZ_S:
Format(instr, "selnez 'rd, 'rs, 'rt");
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterSPECIAL(instr);
break;
case SPECIAL2:
switch (instr->FunctionFieldRaw()) {
case MUL:
Format(instr, "mul 'rd, 'rs, 'rt");
break;
case CLZ:
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "clz 'rd, 'rs");
}
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterSPECIAL2(instr);
break;
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS: {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
} else {
Unknown(instr);
}
break;
}
case EXT: {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
} else {
Unknown(instr);
}
break;
}
default:
UNREACHABLE();
}
DecodeTypeRegisterSPECIAL3(instr);
break;
default:
UNREACHABLE();
......
......@@ -2079,6 +2079,8 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case DIV:
case DIVU:
// div and divu never raise exceptions.
case SELEQZ_S:
case SELNEZ_S:
break;
default:
UNREACHABLE();
......@@ -2130,366 +2132,393 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
}
void Simulator::DecodeTypeRegister(Instruction* instr) {
// 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();
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;
uint32_t cc, fcsr_cc;
int64_t i64;
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
int64_t ft_int = static_cast<int64_t>(ft);
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) {
case SELEQZ_C:
DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0);
break;
case SELNEZ_C:
DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0);
break;
case ADD_D:
set_fpu_register_double(fd_reg, fs + ft);
break;
case SUB_D:
set_fpu_register_double(fd_reg, fs - ft);
break;
case MUL_D:
set_fpu_register_double(fd_reg, fs * ft);
break;
case DIV_D:
set_fpu_register_double(fd_reg, fs / ft);
break;
case ABS_D:
set_fpu_register_double(fd_reg, fabs(fs));
break;
case MOV_D:
set_fpu_register_double(fd_reg, fs);
break;
case NEG_D:
set_fpu_register_double(fd_reg, -fs);
break;
case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs));
break;
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
case C_EQ_D:
set_fcsr_bit(fcsr_cc, (fs == ft));
break;
case C_UEQ_D:
set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLT_D:
set_fcsr_bit(fcsr_cc, (fs < ft));
break;
case C_ULT_D:
set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft));
break;
case C_ULE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case CVT_W_D: // Convert double to word.
// Rounding modes are not yet supported.
DCHECK((FCSR_ & 3) == 0);
// In rounding mode 0 it should behave like ROUND.
case ROUND_W_D: // Round double to word (round half to even).
{
double rounded = std::floor(fs + 0.5);
int32_t result = static_cast<int32_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case TRUNC_W_D: // Truncate double to word (round towards 0).
{
double rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case FLOOR_W_D: // Round double to word towards negative infinity.
{
double rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case CEIL_W_D: // Round double to word towards positive infinity.
{
double rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case CVT_S_D: // Convert double to float (single).
set_fpu_register_float(fd_reg, static_cast<float>(fs));
break;
case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case TRUNC_L_D: { // Mips32r2 instruction.
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case ROUND_L_D: { // Mips32r2 instruction.
double rounded = fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case FLOOR_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::floor(fs));
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
case CEIL_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::ceil(fs));
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
case C_F_D:
UNIMPLEMENTED_MIPS();
break;
default:
UNREACHABLE();
}
}
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;
void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out,
const int32_t& fd_reg,
const int32_t& fs_reg) {
switch (instr->FunctionFieldRaw()) {
case CVT_S_W: // Convert word to float (single).
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
break;
case CVT_D_W: // Convert word to double.
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
break;
default: // Mips64r6 CMP.S instructions unimplemented.
UNREACHABLE();
}
}
// 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.
ConfigureTypeRegister(instr,
&alu_out,
&i64hilo,
&u64hilo,
&next_pc,
&return_addr_reg,
&do_interrupt);
void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
const int32_t& ft_reg,
const int32_t& fs_reg,
const int32_t& fd_reg) {
float f;
double ft = get_fpu_register_double(ft_reg);
int64_t ft_int = static_cast<int64_t>(ft);
switch (instr->FunctionFieldRaw()) {
case CVT_D_S:
f = get_fpu_register_float(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(f));
break;
case SELEQZ_C:
DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(
fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_double(fs_reg) : 0.0);
break;
case SELNEZ_C:
DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(
fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_double(fs_reg) : 0.0);
break;
default:
// CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
// CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
UNREACHABLE();
}
}
// ---------- Raise exceptions triggered.
SignalExceptions();
// ---------- Execution.
switch (op) {
case COP1:
switch (instr->RsFieldRaw()) {
case CFC1:
set_register(rt_reg, alu_out);
break;
case MFC1:
set_register(rt_reg, alu_out);
break;
case MFHC1:
set_register(rt_reg, alu_out);
break;
case CTC1:
// At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister);
FCSR_ = registers_[rt_reg];
break;
case MTC1:
// Hardware writes upper 32-bits to zero on mtc1.
set_fpu_register_hi_word(fs_reg, 0);
set_fpu_register_word(fs_reg, registers_[rt_reg]);
break;
case MTHC1:
set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
break;
case S:
float f;
switch (instr->FunctionFieldRaw()) {
case CVT_D_S:
f = get_fpu_register_float(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(f));
break;
default:
// CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
// CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
UNREACHABLE();
}
break;
case D:
double ft, fs;
uint32_t cc, fcsr_cc;
int64_t i64;
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) {
case ADD_D:
set_fpu_register_double(fd_reg, fs + ft);
break;
case SUB_D:
set_fpu_register_double(fd_reg, fs - ft);
break;
case MUL_D:
set_fpu_register_double(fd_reg, fs * ft);
break;
case DIV_D:
set_fpu_register_double(fd_reg, fs / ft);
break;
case ABS_D:
set_fpu_register_double(fd_reg, fabs(fs));
break;
case MOV_D:
set_fpu_register_double(fd_reg, fs);
break;
case NEG_D:
set_fpu_register_double(fd_reg, -fs);
break;
case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs));
break;
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
case C_EQ_D:
set_fcsr_bit(fcsr_cc, (fs == ft));
break;
case C_UEQ_D:
set_fcsr_bit(fcsr_cc,
(fs == ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLT_D:
set_fcsr_bit(fcsr_cc, (fs < ft));
break;
case C_ULT_D:
set_fcsr_bit(fcsr_cc,
(fs < ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft));
break;
case C_ULE_D:
set_fcsr_bit(fcsr_cc,
(fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case CVT_W_D: // Convert double to word.
// Rounding modes are not yet supported.
DCHECK((FCSR_ & 3) == 0);
// In rounding mode 0 it should behave like ROUND.
case ROUND_W_D: // Round double to word (round half to even).
{
double rounded = std::floor(fs + 0.5);
int32_t result = static_cast<int32_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
}
break;
case TRUNC_W_D: // Truncate double to word (round towards 0).
{
double rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
}
break;
case FLOOR_W_D: // Round double to word towards negative infinity.
{
double rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
}
break;
case CEIL_W_D: // Round double to word towards positive infinity.
{
double rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
}
break;
case CVT_S_D: // Convert double to float (single).
set_fpu_register_float(fd_reg, static_cast<float>(fs));
break;
case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case TRUNC_L_D: { // Mips32r2 instruction.
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case ROUND_L_D: { // Mips32r2 instruction.
double rounded =
fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case FLOOR_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::floor(fs));
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
case CEIL_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::ceil(fs));
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
case C_F_D:
UNIMPLEMENTED_MIPS();
break;
default:
UNREACHABLE();
}
break;
case W:
switch (instr->FunctionFieldRaw()) {
case CVT_S_W: // Convert word to float (single).
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
break;
case CVT_D_W: // Convert word to double.
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
break;
default: // Mips64r6 CMP.S instructions unimplemented.
UNREACHABLE();
}
break;
case L:
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
switch (instr->FunctionFieldRaw()) {
case CVT_D_L: // Mips32r2 instruction.
// Watch the signs here, we want 2 32-bit vals
// to make a sign-64.
if (IsFp64Mode()) {
i64 = get_fpu_register(fs_reg);
} else {
i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg));
i64 |= static_cast<int64_t>(
get_fpu_register_word(fs_reg + 1)) << 32;
}
set_fpu_register_double(fd_reg, static_cast<double>(i64));
break;
case CVT_S_L:
UNIMPLEMENTED_MIPS();
break;
case CMP_AF: // Mips64r6 CMP.D instructions.
UNIMPLEMENTED_MIPS();
break;
case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_EQ:
if (fs == ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LT:
if (fs < ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LE:
if (fs <= ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED.
UNREACHABLE();
}
break;
default:
UNREACHABLE();
void Simulator::DecodeTypeRegisterLRsType(Instruction* instr,
const int32_t& ft_reg,
const int32_t& fs_reg,
const int32_t& fd_reg) {
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.
// Watch the signs here, we want 2 32-bit vals
// to make a sign-64.
int64_t i64;
if (IsFp64Mode()) {
i64 = get_fpu_register(fs_reg);
} else {
i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg));
i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg + 1)) << 32;
}
set_fpu_register_double(fd_reg, static_cast<double>(i64));
break;
case COP1X:
switch (instr->FunctionFieldRaw()) {
case MADD_D:
double fr, ft, fs;
fr = get_fpu_register_double(fr_reg);
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
set_fpu_register_double(fd_reg, fs * ft + fr);
break;
default:
UNREACHABLE();
case CVT_S_L:
UNIMPLEMENTED_MIPS();
break;
case CMP_AF: // Mips64r6 CMP.D instructions.
UNIMPLEMENTED_MIPS();
break;
case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case CMP_EQ:
if (fs == ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LT:
if (fs < ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LE:
if (fs <= ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED.
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterCOP1(
Instruction* instr, const int32_t& rs_reg, const int32_t& rs,
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:
set_register(rt_reg, alu_out);
break;
case MFC1:
set_register(rt_reg, alu_out);
break;
case MFHC1:
set_register(rt_reg, alu_out);
break;
case CTC1:
// At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister);
FCSR_ = registers_[rt_reg];
break;
case MTC1:
// Hardware writes upper 32-bits to zero on mtc1.
set_fpu_register_hi_word(fs_reg, 0);
set_fpu_register_word(fs_reg, registers_[rt_reg]);
break;
case MTHC1:
set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
break;
case S: {
DecodeTypeRegisterSRsType(instr, ft_reg, fs_reg, fd_reg);
break;
}
case D:
DecodeTypeRegisterDRsType(instr, fr_reg, fs_reg, ft_reg, fd_reg);
break;
case W:
DecodeTypeRegisterWRsType(instr, alu_out, fd_reg, fs_reg);
break;
case L:
DecodeTypeRegisterLRsType(instr, ft_reg, fs_reg, fd_reg);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr,
const int32_t& fr_reg,
const int32_t& fs_reg,
const int32_t& ft_reg,
const int32_t& fd_reg) {
switch (instr->FunctionFieldRaw()) {
case MADD_D:
double fr, ft, fs;
fr = get_fpu_register_double(fr_reg);
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
set_fpu_register_double(fd_reg, fs * ft + fr);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterSPECIAL(
Instruction* instr, const int32_t& rs_reg, const int32_t& rs,
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->FunctionFieldRaw()) {
case SELEQZ_S:
DCHECK(IsMipsArchVariant(kMips32r6));
set_register(rd_reg, rt == 0 ? rs : 0);
break;
case SELNEZ_S:
DCHECK(IsMipsArchVariant(kMips32r6));
set_register(rd_reg, rt != 0 ? rs : 0);
break;
case JR: {
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
current_pc+Instruction::kInstrSize);
......@@ -2638,32 +2667,105 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
default: // For other special opcodes we do the default operation.
set_register(rd_reg, alu_out);
}
}
void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr,
const int32_t& rd_reg,
int32_t& alu_out) {
switch (instr->FunctionFieldRaw()) {
case MUL:
set_register(rd_reg, alu_out);
// HI and LO are UNPREDICTABLE after the operation.
set_register(LO, Unpredictable);
set_register(HI, Unpredictable);
break;
default: // For other special2 opcodes we do the default operation.
set_register(rd_reg, alu_out);
}
}
void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr,
const int32_t& rt_reg,
int32_t& alu_out) {
switch (instr->FunctionFieldRaw()) {
case INS:
// Ins instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
break;
case EXT:
// Ext instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegister(Instruction* instr) {
// 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 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.
ConfigureTypeRegister(instr, &alu_out, &i64hilo, &u64hilo, &next_pc,
&return_addr_reg, &do_interrupt);
// ---------- Raise exceptions triggered.
SignalExceptions();
// ---------- Execution.
switch (op) {
case COP1:
DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg,
fr_reg, fs_reg, ft_reg, fd_reg, i64hilo, u64hilo,
alu_out, do_interrupt, current_pc, next_pc,
return_addr_reg);
break;
case COP1X:
DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg);
break;
case SPECIAL:
DecodeTypeRegisterSPECIAL(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u,
rd_reg, fr_reg, fs_reg, ft_reg, fd_reg, i64hilo,
u64hilo, alu_out, do_interrupt, current_pc,
next_pc, return_addr_reg);
break;
case SPECIAL2:
switch (instr->FunctionFieldRaw()) {
case MUL:
set_register(rd_reg, alu_out);
// HI and LO are UNPREDICTABLE after the operation.
set_register(LO, Unpredictable);
set_register(HI, Unpredictable);
break;
default: // For other special2 opcodes we do the default operation.
set_register(rd_reg, alu_out);
}
DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out);
break;
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS:
// Ins instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
break;
case EXT:
// Ext instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out);
break;
// Unimplemented opcodes raised an error in the configuration step before,
// so we can use the default here to set the destination register in common
......
......@@ -265,6 +265,47 @@ class Simulator {
// Executing is handled based on the instruction type.
void DecodeTypeRegister(Instruction* instr);
// Called from DecodeTypeRegisterCOP1
void DecodeTypeRegisterDRsType(Instruction* instr, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg,
const int32_t& fd_reg);
void DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out,
const int32_t& fd_reg, const int32_t& fs_reg);
void DecodeTypeRegisterSRsType(Instruction* instr, const int32_t& ft_reg,
const int32_t& fs_reg, const int32_t& fd_reg);
void DecodeTypeRegisterLRsType(Instruction* instr, const int32_t& ft_reg,
const int32_t& fs_reg, const int32_t& fd_reg);
// Functions called from DeocodeTypeRegister
void DecodeTypeRegisterCOP1(
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,
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);
void DecodeTypeRegisterCOP1X(Instruction* instr, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg,
const int32_t& fd_reg);
void DecodeTypeRegisterSPECIAL(
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,
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);
void DecodeTypeRegisterSPECIAL2(Instruction* instr, const int32_t& rd_reg,
int32_t& alu_out);
void DecodeTypeRegisterSPECIAL3(Instruction* instr, const int32_t& rt_reg,
int32_t& alu_out);
// Helper function for DecodeTypeRegister.
void ConfigureTypeRegister(Instruction* instr,
int32_t* alu_out,
......
......@@ -2139,13 +2139,8 @@ void Assembler::seleqz(Register rs, Register rt, Register rd) {
// FPR.
void Assembler::seleqz(SecondaryField fmt, FPURegister fd,
FPURegister ft, FPURegister fs) {
DCHECK(kArchVariant == kMips64r6);
DCHECK(fmt == D);
DCHECK(fmt == S);
Instr instr = COP1 | fmt << kRsShift | ft.code() << kFtShift |
fs.code() << kFsShift | fd.code() << kFdShift | SELEQZ_C;
emit(instr);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
......@@ -2160,12 +2155,8 @@ void Assembler::selnez(Register rs, Register rt, Register rd) {
void Assembler::selnez(SecondaryField fmt, FPURegister fd,
FPURegister ft, FPURegister fs) {
DCHECK(kArchVariant == kMips64r6);
DCHECK(fmt == D);
DCHECK(fmt == S);
Instr instr = COP1 | fmt << kRsShift | ft.code() << kFtShift |
fs.code() << kFsShift | fd.code() << kFdShift | SELNEZ_C;
emit(instr);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
}
......
......@@ -269,6 +269,8 @@ Instruction::Type Instruction::InstructionType() const {
case MOVZ:
case MOVN:
case MOVCI:
case SELEQZ_S:
case SELNEZ_S:
return kRegisterType;
default:
return kUnsupported;
......
......@@ -101,8 +101,23 @@ class Decoder {
int DecodeBreakInstr(Instruction* instr);
// Each of these functions decodes one particular instruction type.
void DecodeTypeRegisterDRsType(Instruction* instr);
void DecodeTypeRegisterLRsType(Instruction* instr);
void DecodeTypeRegisterSPECIAL(Instruction* instr);
void DecodeTypeRegisterSPECIAL2(Instruction* instr);
void DecodeTypeRegisterSPECIAL3(Instruction* instr);
void DecodeTypeRegisterCOP1(Instruction* instr);
void DecodeTypeRegisterCOP1X(Instruction* instr);
int DecodeTypeRegister(Instruction* instr);
void DecodeTypeImmediateCOP1W(Instruction* instr);
void DecodeTypeImmediateCOP1L(Instruction* instr);
void DecodeTypeImmediateCOP1S(Instruction* instr);
void DecodeTypeImmediateCOP1D(Instruction* instr);
void DecodeTypeImmediateCOP1(Instruction* instr);
void DecodeTypeImmediateREGIMM(Instruction* instr);
void DecodeTypeImmediate(Instruction* instr);
void DecodeTypeJump(Instruction* instr);
const disasm::NameConverter& converter_;
......@@ -474,503 +489,547 @@ int Decoder::DecodeBreakInstr(Instruction* instr) {
}
int Decoder::DecodeTypeRegister(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case COP1: // Coprocessor instructions.
switch (instr->RsFieldRaw()) {
case MFC1:
Format(instr, "mfc1 'rt, 'fs");
break;
case DMFC1:
Format(instr, "dmfc1 'rt, 'fs");
break;
case MFHC1:
Format(instr, "mfhc1 'rt, 'fs");
break;
case MTC1:
Format(instr, "mtc1 'rt, 'fs");
break;
case DMTC1:
Format(instr, "dmtc1 'rt, 'fs");
break;
// These are called "fs" too, although they are not FPU registers.
case CTC1:
Format(instr, "ctc1 'rt, 'fs");
break;
case CFC1:
Format(instr, "cfc1 'rt, 'fs");
break;
case MTHC1:
Format(instr, "mthc1 'rt, 'fs");
break;
case D:
switch (instr->FunctionFieldRaw()) {
case ADD_D:
Format(instr, "add.d 'fd, 'fs, 'ft");
break;
case SUB_D:
Format(instr, "sub.d 'fd, 'fs, 'ft");
break;
case MUL_D:
Format(instr, "mul.d 'fd, 'fs, 'ft");
break;
case DIV_D:
Format(instr, "div.d 'fd, 'fs, 'ft");
break;
case ABS_D:
Format(instr, "abs.d 'fd, 'fs");
break;
case MOV_D:
Format(instr, "mov.d 'fd, 'fs");
break;
case NEG_D:
Format(instr, "neg.d 'fd, 'fs");
break;
case SQRT_D:
Format(instr, "sqrt.d 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.d 'fd, 'fs");
break;
case CVT_L_D:
Format(instr, "cvt.l.d 'fd, 'fs");
break;
case TRUNC_W_D:
Format(instr, "trunc.w.d 'fd, 'fs");
break;
case TRUNC_L_D:
Format(instr, "trunc.l.d 'fd, 'fs");
break;
case ROUND_W_D:
Format(instr, "round.w.d 'fd, 'fs");
break;
case ROUND_L_D:
Format(instr, "round.l.d 'fd, 'fs");
break;
case FLOOR_W_D:
Format(instr, "floor.w.d 'fd, 'fs");
break;
case FLOOR_L_D:
Format(instr, "floor.l.d 'fd, 'fs");
break;
case CEIL_W_D:
Format(instr, "ceil.w.d 'fd, 'fs");
break;
case CEIL_L_D:
Format(instr, "ceil.l.d 'fd, 'fs");
break;
case CVT_S_D:
Format(instr, "cvt.s.d 'fd, 'fs");
break;
case C_F_D:
Format(instr, "c.f.d 'fs, 'ft, 'Cc");
break;
case C_UN_D:
Format(instr, "c.un.d 'fs, 'ft, 'Cc");
break;
case C_EQ_D:
Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
break;
case C_UEQ_D:
Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
break;
case C_OLT_D:
Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
break;
case C_ULT_D:
Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
break;
case C_OLE_D:
Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
break;
case C_ULE_D:
Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
break;
default:
Format(instr, "unknown.cop1.d");
break;
}
break;
case W:
switch (instr->FunctionFieldRaw()) {
case CVT_D_W: // Convert word to double.
Format(instr, "cvt.d.w 'fd, 'fs");
break;
default:
UNREACHABLE();
}
break;
case L:
switch (instr->FunctionFieldRaw()) {
case CVT_D_L:
Format(instr, "cvt.d.l 'fd, 'fs");
break;
case CVT_S_L:
Format(instr, "cvt.s.l 'fd, 'fs");
break;
case CMP_UN:
Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
break;
case CMP_EQ:
Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
break;
case CMP_LT:
Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
break;
case CMP_ULT:
Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
break;
case CMP_LE:
Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
break;
case CMP_ULE:
Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
break;
case CMP_OR:
Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
break;
case CMP_UNE:
Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
break;
case CMP_NE:
Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
break;
default:
UNREACHABLE();
}
break;
default:
UNREACHABLE();
}
void Decoder::DecodeTypeRegisterDRsType(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case SELEQZ_C:
Format(instr, "seleqz.D 'ft, 'fs, 'fd");
break;
case COP1X:
case SELNEZ_C:
Format(instr, "selnez.D 'ft, 'fs, 'fd");
break;
case ADD_D:
Format(instr, "add.d 'fd, 'fs, 'ft");
break;
case SUB_D:
Format(instr, "sub.d 'fd, 'fs, 'ft");
break;
case MUL_D:
Format(instr, "mul.d 'fd, 'fs, 'ft");
break;
case DIV_D:
Format(instr, "div.d 'fd, 'fs, 'ft");
break;
case ABS_D:
Format(instr, "abs.d 'fd, 'fs");
break;
case MOV_D:
Format(instr, "mov.d 'fd, 'fs");
break;
case NEG_D:
Format(instr, "neg.d 'fd, 'fs");
break;
case SQRT_D:
Format(instr, "sqrt.d 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.d 'fd, 'fs");
break;
case CVT_L_D:
Format(instr, "cvt.l.d 'fd, 'fs");
break;
case TRUNC_W_D:
Format(instr, "trunc.w.d 'fd, 'fs");
break;
case TRUNC_L_D:
Format(instr, "trunc.l.d 'fd, 'fs");
break;
case ROUND_W_D:
Format(instr, "round.w.d 'fd, 'fs");
break;
case ROUND_L_D:
Format(instr, "round.l.d 'fd, 'fs");
break;
case FLOOR_W_D:
Format(instr, "floor.w.d 'fd, 'fs");
break;
case FLOOR_L_D:
Format(instr, "floor.l.d 'fd, 'fs");
break;
case CEIL_W_D:
Format(instr, "ceil.w.d 'fd, 'fs");
break;
case CEIL_L_D:
Format(instr, "ceil.l.d 'fd, 'fs");
break;
case CVT_S_D:
Format(instr, "cvt.s.d 'fd, 'fs");
break;
case C_F_D:
Format(instr, "c.f.d 'fs, 'ft, 'Cc");
break;
case C_UN_D:
Format(instr, "c.un.d 'fs, 'ft, 'Cc");
break;
case C_EQ_D:
Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
break;
case C_UEQ_D:
Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
break;
case C_OLT_D:
Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
break;
case C_ULT_D:
Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
break;
case C_OLE_D:
Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
break;
case C_ULE_D:
Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
break;
default:
Format(instr, "unknown.cop1.d");
break;
}
}
void Decoder::DecodeTypeRegisterLRsType(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case CVT_D_L:
Format(instr, "cvt.d.l 'fd, 'fs");
break;
case CVT_S_L:
Format(instr, "cvt.s.l 'fd, 'fs");
break;
case CMP_UN:
Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
break;
case CMP_EQ:
Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
break;
case CMP_LT:
Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
break;
case CMP_ULT:
Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
break;
case CMP_LE:
Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
break;
case CMP_ULE:
Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
break;
case CMP_OR:
Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
break;
case CMP_UNE:
Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
break;
case CMP_NE:
Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterCOP1(Instruction* instr) {
switch (instr->RsFieldRaw()) {
case MFC1:
Format(instr, "mfc1 'rt, 'fs");
break;
case DMFC1:
Format(instr, "dmfc1 'rt, 'fs");
break;
case MFHC1:
Format(instr, "mfhc1 'rt, 'fs");
break;
case MTC1:
Format(instr, "mtc1 'rt, 'fs");
break;
case DMTC1:
Format(instr, "dmtc1 'rt, 'fs");
break;
// These are called "fs" too, although they are not FPU registers.
case CTC1:
Format(instr, "ctc1 'rt, 'fs");
break;
case CFC1:
Format(instr, "cfc1 'rt, 'fs");
break;
case MTHC1:
Format(instr, "mthc1 'rt, 'fs");
break;
case D:
DecodeTypeRegisterDRsType(instr);
break;
case W:
switch (instr->FunctionFieldRaw()) {
case MADD_D:
Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft");
case CVT_D_W: // Convert word to double.
Format(instr, "cvt.d.w 'fd, 'fs");
break;
default:
UNREACHABLE();
}
break;
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case JR:
Format(instr, "jr 'rs");
break;
case JALR:
Format(instr, "jalr 'rs");
break;
case SLL:
if (0x0 == static_cast<int>(instr->InstructionBits()))
Format(instr, "nop");
else
Format(instr, "sll 'rd, 'rt, 'sa");
break;
case DSLL:
Format(instr, "dsll 'rd, 'rt, 'sa");
break;
case D_MUL_MUH: // Equals to DMUL.
if (kArchVariant != kMips64r6) {
Format(instr, "dmult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "dmul 'rd, 'rs, 'rt");
} else {
Format(instr, "dmuh 'rd, 'rs, 'rt");
}
}
break;
case DSLL32:
Format(instr, "dsll32 'rd, 'rt, 'sa");
break;
case SRL:
if (instr->RsValue() == 0) {
Format(instr, "srl 'rd, 'rt, 'sa");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "rotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
}
break;
case DSRL:
if (instr->RsValue() == 0) {
Format(instr, "dsrl 'rd, 'rt, 'sa");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "drotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
}
break;
case DSRL32:
Format(instr, "dsrl32 'rd, 'rt, 'sa");
break;
case SRA:
Format(instr, "sra 'rd, 'rt, 'sa");
break;
case DSRA:
Format(instr, "dsra 'rd, 'rt, 'sa");
break;
case DSRA32:
Format(instr, "dsra32 'rd, 'rt, 'sa");
break;
case SLLV:
Format(instr, "sllv 'rd, 'rt, 'rs");
break;
case DSLLV:
Format(instr, "dsllv 'rd, 'rt, 'rs");
break;
case SRLV:
if (instr->SaValue() == 0) {
Format(instr, "srlv 'rd, 'rt, 'rs");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "rotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
}
break;
case DSRLV:
if (instr->SaValue() == 0) {
Format(instr, "dsrlv 'rd, 'rt, 'rs");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "drotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
}
break;
case SRAV:
Format(instr, "srav 'rd, 'rt, 'rs");
break;
case DSRAV:
Format(instr, "dsrav 'rd, 'rt, 'rs");
break;
case MFHI:
if (instr->Bits(25, 16) == 0) {
Format(instr, "mfhi 'rd");
} else {
if ((instr->FunctionFieldRaw() == CLZ_R6)
&& (instr->FdValue() == 1)) {
Format(instr, "clz 'rd, 'rs");
} else if ((instr->FunctionFieldRaw() == CLO_R6)
&& (instr->FdValue() == 1)) {
Format(instr, "clo 'rd, 'rs");
}
}
break;
case MFLO:
Format(instr, "mflo 'rd");
break;
case D_MUL_MUH_U: // Equals to DMULTU.
if (kArchVariant != kMips64r6) {
Format(instr, "dmultu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "dmulu 'rd, 'rs, 'rt");
} else {
Format(instr, "dmuhu 'rd, 'rs, 'rt");
}
}
break;
case MULT: // @Mips64r6 == MUL_MUH.
if (kArchVariant != kMips64r6) {
Format(instr, "mult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mul 'rd, 'rs, 'rt");
} else {
Format(instr, "muh 'rd, 'rs, 'rt");
}
}
break;
case MULTU: // @Mips64r6 == MUL_MUH_U.
if (kArchVariant != kMips64r6) {
Format(instr, "multu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mulu 'rd, 'rs, 'rt");
} else {
Format(instr, "muhu 'rd, 'rs, 'rt");
}
}
case L:
DecodeTypeRegisterLRsType(instr);
break;
default:
UNREACHABLE();
}
}
break;
case DIV: // @Mips64r6 == DIV_MOD.
if (kArchVariant != kMips64r6) {
Format(instr, "div 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "div 'rd, 'rs, 'rt");
} else {
Format(instr, "mod 'rd, 'rs, 'rt");
}
}
break;
case DDIV: // @Mips64r6 == D_DIV_MOD.
if (kArchVariant != kMips64r6) {
Format(instr, "ddiv 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "ddiv 'rd, 'rs, 'rt");
} else {
Format(instr, "dmod 'rd, 'rs, 'rt");
}
}
break;
case DIVU: // @Mips64r6 == DIV_MOD_U.
if (kArchVariant != kMips64r6) {
Format(instr, "divu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "divu 'rd, 'rs, 'rt");
} else {
Format(instr, "modu 'rd, 'rs, 'rt");
}
}
break;
case DDIVU: // @Mips64r6 == D_DIV_MOD_U.
if (kArchVariant != kMips64r6) {
Format(instr, "ddivu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "ddivu 'rd, 'rs, 'rt");
} else {
Format(instr, "dmodu 'rd, 'rs, 'rt");
}
}
break;
case ADD:
Format(instr, "add 'rd, 'rs, 'rt");
break;
case DADD:
Format(instr, "dadd 'rd, 'rs, 'rt");
break;
case ADDU:
Format(instr, "addu 'rd, 'rs, 'rt");
break;
case DADDU:
Format(instr, "daddu 'rd, 'rs, 'rt");
break;
case SUB:
Format(instr, "sub 'rd, 'rs, 'rt");
break;
case DSUB:
Format(instr, "dsub 'rd, 'rs, 'rt");
break;
case SUBU:
Format(instr, "subu 'rd, 'rs, 'rt");
break;
case DSUBU:
Format(instr, "dsubu 'rd, 'rs, 'rt");
break;
case AND:
Format(instr, "and 'rd, 'rs, 'rt");
break;
case OR:
if (0 == instr->RsValue()) {
Format(instr, "mov 'rd, 'rt");
} else if (0 == instr->RtValue()) {
Format(instr, "mov 'rd, 'rs");
} else {
Format(instr, "or 'rd, 'rs, 'rt");
}
break;
case XOR:
Format(instr, "xor 'rd, 'rs, 'rt");
break;
case NOR:
Format(instr, "nor 'rd, 'rs, 'rt");
break;
case SLT:
Format(instr, "slt 'rd, 'rs, 'rt");
break;
case SLTU:
Format(instr, "sltu 'rd, 'rs, 'rt");
break;
case BREAK:
return DecodeBreakInstr(instr);
case TGE:
Format(instr, "tge 'rs, 'rt, code: 'code");
break;
case TGEU:
Format(instr, "tgeu 'rs, 'rt, code: 'code");
break;
case TLT:
Format(instr, "tlt 'rs, 'rt, code: 'code");
break;
case TLTU:
Format(instr, "tltu 'rs, 'rt, code: 'code");
break;
case TEQ:
Format(instr, "teq 'rs, 'rt, code: 'code");
break;
case TNE:
Format(instr, "tne 'rs, 'rt, code: 'code");
break;
case MOVZ:
Format(instr, "movz 'rd, 'rs, 'rt");
break;
case MOVN:
Format(instr, "movn 'rd, 'rs, 'rt");
break;
case MOVCI:
if (instr->Bit(16)) {
Format(instr, "movt 'rd, 'rs, 'bc");
} else {
Format(instr, "movf 'rd, 'rs, 'bc");
}
break;
case SELEQZ_S:
Format(instr, "seleqz 'rd, 'rs, 'rt");
break;
case SELNEZ_S:
Format(instr, "selnez 'rd, 'rs, 'rt");
break;
default:
UNREACHABLE();
void Decoder::DecodeTypeRegisterCOP1X(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case MADD_D:
Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterSPECIAL(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case JR:
Format(instr, "jr 'rs");
break;
case JALR:
Format(instr, "jalr 'rs");
break;
case SLL:
if (0x0 == static_cast<int>(instr->InstructionBits()))
Format(instr, "nop");
else
Format(instr, "sll 'rd, 'rt, 'sa");
break;
case DSLL:
Format(instr, "dsll 'rd, 'rt, 'sa");
break;
case D_MUL_MUH: // Equals to DMUL.
if (kArchVariant != kMips64r6) {
Format(instr, "dmult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "dmul 'rd, 'rs, 'rt");
} else {
Format(instr, "dmuh 'rd, 'rs, 'rt");
}
}
break;
case SPECIAL2:
switch (instr->FunctionFieldRaw()) {
case MUL:
Format(instr, "mul 'rd, 'rs, 'rt");
break;
case CLZ:
if (kArchVariant != kMips64r6) {
Format(instr, "clz 'rd, 'rs");
}
break;
default:
UNREACHABLE();
case DSLL32:
Format(instr, "dsll32 'rd, 'rt, 'sa");
break;
case SRL:
if (instr->RsValue() == 0) {
Format(instr, "srl 'rd, 'rt, 'sa");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "rotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
}
break;
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS: {
Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
break;
case DSRL:
if (instr->RsValue() == 0) {
Format(instr, "dsrl 'rd, 'rt, 'sa");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "drotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
case EXT: {
Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
break;
}
break;
case DSRL32:
Format(instr, "dsrl32 'rd, 'rt, 'sa");
break;
case SRA:
Format(instr, "sra 'rd, 'rt, 'sa");
break;
case DSRA:
Format(instr, "dsra 'rd, 'rt, 'sa");
break;
case DSRA32:
Format(instr, "dsra32 'rd, 'rt, 'sa");
break;
case SLLV:
Format(instr, "sllv 'rd, 'rt, 'rs");
break;
case DSLLV:
Format(instr, "dsllv 'rd, 'rt, 'rs");
break;
case SRLV:
if (instr->SaValue() == 0) {
Format(instr, "srlv 'rd, 'rt, 'rs");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "rotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
case DEXT: {
Format(instr, "dext 'rt, 'rs, 'sa, 'ss1");
break;
}
break;
case DSRLV:
if (instr->SaValue() == 0) {
Format(instr, "dsrlv 'rd, 'rt, 'rs");
} else {
if (kArchVariant == kMips64r2) {
Format(instr, "drotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
}
break;
case SRAV:
Format(instr, "srav 'rd, 'rt, 'rs");
break;
case DSRAV:
Format(instr, "dsrav 'rd, 'rt, 'rs");
break;
case MFHI:
if (instr->Bits(25, 16) == 0) {
Format(instr, "mfhi 'rd");
} else {
if ((instr->FunctionFieldRaw() == CLZ_R6) && (instr->FdValue() == 1)) {
Format(instr, "clz 'rd, 'rs");
} else if ((instr->FunctionFieldRaw() == CLO_R6) &&
(instr->FdValue() == 1)) {
Format(instr, "clo 'rd, 'rs");
}
}
break;
case MFLO:
Format(instr, "mflo 'rd");
break;
case D_MUL_MUH_U: // Equals to DMULTU.
if (kArchVariant != kMips64r6) {
Format(instr, "dmultu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "dmulu 'rd, 'rs, 'rt");
} else {
Format(instr, "dmuhu 'rd, 'rs, 'rt");
}
}
break;
case MULT: // @Mips64r6 == MUL_MUH.
if (kArchVariant != kMips64r6) {
Format(instr, "mult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mul 'rd, 'rs, 'rt");
} else {
Format(instr, "muh 'rd, 'rs, 'rt");
}
}
break;
case MULTU: // @Mips64r6 == MUL_MUH_U.
if (kArchVariant != kMips64r6) {
Format(instr, "multu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mulu 'rd, 'rs, 'rt");
} else {
Format(instr, "muhu 'rd, 'rs, 'rt");
}
}
break;
case DIV: // @Mips64r6 == DIV_MOD.
if (kArchVariant != kMips64r6) {
Format(instr, "div 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "div 'rd, 'rs, 'rt");
} else {
Format(instr, "mod 'rd, 'rs, 'rt");
}
}
break;
case DDIV: // @Mips64r6 == D_DIV_MOD.
if (kArchVariant != kMips64r6) {
Format(instr, "ddiv 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "ddiv 'rd, 'rs, 'rt");
} else {
Format(instr, "dmod 'rd, 'rs, 'rt");
}
}
break;
case DIVU: // @Mips64r6 == DIV_MOD_U.
if (kArchVariant != kMips64r6) {
Format(instr, "divu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "divu 'rd, 'rs, 'rt");
} else {
Format(instr, "modu 'rd, 'rs, 'rt");
}
}
break;
case DDIVU: // @Mips64r6 == D_DIV_MOD_U.
if (kArchVariant != kMips64r6) {
Format(instr, "ddivu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "ddivu 'rd, 'rs, 'rt");
} else {
Format(instr, "dmodu 'rd, 'rs, 'rt");
}
}
break;
case ADD:
Format(instr, "add 'rd, 'rs, 'rt");
break;
case DADD:
Format(instr, "dadd 'rd, 'rs, 'rt");
break;
case ADDU:
Format(instr, "addu 'rd, 'rs, 'rt");
break;
case DADDU:
Format(instr, "daddu 'rd, 'rs, 'rt");
break;
case SUB:
Format(instr, "sub 'rd, 'rs, 'rt");
break;
case DSUB:
Format(instr, "dsub 'rd, 'rs, 'rt");
break;
case SUBU:
Format(instr, "subu 'rd, 'rs, 'rt");
break;
case DSUBU:
Format(instr, "dsubu 'rd, 'rs, 'rt");
break;
case AND:
Format(instr, "and 'rd, 'rs, 'rt");
break;
case OR:
if (0 == instr->RsValue()) {
Format(instr, "mov 'rd, 'rt");
} else if (0 == instr->RtValue()) {
Format(instr, "mov 'rd, 'rs");
} else {
Format(instr, "or 'rd, 'rs, 'rt");
}
break;
case XOR:
Format(instr, "xor 'rd, 'rs, 'rt");
break;
case NOR:
Format(instr, "nor 'rd, 'rs, 'rt");
break;
case SLT:
Format(instr, "slt 'rd, 'rs, 'rt");
break;
case SLTU:
Format(instr, "sltu 'rd, 'rs, 'rt");
break;
case TGE:
Format(instr, "tge 'rs, 'rt, code: 'code");
break;
case TGEU:
Format(instr, "tgeu 'rs, 'rt, code: 'code");
break;
case TLT:
Format(instr, "tlt 'rs, 'rt, code: 'code");
break;
case TLTU:
Format(instr, "tltu 'rs, 'rt, code: 'code");
break;
case TEQ:
Format(instr, "teq 'rs, 'rt, code: 'code");
break;
case TNE:
Format(instr, "tne 'rs, 'rt, code: 'code");
break;
case MOVZ:
Format(instr, "movz 'rd, 'rs, 'rt");
break;
case MOVN:
Format(instr, "movn 'rd, 'rs, 'rt");
break;
case MOVCI:
if (instr->Bit(16)) {
Format(instr, "movt 'rd, 'rs, 'bc");
} else {
Format(instr, "movf 'rd, 'rs, 'bc");
}
break;
case SELEQZ_S:
Format(instr, "seleqz 'rs, 'rt, 'rd");
break;
case SELNEZ_S:
Format(instr, "selnez 'rs, 'rt, 'rd");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterSPECIAL2(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case MUL:
Format(instr, "mul 'rd, 'rs, 'rt");
break;
case CLZ:
if (kArchVariant != kMips64r6) {
Format(instr, "clz 'rd, 'rs");
}
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeRegisterSPECIAL3(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case INS: {
Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
break;
}
case EXT: {
Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
break;
}
case DEXT: {
Format(instr, "dext 'rt, 'rs, 'sa, 'ss1");
break;
}
default:
UNREACHABLE();
}
}
int Decoder::DecodeTypeRegister(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case COP1: // Coprocessor instructions.
DecodeTypeRegisterCOP1(instr);
break;
case COP1X:
DecodeTypeRegisterCOP1X(instr);
break;
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case BREAK:
return DecodeBreakInstr(instr);
default:
UNREACHABLE();
DecodeTypeRegisterSPECIAL(instr);
break;
}
break;
case SPECIAL2:
DecodeTypeRegisterSPECIAL2(instr);
break;
case SPECIAL3:
DecodeTypeRegisterSPECIAL3(instr);
break;
default:
UNREACHABLE();
}
......@@ -978,187 +1037,216 @@ int Decoder::DecodeTypeRegister(Instruction* instr) {
}
void Decoder::DecodeTypeImmediateCOP1D(Instruction* instr) {
switch (instr->FunctionValue()) {
case SEL:
Format(instr, "sel.D 'ft, 'fs, 'fd");
break;
case SELEQZ_C:
Format(instr, "seleqz.D 'ft, 'fs, 'fd");
break;
case SELNEZ_C:
Format(instr, "selnez.D 'ft, 'fs, 'fd");
break;
case MIN:
Format(instr, "min.D 'ft, 'fs, 'fd");
break;
case MINA:
Format(instr, "mina.D 'ft, 'fs, 'fd");
break;
case MAX:
Format(instr, "max.D 'ft, 'fs, 'fd");
break;
case MAXA:
Format(instr, "maxa.D 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeImmediateCOP1L(Instruction* instr) {
switch (instr->FunctionValue()) {
case CMP_AF:
Format(instr, "cmp.af.D 'ft, 'fs, 'fd");
break;
case CMP_UN:
Format(instr, "cmp.un.D 'ft, 'fs, 'fd");
break;
case CMP_EQ:
Format(instr, "cmp.eq.D 'ft, 'fs, 'fd");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd");
break;
case CMP_LT:
Format(instr, "cmp.lt.D 'ft, 'fs, 'fd");
break;
case CMP_ULT:
Format(instr, "cmp.ult.D 'ft, 'fs, 'fd");
break;
case CMP_LE:
Format(instr, "cmp.le.D 'ft, 'fs, 'fd");
break;
case CMP_ULE:
Format(instr, "cmp.ule.D 'ft, 'fs, 'fd");
break;
case CMP_OR:
Format(instr, "cmp.or.D 'ft, 'fs, 'fd");
break;
case CMP_UNE:
Format(instr, "cmp.une.D 'ft, 'fs, 'fd");
break;
case CMP_NE:
Format(instr, "cmp.ne.D 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeImmediateCOP1S(Instruction* instr) {
switch (instr->FunctionValue()) {
case SEL:
Format(instr, "sel.S 'ft, 'fs, 'fd");
break;
case SELEQZ_C:
Format(instr, "seleqz.S 'ft, 'fs, 'fd");
break;
case SELNEZ_C:
Format(instr, "selnez.S 'ft, 'fs, 'fd");
break;
case MIN:
Format(instr, "min.S 'ft, 'fs, 'fd");
break;
case MINA:
Format(instr, "mina.S 'ft, 'fs, 'fd");
break;
case MAX:
Format(instr, "max.S 'ft, 'fs, 'fd");
break;
case MAXA:
Format(instr, "maxa.S 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeImmediateCOP1W(Instruction* instr) {
switch (instr->FunctionValue()) {
case CMP_AF:
Format(instr, "cmp.af.S 'ft, 'fs, 'fd");
break;
case CMP_UN:
Format(instr, "cmp.un.S 'ft, 'fs, 'fd");
break;
case CMP_EQ:
Format(instr, "cmp.eq.S 'ft, 'fs, 'fd");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd");
break;
case CMP_LT:
Format(instr, "cmp.lt.S 'ft, 'fs, 'fd");
break;
case CMP_ULT:
Format(instr, "cmp.ult.S 'ft, 'fs, 'fd");
break;
case CMP_LE:
Format(instr, "cmp.le.S 'ft, 'fs, 'fd");
break;
case CMP_ULE:
Format(instr, "cmp.ule.S 'ft, 'fs, 'fd");
break;
case CMP_OR:
Format(instr, "cmp.or.S 'ft, 'fs, 'fd");
break;
case CMP_UNE:
Format(instr, "cmp.une.S 'ft, 'fs, 'fd");
break;
case CMP_NE:
Format(instr, "cmp.ne.S 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeImmediateCOP1(Instruction* instr) {
switch (instr->RsFieldRaw()) {
case BC1:
if (instr->FBtrueValue()) {
Format(instr, "bc1t 'bc, 'imm16u");
} else {
Format(instr, "bc1f 'bc, 'imm16u");
}
break;
case BC1EQZ:
Format(instr, "bc1eqz 'ft, 'imm16u");
break;
case BC1NEZ:
Format(instr, "bc1nez 'ft, 'imm16u");
break;
case W: // CMP.S instruction.
DecodeTypeImmediateCOP1W(instr);
break;
case L: // CMP.D instruction.
DecodeTypeImmediateCOP1L(instr);
break;
case S:
DecodeTypeImmediateCOP1S(instr);
break;
case D:
DecodeTypeImmediateCOP1D(instr);
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeImmediateREGIMM(Instruction* instr) {
switch (instr->RtFieldRaw()) {
case BLTZ:
Format(instr, "bltz 'rs, 'imm16u");
break;
case BLTZAL:
Format(instr, "bltzal 'rs, 'imm16u");
break;
case BGEZ:
Format(instr, "bgez 'rs, 'imm16u");
break;
case BGEZAL:
Format(instr, "bgezal 'rs, 'imm16u");
break;
case BGEZALL:
Format(instr, "bgezall 'rs, 'imm16u");
break;
case DAHI:
Format(instr, "dahi 'rs, 'imm16u");
break;
case DATI:
Format(instr, "dati 'rs, 'imm16u");
break;
default:
UNREACHABLE();
}
}
void Decoder::DecodeTypeImmediate(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case COP1:
switch (instr->RsFieldRaw()) {
case BC1:
if (instr->FBtrueValue()) {
Format(instr, "bc1t 'bc, 'imm16u");
} else {
Format(instr, "bc1f 'bc, 'imm16u");
}
break;
case BC1EQZ:
Format(instr, "bc1eqz 'ft, 'imm16u");
break;
case BC1NEZ:
Format(instr, "bc1nez 'ft, 'imm16u");
break;
case W: // CMP.S instruction.
switch (instr->FunctionValue()) {
case CMP_AF:
Format(instr, "cmp.af.S 'ft, 'fs, 'fd");
break;
case CMP_UN:
Format(instr, "cmp.un.S 'ft, 'fs, 'fd");
break;
case CMP_EQ:
Format(instr, "cmp.eq.S 'ft, 'fs, 'fd");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd");
break;
case CMP_LT:
Format(instr, "cmp.lt.S 'ft, 'fs, 'fd");
break;
case CMP_ULT:
Format(instr, "cmp.ult.S 'ft, 'fs, 'fd");
break;
case CMP_LE:
Format(instr, "cmp.le.S 'ft, 'fs, 'fd");
break;
case CMP_ULE:
Format(instr, "cmp.ule.S 'ft, 'fs, 'fd");
break;
case CMP_OR:
Format(instr, "cmp.or.S 'ft, 'fs, 'fd");
break;
case CMP_UNE:
Format(instr, "cmp.une.S 'ft, 'fs, 'fd");
break;
case CMP_NE:
Format(instr, "cmp.ne.S 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
case L: // CMP.D instruction.
switch (instr->FunctionValue()) {
case CMP_AF:
Format(instr, "cmp.af.D 'ft, 'fs, 'fd");
break;
case CMP_UN:
Format(instr, "cmp.un.D 'ft, 'fs, 'fd");
break;
case CMP_EQ:
Format(instr, "cmp.eq.D 'ft, 'fs, 'fd");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd");
break;
case CMP_LT:
Format(instr, "cmp.lt.D 'ft, 'fs, 'fd");
break;
case CMP_ULT:
Format(instr, "cmp.ult.D 'ft, 'fs, 'fd");
break;
case CMP_LE:
Format(instr, "cmp.le.D 'ft, 'fs, 'fd");
break;
case CMP_ULE:
Format(instr, "cmp.ule.D 'ft, 'fs, 'fd");
break;
case CMP_OR:
Format(instr, "cmp.or.D 'ft, 'fs, 'fd");
break;
case CMP_UNE:
Format(instr, "cmp.une.D 'ft, 'fs, 'fd");
break;
case CMP_NE:
Format(instr, "cmp.ne.D 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
case S:
switch (instr->FunctionValue()) {
case SEL:
Format(instr, "sel.S 'ft, 'fs, 'fd");
break;
case SELEQZ_C:
Format(instr, "seleqz.S 'ft, 'fs, 'fd");
break;
case SELNEZ_C:
Format(instr, "selnez.S 'ft, 'fs, 'fd");
break;
case MIN:
Format(instr, "min.S 'ft, 'fs, 'fd");
break;
case MINA:
Format(instr, "mina.S 'ft, 'fs, 'fd");
break;
case MAX:
Format(instr, "max.S 'ft, 'fs, 'fd");
break;
case MAXA:
Format(instr, "maxa.S 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
case D:
switch (instr->FunctionValue()) {
case SEL:
Format(instr, "sel.D 'ft, 'fs, 'fd");
break;
case SELEQZ_C:
Format(instr, "seleqz.D 'ft, 'fs, 'fd");
break;
case SELNEZ_C:
Format(instr, "selnez.D 'ft, 'fs, 'fd");
break;
case MIN:
Format(instr, "min.D 'ft, 'fs, 'fd");
break;
case MINA:
Format(instr, "mina.D 'ft, 'fs, 'fd");
break;
case MAX:
Format(instr, "max.D 'ft, 'fs, 'fd");
break;
case MAXA:
Format(instr, "maxa.D 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
default:
UNREACHABLE();
}
DecodeTypeImmediateCOP1(instr);
break; // Case COP1.
// ------------- REGIMM class.
case REGIMM:
switch (instr->RtFieldRaw()) {
case BLTZ:
Format(instr, "bltz 'rs, 'imm16u");
break;
case BLTZAL:
Format(instr, "bltzal 'rs, 'imm16u");
break;
case BGEZ:
Format(instr, "bgez 'rs, 'imm16u");
break;
case BGEZAL:
Format(instr, "bgezal 'rs, 'imm16u");
break;
case BGEZALL:
Format(instr, "bgezall 'rs, 'imm16u");
break;
case DAHI:
Format(instr, "dahi 'rs, 'imm16u");
break;
case DATI:
Format(instr, "dati 'rs, 'imm16u");
break;
default:
UNREACHABLE();
}
break; // Case REGIMM.
// ------------- Branch instructions.
case BEQ:
......
......@@ -2206,6 +2206,9 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case DDIVU:
// div and divu never raise exceptions.
break;
case SELEQZ_S:
case SELNEZ_S:
break;
default:
UNREACHABLE();
}
......@@ -2267,6 +2270,561 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
}
void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
const int32_t& fs_reg,
const int64_t& fd_reg) {
float f;
switch (instr->FunctionFieldRaw()) {
case CVT_D_S:
f = get_fpu_register_float(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(f));
break;
default:
// CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
// CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
const int32_t& fs_reg,
const int64_t& ft_reg,
const int32_t& fd_reg) {
double ft, fs;
uint32_t cc, fcsr_cc;
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
int64_t ft_int = static_cast<int64_t>(ft);
switch (instr->FunctionFieldRaw()) {
case SELEQZ_C:
DCHECK(kArchVariant == kMips64r6);
set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0);
break;
case SELNEZ_C:
DCHECK(kArchVariant == kMips64r6);
set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0);
break;
case ADD_D:
set_fpu_register_double(fd_reg, fs + ft);
break;
case SUB_D:
set_fpu_register_double(fd_reg, fs - ft);
break;
case MUL_D:
set_fpu_register_double(fd_reg, fs * ft);
break;
case DIV_D:
set_fpu_register_double(fd_reg, fs / ft);
break;
case ABS_D:
set_fpu_register_double(fd_reg, fabs(fs));
break;
case MOV_D:
set_fpu_register_double(fd_reg, fs);
break;
case NEG_D:
set_fpu_register_double(fd_reg, -fs);
break;
case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs));
break;
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
case C_EQ_D:
set_fcsr_bit(fcsr_cc, (fs == ft));
break;
case C_UEQ_D:
set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLT_D:
set_fcsr_bit(fcsr_cc, (fs < ft));
break;
case C_ULT_D:
set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft));
break;
case C_ULE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case CVT_W_D: // Convert double to word.
// Rounding modes are not yet supported.
DCHECK((FCSR_ & 3) == 0);
// In rounding mode 0 it should behave like ROUND.
// No break.
case ROUND_W_D: // Round double to word (round half to even).
{
double rounded = std::floor(fs + 0.5);
int32_t result = static_cast<int32_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
} break;
case TRUNC_W_D: // Truncate double to word (round towards 0).
{
double rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
} break;
case FLOOR_W_D: // Round double to word towards negative infinity.
{
double rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
} break;
case CEIL_W_D: // Round double to word towards positive infinity.
{
double rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
} break;
case CVT_S_D: // Convert double to float (single).
set_fpu_register_float(fd_reg, static_cast<float>(fs));
break;
case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word.
// Rounding modes are not yet supported.
DCHECK((FCSR_ & 3) == 0);
// In rounding mode 0 it should behave like ROUND.
// No break.
case ROUND_L_D: { // Mips64r2 instruction.
// check error cases
double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case TRUNC_L_D: { // Mips64r2 instruction.
double rounded = trunc(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case FLOOR_L_D: { // Mips64r2 instruction.
double rounded = floor(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case CEIL_L_D: { // Mips64r2 instruction.
double rounded = ceil(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case C_F_D:
UNIMPLEMENTED_MIPS();
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterWRsType(Instruction* instr,
const int32_t& fs_reg,
const int32_t& fd_reg,
int64_t& alu_out) {
switch (instr->FunctionFieldRaw()) {
case CVT_S_W: // Convert word to float (single).
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
break;
case CVT_D_W: // Convert word to double.
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
break;
default: // Mips64r6 CMP.S instructions unimplemented.
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterLRsType(Instruction* instr,
const int32_t& fs_reg,
const int32_t& fd_reg,
const int32_t& ft_reg) {
double fs = get_fpu_register_double(fs_reg);
double ft = get_fpu_register_double(ft_reg);
int64_t i64;
switch (instr->FunctionFieldRaw()) {
case CVT_D_L: // Mips32r2 instruction.
i64 = get_fpu_register(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(i64));
break;
case CVT_S_L:
UNIMPLEMENTED_MIPS();
break;
case CMP_AF: // Mips64r6 CMP.D instructions.
UNIMPLEMENTED_MIPS();
break;
case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_EQ:
if (fs == ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LT:
if (fs < ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LE:
if (fs <= ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterCOP1(
Instruction* instr, const int64_t& rs_reg, const int64_t& rs,
const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt,
const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg,
int64_t& alu_out) {
switch (instr->RsFieldRaw()) {
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
UNREACHABLE();
break;
case CFC1:
set_register(rt_reg, alu_out);
break;
case MFC1:
case DMFC1:
case MFHC1:
set_register(rt_reg, alu_out);
break;
case CTC1:
// At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister);
FCSR_ = registers_[rt_reg];
break;
case MTC1:
// Hardware writes upper 32-bits to zero on mtc1.
set_fpu_register_hi_word(fs_reg, 0);
set_fpu_register_word(fs_reg, registers_[rt_reg]);
break;
case DMTC1:
set_fpu_register(fs_reg, registers_[rt_reg]);
break;
case MTHC1:
set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
break;
case S:
DecodeTypeRegisterSRsType(instr, fs_reg, fd_reg);
break;
case D:
DecodeTypeRegisterDRsType(instr, fs_reg, ft_reg, fd_reg);
break;
case W:
DecodeTypeRegisterWRsType(instr, fs_reg, fd_reg, alu_out);
break;
case L:
DecodeTypeRegisterLRsType(instr, fs_reg, fd_reg, ft_reg);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr,
const int32_t& fr_reg,
const int32_t& fs_reg,
const int32_t& ft_reg,
const int64_t& fd_reg) {
switch (instr->FunctionFieldRaw()) {
case MADD_D:
double fr, ft, fs;
fr = get_fpu_register_double(fr_reg);
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
set_fpu_register_double(fd_reg, fs * ft + fr);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegisterSPECIAL(
Instruction* instr, const int64_t& rs_reg, const int64_t& rs,
const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt,
const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg,
int64_t& i64hilo, uint64_t& u64hilo, int64_t& alu_out, bool& do_interrupt,
int64_t& current_pc, int64_t& next_pc, int64_t& return_addr_reg,
int64_t& i128resultH, int64_t& i128resultL) {
switch (instr->FunctionFieldRaw()) {
case SELEQZ_S:
DCHECK(kArchVariant == kMips64r6);
set_register(rd_reg, rt == 0 ? rs : 0);
break;
case SELNEZ_S:
DCHECK(kArchVariant == kMips64r6);
set_register(rd_reg, rt != 0 ? rs : 0);
break;
case JR: {
Instruction* branch_delay_instr =
reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr);
set_pc(next_pc);
pc_modified_ = true;
break;
}
case JALR: {
Instruction* branch_delay_instr =
reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr);
set_register(return_addr_reg, current_pc + 2 * Instruction::kInstrSize);
set_pc(next_pc);
pc_modified_ = true;
break;
}
// Instructions using HI and LO registers.
case MULT:
if (kArchVariant != kMips64r6) {
set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(i64hilo >> 32));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg, static_cast<int32_t>(i64hilo & 0xffffffff));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case MULTU:
set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(u64hilo >> 32));
break;
case DMULT: // DMULT == D_MUL_MUH.
if (kArchVariant != kMips64r6) {
set_register(LO, static_cast<int64_t>(i128resultL));
set_register(HI, static_cast<int64_t>(i128resultH));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg, static_cast<int64_t>(i128resultL));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int64_t>(i128resultH));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case DMULTU:
UNIMPLEMENTED_MIPS();
break;
case DSLL:
set_register(rd_reg, alu_out);
break;
case DIV:
case DDIV:
switch (kArchVariant) {
case kMips64r2:
// Divide by zero and overflow was not checked in the
// configuration step - div and divu do not raise exceptions. On
// division by 0 the result will be UNPREDICTABLE. On overflow
// (INT_MIN/-1), return INT_MIN which is what the hardware does.
if (rs == INT_MIN && rt == -1) {
set_register(LO, INT_MIN);
set_register(HI, 0);
} else if (rt != 0) {
set_register(LO, rs / rt);
set_register(HI, rs % rt);
}
break;
case kMips64r6:
switch (instr->SaValue()) {
case DIV_OP:
if (rs == INT_MIN && rt == -1) {
set_register(rd_reg, INT_MIN);
} else if (rt != 0) {
set_register(rd_reg, rs / rt);
}
break;
case MOD_OP:
if (rs == INT_MIN && rt == -1) {
set_register(rd_reg, 0);
} else if (rt != 0) {
set_register(rd_reg, rs % rt);
}
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
break;
default:
break;
}
break;
case DIVU:
if (rt_u != 0) {
set_register(LO, rs_u / rt_u);
set_register(HI, rs_u % rt_u);
}
break;
// Break and trap instructions.
case BREAK:
case TGE:
case TGEU:
case TLT:
case TLTU:
case TEQ:
case TNE:
if (do_interrupt) {
SoftwareInterrupt(instr);
}
break;
// Conditional moves.
case MOVN:
if (rt) {
set_register(rd_reg, rs);
TraceRegWr(rs);
}
break;
case MOVCI: {
uint32_t cc = instr->FBccValue();
uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
if (instr->Bit(16)) { // Read Tf bit.
if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
} else {
if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
}
break;
}
case MOVZ:
if (!rt) {
set_register(rd_reg, rs);
TraceRegWr(rs);
}
break;
default: // For other special opcodes we do the default operation.
set_register(rd_reg, alu_out);
TraceRegWr(alu_out);
}
}
void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr,
const int64_t& rd_reg,
int64_t& alu_out) {
switch (instr->FunctionFieldRaw()) {
case MUL:
set_register(rd_reg, alu_out);
TraceRegWr(alu_out);
// HI and LO are UNPREDICTABLE after the operation.
set_register(LO, Unpredictable);
set_register(HI, Unpredictable);
break;
default: // For other special2 opcodes we do the default operation.
set_register(rd_reg, alu_out);
}
}
void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr,
const int64_t& rt_reg,
int64_t& alu_out) {
switch (instr->FunctionFieldRaw()) {
case INS:
// Ins instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
TraceRegWr(alu_out);
break;
case EXT:
case DEXT:
// Dext/Ext instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
TraceRegWr(alu_out);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeRegister(Instruction* instr) {
// Instruction fields.
const Opcode op = instr->OpcodeFieldRaw();
......@@ -2320,490 +2878,23 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
// ---------- Execution.
switch (op) {
case COP1:
switch (instr->RsFieldRaw()) {
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
UNREACHABLE();
break;
case CFC1:
set_register(rt_reg, alu_out);
break;
case MFC1:
case DMFC1:
case MFHC1:
set_register(rt_reg, alu_out);
break;
case CTC1:
// At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister);
FCSR_ = registers_[rt_reg];
break;
case MTC1:
// Hardware writes upper 32-bits to zero on mtc1.
set_fpu_register_hi_word(fs_reg, 0);
set_fpu_register_word(fs_reg, registers_[rt_reg]);
break;
case DMTC1:
set_fpu_register(fs_reg, registers_[rt_reg]);
break;
case MTHC1:
set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
break;
case S:
float f;
switch (instr->FunctionFieldRaw()) {
case CVT_D_S:
f = get_fpu_register_float(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(f));
break;
default:
// CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
// CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
UNREACHABLE();
}
break;
case D:
double ft, fs;
uint32_t cc, fcsr_cc;
int64_t i64;
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) {
case ADD_D:
set_fpu_register_double(fd_reg, fs + ft);
break;
case SUB_D:
set_fpu_register_double(fd_reg, fs - ft);
break;
case MUL_D:
set_fpu_register_double(fd_reg, fs * ft);
break;
case DIV_D:
set_fpu_register_double(fd_reg, fs / ft);
break;
case ABS_D:
set_fpu_register_double(fd_reg, fabs(fs));
break;
case MOV_D:
set_fpu_register_double(fd_reg, fs);
break;
case NEG_D:
set_fpu_register_double(fd_reg, -fs);
break;
case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs));
break;
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
case C_EQ_D:
set_fcsr_bit(fcsr_cc, (fs == ft));
break;
case C_UEQ_D:
set_fcsr_bit(fcsr_cc,
(fs == ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLT_D:
set_fcsr_bit(fcsr_cc, (fs < ft));
break;
case C_ULT_D:
set_fcsr_bit(fcsr_cc,
(fs < ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case C_OLE_D:
set_fcsr_bit(fcsr_cc, (fs <= ft));
break;
case C_ULE_D:
set_fcsr_bit(fcsr_cc,
(fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
break;
case CVT_W_D: // Convert double to word.
// Rounding modes are not yet supported.
DCHECK((FCSR_ & 3) == 0);
// In rounding mode 0 it should behave like ROUND.
// No break.
case ROUND_W_D: // Round double to word (round half to even).
{
double rounded = std::floor(fs + 0.5);
int32_t result = static_cast<int32_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
}
break;
case TRUNC_W_D: // Truncate double to word (round towards 0).
{
double rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
}
break;
case FLOOR_W_D: // Round double to word towards negative infinity.
{
double rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
}
break;
case CEIL_W_D: // Round double to word towards positive infinity.
{
double rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
}
break;
case CVT_S_D: // Convert double to float (single).
set_fpu_register_float(fd_reg, static_cast<float>(fs));
break;
case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word.
// Rounding modes are not yet supported.
DCHECK((FCSR_ & 3) == 0);
// In rounding mode 0 it should behave like ROUND.
// No break.
case ROUND_L_D: { // Mips64r2 instruction.
// check error cases
double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case TRUNC_L_D: { // Mips64r2 instruction.
double rounded = trunc(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case FLOOR_L_D: { // Mips64r2 instruction.
double rounded = floor(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case CEIL_L_D: { // Mips64r2 instruction.
double rounded = ceil(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case C_F_D:
UNIMPLEMENTED_MIPS();
break;
default:
UNREACHABLE();
}
break;
case W:
switch (instr->FunctionFieldRaw()) {
case CVT_S_W: // Convert word to float (single).
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
break;
case CVT_D_W: // Convert word to double.
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
break;
default: // Mips64r6 CMP.S instructions unimplemented.
UNREACHABLE();
}
break;
case L:
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
switch (instr->FunctionFieldRaw()) {
case CVT_D_L: // Mips32r2 instruction.
i64 = get_fpu_register(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(i64));
break;
case CVT_S_L:
UNIMPLEMENTED_MIPS();
break;
case CMP_AF: // Mips64r6 CMP.D instructions.
UNIMPLEMENTED_MIPS();
break;
case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_EQ:
if (fs == ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LT:
if (fs < ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LE:
if (fs <= ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED
UNREACHABLE();
}
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg,
fr_reg, fs_reg, ft_reg, fd_reg, alu_out);
break;
case COP1X:
switch (instr->FunctionFieldRaw()) {
case MADD_D:
double fr, ft, fs;
fr = get_fpu_register_double(fr_reg);
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
set_fpu_register_double(fd_reg, fs * ft + fr);
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg);
break;
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case JR: {
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
current_pc+Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr);
set_pc(next_pc);
pc_modified_ = true;
break;
}
case JALR: {
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
current_pc+Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr);
set_register(return_addr_reg,
current_pc + 2 * Instruction::kInstrSize);
set_pc(next_pc);
pc_modified_ = true;
break;
}
// Instructions using HI and LO registers.
case MULT:
if (kArchVariant != kMips64r6) {
set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(i64hilo >> 32));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg,
static_cast<int32_t>(i64hilo & 0xffffffff));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case MULTU:
set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(u64hilo >> 32));
break;
case DMULT: // DMULT == D_MUL_MUH.
if (kArchVariant != kMips64r6) {
set_register(LO, static_cast<int64_t>(i128resultL));
set_register(HI, static_cast<int64_t>(i128resultH));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg, static_cast<int64_t>(i128resultL));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int64_t>(i128resultH));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case DMULTU:
UNIMPLEMENTED_MIPS();
break;
case DSLL:
set_register(rd_reg, alu_out);
break;
case DIV:
case DDIV:
switch (kArchVariant) {
case kMips64r2:
// Divide by zero and overflow was not checked in the
// configuration step - div and divu do not raise exceptions. On
// division by 0 the result will be UNPREDICTABLE. On overflow
// (INT_MIN/-1), return INT_MIN which is what the hardware does.
if (rs == INT_MIN && rt == -1) {
set_register(LO, INT_MIN);
set_register(HI, 0);
} else if (rt != 0) {
set_register(LO, rs / rt);
set_register(HI, rs % rt);
}
break;
case kMips64r6:
switch (instr->SaValue()) {
case DIV_OP:
if (rs == INT_MIN && rt == -1) {
set_register(rd_reg, INT_MIN);
} else if (rt != 0) {
set_register(rd_reg, rs / rt);
}
break;
case MOD_OP:
if (rs == INT_MIN && rt == -1) {
set_register(rd_reg, 0);
} else if (rt != 0) {
set_register(rd_reg, rs % rt);
}
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
break;
default:
break;
}
break;
case DIVU:
if (rt_u != 0) {
set_register(LO, rs_u / rt_u);
set_register(HI, rs_u % rt_u);
}
break;
// Break and trap instructions.
case BREAK:
case TGE:
case TGEU:
case TLT:
case TLTU:
case TEQ:
case TNE:
if (do_interrupt) {
SoftwareInterrupt(instr);
}
break;
// Conditional moves.
case MOVN:
if (rt) {
set_register(rd_reg, rs);
TraceRegWr(rs);
}
break;
case MOVCI: {
uint32_t cc = instr->FBccValue();
uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
if (instr->Bit(16)) { // Read Tf bit.
if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
} else {
if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
}
break;
}
case MOVZ:
if (!rt) {
set_register(rd_reg, rs);
TraceRegWr(rs);
}
break;
default: // For other special opcodes we do the default operation.
set_register(rd_reg, alu_out);
TraceRegWr(alu_out);
}
DecodeTypeRegisterSPECIAL(
instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, fr_reg, fs_reg,
ft_reg, fd_reg, i64hilo, u64hilo, alu_out, do_interrupt, current_pc,
next_pc, return_addr_reg, i128resultH, i128resultL);
break;
case SPECIAL2:
switch (instr->FunctionFieldRaw()) {
case MUL:
set_register(rd_reg, alu_out);
TraceRegWr(alu_out);
// HI and LO are UNPREDICTABLE after the operation.
set_register(LO, Unpredictable);
set_register(HI, Unpredictable);
break;
default: // For other special2 opcodes we do the default operation.
set_register(rd_reg, alu_out);
}
DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out);
break;
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS:
// Ins instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
TraceRegWr(alu_out);
break;
case EXT:
case DEXT:
// Dext/Ext instr leaves result in Rt, rather than Rd.
set_register(rt_reg, alu_out);
TraceRegWr(alu_out);
break;
default:
UNREACHABLE();
}
DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out);
break;
// Unimplemented opcodes raised an error in the configuration step before,
// so we can use the default here to set the destination register in common
......
......@@ -312,6 +312,45 @@ class Simulator {
inline int32_t SetDoubleHIW(double* addr);
inline int32_t SetDoubleLOW(double* addr);
// functions called from DecodeTypeRegister
void DecodeTypeRegisterCOP1(Instruction* instr, const int64_t& rs_reg,
const int64_t& rs, const uint64_t& rs_u,
const int64_t& rt_reg, const int64_t& rt,
const uint64_t& rt_u, const int64_t& rd_reg,
const int32_t& fr_reg, const int32_t& fs_reg,
const int32_t& ft_reg, const int64_t& fd_reg,
int64_t& alu_out);
void DecodeTypeRegisterCOP1X(Instruction* instr, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg,
const int64_t& fd_reg);
void DecodeTypeRegisterSPECIAL(
Instruction* instr, const int64_t& rs_reg, const int64_t& rs,
const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt,
const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg,
const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg,
int64_t& i64hilo, uint64_t& u64hilo, int64_t& alu_out, bool& do_interrupt,
int64_t& current_pc, int64_t& next_pc, int64_t& return_addr_reg,
int64_t& i128resultH, int64_t& i128resultL);
void DecodeTypeRegisterSPECIAL2(Instruction* instr, const int64_t& rd_reg,
int64_t& alu_out);
void DecodeTypeRegisterSPECIAL3(Instruction* instr, const int64_t& rt_reg,
int64_t& alu_out);
void DecodeTypeRegisterSRsType(Instruction* instr, const int32_t& fs_reg,
const int64_t& fd_reg);
void DecodeTypeRegisterDRsType(Instruction* instr, const int32_t& fs_reg,
const int64_t& ft_reg, const int32_t& fd_reg);
void DecodeTypeRegisterWRsType(Instruction* instr, const int32_t& fs_reg,
const int32_t& fd_reg, int64_t& alu_out);
void DecodeTypeRegisterLRsType(Instruction* instr, const int32_t& fs_reg,
const int32_t& fd_reg, const int32_t& ft_reg);
// Executing is handled based on the instruction type.
void DecodeTypeRegister(Instruction* instr);
......
......@@ -48,6 +48,93 @@ typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4);
#define __ assm.
TEST(MIPS16) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, NULL, 0);
typedef struct test {
int a;
int b;
int c;
int d;
double e;
double f;
double g;
double h;
double i;
double j;
double k;
double l;
} Test;
Test test;
// integer part of test
__ addiu(t1, zero_reg, 1); // t1=1
__ seleqz(t1, zero_reg, t3); // t3=1
__ sw(t3, MemOperand(a0, OFFSET_OF(Test, a))); // a=1
__ seleqz(t1, t1, t2); // t2=0
__ sw(t2, MemOperand(a0, OFFSET_OF(Test, b))); // b=0
__ selnez(t1, zero_reg, t3); // t3=1;
__ sw(t3, MemOperand(a0, OFFSET_OF(Test, c))); // c=0
__ selnez(t1, t1, t3); // t3=1
__ sw(t3, MemOperand(a0, OFFSET_OF(Test, d))); // d=1
// floating point part of test S format
__ li(t0, 0x80);
__ mtc1(t0, f4);
__ cvt_d_w(f4, f4); // f4=0x80
__ li(t0, 0xf3);
__ mtc1(t0, f6);
__ cvt_d_w(f6, f6); // f6=0xf3
__ seleqz(S, f8, f4, f6); // f8=0xf3
__ seleqz(S, f10, f6, f6); // f10=0
__ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, e))); // e=0xf3
__ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, f))); // f=0
__ selnez(S, f8, f4, f6); // f8=0
__ selnez(S, f10, f6, f6); // f10=0xf3*/
__ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, g))); // g=0
__ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, h))); // h=0xf3
__ li(t0, 0x80);
__ mtc1(t0, f4);
__ cvt_d_w(f4, f4); // f4=0x80
__ li(t0, 0xf3);
__ mtc1(t0, f6);
__ cvt_d_w(f6, f6); // f6=0xf3
__ seleqz(D, f8, f4, f6); // f8=0xf3
__ seleqz(D, f10, f6, f6); // f10=0
__ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, i))); // i=0xf3
__ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, j))); // j=0
__ selnez(S, f8, f4, f6); // f8=0
__ selnez(S, f10, f6, f6); // f10=0xf3*/
__ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, k))); // k=0
__ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, l))); // l=0xf3
__ jr(ra);
__ nop();
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
F3 f = FUNCTION_CAST<F3>(code->entry());
(CALL_GENERATED_CODE(f, &test, 0, 0, 0, 0));
CHECK_EQ(test.a, 1);
CHECK_EQ(test.b, 0);
CHECK_EQ(test.c, 0);
CHECK_EQ(test.d, 1);
CHECK_EQ(test.e, 0xf3);
CHECK_EQ(test.f, 0x0);
CHECK_EQ(test.g, 0);
CHECK_EQ(test.h, 0xf3);
CHECK_EQ(test.i, 0xf3);
CHECK_EQ(test.j, 0x0);
CHECK_EQ(test.k, 0);
CHECK_EQ(test.l, 0xf3);
}
TEST(MIPS0) {
......
......@@ -48,6 +48,72 @@ typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4);
#define __ assm.
TEST(MIPS17) {
if (kArchVariant == kMips64r6) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, NULL, 0);
typedef struct test {
int a;
int b;
int c;
int d;
double i;
double j;
double k;
double l;
} Test;
Test test;
// integer part of test
__ addiu(t1, zero_reg, 1); // t1=1
__ seleqz(t1, zero_reg, t3); // t3=1
__ sw(t3, MemOperand(a0, OFFSET_OF(Test, a))); // a=1
__ seleqz(t1, t1, t2); // t2=0
__ sw(t2, MemOperand(a0, OFFSET_OF(Test, b))); // b=0
__ selnez(t1, zero_reg, t3); // t3=1;
__ sw(t3, MemOperand(a0, OFFSET_OF(Test, c))); // c=0
__ selnez(t1, t1, t3); // t3=1
__ sw(t3, MemOperand(a0, OFFSET_OF(Test, d))); // d=1
// floating point part of test
__ li(t0, 0x80);
__ mtc1(t0, f4);
__ cvt_d_w(f4, f4); // f4=0x80
__ li(t0, 0xf3);
__ mtc1(t0, f6);
__ cvt_d_w(f6, f6); // f6=0xf3
__ seleqz(D, f8, f4, f6); // f8=0xf3
__ seleqz(D, f10, f6, f6); // f10=0
__ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, i))); // i=0xf3
__ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, j))); // j=0
__ selnez(D, f8, f4, f6); // f8=0
__ selnez(D, f10, f6, f6); // f10=0xf3*/
__ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, k))); // k=0
__ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, l))); // l=0xf3
__ jr(ra);
__ nop();
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
F3 f = FUNCTION_CAST<F3>(code->entry());
(CALL_GENERATED_CODE(f, &test, 0, 0, 0, 0));
CHECK_EQ(test.a, 1);
CHECK_EQ(test.b, 0);
CHECK_EQ(test.c, 0);
CHECK_EQ(test.d, 1);
CHECK_EQ(test.i, 0xf3);
CHECK_EQ(test.j, 0x0);
CHECK_EQ(test.k, 0);
CHECK_EQ(test.l, 0xf3);
}
}
TEST(MIPS0) {
CcTest::InitializeVM();
......
......@@ -90,6 +90,20 @@ bool DisassembleAndCompare(byte* pc, const char* compare_string) {
if (failure) { \
V8_Fatal(__FILE__, __LINE__, "MIPS Disassembler tests failed.\n"); \
}
// tests only seleqz, selnez, seleqz.fmt and selnez.fmt
TEST(Type1) {
SET_UP();
if (IsMipsArchVariant(kMips32r6)) {
COMPARE(seleqz(a0, a1, a2), "00853035 seleqz a0, a1, a2");
COMPARE(selnez(a0, a1, a2), "00853037 selnez a0, a1, a2");
COMPARE(seleqz(S, f0, f1, f2), "45000894 seleqz.S f0, f1, f2");
COMPARE(selnez(S, f0, f1, f2), "45000897 selnez.S f0, f1, f2");
COMPARE(seleqz(D, f3, f4, f5), "00853035 seleqz.D f3, f4, f5");
COMPARE(selnez(D, f3, f4, f5), "00853037 selnez.D f3, f4, f5");
}
VERIFY_RUN();
}
TEST(Type0) {
......
......@@ -92,6 +92,25 @@ if (failure) { \
}
TEST(Type1) {
if (kArchVariant == kMips64r6) {
SET_UP();
COMPARE(seleqz(a0, a1, a2), "00853035 seleqz a0, a1, a2");
COMPARE(selnez(a0, a1, a2), "00853037 selnez a0, a1, a2");
COMPARE(seleqz(D, f3, f4, f5), "462428d4 seleqz.D f4, f5, f3");
COMPARE(selnez(D, f3, f4, f5), "462428d7 selnez.D f4, f5, f3");
/*COMPARE(min(D, f3, f4, f5),
"462428dc min.D f4, f5, f3");
COMPARE(max(D, f3, f4, f5),
"462428de max.D f4, f5, f3");*/
VERIFY_RUN();
}
}
TEST(Type0) {
SET_UP();
......
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