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:
parent
00477a5d72
commit
f00b4e94fb
@ -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();
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user