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}
This commit is contained in:
dusan.milosavljevic 2015-03-30 10:36:56 -07:00 committed by Commit bot
parent 00477a5d72
commit f00b4e94fb
14 changed files with 2455 additions and 1853 deletions

View File

@ -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)) {

View File

@ -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;

View File

@ -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();

View File

@ -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();
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:
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::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 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 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 SPECIAL:
switch (instr->FunctionFieldRaw()) {
}
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();
}
}
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();
}
}
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();
}
}
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 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 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

View File

@ -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,

View File

@ -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);
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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) {

View File

@ -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();

View File

@ -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) {

View File

@ -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();