From f00b4e94fb99f68cb6845e6d704001f341c484b9 Mon Sep 17 00:00:00 2001 From: "dusan.milosavljevic" Date: Mon, 30 Mar 2015 10:36:56 -0700 Subject: [PATCH] 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} --- src/mips/assembler-mips.cc | 28 + src/mips/constants-mips.cc | 4 + src/mips/disasm-mips.cc | 694 +++++++------ src/mips/simulator-mips.cc | 858 +++++++++------- src/mips/simulator-mips.h | 41 + src/mips64/assembler-mips64.cc | 17 +- src/mips64/constants-mips64.cc | 2 + src/mips64/disasm-mips64.cc | 1396 ++++++++++++++------------ src/mips64/simulator-mips64.cc | 1043 ++++++++++--------- src/mips64/simulator-mips64.h | 39 + test/cctest/test-assembler-mips.cc | 87 ++ test/cctest/test-assembler-mips64.cc | 66 ++ test/cctest/test-disasm-mips.cc | 14 + test/cctest/test-disasm-mips64.cc | 19 + 14 files changed, 2455 insertions(+), 1853 deletions(-) diff --git a/src/mips/assembler-mips.cc b/src/mips/assembler-mips.cc index c26a8514bb..7249f77e4b 100644 --- a/src/mips/assembler-mips.cc +++ b/src/mips/assembler-mips.cc @@ -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)) { diff --git a/src/mips/constants-mips.cc b/src/mips/constants-mips.cc index 30e4b2e003..419462311a 100644 --- a/src/mips/constants-mips.cc +++ b/src/mips/constants-mips.cc @@ -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; diff --git a/src/mips/disasm-mips.cc b/src/mips/disasm-mips.cc index 4f725cff54..62ea55da04 100644 --- a/src/mips/disasm-mips.cc +++ b/src/mips/disasm-mips.cc @@ -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(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(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(); diff --git a/src/mips/simulator-mips.cc b/src/mips/simulator-mips.cc index 7ea3841935..2e4921c710 100644 --- a/src/mips/simulator-mips.cc +++ b/src/mips/simulator-mips.cc @@ -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(rs); - const int32_t rt_reg = instr->RtValue(); - const int32_t rt = get_register(rt_reg); - const uint32_t rt_u = static_cast(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(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(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(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(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(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(fs)); - break; - case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word. - double rounded = trunc(fs); - i64 = static_cast(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(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(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(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(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(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(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(get_fpu_register_word(fs_reg)); - i64 |= static_cast( - get_fpu_register_word(fs_reg + 1)) << 32; - } - set_fpu_register_double(fd_reg, static_cast(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(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(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(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(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(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(fs)); + break; + case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word. + double rounded = trunc(fs); + i64 = static_cast(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(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(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(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(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(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(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(ft); + switch (instr->FunctionFieldRaw()) { + case CVT_D_S: + f = get_fpu_register_float(fs_reg); + set_fpu_register_double(fd_reg, static_cast(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(get_fpu_register_word(fs_reg)); + i64 |= static_cast(get_fpu_register_word(fs_reg + 1)) << 32; + } + set_fpu_register_double(fd_reg, static_cast(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( 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(rs); + const int32_t rt_reg = instr->RtValue(); + const int32_t rt = get_register(rt_reg); + const uint32_t rt_u = static_cast(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 diff --git a/src/mips/simulator-mips.h b/src/mips/simulator-mips.h index 85f64779f1..c1022061b7 100644 --- a/src/mips/simulator-mips.h +++ b/src/mips/simulator-mips.h @@ -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, diff --git a/src/mips64/assembler-mips64.cc b/src/mips64/assembler-mips64.cc index 9501371b63..5c1677de3c 100644 --- a/src/mips64/assembler-mips64.cc +++ b/src/mips64/assembler-mips64.cc @@ -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); } diff --git a/src/mips64/constants-mips64.cc b/src/mips64/constants-mips64.cc index dd34c57bfd..6a26c2bd5b 100644 --- a/src/mips64/constants-mips64.cc +++ b/src/mips64/constants-mips64.cc @@ -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; diff --git a/src/mips64/disasm-mips64.cc b/src/mips64/disasm-mips64.cc index 3f0642db2c..fc83955fe3 100644 --- a/src/mips64/disasm-mips64.cc +++ b/src/mips64/disasm-mips64.cc @@ -101,8 +101,23 @@ class Decoder { int DecodeBreakInstr(Instruction* instr); // Each of these functions decodes one particular instruction type. + void DecodeTypeRegisterDRsType(Instruction* instr); + void DecodeTypeRegisterLRsType(Instruction* instr); + void DecodeTypeRegisterSPECIAL(Instruction* instr); + void DecodeTypeRegisterSPECIAL2(Instruction* instr); + void DecodeTypeRegisterSPECIAL3(Instruction* instr); + void DecodeTypeRegisterCOP1(Instruction* instr); + void DecodeTypeRegisterCOP1X(Instruction* instr); int DecodeTypeRegister(Instruction* instr); + + void DecodeTypeImmediateCOP1W(Instruction* instr); + void DecodeTypeImmediateCOP1L(Instruction* instr); + void DecodeTypeImmediateCOP1S(Instruction* instr); + void DecodeTypeImmediateCOP1D(Instruction* instr); + void DecodeTypeImmediateCOP1(Instruction* instr); + void DecodeTypeImmediateREGIMM(Instruction* instr); void DecodeTypeImmediate(Instruction* instr); + void DecodeTypeJump(Instruction* instr); const disasm::NameConverter& converter_; @@ -474,502 +489,546 @@ int Decoder::DecodeBreakInstr(Instruction* instr) { } -int Decoder::DecodeTypeRegister(Instruction* instr) { - switch (instr->OpcodeFieldRaw()) { - case COP1: // Coprocessor instructions. - switch (instr->RsFieldRaw()) { - case MFC1: - Format(instr, "mfc1 'rt, 'fs"); - break; - case DMFC1: - Format(instr, "dmfc1 'rt, 'fs"); - break; - case MFHC1: - Format(instr, "mfhc1 'rt, 'fs"); - break; - case MTC1: - Format(instr, "mtc1 'rt, 'fs"); - break; - case DMTC1: - Format(instr, "dmtc1 'rt, 'fs"); - break; - // These are called "fs" too, although they are not FPU registers. - case CTC1: - Format(instr, "ctc1 'rt, 'fs"); - break; - case CFC1: - Format(instr, "cfc1 'rt, 'fs"); - break; - case MTHC1: - Format(instr, "mthc1 'rt, 'fs"); - break; - case D: - switch (instr->FunctionFieldRaw()) { - case ADD_D: - Format(instr, "add.d 'fd, 'fs, 'ft"); - break; - case SUB_D: - Format(instr, "sub.d 'fd, 'fs, 'ft"); - break; - case MUL_D: - Format(instr, "mul.d 'fd, 'fs, 'ft"); - break; - case DIV_D: - Format(instr, "div.d 'fd, 'fs, 'ft"); - break; - case ABS_D: - Format(instr, "abs.d 'fd, 'fs"); - break; - case MOV_D: - Format(instr, "mov.d 'fd, 'fs"); - break; - case NEG_D: - Format(instr, "neg.d 'fd, 'fs"); - break; - case SQRT_D: - Format(instr, "sqrt.d 'fd, 'fs"); - break; - case CVT_W_D: - Format(instr, "cvt.w.d 'fd, 'fs"); - break; - case CVT_L_D: - Format(instr, "cvt.l.d 'fd, 'fs"); - break; - case TRUNC_W_D: - Format(instr, "trunc.w.d 'fd, 'fs"); - break; - case TRUNC_L_D: - Format(instr, "trunc.l.d 'fd, 'fs"); - break; - case ROUND_W_D: - Format(instr, "round.w.d 'fd, 'fs"); - break; - case ROUND_L_D: - Format(instr, "round.l.d 'fd, 'fs"); - break; - case FLOOR_W_D: - Format(instr, "floor.w.d 'fd, 'fs"); - break; - case FLOOR_L_D: - Format(instr, "floor.l.d 'fd, 'fs"); - break; - case CEIL_W_D: - Format(instr, "ceil.w.d 'fd, 'fs"); - break; - case CEIL_L_D: - Format(instr, "ceil.l.d 'fd, 'fs"); - break; - case CVT_S_D: - Format(instr, "cvt.s.d 'fd, 'fs"); - break; - case C_F_D: - Format(instr, "c.f.d 'fs, 'ft, 'Cc"); - break; - case C_UN_D: - Format(instr, "c.un.d 'fs, 'ft, 'Cc"); - break; - case C_EQ_D: - Format(instr, "c.eq.d 'fs, 'ft, 'Cc"); - break; - case C_UEQ_D: - Format(instr, "c.ueq.d 'fs, 'ft, 'Cc"); - break; - case C_OLT_D: - Format(instr, "c.olt.d 'fs, 'ft, 'Cc"); - break; - case C_ULT_D: - Format(instr, "c.ult.d 'fs, 'ft, 'Cc"); - break; - case C_OLE_D: - Format(instr, "c.ole.d 'fs, 'ft, 'Cc"); - break; - case C_ULE_D: - Format(instr, "c.ule.d 'fs, 'ft, 'Cc"); - break; - default: - Format(instr, "unknown.cop1.d"); - break; - } - break; - case W: - switch (instr->FunctionFieldRaw()) { - case CVT_D_W: // Convert word to double. - Format(instr, "cvt.d.w 'fd, 'fs"); - break; - default: - UNREACHABLE(); - } - break; - case L: - switch (instr->FunctionFieldRaw()) { - case CVT_D_L: - Format(instr, "cvt.d.l 'fd, 'fs"); - break; - case CVT_S_L: - Format(instr, "cvt.s.l 'fd, 'fs"); - break; - case CMP_UN: - Format(instr, "cmp.un.d 'fd, 'fs, 'ft"); - break; - case CMP_EQ: - Format(instr, "cmp.eq.d 'fd, 'fs, 'ft"); - break; - case CMP_UEQ: - Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft"); - break; - case CMP_LT: - Format(instr, "cmp.lt.d 'fd, 'fs, 'ft"); - break; - case CMP_ULT: - Format(instr, "cmp.ult.d 'fd, 'fs, 'ft"); - break; - case CMP_LE: - Format(instr, "cmp.le.d 'fd, 'fs, 'ft"); - break; - case CMP_ULE: - Format(instr, "cmp.ule.d 'fd, 'fs, 'ft"); - break; - case CMP_OR: - Format(instr, "cmp.or.d 'fd, 'fs, 'ft"); - break; - case CMP_UNE: - Format(instr, "cmp.une.d 'fd, 'fs, 'ft"); - break; - case CMP_NE: - Format(instr, "cmp.ne.d 'fd, 'fs, 'ft"); - break; - default: - UNREACHABLE(); - } +void Decoder::DecodeTypeRegisterDRsType(Instruction* instr) { + switch (instr->FunctionFieldRaw()) { + case SELEQZ_C: + Format(instr, "seleqz.D 'ft, 'fs, 'fd"); + break; + case SELNEZ_C: + Format(instr, "selnez.D 'ft, 'fs, 'fd"); + break; + case ADD_D: + Format(instr, "add.d 'fd, 'fs, 'ft"); + break; + case SUB_D: + Format(instr, "sub.d 'fd, 'fs, 'ft"); + break; + case MUL_D: + Format(instr, "mul.d 'fd, 'fs, 'ft"); + break; + case DIV_D: + Format(instr, "div.d 'fd, 'fs, 'ft"); + break; + case ABS_D: + Format(instr, "abs.d 'fd, 'fs"); + break; + case MOV_D: + Format(instr, "mov.d 'fd, 'fs"); + break; + case NEG_D: + Format(instr, "neg.d 'fd, 'fs"); + break; + case SQRT_D: + Format(instr, "sqrt.d 'fd, 'fs"); + break; + case CVT_W_D: + Format(instr, "cvt.w.d 'fd, 'fs"); + break; + case CVT_L_D: + Format(instr, "cvt.l.d 'fd, 'fs"); + break; + case TRUNC_W_D: + Format(instr, "trunc.w.d 'fd, 'fs"); + break; + case TRUNC_L_D: + Format(instr, "trunc.l.d 'fd, 'fs"); + break; + case ROUND_W_D: + Format(instr, "round.w.d 'fd, 'fs"); + break; + case ROUND_L_D: + Format(instr, "round.l.d 'fd, 'fs"); + break; + case FLOOR_W_D: + Format(instr, "floor.w.d 'fd, 'fs"); + break; + case FLOOR_L_D: + Format(instr, "floor.l.d 'fd, 'fs"); + break; + case CEIL_W_D: + Format(instr, "ceil.w.d 'fd, 'fs"); + break; + case CEIL_L_D: + Format(instr, "ceil.l.d 'fd, 'fs"); + break; + case CVT_S_D: + Format(instr, "cvt.s.d 'fd, 'fs"); + break; + case C_F_D: + Format(instr, "c.f.d 'fs, 'ft, 'Cc"); + break; + case C_UN_D: + Format(instr, "c.un.d 'fs, 'ft, 'Cc"); + break; + case C_EQ_D: + Format(instr, "c.eq.d 'fs, 'ft, 'Cc"); + break; + case C_UEQ_D: + Format(instr, "c.ueq.d 'fs, 'ft, 'Cc"); + break; + case C_OLT_D: + Format(instr, "c.olt.d 'fs, 'ft, 'Cc"); + break; + case C_ULT_D: + Format(instr, "c.ult.d 'fs, 'ft, 'Cc"); + break; + case C_OLE_D: + Format(instr, "c.ole.d 'fs, 'ft, 'Cc"); + break; + case C_ULE_D: + Format(instr, "c.ule.d 'fs, 'ft, 'Cc"); + break; + default: + Format(instr, "unknown.cop1.d"); + break; + } +} + + +void Decoder::DecodeTypeRegisterLRsType(Instruction* instr) { + switch (instr->FunctionFieldRaw()) { + case CVT_D_L: + Format(instr, "cvt.d.l 'fd, 'fs"); + break; + case CVT_S_L: + Format(instr, "cvt.s.l 'fd, 'fs"); + break; + case CMP_UN: + Format(instr, "cmp.un.d 'fd, 'fs, 'ft"); + break; + case CMP_EQ: + Format(instr, "cmp.eq.d 'fd, 'fs, 'ft"); + break; + case CMP_UEQ: + Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft"); + break; + case CMP_LT: + Format(instr, "cmp.lt.d 'fd, 'fs, 'ft"); + break; + case CMP_ULT: + Format(instr, "cmp.ult.d 'fd, 'fs, 'ft"); + break; + case CMP_LE: + Format(instr, "cmp.le.d 'fd, 'fs, 'ft"); + break; + case CMP_ULE: + Format(instr, "cmp.ule.d 'fd, 'fs, 'ft"); + break; + case CMP_OR: + Format(instr, "cmp.or.d 'fd, 'fs, 'ft"); + break; + case CMP_UNE: + Format(instr, "cmp.une.d 'fd, 'fs, 'ft"); + break; + case CMP_NE: + Format(instr, "cmp.ne.d 'fd, 'fs, 'ft"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeRegisterCOP1(Instruction* instr) { + switch (instr->RsFieldRaw()) { + case MFC1: + Format(instr, "mfc1 'rt, 'fs"); + break; + case DMFC1: + Format(instr, "dmfc1 'rt, 'fs"); + break; + case MFHC1: + Format(instr, "mfhc1 'rt, 'fs"); + break; + case MTC1: + Format(instr, "mtc1 'rt, 'fs"); + break; + case DMTC1: + Format(instr, "dmtc1 'rt, 'fs"); + break; + // These are called "fs" too, although they are not FPU registers. + case CTC1: + Format(instr, "ctc1 'rt, 'fs"); + break; + case CFC1: + Format(instr, "cfc1 'rt, 'fs"); + break; + case MTHC1: + Format(instr, "mthc1 'rt, 'fs"); + break; + case D: + DecodeTypeRegisterDRsType(instr); + break; + case W: + switch (instr->FunctionFieldRaw()) { + case CVT_D_W: // Convert word to double. + Format(instr, "cvt.d.w 'fd, 'fs"); break; default: UNREACHABLE(); } break; - case COP1X: - switch (instr->FunctionFieldRaw()) { - case MADD_D: - Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft"); - break; - default: - UNREACHABLE(); + case L: + DecodeTypeRegisterLRsType(instr); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeRegisterCOP1X(Instruction* instr) { + switch (instr->FunctionFieldRaw()) { + case MADD_D: + Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeRegisterSPECIAL(Instruction* instr) { + switch (instr->FunctionFieldRaw()) { + case JR: + Format(instr, "jr 'rs"); + break; + case JALR: + Format(instr, "jalr 'rs"); + break; + case SLL: + if (0x0 == static_cast(instr->InstructionBits())) + Format(instr, "nop"); + else + Format(instr, "sll 'rd, 'rt, 'sa"); + break; + case DSLL: + Format(instr, "dsll 'rd, 'rt, 'sa"); + break; + case D_MUL_MUH: // Equals to DMUL. + if (kArchVariant != kMips64r6) { + Format(instr, "dmult 'rs, 'rt"); + } else { + if (instr->SaValue() == MUL_OP) { + Format(instr, "dmul 'rd, 'rs, 'rt"); + } else { + Format(instr, "dmuh 'rd, 'rs, 'rt"); + } } break; + case DSLL32: + Format(instr, "dsll32 'rd, 'rt, 'sa"); + break; + case SRL: + if (instr->RsValue() == 0) { + Format(instr, "srl 'rd, 'rt, 'sa"); + } else { + if (kArchVariant == kMips64r2) { + Format(instr, "rotr 'rd, 'rt, 'sa"); + } else { + Unknown(instr); + } + } + break; + case DSRL: + if (instr->RsValue() == 0) { + Format(instr, "dsrl 'rd, 'rt, 'sa"); + } else { + if (kArchVariant == kMips64r2) { + Format(instr, "drotr 'rd, 'rt, 'sa"); + } else { + Unknown(instr); + } + } + break; + case DSRL32: + Format(instr, "dsrl32 'rd, 'rt, 'sa"); + break; + case SRA: + Format(instr, "sra 'rd, 'rt, 'sa"); + break; + case DSRA: + Format(instr, "dsra 'rd, 'rt, 'sa"); + break; + case DSRA32: + Format(instr, "dsra32 'rd, 'rt, 'sa"); + break; + case SLLV: + Format(instr, "sllv 'rd, 'rt, 'rs"); + break; + case DSLLV: + Format(instr, "dsllv 'rd, 'rt, 'rs"); + break; + case SRLV: + if (instr->SaValue() == 0) { + Format(instr, "srlv 'rd, 'rt, 'rs"); + } else { + if (kArchVariant == kMips64r2) { + Format(instr, "rotrv 'rd, 'rt, 'rs"); + } else { + Unknown(instr); + } + } + break; + case DSRLV: + if (instr->SaValue() == 0) { + Format(instr, "dsrlv 'rd, 'rt, 'rs"); + } else { + if (kArchVariant == kMips64r2) { + Format(instr, "drotrv 'rd, 'rt, 'rs"); + } else { + Unknown(instr); + } + } + break; + case SRAV: + Format(instr, "srav 'rd, 'rt, 'rs"); + break; + case DSRAV: + Format(instr, "dsrav 'rd, 'rt, 'rs"); + break; + case MFHI: + if (instr->Bits(25, 16) == 0) { + Format(instr, "mfhi 'rd"); + } else { + if ((instr->FunctionFieldRaw() == CLZ_R6) && (instr->FdValue() == 1)) { + Format(instr, "clz 'rd, 'rs"); + } else if ((instr->FunctionFieldRaw() == CLO_R6) && + (instr->FdValue() == 1)) { + Format(instr, "clo 'rd, 'rs"); + } + } + break; + case MFLO: + Format(instr, "mflo 'rd"); + break; + case D_MUL_MUH_U: // Equals to DMULTU. + if (kArchVariant != kMips64r6) { + Format(instr, "dmultu 'rs, 'rt"); + } else { + if (instr->SaValue() == MUL_OP) { + Format(instr, "dmulu 'rd, 'rs, 'rt"); + } else { + Format(instr, "dmuhu 'rd, 'rs, 'rt"); + } + } + break; + case MULT: // @Mips64r6 == MUL_MUH. + if (kArchVariant != kMips64r6) { + Format(instr, "mult 'rs, 'rt"); + } else { + if (instr->SaValue() == MUL_OP) { + Format(instr, "mul 'rd, 'rs, 'rt"); + } else { + Format(instr, "muh 'rd, 'rs, 'rt"); + } + } + break; + case MULTU: // @Mips64r6 == MUL_MUH_U. + if (kArchVariant != kMips64r6) { + Format(instr, "multu 'rs, 'rt"); + } else { + if (instr->SaValue() == MUL_OP) { + Format(instr, "mulu 'rd, 'rs, 'rt"); + } else { + Format(instr, "muhu 'rd, 'rs, 'rt"); + } + } + + break; + case DIV: // @Mips64r6 == DIV_MOD. + if (kArchVariant != kMips64r6) { + Format(instr, "div 'rs, 'rt"); + } else { + if (instr->SaValue() == DIV_OP) { + Format(instr, "div 'rd, 'rs, 'rt"); + } else { + Format(instr, "mod 'rd, 'rs, 'rt"); + } + } + break; + case DDIV: // @Mips64r6 == D_DIV_MOD. + if (kArchVariant != kMips64r6) { + Format(instr, "ddiv 'rs, 'rt"); + } else { + if (instr->SaValue() == DIV_OP) { + Format(instr, "ddiv 'rd, 'rs, 'rt"); + } else { + Format(instr, "dmod 'rd, 'rs, 'rt"); + } + } + break; + case DIVU: // @Mips64r6 == DIV_MOD_U. + if (kArchVariant != kMips64r6) { + Format(instr, "divu 'rs, 'rt"); + } else { + if (instr->SaValue() == DIV_OP) { + Format(instr, "divu 'rd, 'rs, 'rt"); + } else { + Format(instr, "modu 'rd, 'rs, 'rt"); + } + } + break; + case DDIVU: // @Mips64r6 == D_DIV_MOD_U. + if (kArchVariant != kMips64r6) { + Format(instr, "ddivu 'rs, 'rt"); + } else { + if (instr->SaValue() == DIV_OP) { + Format(instr, "ddivu 'rd, 'rs, 'rt"); + } else { + Format(instr, "dmodu 'rd, 'rs, 'rt"); + } + } + break; + case ADD: + Format(instr, "add 'rd, 'rs, 'rt"); + break; + case DADD: + Format(instr, "dadd 'rd, 'rs, 'rt"); + break; + case ADDU: + Format(instr, "addu 'rd, 'rs, 'rt"); + break; + case DADDU: + Format(instr, "daddu 'rd, 'rs, 'rt"); + break; + case SUB: + Format(instr, "sub 'rd, 'rs, 'rt"); + break; + case DSUB: + Format(instr, "dsub 'rd, 'rs, 'rt"); + break; + case SUBU: + Format(instr, "subu 'rd, 'rs, 'rt"); + break; + case DSUBU: + Format(instr, "dsubu 'rd, 'rs, 'rt"); + break; + case AND: + Format(instr, "and 'rd, 'rs, 'rt"); + break; + case OR: + if (0 == instr->RsValue()) { + Format(instr, "mov 'rd, 'rt"); + } else if (0 == instr->RtValue()) { + Format(instr, "mov 'rd, 'rs"); + } else { + Format(instr, "or 'rd, 'rs, 'rt"); + } + break; + case XOR: + Format(instr, "xor 'rd, 'rs, 'rt"); + break; + case NOR: + Format(instr, "nor 'rd, 'rs, 'rt"); + break; + case SLT: + Format(instr, "slt 'rd, 'rs, 'rt"); + break; + case SLTU: + Format(instr, "sltu 'rd, 'rs, 'rt"); + break; + case TGE: + Format(instr, "tge 'rs, 'rt, code: 'code"); + break; + case TGEU: + Format(instr, "tgeu 'rs, 'rt, code: 'code"); + break; + case TLT: + Format(instr, "tlt 'rs, 'rt, code: 'code"); + break; + case TLTU: + Format(instr, "tltu 'rs, 'rt, code: 'code"); + break; + case TEQ: + Format(instr, "teq 'rs, 'rt, code: 'code"); + break; + case TNE: + Format(instr, "tne 'rs, 'rt, code: 'code"); + break; + case MOVZ: + Format(instr, "movz 'rd, 'rs, 'rt"); + break; + case MOVN: + Format(instr, "movn 'rd, 'rs, 'rt"); + break; + case MOVCI: + if (instr->Bit(16)) { + Format(instr, "movt 'rd, 'rs, 'bc"); + } else { + Format(instr, "movf 'rd, 'rs, 'bc"); + } + break; + case SELEQZ_S: + Format(instr, "seleqz 'rs, 'rt, 'rd"); + break; + case SELNEZ_S: + Format(instr, "selnez 'rs, 'rt, 'rd"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeRegisterSPECIAL2(Instruction* instr) { + switch (instr->FunctionFieldRaw()) { + case MUL: + Format(instr, "mul 'rd, 'rs, 'rt"); + break; + case CLZ: + if (kArchVariant != kMips64r6) { + Format(instr, "clz 'rd, 'rs"); + } + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeRegisterSPECIAL3(Instruction* instr) { + switch (instr->FunctionFieldRaw()) { + case INS: { + Format(instr, "ins 'rt, 'rs, 'sa, 'ss2"); + break; + } + case EXT: { + Format(instr, "ext 'rt, 'rs, 'sa, 'ss1"); + break; + } + case DEXT: { + Format(instr, "dext 'rt, 'rs, 'sa, 'ss1"); + break; + } + default: + UNREACHABLE(); + } +} + + +int Decoder::DecodeTypeRegister(Instruction* instr) { + switch (instr->OpcodeFieldRaw()) { + case COP1: // Coprocessor instructions. + DecodeTypeRegisterCOP1(instr); + break; + case COP1X: + DecodeTypeRegisterCOP1X(instr); + break; case SPECIAL: switch (instr->FunctionFieldRaw()) { - case JR: - Format(instr, "jr 'rs"); - break; - case JALR: - Format(instr, "jalr 'rs"); - break; - case SLL: - if (0x0 == static_cast(instr->InstructionBits())) - Format(instr, "nop"); - else - Format(instr, "sll 'rd, 'rt, 'sa"); - break; - case DSLL: - Format(instr, "dsll 'rd, 'rt, 'sa"); - break; - case D_MUL_MUH: // Equals to DMUL. - if (kArchVariant != kMips64r6) { - Format(instr, "dmult 'rs, 'rt"); - } else { - if (instr->SaValue() == MUL_OP) { - Format(instr, "dmul 'rd, 'rs, 'rt"); - } else { - Format(instr, "dmuh 'rd, 'rs, 'rt"); - } - } - break; - case DSLL32: - Format(instr, "dsll32 'rd, 'rt, 'sa"); - break; - case SRL: - if (instr->RsValue() == 0) { - Format(instr, "srl 'rd, 'rt, 'sa"); - } else { - if (kArchVariant == kMips64r2) { - Format(instr, "rotr 'rd, 'rt, 'sa"); - } else { - Unknown(instr); - } - } - break; - case DSRL: - if (instr->RsValue() == 0) { - Format(instr, "dsrl 'rd, 'rt, 'sa"); - } else { - if (kArchVariant == kMips64r2) { - Format(instr, "drotr 'rd, 'rt, 'sa"); - } else { - Unknown(instr); - } - } - break; - case DSRL32: - Format(instr, "dsrl32 'rd, 'rt, 'sa"); - break; - case SRA: - Format(instr, "sra 'rd, 'rt, 'sa"); - break; - case DSRA: - Format(instr, "dsra 'rd, 'rt, 'sa"); - break; - case DSRA32: - Format(instr, "dsra32 'rd, 'rt, 'sa"); - break; - case SLLV: - Format(instr, "sllv 'rd, 'rt, 'rs"); - break; - case DSLLV: - Format(instr, "dsllv 'rd, 'rt, 'rs"); - break; - case SRLV: - if (instr->SaValue() == 0) { - Format(instr, "srlv 'rd, 'rt, 'rs"); - } else { - if (kArchVariant == kMips64r2) { - Format(instr, "rotrv 'rd, 'rt, 'rs"); - } else { - Unknown(instr); - } - } - break; - case DSRLV: - if (instr->SaValue() == 0) { - Format(instr, "dsrlv 'rd, 'rt, 'rs"); - } else { - if (kArchVariant == kMips64r2) { - Format(instr, "drotrv 'rd, 'rt, 'rs"); - } else { - Unknown(instr); - } - } - break; - case SRAV: - Format(instr, "srav 'rd, 'rt, 'rs"); - break; - case DSRAV: - Format(instr, "dsrav 'rd, 'rt, 'rs"); - break; - case MFHI: - if (instr->Bits(25, 16) == 0) { - Format(instr, "mfhi 'rd"); - } else { - if ((instr->FunctionFieldRaw() == CLZ_R6) - && (instr->FdValue() == 1)) { - Format(instr, "clz 'rd, 'rs"); - } else if ((instr->FunctionFieldRaw() == CLO_R6) - && (instr->FdValue() == 1)) { - Format(instr, "clo 'rd, 'rs"); - } - } - break; - case MFLO: - Format(instr, "mflo 'rd"); - break; - case D_MUL_MUH_U: // Equals to DMULTU. - if (kArchVariant != kMips64r6) { - Format(instr, "dmultu 'rs, 'rt"); - } else { - if (instr->SaValue() == MUL_OP) { - Format(instr, "dmulu 'rd, 'rs, 'rt"); - } else { - Format(instr, "dmuhu 'rd, 'rs, 'rt"); - } - } - break; - case MULT: // @Mips64r6 == MUL_MUH. - if (kArchVariant != kMips64r6) { - Format(instr, "mult 'rs, 'rt"); - } else { - if (instr->SaValue() == MUL_OP) { - Format(instr, "mul 'rd, 'rs, 'rt"); - } else { - Format(instr, "muh 'rd, 'rs, 'rt"); - } - } - break; - case MULTU: // @Mips64r6 == MUL_MUH_U. - if (kArchVariant != kMips64r6) { - Format(instr, "multu 'rs, 'rt"); - } else { - if (instr->SaValue() == MUL_OP) { - Format(instr, "mulu 'rd, 'rs, 'rt"); - } else { - Format(instr, "muhu 'rd, 'rs, 'rt"); - } - } - - break; - case DIV: // @Mips64r6 == DIV_MOD. - if (kArchVariant != kMips64r6) { - Format(instr, "div 'rs, 'rt"); - } else { - if (instr->SaValue() == DIV_OP) { - Format(instr, "div 'rd, 'rs, 'rt"); - } else { - Format(instr, "mod 'rd, 'rs, 'rt"); - } - } - break; - case DDIV: // @Mips64r6 == D_DIV_MOD. - if (kArchVariant != kMips64r6) { - Format(instr, "ddiv 'rs, 'rt"); - } else { - if (instr->SaValue() == DIV_OP) { - Format(instr, "ddiv 'rd, 'rs, 'rt"); - } else { - Format(instr, "dmod 'rd, 'rs, 'rt"); - } - } - break; - case DIVU: // @Mips64r6 == DIV_MOD_U. - if (kArchVariant != kMips64r6) { - Format(instr, "divu 'rs, 'rt"); - } else { - if (instr->SaValue() == DIV_OP) { - Format(instr, "divu 'rd, 'rs, 'rt"); - } else { - Format(instr, "modu 'rd, 'rs, 'rt"); - } - } - break; - case DDIVU: // @Mips64r6 == D_DIV_MOD_U. - if (kArchVariant != kMips64r6) { - Format(instr, "ddivu 'rs, 'rt"); - } else { - if (instr->SaValue() == DIV_OP) { - Format(instr, "ddivu 'rd, 'rs, 'rt"); - } else { - Format(instr, "dmodu 'rd, 'rs, 'rt"); - } - } - break; - case ADD: - Format(instr, "add 'rd, 'rs, 'rt"); - break; - case DADD: - Format(instr, "dadd 'rd, 'rs, 'rt"); - break; - case ADDU: - Format(instr, "addu 'rd, 'rs, 'rt"); - break; - case DADDU: - Format(instr, "daddu 'rd, 'rs, 'rt"); - break; - case SUB: - Format(instr, "sub 'rd, 'rs, 'rt"); - break; - case DSUB: - Format(instr, "dsub 'rd, 'rs, 'rt"); - break; - case SUBU: - Format(instr, "subu 'rd, 'rs, 'rt"); - break; - case DSUBU: - Format(instr, "dsubu 'rd, 'rs, 'rt"); - break; - case AND: - Format(instr, "and 'rd, 'rs, 'rt"); - break; - case OR: - if (0 == instr->RsValue()) { - Format(instr, "mov 'rd, 'rt"); - } else if (0 == instr->RtValue()) { - Format(instr, "mov 'rd, 'rs"); - } else { - Format(instr, "or 'rd, 'rs, 'rt"); - } - break; - case XOR: - Format(instr, "xor 'rd, 'rs, 'rt"); - break; - case NOR: - Format(instr, "nor 'rd, 'rs, 'rt"); - break; - case SLT: - Format(instr, "slt 'rd, 'rs, 'rt"); - break; - case SLTU: - Format(instr, "sltu 'rd, 'rs, 'rt"); - break; case BREAK: return DecodeBreakInstr(instr); - case TGE: - Format(instr, "tge 'rs, 'rt, code: 'code"); - break; - case TGEU: - Format(instr, "tgeu 'rs, 'rt, code: 'code"); - break; - case TLT: - Format(instr, "tlt 'rs, 'rt, code: 'code"); - break; - case TLTU: - Format(instr, "tltu 'rs, 'rt, code: 'code"); - break; - case TEQ: - Format(instr, "teq 'rs, 'rt, code: 'code"); - break; - case TNE: - Format(instr, "tne 'rs, 'rt, code: 'code"); - break; - case MOVZ: - Format(instr, "movz 'rd, 'rs, 'rt"); - break; - case MOVN: - Format(instr, "movn 'rd, 'rs, 'rt"); - break; - case MOVCI: - if (instr->Bit(16)) { - Format(instr, "movt 'rd, 'rs, 'bc"); - } else { - Format(instr, "movf 'rd, 'rs, 'bc"); - } - break; - case SELEQZ_S: - Format(instr, "seleqz 'rd, 'rs, 'rt"); - break; - case SELNEZ_S: - Format(instr, "selnez 'rd, 'rs, 'rt"); - break; default: - UNREACHABLE(); + DecodeTypeRegisterSPECIAL(instr); + break; } break; case SPECIAL2: - switch (instr->FunctionFieldRaw()) { - case MUL: - Format(instr, "mul 'rd, 'rs, 'rt"); - break; - case CLZ: - if (kArchVariant != kMips64r6) { - Format(instr, "clz 'rd, 'rs"); - } - break; - default: - UNREACHABLE(); - } + DecodeTypeRegisterSPECIAL2(instr); break; case SPECIAL3: - switch (instr->FunctionFieldRaw()) { - case INS: { - Format(instr, "ins 'rt, 'rs, 'sa, 'ss2"); - break; - } - case EXT: { - Format(instr, "ext 'rt, 'rs, 'sa, 'ss1"); - break; - } - case DEXT: { - Format(instr, "dext 'rt, 'rs, 'sa, 'ss1"); - break; - } - default: - UNREACHABLE(); - } + DecodeTypeRegisterSPECIAL3(instr); break; default: UNREACHABLE(); @@ -978,187 +1037,216 @@ int Decoder::DecodeTypeRegister(Instruction* instr) { } +void Decoder::DecodeTypeImmediateCOP1D(Instruction* instr) { + switch (instr->FunctionValue()) { + case SEL: + Format(instr, "sel.D 'ft, 'fs, 'fd"); + break; + case SELEQZ_C: + Format(instr, "seleqz.D 'ft, 'fs, 'fd"); + break; + case SELNEZ_C: + Format(instr, "selnez.D 'ft, 'fs, 'fd"); + break; + case MIN: + Format(instr, "min.D 'ft, 'fs, 'fd"); + break; + case MINA: + Format(instr, "mina.D 'ft, 'fs, 'fd"); + break; + case MAX: + Format(instr, "max.D 'ft, 'fs, 'fd"); + break; + case MAXA: + Format(instr, "maxa.D 'ft, 'fs, 'fd"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeImmediateCOP1L(Instruction* instr) { + switch (instr->FunctionValue()) { + case CMP_AF: + Format(instr, "cmp.af.D 'ft, 'fs, 'fd"); + break; + case CMP_UN: + Format(instr, "cmp.un.D 'ft, 'fs, 'fd"); + break; + case CMP_EQ: + Format(instr, "cmp.eq.D 'ft, 'fs, 'fd"); + break; + case CMP_UEQ: + Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd"); + break; + case CMP_LT: + Format(instr, "cmp.lt.D 'ft, 'fs, 'fd"); + break; + case CMP_ULT: + Format(instr, "cmp.ult.D 'ft, 'fs, 'fd"); + break; + case CMP_LE: + Format(instr, "cmp.le.D 'ft, 'fs, 'fd"); + break; + case CMP_ULE: + Format(instr, "cmp.ule.D 'ft, 'fs, 'fd"); + break; + case CMP_OR: + Format(instr, "cmp.or.D 'ft, 'fs, 'fd"); + break; + case CMP_UNE: + Format(instr, "cmp.une.D 'ft, 'fs, 'fd"); + break; + case CMP_NE: + Format(instr, "cmp.ne.D 'ft, 'fs, 'fd"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeImmediateCOP1S(Instruction* instr) { + switch (instr->FunctionValue()) { + case SEL: + Format(instr, "sel.S 'ft, 'fs, 'fd"); + break; + case SELEQZ_C: + Format(instr, "seleqz.S 'ft, 'fs, 'fd"); + break; + case SELNEZ_C: + Format(instr, "selnez.S 'ft, 'fs, 'fd"); + break; + case MIN: + Format(instr, "min.S 'ft, 'fs, 'fd"); + break; + case MINA: + Format(instr, "mina.S 'ft, 'fs, 'fd"); + break; + case MAX: + Format(instr, "max.S 'ft, 'fs, 'fd"); + break; + case MAXA: + Format(instr, "maxa.S 'ft, 'fs, 'fd"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeImmediateCOP1W(Instruction* instr) { + switch (instr->FunctionValue()) { + case CMP_AF: + Format(instr, "cmp.af.S 'ft, 'fs, 'fd"); + break; + case CMP_UN: + Format(instr, "cmp.un.S 'ft, 'fs, 'fd"); + break; + case CMP_EQ: + Format(instr, "cmp.eq.S 'ft, 'fs, 'fd"); + break; + case CMP_UEQ: + Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd"); + break; + case CMP_LT: + Format(instr, "cmp.lt.S 'ft, 'fs, 'fd"); + break; + case CMP_ULT: + Format(instr, "cmp.ult.S 'ft, 'fs, 'fd"); + break; + case CMP_LE: + Format(instr, "cmp.le.S 'ft, 'fs, 'fd"); + break; + case CMP_ULE: + Format(instr, "cmp.ule.S 'ft, 'fs, 'fd"); + break; + case CMP_OR: + Format(instr, "cmp.or.S 'ft, 'fs, 'fd"); + break; + case CMP_UNE: + Format(instr, "cmp.une.S 'ft, 'fs, 'fd"); + break; + case CMP_NE: + Format(instr, "cmp.ne.S 'ft, 'fs, 'fd"); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeImmediateCOP1(Instruction* instr) { + switch (instr->RsFieldRaw()) { + case BC1: + if (instr->FBtrueValue()) { + Format(instr, "bc1t 'bc, 'imm16u"); + } else { + Format(instr, "bc1f 'bc, 'imm16u"); + } + break; + case BC1EQZ: + Format(instr, "bc1eqz 'ft, 'imm16u"); + break; + case BC1NEZ: + Format(instr, "bc1nez 'ft, 'imm16u"); + break; + case W: // CMP.S instruction. + DecodeTypeImmediateCOP1W(instr); + break; + case L: // CMP.D instruction. + DecodeTypeImmediateCOP1L(instr); + break; + case S: + DecodeTypeImmediateCOP1S(instr); + break; + case D: + DecodeTypeImmediateCOP1D(instr); + break; + default: + UNREACHABLE(); + } +} + + +void Decoder::DecodeTypeImmediateREGIMM(Instruction* instr) { + switch (instr->RtFieldRaw()) { + case BLTZ: + Format(instr, "bltz 'rs, 'imm16u"); + break; + case BLTZAL: + Format(instr, "bltzal 'rs, 'imm16u"); + break; + case BGEZ: + Format(instr, "bgez 'rs, 'imm16u"); + break; + case BGEZAL: + Format(instr, "bgezal 'rs, 'imm16u"); + break; + case BGEZALL: + Format(instr, "bgezall 'rs, 'imm16u"); + break; + case DAHI: + Format(instr, "dahi 'rs, 'imm16u"); + break; + case DATI: + Format(instr, "dati 'rs, 'imm16u"); + break; + default: + UNREACHABLE(); + } +} + + void Decoder::DecodeTypeImmediate(Instruction* instr) { switch (instr->OpcodeFieldRaw()) { case COP1: - switch (instr->RsFieldRaw()) { - case BC1: - if (instr->FBtrueValue()) { - Format(instr, "bc1t 'bc, 'imm16u"); - } else { - Format(instr, "bc1f 'bc, 'imm16u"); - } - break; - case BC1EQZ: - Format(instr, "bc1eqz 'ft, 'imm16u"); - break; - case BC1NEZ: - Format(instr, "bc1nez 'ft, 'imm16u"); - break; - case W: // CMP.S instruction. - switch (instr->FunctionValue()) { - case CMP_AF: - Format(instr, "cmp.af.S 'ft, 'fs, 'fd"); - break; - case CMP_UN: - Format(instr, "cmp.un.S 'ft, 'fs, 'fd"); - break; - case CMP_EQ: - Format(instr, "cmp.eq.S 'ft, 'fs, 'fd"); - break; - case CMP_UEQ: - Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd"); - break; - case CMP_LT: - Format(instr, "cmp.lt.S 'ft, 'fs, 'fd"); - break; - case CMP_ULT: - Format(instr, "cmp.ult.S 'ft, 'fs, 'fd"); - break; - case CMP_LE: - Format(instr, "cmp.le.S 'ft, 'fs, 'fd"); - break; - case CMP_ULE: - Format(instr, "cmp.ule.S 'ft, 'fs, 'fd"); - break; - case CMP_OR: - Format(instr, "cmp.or.S 'ft, 'fs, 'fd"); - break; - case CMP_UNE: - Format(instr, "cmp.une.S 'ft, 'fs, 'fd"); - break; - case CMP_NE: - Format(instr, "cmp.ne.S 'ft, 'fs, 'fd"); - break; - default: - UNREACHABLE(); - } - break; - case L: // CMP.D instruction. - switch (instr->FunctionValue()) { - case CMP_AF: - Format(instr, "cmp.af.D 'ft, 'fs, 'fd"); - break; - case CMP_UN: - Format(instr, "cmp.un.D 'ft, 'fs, 'fd"); - break; - case CMP_EQ: - Format(instr, "cmp.eq.D 'ft, 'fs, 'fd"); - break; - case CMP_UEQ: - Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd"); - break; - case CMP_LT: - Format(instr, "cmp.lt.D 'ft, 'fs, 'fd"); - break; - case CMP_ULT: - Format(instr, "cmp.ult.D 'ft, 'fs, 'fd"); - break; - case CMP_LE: - Format(instr, "cmp.le.D 'ft, 'fs, 'fd"); - break; - case CMP_ULE: - Format(instr, "cmp.ule.D 'ft, 'fs, 'fd"); - break; - case CMP_OR: - Format(instr, "cmp.or.D 'ft, 'fs, 'fd"); - break; - case CMP_UNE: - Format(instr, "cmp.une.D 'ft, 'fs, 'fd"); - break; - case CMP_NE: - Format(instr, "cmp.ne.D 'ft, 'fs, 'fd"); - break; - default: - UNREACHABLE(); - } - break; - case S: - switch (instr->FunctionValue()) { - case SEL: - Format(instr, "sel.S 'ft, 'fs, 'fd"); - break; - case SELEQZ_C: - Format(instr, "seleqz.S 'ft, 'fs, 'fd"); - break; - case SELNEZ_C: - Format(instr, "selnez.S 'ft, 'fs, 'fd"); - break; - case MIN: - Format(instr, "min.S 'ft, 'fs, 'fd"); - break; - case MINA: - Format(instr, "mina.S 'ft, 'fs, 'fd"); - break; - case MAX: - Format(instr, "max.S 'ft, 'fs, 'fd"); - break; - case MAXA: - Format(instr, "maxa.S 'ft, 'fs, 'fd"); - break; - default: - UNREACHABLE(); - } - break; - case D: - switch (instr->FunctionValue()) { - case SEL: - Format(instr, "sel.D 'ft, 'fs, 'fd"); - break; - case SELEQZ_C: - Format(instr, "seleqz.D 'ft, 'fs, 'fd"); - break; - case SELNEZ_C: - Format(instr, "selnez.D 'ft, 'fs, 'fd"); - break; - case MIN: - Format(instr, "min.D 'ft, 'fs, 'fd"); - break; - case MINA: - Format(instr, "mina.D 'ft, 'fs, 'fd"); - break; - case MAX: - Format(instr, "max.D 'ft, 'fs, 'fd"); - break; - case MAXA: - Format(instr, "maxa.D 'ft, 'fs, 'fd"); - break; - default: - UNREACHABLE(); - } - break; - default: - UNREACHABLE(); - } - + DecodeTypeImmediateCOP1(instr); break; // Case COP1. // ------------- REGIMM class. case REGIMM: - switch (instr->RtFieldRaw()) { - case BLTZ: - Format(instr, "bltz 'rs, 'imm16u"); - break; - case BLTZAL: - Format(instr, "bltzal 'rs, 'imm16u"); - break; - case BGEZ: - Format(instr, "bgez 'rs, 'imm16u"); - break; - case BGEZAL: - Format(instr, "bgezal 'rs, 'imm16u"); - break; - case BGEZALL: - Format(instr, "bgezall 'rs, 'imm16u"); - break; - case DAHI: - Format(instr, "dahi 'rs, 'imm16u"); - break; - case DATI: - Format(instr, "dati 'rs, 'imm16u"); - break; - default: - UNREACHABLE(); - } + break; // Case REGIMM. // ------------- Branch instructions. case BEQ: diff --git a/src/mips64/simulator-mips64.cc b/src/mips64/simulator-mips64.cc index 9ca57f6948..bf4c2066c3 100644 --- a/src/mips64/simulator-mips64.cc +++ b/src/mips64/simulator-mips64.cc @@ -2206,6 +2206,9 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, case DDIVU: // div and divu never raise exceptions. break; + case SELEQZ_S: + case SELNEZ_S: + break; default: UNREACHABLE(); } @@ -2267,6 +2270,561 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, } +void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, + const int32_t& fs_reg, + const int64_t& fd_reg) { + float f; + switch (instr->FunctionFieldRaw()) { + case CVT_D_S: + f = get_fpu_register_float(fs_reg); + set_fpu_register_double(fd_reg, static_cast(f)); + break; + default: + // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S + // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented. + UNREACHABLE(); + } +} + + +void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, + const int32_t& fs_reg, + const int64_t& ft_reg, + const int32_t& fd_reg) { + double ft, fs; + uint32_t cc, fcsr_cc; + fs = get_fpu_register_double(fs_reg); + ft = get_fpu_register_double(ft_reg); + cc = instr->FCccValue(); + fcsr_cc = get_fcsr_condition_bit(cc); + int64_t ft_int = static_cast(ft); + switch (instr->FunctionFieldRaw()) { + case SELEQZ_C: + DCHECK(kArchVariant == kMips64r6); + set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0); + break; + case SELNEZ_C: + DCHECK(kArchVariant == kMips64r6); + set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0); + break; + case ADD_D: + set_fpu_register_double(fd_reg, fs + ft); + break; + case SUB_D: + set_fpu_register_double(fd_reg, fs - ft); + break; + case MUL_D: + set_fpu_register_double(fd_reg, fs * ft); + break; + case DIV_D: + set_fpu_register_double(fd_reg, fs / ft); + break; + case ABS_D: + set_fpu_register_double(fd_reg, fabs(fs)); + break; + case MOV_D: + set_fpu_register_double(fd_reg, fs); + break; + case NEG_D: + set_fpu_register_double(fd_reg, -fs); + break; + case SQRT_D: + set_fpu_register_double(fd_reg, fast_sqrt(fs)); + break; + case C_UN_D: + set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); + break; + case C_EQ_D: + set_fcsr_bit(fcsr_cc, (fs == ft)); + break; + case C_UEQ_D: + set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft))); + break; + case C_OLT_D: + set_fcsr_bit(fcsr_cc, (fs < ft)); + break; + case C_ULT_D: + set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft))); + break; + case C_OLE_D: + set_fcsr_bit(fcsr_cc, (fs <= ft)); + break; + case C_ULE_D: + set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); + break; + case CVT_W_D: // Convert double to word. + // Rounding modes are not yet supported. + DCHECK((FCSR_ & 3) == 0); + // In rounding mode 0 it should behave like ROUND. + // No break. + case ROUND_W_D: // Round double to word (round half to even). + { + double rounded = std::floor(fs + 0.5); + int32_t result = static_cast(rounded); + if ((result & 1) != 0 && result - fs == 0.5) { + // If the number is halfway between two integers, + // round to the even one. + result--; + } + set_fpu_register_word(fd_reg, result); + if (set_fcsr_round_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPUInvalidResult); + } + } break; + case TRUNC_W_D: // Truncate double to word (round towards 0). + { + double rounded = trunc(fs); + int32_t result = static_cast(rounded); + set_fpu_register_word(fd_reg, result); + if (set_fcsr_round_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPUInvalidResult); + } + } break; + case FLOOR_W_D: // Round double to word towards negative infinity. + { + double rounded = std::floor(fs); + int32_t result = static_cast(rounded); + set_fpu_register_word(fd_reg, result); + if (set_fcsr_round_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPUInvalidResult); + } + } break; + case CEIL_W_D: // Round double to word towards positive infinity. + { + double rounded = std::ceil(fs); + int32_t result = static_cast(rounded); + set_fpu_register_word(fd_reg, result); + if (set_fcsr_round_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPUInvalidResult); + } + } break; + case CVT_S_D: // Convert double to float (single). + set_fpu_register_float(fd_reg, static_cast(fs)); + break; + case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word. + // Rounding modes are not yet supported. + DCHECK((FCSR_ & 3) == 0); + // In rounding mode 0 it should behave like ROUND. + // No break. + case ROUND_L_D: { // Mips64r2 instruction. + // check error cases + double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5); + int64_t result = static_cast(rounded); + set_fpu_register(fd_reg, result); + if (set_fcsr_round64_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPU64InvalidResult); + } + break; + } + case TRUNC_L_D: { // Mips64r2 instruction. + double rounded = trunc(fs); + int64_t result = static_cast(rounded); + set_fpu_register(fd_reg, result); + if (set_fcsr_round64_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPU64InvalidResult); + } + break; + } + case FLOOR_L_D: { // Mips64r2 instruction. + double rounded = floor(fs); + int64_t result = static_cast(rounded); + set_fpu_register(fd_reg, result); + if (set_fcsr_round64_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPU64InvalidResult); + } + break; + } + case CEIL_L_D: { // Mips64r2 instruction. + double rounded = ceil(fs); + int64_t result = static_cast(rounded); + set_fpu_register(fd_reg, result); + if (set_fcsr_round64_error(fs, rounded)) { + set_fpu_register(fd_reg, kFPU64InvalidResult); + } + break; + } + case C_F_D: + UNIMPLEMENTED_MIPS(); + break; + default: + UNREACHABLE(); + } +} + + +void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, + const int32_t& fs_reg, + const int32_t& fd_reg, + int64_t& alu_out) { + switch (instr->FunctionFieldRaw()) { + case CVT_S_W: // Convert word to float (single). + alu_out = get_fpu_register_signed_word(fs_reg); + set_fpu_register_float(fd_reg, static_cast(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(alu_out)); + break; + default: // Mips64r6 CMP.S instructions unimplemented. + UNREACHABLE(); + } +} + + +void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, + const int32_t& fs_reg, + const int32_t& fd_reg, + const int32_t& ft_reg) { + double fs = get_fpu_register_double(fs_reg); + double ft = get_fpu_register_double(ft_reg); + int64_t i64; + switch (instr->FunctionFieldRaw()) { + case CVT_D_L: // Mips32r2 instruction. + i64 = get_fpu_register(fs_reg); + set_fpu_register_double(fd_reg, static_cast(i64)); + break; + case CVT_S_L: + UNIMPLEMENTED_MIPS(); + break; + case CMP_AF: // Mips64r6 CMP.D instructions. + UNIMPLEMENTED_MIPS(); + break; + case CMP_UN: + if (std::isnan(fs) || std::isnan(ft)) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + case CMP_EQ: + if (fs == ft) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + case CMP_UEQ: + if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + case CMP_LT: + if (fs < ft) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + case CMP_ULT: + if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + case CMP_LE: + if (fs <= ft) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + case CMP_ULE: + if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { + set_fpu_register(fd_reg, -1); + } else { + set_fpu_register(fd_reg, 0); + } + break; + default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED + UNREACHABLE(); + } +} + + +void Simulator::DecodeTypeRegisterCOP1( + Instruction* instr, const int64_t& rs_reg, const int64_t& rs, + const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt, + const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg, + const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg, + int64_t& alu_out) { + switch (instr->RsFieldRaw()) { + case BC1: // Branch on coprocessor condition. + case BC1EQZ: + case BC1NEZ: + UNREACHABLE(); + break; + case CFC1: + set_register(rt_reg, alu_out); + break; + case MFC1: + case DMFC1: + case MFHC1: + set_register(rt_reg, alu_out); + break; + case CTC1: + // At the moment only FCSR is supported. + DCHECK(fs_reg == kFCSRRegister); + FCSR_ = registers_[rt_reg]; + break; + case MTC1: + // Hardware writes upper 32-bits to zero on mtc1. + set_fpu_register_hi_word(fs_reg, 0); + set_fpu_register_word(fs_reg, registers_[rt_reg]); + break; + case DMTC1: + set_fpu_register(fs_reg, registers_[rt_reg]); + break; + case MTHC1: + set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); + break; + case S: + DecodeTypeRegisterSRsType(instr, fs_reg, fd_reg); + break; + case D: + DecodeTypeRegisterDRsType(instr, fs_reg, ft_reg, fd_reg); + break; + case W: + DecodeTypeRegisterWRsType(instr, fs_reg, fd_reg, alu_out); + break; + case L: + DecodeTypeRegisterLRsType(instr, fs_reg, fd_reg, ft_reg); + break; + default: + UNREACHABLE(); + } +} + + +void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr, + const int32_t& fr_reg, + const int32_t& fs_reg, + const int32_t& ft_reg, + const int64_t& fd_reg) { + switch (instr->FunctionFieldRaw()) { + case MADD_D: + double fr, ft, fs; + fr = get_fpu_register_double(fr_reg); + fs = get_fpu_register_double(fs_reg); + ft = get_fpu_register_double(ft_reg); + set_fpu_register_double(fd_reg, fs * ft + fr); + break; + default: + UNREACHABLE(); + } +} + + +void Simulator::DecodeTypeRegisterSPECIAL( + Instruction* instr, const int64_t& rs_reg, const int64_t& rs, + const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt, + const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg, + const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg, + int64_t& i64hilo, uint64_t& u64hilo, int64_t& alu_out, bool& do_interrupt, + int64_t& current_pc, int64_t& next_pc, int64_t& return_addr_reg, + int64_t& i128resultH, int64_t& i128resultL) { + switch (instr->FunctionFieldRaw()) { + case SELEQZ_S: + DCHECK(kArchVariant == kMips64r6); + set_register(rd_reg, rt == 0 ? rs : 0); + break; + case SELNEZ_S: + DCHECK(kArchVariant == kMips64r6); + set_register(rd_reg, rt != 0 ? rs : 0); + break; + case JR: { + Instruction* branch_delay_instr = + reinterpret_cast(current_pc + Instruction::kInstrSize); + BranchDelayInstructionDecode(branch_delay_instr); + set_pc(next_pc); + pc_modified_ = true; + break; + } + case JALR: { + Instruction* branch_delay_instr = + reinterpret_cast(current_pc + Instruction::kInstrSize); + BranchDelayInstructionDecode(branch_delay_instr); + set_register(return_addr_reg, current_pc + 2 * Instruction::kInstrSize); + set_pc(next_pc); + pc_modified_ = true; + break; + } + // Instructions using HI and LO registers. + case MULT: + if (kArchVariant != kMips64r6) { + set_register(LO, static_cast(i64hilo & 0xffffffff)); + set_register(HI, static_cast(i64hilo >> 32)); + } else { + switch (instr->SaValue()) { + case MUL_OP: + set_register(rd_reg, static_cast(i64hilo & 0xffffffff)); + break; + case MUH_OP: + set_register(rd_reg, static_cast(i64hilo >> 32)); + break; + default: + UNIMPLEMENTED_MIPS(); + break; + } + } + break; + case MULTU: + set_register(LO, static_cast(u64hilo & 0xffffffff)); + set_register(HI, static_cast(u64hilo >> 32)); + break; + case DMULT: // DMULT == D_MUL_MUH. + if (kArchVariant != kMips64r6) { + set_register(LO, static_cast(i128resultL)); + set_register(HI, static_cast(i128resultH)); + } else { + switch (instr->SaValue()) { + case MUL_OP: + set_register(rd_reg, static_cast(i128resultL)); + break; + case MUH_OP: + set_register(rd_reg, static_cast(i128resultH)); + break; + default: + UNIMPLEMENTED_MIPS(); + break; + } + } + break; + case DMULTU: + UNIMPLEMENTED_MIPS(); + break; + case DSLL: + set_register(rd_reg, alu_out); + break; + case DIV: + case DDIV: + switch (kArchVariant) { + case kMips64r2: + // Divide by zero and overflow was not checked in the + // configuration step - div and divu do not raise exceptions. On + // division by 0 the result will be UNPREDICTABLE. On overflow + // (INT_MIN/-1), return INT_MIN which is what the hardware does. + if (rs == INT_MIN && rt == -1) { + set_register(LO, INT_MIN); + set_register(HI, 0); + } else if (rt != 0) { + set_register(LO, rs / rt); + set_register(HI, rs % rt); + } + break; + case kMips64r6: + switch (instr->SaValue()) { + case DIV_OP: + if (rs == INT_MIN && rt == -1) { + set_register(rd_reg, INT_MIN); + } else if (rt != 0) { + set_register(rd_reg, rs / rt); + } + break; + case MOD_OP: + if (rs == INT_MIN && rt == -1) { + set_register(rd_reg, 0); + } else if (rt != 0) { + set_register(rd_reg, rs % rt); + } + break; + default: + UNIMPLEMENTED_MIPS(); + break; + } + break; + default: + break; + } + break; + case DIVU: + if (rt_u != 0) { + set_register(LO, rs_u / rt_u); + set_register(HI, rs_u % rt_u); + } + break; + // Break and trap instructions. + case BREAK: + case TGE: + case TGEU: + case TLT: + case TLTU: + case TEQ: + case TNE: + if (do_interrupt) { + SoftwareInterrupt(instr); + } + break; + // Conditional moves. + case MOVN: + if (rt) { + set_register(rd_reg, rs); + TraceRegWr(rs); + } + break; + case MOVCI: { + uint32_t cc = instr->FBccValue(); + uint32_t fcsr_cc = get_fcsr_condition_bit(cc); + if (instr->Bit(16)) { // Read Tf bit. + if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); + } else { + if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); + } + break; + } + case MOVZ: + if (!rt) { + set_register(rd_reg, rs); + TraceRegWr(rs); + } + break; + default: // For other special opcodes we do the default operation. + set_register(rd_reg, alu_out); + TraceRegWr(alu_out); + } +} + + +void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr, + const int64_t& rd_reg, + int64_t& alu_out) { + switch (instr->FunctionFieldRaw()) { + case MUL: + set_register(rd_reg, alu_out); + TraceRegWr(alu_out); + // HI and LO are UNPREDICTABLE after the operation. + set_register(LO, Unpredictable); + set_register(HI, Unpredictable); + break; + default: // For other special2 opcodes we do the default operation. + set_register(rd_reg, alu_out); + } +} + + +void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, + const int64_t& rt_reg, + int64_t& alu_out) { + switch (instr->FunctionFieldRaw()) { + case INS: + // Ins instr leaves result in Rt, rather than Rd. + set_register(rt_reg, alu_out); + TraceRegWr(alu_out); + break; + case EXT: + case DEXT: + // Dext/Ext instr leaves result in Rt, rather than Rd. + set_register(rt_reg, alu_out); + TraceRegWr(alu_out); + break; + default: + UNREACHABLE(); + } +} + + void Simulator::DecodeTypeRegister(Instruction* instr) { // Instruction fields. const Opcode op = instr->OpcodeFieldRaw(); @@ -2320,490 +2878,23 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { // ---------- Execution. switch (op) { case COP1: - switch (instr->RsFieldRaw()) { - case BC1: // Branch on coprocessor condition. - case BC1EQZ: - case BC1NEZ: - UNREACHABLE(); - break; - case CFC1: - set_register(rt_reg, alu_out); - break; - case MFC1: - case DMFC1: - case MFHC1: - set_register(rt_reg, alu_out); - break; - case CTC1: - // At the moment only FCSR is supported. - DCHECK(fs_reg == kFCSRRegister); - FCSR_ = registers_[rt_reg]; - break; - case MTC1: - // Hardware writes upper 32-bits to zero on mtc1. - set_fpu_register_hi_word(fs_reg, 0); - set_fpu_register_word(fs_reg, registers_[rt_reg]); - break; - case DMTC1: - set_fpu_register(fs_reg, registers_[rt_reg]); - break; - case MTHC1: - set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); - break; - case S: - float f; - switch (instr->FunctionFieldRaw()) { - case CVT_D_S: - f = get_fpu_register_float(fs_reg); - set_fpu_register_double(fd_reg, static_cast(f)); - break; - default: - // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S - // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented. - UNREACHABLE(); - } - break; - case D: - double ft, fs; - uint32_t cc, fcsr_cc; - int64_t i64; - fs = get_fpu_register_double(fs_reg); - ft = get_fpu_register_double(ft_reg); - cc = instr->FCccValue(); - fcsr_cc = get_fcsr_condition_bit(cc); - switch (instr->FunctionFieldRaw()) { - case ADD_D: - set_fpu_register_double(fd_reg, fs + ft); - break; - case SUB_D: - set_fpu_register_double(fd_reg, fs - ft); - break; - case MUL_D: - set_fpu_register_double(fd_reg, fs * ft); - break; - case DIV_D: - set_fpu_register_double(fd_reg, fs / ft); - break; - case ABS_D: - set_fpu_register_double(fd_reg, fabs(fs)); - break; - case MOV_D: - set_fpu_register_double(fd_reg, fs); - break; - case NEG_D: - set_fpu_register_double(fd_reg, -fs); - break; - case SQRT_D: - set_fpu_register_double(fd_reg, fast_sqrt(fs)); - break; - case C_UN_D: - set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); - break; - case C_EQ_D: - set_fcsr_bit(fcsr_cc, (fs == ft)); - break; - case C_UEQ_D: - set_fcsr_bit(fcsr_cc, - (fs == ft) || (std::isnan(fs) || std::isnan(ft))); - break; - case C_OLT_D: - set_fcsr_bit(fcsr_cc, (fs < ft)); - break; - case C_ULT_D: - set_fcsr_bit(fcsr_cc, - (fs < ft) || (std::isnan(fs) || std::isnan(ft))); - break; - case C_OLE_D: - set_fcsr_bit(fcsr_cc, (fs <= ft)); - break; - case C_ULE_D: - set_fcsr_bit(fcsr_cc, - (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); - break; - case CVT_W_D: // Convert double to word. - // Rounding modes are not yet supported. - DCHECK((FCSR_ & 3) == 0); - // In rounding mode 0 it should behave like ROUND. - // No break. - case ROUND_W_D: // Round double to word (round half to even). - { - double rounded = std::floor(fs + 0.5); - int32_t result = static_cast(rounded); - if ((result & 1) != 0 && result - fs == 0.5) { - // If the number is halfway between two integers, - // round to the even one. - result--; - } - set_fpu_register_word(fd_reg, result); - if (set_fcsr_round_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPUInvalidResult); - } - } - break; - case TRUNC_W_D: // Truncate double to word (round towards 0). - { - double rounded = trunc(fs); - int32_t result = static_cast(rounded); - set_fpu_register_word(fd_reg, result); - if (set_fcsr_round_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPUInvalidResult); - } - } - break; - case FLOOR_W_D: // Round double to word towards negative infinity. - { - double rounded = std::floor(fs); - int32_t result = static_cast(rounded); - set_fpu_register_word(fd_reg, result); - if (set_fcsr_round_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPUInvalidResult); - } - } - break; - case CEIL_W_D: // Round double to word towards positive infinity. - { - double rounded = std::ceil(fs); - int32_t result = static_cast(rounded); - set_fpu_register_word(fd_reg, result); - if (set_fcsr_round_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPUInvalidResult); - } - } - break; - case CVT_S_D: // Convert double to float (single). - set_fpu_register_float(fd_reg, static_cast(fs)); - break; - case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word. - // Rounding modes are not yet supported. - DCHECK((FCSR_ & 3) == 0); - // In rounding mode 0 it should behave like ROUND. - // No break. - case ROUND_L_D: { // Mips64r2 instruction. - // check error cases - double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5); - int64_t result = static_cast(rounded); - set_fpu_register(fd_reg, result); - if (set_fcsr_round64_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPU64InvalidResult); - } - break; - } - case TRUNC_L_D: { // Mips64r2 instruction. - double rounded = trunc(fs); - int64_t result = static_cast(rounded); - set_fpu_register(fd_reg, result); - if (set_fcsr_round64_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPU64InvalidResult); - } - break; - } - case FLOOR_L_D: { // Mips64r2 instruction. - double rounded = floor(fs); - int64_t result = static_cast(rounded); - set_fpu_register(fd_reg, result); - if (set_fcsr_round64_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPU64InvalidResult); - } - break; - } - case CEIL_L_D: { // Mips64r2 instruction. - double rounded = ceil(fs); - int64_t result = static_cast(rounded); - set_fpu_register(fd_reg, result); - if (set_fcsr_round64_error(fs, rounded)) { - set_fpu_register(fd_reg, kFPU64InvalidResult); - } - break; - } - case C_F_D: - UNIMPLEMENTED_MIPS(); - break; - default: - UNREACHABLE(); - } - break; - case W: - switch (instr->FunctionFieldRaw()) { - case CVT_S_W: // Convert word to float (single). - alu_out = get_fpu_register_signed_word(fs_reg); - set_fpu_register_float(fd_reg, static_cast(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(alu_out)); - break; - default: // Mips64r6 CMP.S instructions unimplemented. - UNREACHABLE(); - } - break; - case L: - fs = get_fpu_register_double(fs_reg); - ft = get_fpu_register_double(ft_reg); - switch (instr->FunctionFieldRaw()) { - case CVT_D_L: // Mips32r2 instruction. - i64 = get_fpu_register(fs_reg); - set_fpu_register_double(fd_reg, static_cast(i64)); - break; - case CVT_S_L: - UNIMPLEMENTED_MIPS(); - break; - case CMP_AF: // Mips64r6 CMP.D instructions. - UNIMPLEMENTED_MIPS(); - break; - case CMP_UN: - if (std::isnan(fs) || std::isnan(ft)) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - case CMP_EQ: - if (fs == ft) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - case CMP_UEQ: - if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - case CMP_LT: - if (fs < ft) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - case CMP_ULT: - if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - case CMP_LE: - if (fs <= ft) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - case CMP_ULE: - if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { - set_fpu_register(fd_reg, -1); - } else { - set_fpu_register(fd_reg, 0); - } - break; - default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED - UNREACHABLE(); - } - break; - default: - UNREACHABLE(); - } + DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, + fr_reg, fs_reg, ft_reg, fd_reg, alu_out); break; case COP1X: - switch (instr->FunctionFieldRaw()) { - case MADD_D: - double fr, ft, fs; - fr = get_fpu_register_double(fr_reg); - fs = get_fpu_register_double(fs_reg); - ft = get_fpu_register_double(ft_reg); - set_fpu_register_double(fd_reg, fs * ft + fr); - break; - default: - UNREACHABLE(); - } + DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg); break; case SPECIAL: - switch (instr->FunctionFieldRaw()) { - case JR: { - Instruction* branch_delay_instr = reinterpret_cast( - current_pc+Instruction::kInstrSize); - BranchDelayInstructionDecode(branch_delay_instr); - set_pc(next_pc); - pc_modified_ = true; - break; - } - case JALR: { - Instruction* branch_delay_instr = reinterpret_cast( - current_pc+Instruction::kInstrSize); - BranchDelayInstructionDecode(branch_delay_instr); - set_register(return_addr_reg, - current_pc + 2 * Instruction::kInstrSize); - set_pc(next_pc); - pc_modified_ = true; - break; - } - // Instructions using HI and LO registers. - case MULT: - if (kArchVariant != kMips64r6) { - set_register(LO, static_cast(i64hilo & 0xffffffff)); - set_register(HI, static_cast(i64hilo >> 32)); - } else { - switch (instr->SaValue()) { - case MUL_OP: - set_register(rd_reg, - static_cast(i64hilo & 0xffffffff)); - break; - case MUH_OP: - set_register(rd_reg, static_cast(i64hilo >> 32)); - break; - default: - UNIMPLEMENTED_MIPS(); - break; - } - } - break; - case MULTU: - set_register(LO, static_cast(u64hilo & 0xffffffff)); - set_register(HI, static_cast(u64hilo >> 32)); - break; - case DMULT: // DMULT == D_MUL_MUH. - if (kArchVariant != kMips64r6) { - set_register(LO, static_cast(i128resultL)); - set_register(HI, static_cast(i128resultH)); - } else { - switch (instr->SaValue()) { - case MUL_OP: - set_register(rd_reg, static_cast(i128resultL)); - break; - case MUH_OP: - set_register(rd_reg, static_cast(i128resultH)); - break; - default: - UNIMPLEMENTED_MIPS(); - break; - } - } - break; - case DMULTU: - UNIMPLEMENTED_MIPS(); - break; - case DSLL: - set_register(rd_reg, alu_out); - break; - case DIV: - case DDIV: - switch (kArchVariant) { - case kMips64r2: - // Divide by zero and overflow was not checked in the - // configuration step - div and divu do not raise exceptions. On - // division by 0 the result will be UNPREDICTABLE. On overflow - // (INT_MIN/-1), return INT_MIN which is what the hardware does. - if (rs == INT_MIN && rt == -1) { - set_register(LO, INT_MIN); - set_register(HI, 0); - } else if (rt != 0) { - set_register(LO, rs / rt); - set_register(HI, rs % rt); - } - break; - case kMips64r6: - switch (instr->SaValue()) { - case DIV_OP: - if (rs == INT_MIN && rt == -1) { - set_register(rd_reg, INT_MIN); - } else if (rt != 0) { - set_register(rd_reg, rs / rt); - } - break; - case MOD_OP: - if (rs == INT_MIN && rt == -1) { - set_register(rd_reg, 0); - } else if (rt != 0) { - set_register(rd_reg, rs % rt); - } - break; - default: - UNIMPLEMENTED_MIPS(); - break; - } - break; - default: - break; - } - break; - case DIVU: - if (rt_u != 0) { - set_register(LO, rs_u / rt_u); - set_register(HI, rs_u % rt_u); - } - break; - // Break and trap instructions. - case BREAK: - case TGE: - case TGEU: - case TLT: - case TLTU: - case TEQ: - case TNE: - if (do_interrupt) { - SoftwareInterrupt(instr); - } - break; - // Conditional moves. - case MOVN: - if (rt) { - set_register(rd_reg, rs); - TraceRegWr(rs); - } - break; - case MOVCI: { - uint32_t cc = instr->FBccValue(); - uint32_t fcsr_cc = get_fcsr_condition_bit(cc); - if (instr->Bit(16)) { // Read Tf bit. - if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); - } else { - if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs); - } - break; - } - case MOVZ: - if (!rt) { - set_register(rd_reg, rs); - TraceRegWr(rs); - } - break; - default: // For other special opcodes we do the default operation. - set_register(rd_reg, alu_out); - TraceRegWr(alu_out); - } + DecodeTypeRegisterSPECIAL( + instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, fr_reg, fs_reg, + ft_reg, fd_reg, i64hilo, u64hilo, alu_out, do_interrupt, current_pc, + next_pc, return_addr_reg, i128resultH, i128resultL); break; case SPECIAL2: - switch (instr->FunctionFieldRaw()) { - case MUL: - set_register(rd_reg, alu_out); - TraceRegWr(alu_out); - // HI and LO are UNPREDICTABLE after the operation. - set_register(LO, Unpredictable); - set_register(HI, Unpredictable); - break; - default: // For other special2 opcodes we do the default operation. - set_register(rd_reg, alu_out); - } + DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out); break; case SPECIAL3: - switch (instr->FunctionFieldRaw()) { - case INS: - // Ins instr leaves result in Rt, rather than Rd. - set_register(rt_reg, alu_out); - TraceRegWr(alu_out); - break; - case EXT: - case DEXT: - // Dext/Ext instr leaves result in Rt, rather than Rd. - set_register(rt_reg, alu_out); - TraceRegWr(alu_out); - break; - default: - UNREACHABLE(); - } + DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out); break; // Unimplemented opcodes raised an error in the configuration step before, // so we can use the default here to set the destination register in common diff --git a/src/mips64/simulator-mips64.h b/src/mips64/simulator-mips64.h index 3087dcdab8..237a244dbf 100644 --- a/src/mips64/simulator-mips64.h +++ b/src/mips64/simulator-mips64.h @@ -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); diff --git a/test/cctest/test-assembler-mips.cc b/test/cctest/test-assembler-mips.cc index 730de9d454..d29419e2ff 100644 --- a/test/cctest/test-assembler-mips.cc +++ b/test/cctest/test-assembler-mips.cc @@ -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 = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); + F3 f = FUNCTION_CAST(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) { diff --git a/test/cctest/test-assembler-mips64.cc b/test/cctest/test-assembler-mips64.cc index e55fb24282..dd8841a26a 100644 --- a/test/cctest/test-assembler-mips64.cc +++ b/test/cctest/test-assembler-mips64.cc @@ -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 = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); + F3 f = FUNCTION_CAST(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(); diff --git a/test/cctest/test-disasm-mips.cc b/test/cctest/test-disasm-mips.cc index ca928a61eb..1023ca445c 100644 --- a/test/cctest/test-disasm-mips.cc +++ b/test/cctest/test-disasm-mips.cc @@ -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) { diff --git a/test/cctest/test-disasm-mips64.cc b/test/cctest/test-disasm-mips64.cc index a2a93c611a..638cfee3b8 100644 --- a/test/cctest/test-disasm-mips64.cc +++ b/test/cctest/test-disasm-mips64.cc @@ -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();