MIPS: Add float instructions and test coverage, part one

Implement assembler, disassembler tests for all instructions for mips32 and mips64. Additionally, add missing single precision float instructions for r2 and r6 architecture variants in assembler, simulator and disassembler with corresponding tests.

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

Cr-Commit-Position: refs/heads/master@{#28402}
This commit is contained in:
Djordje.Pesic 2015-05-14 07:02:40 -07:00 committed by Commit bot
parent 19a421c694
commit 2a6a87d71a
16 changed files with 4223 additions and 353 deletions

View File

@ -1903,45 +1903,12 @@ void Assembler::movf(Register rd, Register rs, uint16_t cc) {
}
void Assembler::sel(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
Instr instr = COP1 | fmt << kRsShift | ft.code() << kFtShift |
fs.code() << kFsShift | fd.code() << kFdShift | SEL;
emit(instr);
}
void Assembler::seleqz(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELEQZ_S);
}
void Assembler::seleqz(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
void Assembler::selnez(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELNEZ_S);
}
void Assembler::selnez(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
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)) {
@ -2110,10 +2077,127 @@ void Assembler::DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
}
void Assembler::movn_s(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, S, rt, fs, fd, MOVN_C);
}
void Assembler::movn_d(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, D, rt, fs, fd, MOVN_C);
}
void Assembler::sel(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SEL);
}
void Assembler::sel_s(FPURegister fd, FPURegister fs, FPURegister ft) {
sel(S, fd, fs, ft);
}
void Assembler::sel_d(FPURegister fd, FPURegister fs, FPURegister ft) {
sel(D, fd, fs, ft);
}
void Assembler::seleqz(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
void Assembler::selnez(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELNEZ_S);
}
void Assembler::selnez(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
}
void Assembler::seleqz_d(FPURegister fd, FPURegister fs, FPURegister ft) {
seleqz(D, fd, fs, ft);
}
void Assembler::seleqz_s(FPURegister fd, FPURegister fs, FPURegister ft) {
seleqz(S, fd, fs, ft);
}
void Assembler::selnez_d(FPURegister fd, FPURegister fs, FPURegister ft) {
selnez(D, fd, fs, ft);
}
void Assembler::selnez_s(FPURegister fd, FPURegister fs, FPURegister ft) {
selnez(S, fd, fs, ft);
}
void Assembler::movz_s(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, S, rt, fs, fd, MOVZ_C);
}
void Assembler::movz_d(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, D, rt, fs, fd, MOVZ_C);
}
void Assembler::movt_s(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(IsMipsArchVariant(kMips32r2));
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 1;
GenInstrRegister(COP1, S, ft, fs, fd, MOVF);
}
void Assembler::movt_d(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(IsMipsArchVariant(kMips32r2));
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 1;
GenInstrRegister(COP1, D, ft, fs, fd, MOVF);
}
void Assembler::movf_s(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(IsMipsArchVariant(kMips32r2));
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 0;
GenInstrRegister(COP1, S, ft, fs, fd, MOVF);
}
void Assembler::movf_d(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(IsMipsArchVariant(kMips32r2));
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 0;
GenInstrRegister(COP1, D, ft, fs, fd, MOVF);
}
// Arithmetic.
void Assembler::add_s(FPURegister fd, FPURegister fs, FPURegister ft) {
GenInstrRegister(COP1, S, ft, fs, fd, ADD_D);
GenInstrRegister(COP1, S, ft, fs, fd, ADD_S);
}
@ -2123,7 +2207,7 @@ void Assembler::add_d(FPURegister fd, FPURegister fs, FPURegister ft) {
void Assembler::sub_s(FPURegister fd, FPURegister fs, FPURegister ft) {
GenInstrRegister(COP1, S, ft, fs, fd, SUB_D);
GenInstrRegister(COP1, S, ft, fs, fd, SUB_S);
}
@ -2133,7 +2217,7 @@ void Assembler::sub_d(FPURegister fd, FPURegister fs, FPURegister ft) {
void Assembler::mul_s(FPURegister fd, FPURegister fs, FPURegister ft) {
GenInstrRegister(COP1, S, ft, fs, fd, MUL_D);
GenInstrRegister(COP1, S, ft, fs, fd, MUL_S);
}
@ -2150,7 +2234,7 @@ void Assembler::madd_d(FPURegister fd, FPURegister fr, FPURegister fs,
void Assembler::div_s(FPURegister fd, FPURegister fs, FPURegister ft) {
GenInstrRegister(COP1, S, ft, fs, fd, DIV_D);
GenInstrRegister(COP1, S, ft, fs, fd, DIV_S);
}
@ -2160,7 +2244,7 @@ void Assembler::div_d(FPURegister fd, FPURegister fs, FPURegister ft) {
void Assembler::abs_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, ABS_D);
GenInstrRegister(COP1, S, f0, fs, fd, ABS_S);
}
@ -2170,12 +2254,17 @@ void Assembler::abs_d(FPURegister fd, FPURegister fs) {
void Assembler::mov_d(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, D, f0, fs, fd, MOV_D);
GenInstrRegister(COP1, D, f0, fs, fd, MOV_S);
}
void Assembler::mov_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, MOV_D);
}
void Assembler::neg_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, NEG_D);
GenInstrRegister(COP1, S, f0, fs, fd, NEG_S);
}
@ -2185,7 +2274,7 @@ void Assembler::neg_d(FPURegister fd, FPURegister fs) {
void Assembler::sqrt_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, SQRT_D);
GenInstrRegister(COP1, S, f0, fs, fd, SQRT_S);
}
@ -2194,6 +2283,26 @@ void Assembler::sqrt_d(FPURegister fd, FPURegister fs) {
}
void Assembler::rsqrt_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, RSQRT_S);
}
void Assembler::rsqrt_d(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, D, f0, fs, fd, RSQRT_D);
}
void Assembler::recip_d(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, D, f0, fs, fd, RECIP_D);
}
void Assembler::recip_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, RECIP_S);
}
// Conversions.
void Assembler::cvt_w_s(FPURegister fd, FPURegister fs) {
@ -2251,6 +2360,7 @@ void Assembler::rint_s(FPURegister fd, FPURegister fs) { rint(S, fd, fs); }
void Assembler::rint(SecondaryField fmt, FPURegister fd, FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, f0, fs, fd, RINT);
}

View File

@ -853,13 +853,27 @@ class Assembler : public AssemblerBase {
void movf(Register rd, Register rs, uint16_t cc = 0);
void sel(SecondaryField fmt, FPURegister fd, FPURegister fs, FPURegister ft);
void sel_s(FPURegister fd, FPURegister fs, FPURegister ft);
void sel_d(FPURegister fd, FPURegister fs, FPURegister ft);
void seleqz(Register rd, Register rs, Register rt);
void seleqz(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft);
void selnez(Register rd, Register rs, Register rt);
void selnez(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft);
void seleqz_d(FPURegister fd, FPURegister fs, FPURegister ft);
void seleqz_s(FPURegister fd, FPURegister fs, FPURegister ft);
void selnez_d(FPURegister fd, FPURegister fs, FPURegister ft);
void selnez_s(FPURegister fd, FPURegister fs, FPURegister ft);
void movz_s(FPURegister fd, FPURegister fs, Register rt);
void movz_d(FPURegister fd, FPURegister fs, Register rt);
void movt_s(FPURegister fd, FPURegister fs, uint16_t cc);
void movt_d(FPURegister fd, FPURegister fs, uint16_t cc);
void movf_s(FPURegister fd, FPURegister fs, uint16_t cc);
void movf_d(FPURegister fd, FPURegister fs, uint16_t cc);
void movn_s(FPURegister fd, FPURegister fs, Register rt);
void movn_d(FPURegister fd, FPURegister fs, Register rt);
// Bit twiddling.
void clz(Register rd, Register rs);
void ins_(Register rt, Register rs, uint16_t pos, uint16_t size);
@ -896,10 +910,15 @@ class Assembler : public AssemblerBase {
void abs_s(FPURegister fd, FPURegister fs);
void abs_d(FPURegister fd, FPURegister fs);
void mov_d(FPURegister fd, FPURegister fs);
void mov_s(FPURegister fd, FPURegister fs);
void neg_s(FPURegister fd, FPURegister fs);
void neg_d(FPURegister fd, FPURegister fs);
void sqrt_s(FPURegister fd, FPURegister fs);
void sqrt_d(FPURegister fd, FPURegister fs);
void rsqrt_s(FPURegister fd, FPURegister fs);
void rsqrt_d(FPURegister fd, FPURegister fs);
void recip_d(FPURegister fd, FPURegister fs);
void recip_s(FPURegister fd, FPURegister fs);
// Conversion.
void cvt_w_s(FPURegister fd, FPURegister fs);

View File

@ -457,6 +457,14 @@ enum SecondaryField {
L = ((2 << 3) + 5) << 21,
PS = ((2 << 3) + 6) << 21,
// COP1 Encoding of Function Field When rs=S.
ADD_S = ((0 << 3) + 0),
SUB_S = ((0 << 3) + 1),
MUL_S = ((0 << 3) + 2),
DIV_S = ((0 << 3) + 3),
ABS_S = ((0 << 3) + 5),
SQRT_S = ((0 << 3) + 4),
MOV_S = ((0 << 3) + 6),
NEG_S = ((0 << 3) + 7),
ROUND_L_S = ((1 << 3) + 0),
TRUNC_L_S = ((1 << 3) + 1),
CEIL_L_S = ((1 << 3) + 2),
@ -465,6 +473,8 @@ enum SecondaryField {
TRUNC_W_S = ((1 << 3) + 5),
CEIL_W_S = ((1 << 3) + 6),
FLOOR_W_S = ((1 << 3) + 7),
RECIP_S = ((2 << 3) + 5),
RSQRT_S = ((2 << 3) + 6),
CVT_D_S = ((4 << 3) + 1),
CVT_W_S = ((4 << 3) + 4),
CVT_L_S = ((4 << 3) + 5),
@ -486,10 +496,8 @@ enum SecondaryField {
TRUNC_W_D = ((1 << 3) + 5),
CEIL_W_D = ((1 << 3) + 6),
FLOOR_W_D = ((1 << 3) + 7),
MIN = ((3 << 3) + 4),
MINA = ((3 << 3) + 5),
MAX = ((3 << 3) + 6),
MAXA = ((3 << 3) + 7),
RECIP_D = ((2 << 3) + 5),
RSQRT_D = ((2 << 3) + 6),
CVT_S_D = ((4 << 3) + 0),
CVT_W_D = ((4 << 3) + 4),
CVT_L_D = ((4 << 3) + 5),
@ -543,8 +551,15 @@ enum SecondaryField {
CMP_SUGT = ((3 << 3) + 6), // Reserved, not implemented.
CMP_SOGT = ((3 << 3) + 7), // Reserved, not implemented.
MIN = ((3 << 3) + 4),
MINA = ((3 << 3) + 5),
MAX = ((3 << 3) + 6),
MAXA = ((3 << 3) + 7),
SEL = ((2 << 3) + 0),
MOVZ_C = ((2 << 3) + 2),
MOVN_C = ((2 << 3) + 3),
SELEQZ_C = ((2 << 3) + 4), // COP1 on FPR registers.
MOVF = ((2 << 3) + 1), // Function field for MOVT.fmt and MOVF.fmt
SELNEZ_C = ((2 << 3) + 7), // COP1 on FPR registers.
// COP1 Encoding of Function Field When rs=PS.
// COP1X Encoding of Function Field.

View File

@ -511,6 +511,19 @@ bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
case SELNEZ_C:
Format(instr, "selnez.'t 'fd, 'fs, 'ft");
break;
case MOVZ_C:
Format(instr, "movz.'t 'fd, 'fs, 'rt");
break;
case MOVN_C:
Format(instr, "movn.'t 'fd, 'fs, 'rt");
break;
case MOVF:
if (instr->Bit(16)) {
Format(instr, "movt.'t 'fd, 'fs, 'Cc");
} else {
Format(instr, "movf.'t 'fd, 'fs, 'Cc");
}
break;
case ADD_D:
Format(instr, "add.'t 'fd, 'fs, 'ft");
break;
@ -535,6 +548,12 @@ bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
case SQRT_D:
Format(instr, "sqrt.'t 'fd, 'fs");
break;
case RECIP_D:
Format(instr, "recip.'t 'fd, 'fs");
break;
case RSQRT_D:
Format(instr, "rsqrt.'t 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.'t 'fd, 'fs");
break;
@ -550,12 +569,21 @@ bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
case ROUND_W_D:
Format(instr, "round.w.'t 'fd, 'fs");
break;
case ROUND_L_D:
Format(instr, "round.l.'t 'fd, 'fs");
break;
case FLOOR_W_D:
Format(instr, "floor.w.'t 'fd, 'fs");
break;
case FLOOR_L_D:
Format(instr, "floor.l.'t 'fd, 'fs");
break;
case CEIL_W_D:
Format(instr, "ceil.w.'t 'fd, 'fs");
break;
case CEIL_L_D:
Format(instr, "ceil.l.'t 'fd, 'fs");
break;
case CVT_S_D:
Format(instr, "cvt.s.'t 'fd, 'fs");
break;

View File

@ -1282,8 +1282,6 @@ unsigned int Simulator::get_fcsr_rounding_mode() {
}
// Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round_error(double original, double rounded) {
bool ret = false;
double max_int32 = std::numeric_limits<int32_t>::max();
@ -1303,7 +1301,101 @@ bool Simulator::set_fcsr_round_error(double original, double rounded) {
ret = true;
}
if (rounded > max_int32 || rounded < min_int32) {
if (rounded >= max_int32 || rounded <= min_int32) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
return ret;
}
// Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round64_error(double original, double rounded) {
bool ret = false;
double max_int64 = std::numeric_limits<int64_t>::max();
double min_int64 = std::numeric_limits<int64_t>::min();
if (!std::isfinite(original) || !std::isfinite(rounded)) {
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
if (original != rounded) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
set_fcsr_bit(kFCSRUnderflowFlagBit, true);
ret = true;
}
if (rounded >= max_int64 || rounded <= min_int64) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
return ret;
}
bool Simulator::set_fcsr_round_error(float original, float rounded) {
bool ret = false;
double max_int32 = std::numeric_limits<int32_t>::max();
double min_int32 = std::numeric_limits<int32_t>::min();
if (!std::isfinite(original) || !std::isfinite(rounded)) {
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
if (original != rounded) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) {
set_fcsr_bit(kFCSRUnderflowFlagBit, true);
ret = true;
}
if (rounded >= max_int32 || rounded <= min_int32) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
return ret;
}
// Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round64_error(float original, float rounded) {
bool ret = false;
double max_int64 = std::numeric_limits<int64_t>::max();
double min_int64 = std::numeric_limits<int64_t>::min();
if (!std::isfinite(original) || !std::isfinite(rounded)) {
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
if (original != rounded) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) {
set_fcsr_bit(kFCSRUnderflowFlagBit, true);
ret = true;
}
if (rounded >= max_int64 || rounded <= min_int64) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
@ -2191,7 +2283,10 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
uint32_t cc, fcsr_cc;
int64_t i64;
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
if (instr->FunctionFieldRaw() != MOVF) {
ft = get_fpu_register_double(ft_reg);
}
fd = get_fpu_register_double(fd_reg);
int64_t ft_int = bit_cast<int64_t>(ft);
int64_t fd_int = bit_cast<int64_t>(fd);
cc = instr->FCccValue();
@ -2246,6 +2341,37 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0);
break;
case MOVZ_C: {
DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue();
int32_t rt = get_register(rt_reg);
if (rt == 0) {
set_fpu_register_double(fd_reg, fs);
}
break;
}
case MOVN_C: {
DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue();
int32_t rt = get_register(rt_reg);
if (rt != 0) {
set_fpu_register_double(fd_reg, fs);
}
break;
}
case MOVF: {
// Same function field for MOVT.D and MOVF.D
uint32_t ft_cc = (ft_reg >> 2) & 0x7;
ft_cc = get_fcsr_condition_bit(ft_cc);
if (instr->Bit(16)) { // Read Tf bit.
// MOVT.D
if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs);
} else {
// MOVF.D
if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs);
}
break;
}
case MIN:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg);
@ -2259,6 +2385,48 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
set_fpu_register_double(fd_reg, (fs >= ft) ? ft : fs);
}
break;
case MINA:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else {
double result;
if (fabs(fs) > fabs(ft)) {
result = ft;
} else if (fabs(fs) < fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_double(fd_reg, result);
}
break;
case MAXA:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else {
double result;
if (fabs(fs) < fabs(ft)) {
result = ft;
} else if (fabs(fs) > fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_double(fd_reg, result);
}
break;
case MAX:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_double(fs_reg);
@ -2297,6 +2465,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs));
break;
case RSQRT_D: {
double result = 1.0 / fast_sqrt(fs);
set_fpu_register_double(fd_reg, result);
break;
}
case RECIP_D: {
double result = 1.0 / fs;
set_fpu_register_double(fd_reg, result);
break;
}
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
@ -2377,51 +2555,72 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
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);
UNSUPPORTED();
}
break;
}
case TRUNC_L_D: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
UNSUPPORTED();
}
break;
}
case ROUND_L_D: { // Mips32r2 instruction.
double rounded = fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
i64 = static_cast<int64_t>(rounded);
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
double rounded = std::floor(fs + 0.5);
int64_t result = static_cast<int64_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
int64_t i64 = static_cast<int64_t>(result);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
UNSUPPORTED();
}
break;
}
case FLOOR_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::floor(fs));
case FLOOR_L_D: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
double rounded = std::floor(fs);
int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
UNSUPPORTED();
}
break;
case CEIL_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::ceil(fs));
}
case CEIL_L_D: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
double rounded = std::ceil(fs);
int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
UNSUPPORTED();
}
break;
}
case C_F_D:
UNIMPLEMENTED_MIPS();
break;
@ -2453,38 +2652,88 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
const int32_t& ft_reg,
const int32_t& fs_reg,
const int32_t& fd_reg) {
float fs, ft;
float fs, ft, fd;
fs = get_fpu_register_float(fs_reg);
ft = get_fpu_register_float(ft_reg);
int64_t ft_int = static_cast<int64_t>(get_fpu_register_double(ft_reg));
fd = get_fpu_register_float(fd_reg);
int32_t ft_int = bit_cast<int32_t>(ft);
int32_t fd_int = bit_cast<int32_t>(fd);
uint32_t cc, fcsr_cc;
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) {
case ADD_D:
case RINT: {
DCHECK(IsMipsArchVariant(kMips32r6));
float result, temp_result;
double temp;
float upper = std::ceil(fs);
float lower = std::floor(fs);
switch (get_fcsr_rounding_mode()) {
case kRoundToNearest:
if (upper - fs < fs - lower) {
result = upper;
} else if (upper - fs > fs - lower) {
result = lower;
} else {
temp_result = upper / 2;
float reminder = modf(temp_result, &temp);
if (reminder == 0) {
result = upper;
} else {
result = lower;
}
}
break;
case kRoundToZero:
result = (fs > 0 ? lower : upper);
break;
case kRoundToPlusInf:
result = upper;
break;
case kRoundToMinusInf:
result = lower;
break;
}
set_fpu_register_float(fd_reg, result);
if (result != fs) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
break;
}
case ADD_S:
set_fpu_register_float(fd_reg, fs + ft);
break;
case SUB_D:
case SUB_S:
set_fpu_register_float(fd_reg, fs - ft);
break;
case MUL_D:
case MUL_S:
set_fpu_register_float(fd_reg, fs * ft);
break;
case DIV_D:
case DIV_S:
set_fpu_register_float(fd_reg, fs / ft);
break;
case ABS_D:
case ABS_S:
set_fpu_register_float(fd_reg, fabs(fs));
break;
case MOV_D:
case MOV_S:
set_fpu_register_float(fd_reg, fs);
break;
case NEG_D:
case NEG_S:
set_fpu_register_float(fd_reg, -fs);
break;
case SQRT_D:
case SQRT_S:
set_fpu_register_float(fd_reg, fast_sqrt(fs));
break;
case RSQRT_S: {
float result = 1.0 / fast_sqrt(fs);
set_fpu_register_float(fd_reg, result);
break;
}
case RECIP_S: {
float result = 1.0 / fs;
set_fpu_register_float(fd_reg, result);
break;
}
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
@ -2509,18 +2758,224 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
case CVT_D_S:
set_fpu_register_double(fd_reg, static_cast<double>(fs));
break;
case SEL:
DCHECK(IsMipsArchVariant(kMips32r6));
set_fpu_register_float(fd_reg, (fd_int & 0x1) == 0 ? fs : ft);
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);
set_fpu_register_float(
fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_float(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);
set_fpu_register_float(
fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_float(fs_reg) : 0.0);
break;
case MOVZ_C: {
DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue();
int32_t rt = get_register(rt_reg);
if (rt == 0) {
set_fpu_register_float(fd_reg, fs);
}
break;
}
case MOVN_C: {
DCHECK(IsMipsArchVariant(kMips32r2));
int32_t rt_reg = instr->RtValue();
int32_t rt = get_register(rt_reg);
if (rt != 0) {
set_fpu_register_float(fd_reg, fs);
}
break;
}
case MOVF: {
// Same function field for MOVT.D and MOVF.D
uint32_t ft_cc = (ft_reg >> 2) & 0x7;
ft_cc = get_fcsr_condition_bit(ft_cc);
if (instr->Bit(16)) { // Read Tf bit.
// MOVT.D
if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs);
} else {
// MOVF.D
if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs);
}
break;
}
case TRUNC_W_S: { // Truncate single to word (round towards 0).
float rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case TRUNC_L_S: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
float rounded = trunc(fs);
int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
UNSUPPORTED();
}
break;
}
case FLOOR_W_S: // Round double to word towards negative infinity.
{
float rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case FLOOR_L_S: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
float rounded = std::floor(fs);
int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
UNSUPPORTED();
}
break;
}
case ROUND_W_S: {
float rounded = std::floor(fs + 0.5);
int32_t result = static_cast<int32_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
break;
}
case ROUND_L_S: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
float rounded = std::floor(fs + 0.5);
int64_t result = static_cast<int64_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
int64_t i64 = static_cast<int64_t>(result);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
UNSUPPORTED();
}
break;
}
case CEIL_W_S: // Round double to word towards positive infinity.
{
float rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case CEIL_L_S: { // Mips32r2 instruction.
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
float rounded = std::ceil(fs);
int64_t i64 = static_cast<int64_t>(rounded);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
} else {
UNSUPPORTED();
}
break;
}
case MIN:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
set_fpu_register_float(fd_reg, (fs >= ft) ? ft : fs);
}
break;
case MAX:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
set_fpu_register_float(fd_reg, (fs <= ft) ? ft : fs);
}
break;
case MINA:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
float result;
if (fabs(fs) > fabs(ft)) {
result = ft;
} else if (fabs(fs) < fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_float(fd_reg, result);
}
break;
case MAXA:
DCHECK(IsMipsArchVariant(kMips32r6));
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
float result;
if (fabs(fs) < fabs(ft)) {
result = ft;
} else if (fabs(fs) > fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_float(fd_reg, result);
}
break;
default:
// CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
// CVT_W_S CVT_L_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();
}

View File

@ -178,6 +178,9 @@ class Simulator {
void set_fcsr_rounding_mode(FPURoundingMode mode);
unsigned int get_fcsr_rounding_mode();
bool set_fcsr_round_error(double original, double rounded);
bool set_fcsr_round_error(float original, float rounded);
bool set_fcsr_round64_error(double original, double rounded);
bool set_fcsr_round64_error(float original, float rounded);
void round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs);
// Special case of set_register and get_register to access the raw PC value.

View File

@ -2140,33 +2140,6 @@ void Assembler::maxa_d(FPURegister fd, FPURegister fs, FPURegister ft) {
}
void Assembler::sel(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
Instr instr = COP1 | fmt << kRsShift | ft.code() << kFtShift |
fs.code() << kFsShift | fd.code() << kFdShift | SEL;
emit(instr);
}
void Assembler::max(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MAX);
}
void Assembler::min(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MIN);
}
// GPR.
void Assembler::seleqz(Register rd, Register rs, Register rt) {
DCHECK(kArchVariant == kMips64r6);
@ -2174,14 +2147,6 @@ void Assembler::seleqz(Register rd, Register rs, Register rt) {
}
// FPR.
void Assembler::seleqz(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
// GPR.
void Assembler::selnez(Register rd, Register rs, Register rt) {
DCHECK(kArchVariant == kMips64r6);
@ -2189,15 +2154,6 @@ void Assembler::selnez(Register rd, Register rs, Register rt) {
}
// FPR.
void Assembler::selnez(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
}
// Bit twiddling.
void Assembler::clz(Register rd, Register rs) {
if (kArchVariant != kMips64r6) {
@ -2333,6 +2289,134 @@ void Assembler::DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
}
void Assembler::sel(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SEL);
}
void Assembler::sel_s(FPURegister fd, FPURegister fs, FPURegister ft) {
sel(S, fd, fs, ft);
}
void Assembler::sel_d(FPURegister fd, FPURegister fs, FPURegister ft) {
sel(D, fd, fs, ft);
}
void Assembler::max(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MAX);
}
void Assembler::min(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MIN);
}
// FPR.
void Assembler::seleqz(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
void Assembler::seleqz_d(FPURegister fd, FPURegister fs, FPURegister ft) {
seleqz(D, fd, fs, ft);
}
void Assembler::seleqz_s(FPURegister fd, FPURegister fs, FPURegister ft) {
seleqz(S, fd, fs, ft);
}
void Assembler::selnez_d(FPURegister fd, FPURegister fs, FPURegister ft) {
selnez(D, fd, fs, ft);
}
void Assembler::selnez_s(FPURegister fd, FPURegister fs, FPURegister ft) {
selnez(S, fd, fs, ft);
}
void Assembler::movz_s(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(kArchVariant == kMips64r2);
GenInstrRegister(COP1, S, rt, fs, fd, MOVZ_C);
}
void Assembler::movz_d(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(kArchVariant == kMips64r2);
GenInstrRegister(COP1, D, rt, fs, fd, MOVZ_C);
}
void Assembler::movt_s(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(kArchVariant == kMips64r2);
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 1;
GenInstrRegister(COP1, S, ft, fs, fd, MOVF);
}
void Assembler::movt_d(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(kArchVariant == kMips64r2);
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 1;
GenInstrRegister(COP1, D, ft, fs, fd, MOVF);
}
void Assembler::movf_s(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(kArchVariant == kMips64r2);
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 0;
GenInstrRegister(COP1, S, ft, fs, fd, MOVF);
}
void Assembler::movf_d(FPURegister fd, FPURegister fs, uint16_t cc) {
DCHECK(kArchVariant == kMips64r2);
FPURegister ft;
ft.code_ = (cc & 0x0007) << 2 | 0;
GenInstrRegister(COP1, D, ft, fs, fd, MOVF);
}
void Assembler::movn_s(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(kArchVariant == kMips64r2);
GenInstrRegister(COP1, S, rt, fs, fd, MOVN_C);
}
void Assembler::movn_d(FPURegister fd, FPURegister fs, Register rt) {
DCHECK(kArchVariant == kMips64r2);
GenInstrRegister(COP1, D, rt, fs, fd, MOVN_C);
}
// FPR.
void Assembler::selnez(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
}
// Arithmetic.
void Assembler::add_s(FPURegister fd, FPURegister fs, FPURegister ft) {
@ -2396,6 +2480,11 @@ void Assembler::mov_d(FPURegister fd, FPURegister fs) {
}
void Assembler::mov_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, MOV_D);
}
void Assembler::neg_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, NEG_D);
}
@ -2416,8 +2505,27 @@ void Assembler::sqrt_d(FPURegister fd, FPURegister fs) {
}
// Conversions.
void Assembler::rsqrt_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, RSQRT_S);
}
void Assembler::rsqrt_d(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, D, f0, fs, fd, RSQRT_D);
}
void Assembler::recip_d(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, D, f0, fs, fd, RECIP_D);
}
void Assembler::recip_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, RECIP_S);
}
// Conversions.
void Assembler::cvt_w_s(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, S, f0, fs, fd, CVT_W_S);
}
@ -2476,18 +2584,18 @@ void Assembler::rint_d(FPURegister fd, FPURegister fs) { rint(D, fd, fs); }
void Assembler::rint(SecondaryField fmt, FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r6);
GenInstrRegister(COP1, D, f0, fs, fd, RINT);
GenInstrRegister(COP1, fmt, f0, fs, fd, RINT);
}
void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r2);
DCHECK(kArchVariant == kMips64r2 || kArchVariant == kMips64r6);
GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S);
}
void Assembler::cvt_l_d(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r2);
DCHECK(kArchVariant == kMips64r2 || kArchVariant == kMips64r6);
GenInstrRegister(COP1, D, f0, fs, fd, CVT_L_D);
}
@ -2534,16 +2642,16 @@ void Assembler::ceil_l_d(FPURegister fd, FPURegister fs) {
}
void Assembler::mina(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
void Assembler::mina(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MINA);
}
void Assembler::maxa(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
void Assembler::maxa(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft) {
DCHECK(kArchVariant == kMips64r6);
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MAXA);
@ -2556,7 +2664,7 @@ void Assembler::cvt_s_w(FPURegister fd, FPURegister fs) {
void Assembler::cvt_s_l(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r2);
DCHECK(kArchVariant == kMips64r2 || kArchVariant == kMips64r6);
GenInstrRegister(COP1, L, f0, fs, fd, CVT_S_L);
}
@ -2572,7 +2680,7 @@ void Assembler::cvt_d_w(FPURegister fd, FPURegister fs) {
void Assembler::cvt_d_l(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips64r2);
DCHECK(kArchVariant == kMips64r2 || kArchVariant == kMips64r6);
GenInstrRegister(COP1, L, f0, fs, fd, CVT_D_L);
}
@ -2612,6 +2720,7 @@ void Assembler::c(FPUCondition cond, SecondaryField fmt,
FPURegister fs, FPURegister ft, uint16_t cc) {
DCHECK(kArchVariant != kMips64r6);
DCHECK(is_uint3(cc));
DCHECK(fmt == S || fmt == D);
DCHECK((fmt & ~(31 << kRsShift)) == 0);
Instr instr = COP1 | fmt | ft.code() << kFtShift | fs.code() << kFsShift
| cc << 8 | 3 << 4 | cond;

View File

@ -881,12 +881,27 @@ class Assembler : public AssemblerBase {
void movf(Register rd, Register rs, uint16_t cc = 0);
void sel(SecondaryField fmt, FPURegister fd, FPURegister fs, FPURegister ft);
void sel_s(FPURegister fd, FPURegister fs, FPURegister ft);
void sel_d(FPURegister fd, FPURegister fs, FPURegister ft);
void seleqz(Register rd, Register rs, Register rt);
void seleqz(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft);
void selnez(Register rs, Register rt, Register rd);
void selnez(SecondaryField fmt, FPURegister fd, FPURegister fs,
FPURegister ft);
void seleqz_d(FPURegister fd, FPURegister fs, FPURegister ft);
void seleqz_s(FPURegister fd, FPURegister fs, FPURegister ft);
void selnez_d(FPURegister fd, FPURegister fs, FPURegister ft);
void selnez_s(FPURegister fd, FPURegister fs, FPURegister ft);
void movz_s(FPURegister fd, FPURegister fs, Register rt);
void movz_d(FPURegister fd, FPURegister fs, Register rt);
void movt_s(FPURegister fd, FPURegister fs, uint16_t cc);
void movt_d(FPURegister fd, FPURegister fs, uint16_t cc);
void movf_s(FPURegister fd, FPURegister fs, uint16_t cc);
void movf_d(FPURegister fd, FPURegister fs, uint16_t cc);
void movn_s(FPURegister fd, FPURegister fs, Register rt);
void movn_d(FPURegister fd, FPURegister fs, Register rt);
// Bit twiddling.
void clz(Register rd, Register rs);
void ins_(Register rt, Register rs, uint16_t pos, uint16_t size);
@ -926,10 +941,15 @@ class Assembler : public AssemblerBase {
void abs_s(FPURegister fd, FPURegister fs);
void abs_d(FPURegister fd, FPURegister fs);
void mov_d(FPURegister fd, FPURegister fs);
void mov_s(FPURegister fd, FPURegister fs);
void neg_s(FPURegister fd, FPURegister fs);
void neg_d(FPURegister fd, FPURegister fs);
void sqrt_s(FPURegister fd, FPURegister fs);
void sqrt_d(FPURegister fd, FPURegister fs);
void rsqrt_s(FPURegister fd, FPURegister fs);
void rsqrt_d(FPURegister fd, FPURegister fs);
void recip_d(FPURegister fd, FPURegister fs);
void recip_s(FPURegister fd, FPURegister fs);
// Conversion.
void cvt_w_s(FPURegister fd, FPURegister fs);

View File

@ -470,6 +470,14 @@ enum SecondaryField {
L = ((2 << 3) + 5) << 21,
PS = ((2 << 3) + 6) << 21,
// COP1 Encoding of Function Field When rs=S.
ADD_S = ((0 << 3) + 0),
SUB_S = ((0 << 3) + 1),
MUL_S = ((0 << 3) + 2),
DIV_S = ((0 << 3) + 3),
ABS_S = ((0 << 3) + 5),
SQRT_S = ((0 << 3) + 4),
MOV_S = ((0 << 3) + 6),
NEG_S = ((0 << 3) + 7),
ROUND_L_S = ((1 << 3) + 0),
TRUNC_L_S = ((1 << 3) + 1),
CEIL_L_S = ((1 << 3) + 2),
@ -478,6 +486,8 @@ enum SecondaryField {
TRUNC_W_S = ((1 << 3) + 5),
CEIL_W_S = ((1 << 3) + 6),
FLOOR_W_S = ((1 << 3) + 7),
RECIP_S = ((2 << 3) + 5),
RSQRT_S = ((2 << 3) + 6),
CVT_D_S = ((4 << 3) + 1),
CVT_W_S = ((4 << 3) + 4),
CVT_L_S = ((4 << 3) + 5),
@ -499,10 +509,8 @@ enum SecondaryField {
TRUNC_W_D = ((1 << 3) + 5),
CEIL_W_D = ((1 << 3) + 6),
FLOOR_W_D = ((1 << 3) + 7),
MIN = ((3 << 3) + 4),
MINA = ((3 << 3) + 5),
MAX = ((3 << 3) + 6),
MAXA = ((3 << 3) + 7),
RECIP_D = ((2 << 3) + 5),
RSQRT_D = ((2 << 3) + 6),
CVT_S_D = ((4 << 3) + 0),
CVT_W_D = ((4 << 3) + 4),
CVT_L_D = ((4 << 3) + 5),
@ -556,7 +564,14 @@ enum SecondaryField {
CMP_SUGT = ((3 << 3) + 6), // Reserved, not implemented.
CMP_SOGT = ((3 << 3) + 7), // Reserved, not implemented.
MIN = ((3 << 3) + 4),
MINA = ((3 << 3) + 5),
MAX = ((3 << 3) + 6),
MAXA = ((3 << 3) + 7),
SEL = ((2 << 3) + 0),
MOVF = ((2 << 3) + 1), // Function field for MOVT.fmt and MOVF.fmt
MOVZ_C = ((2 << 3) + 2), // COP1 on FPR registers.
MOVN_C = ((2 << 3) + 3), // COP1 on FPR registers.
SELEQZ_C = ((2 << 3) + 4), // COP1 on FPR registers.
SELNEZ_C = ((2 << 3) + 7), // COP1 on FPR registers.

View File

@ -520,12 +520,28 @@ bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
case RINT:
Format(instr, "rint.'t 'fd, 'fs");
break;
case SEL:
Format(instr, "sel.'t 'fd, 'fs, 'ft");
break;
case SELEQZ_C:
Format(instr, "seleqz.'t 'fd, 'fs, 'ft");
break;
case SELNEZ_C:
Format(instr, "selnez.'t 'fd, 'fs, 'ft");
break;
case MOVZ_C:
Format(instr, "movz.'t 'fd, 'fs, 'rt");
break;
case MOVN_C:
Format(instr, "movn.'t 'fd, 'fs, 'rt");
break;
case MOVF:
if (instr->Bit(16)) {
Format(instr, "movt.'t 'fd, 'fs, 'Cc");
} else {
Format(instr, "movf.'t 'fd, 'fs, 'Cc");
}
break;
case MIN:
Format(instr, "min.'t 'fd, 'fs, 'ft");
break;
@ -562,6 +578,12 @@ bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
case SQRT_D:
Format(instr, "sqrt.'t 'fd, 'fs");
break;
case RECIP_D:
Format(instr, "recip.'t 'fd, 'fs");
break;
case RSQRT_D:
Format(instr, "rsqrt.'t 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.'t 'fd, 'fs");
break;

View File

@ -1188,6 +1188,16 @@ bool Simulator::test_fcsr_bit(uint32_t cc) {
}
void Simulator::set_fcsr_rounding_mode(FPURoundingMode mode) {
FCSR_ |= mode & kFPURoundingModeMask;
}
unsigned int Simulator::get_fcsr_rounding_mode() {
return FCSR_ & kFPURoundingModeMask;
}
// Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round_error(double original, double rounded) {
@ -1209,7 +1219,7 @@ bool Simulator::set_fcsr_round_error(double original, double rounded) {
ret = true;
}
if (rounded > max_int32 || rounded < min_int32) {
if (rounded >= max_int32 || rounded <= min_int32) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
@ -1241,7 +1251,69 @@ bool Simulator::set_fcsr_round64_error(double original, double rounded) {
ret = true;
}
if (rounded > max_int64 || rounded < min_int64) {
if (rounded >= max_int64 || rounded <= min_int64) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
return ret;
}
bool Simulator::set_fcsr_round_error(float original, float rounded) {
bool ret = false;
double max_int32 = std::numeric_limits<int32_t>::max();
double min_int32 = std::numeric_limits<int32_t>::min();
if (!std::isfinite(original) || !std::isfinite(rounded)) {
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
if (original != rounded) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) {
set_fcsr_bit(kFCSRUnderflowFlagBit, true);
ret = true;
}
if (rounded >= max_int32 || rounded <= min_int32) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
return ret;
}
// Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round64_error(float original, float rounded) {
bool ret = false;
double max_int64 = std::numeric_limits<int64_t>::max();
double min_int64 = std::numeric_limits<int64_t>::min();
if (!std::isfinite(original) || !std::isfinite(rounded)) {
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
ret = true;
}
if (original != rounded) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
if (rounded < FLT_MIN && rounded > -FLT_MIN && rounded != 0) {
set_fcsr_bit(kFCSRUnderflowFlagBit, true);
ret = true;
}
if (rounded >= max_int64 || rounded <= min_int64) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
@ -2356,37 +2428,88 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
const int32_t& fs_reg,
const int32_t& ft_reg,
const int32_t& fd_reg) {
float fs, ft;
float fs, ft, fd;
fs = get_fpu_register_float(fs_reg);
ft = get_fpu_register_float(ft_reg);
fd = get_fpu_register_float(fd_reg);
int32_t ft_int = bit_cast<int32_t>(ft);
int32_t fd_int = bit_cast<int32_t>(fd);
uint32_t cc, fcsr_cc;
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->FunctionFieldRaw()) {
case ADD_D:
case RINT: {
DCHECK(kArchVariant == kMips64r6);
float result, temp_result;
double temp;
float upper = std::ceil(fs);
float lower = std::floor(fs);
switch (get_fcsr_rounding_mode()) {
case kRoundToNearest:
if (upper - fs < fs - lower) {
result = upper;
} else if (upper - fs > fs - lower) {
result = lower;
} else {
temp_result = upper / 2;
float reminder = modf(temp_result, &temp);
if (reminder == 0) {
result = upper;
} else {
result = lower;
}
}
break;
case kRoundToZero:
result = (fs > 0 ? lower : upper);
break;
case kRoundToPlusInf:
result = upper;
break;
case kRoundToMinusInf:
result = lower;
break;
}
set_fpu_register_float(fd_reg, result);
if (result != fs) {
set_fcsr_bit(kFCSRInexactFlagBit, true);
}
break;
}
case ADD_S:
set_fpu_register_float(fd_reg, fs + ft);
break;
case SUB_D:
case SUB_S:
set_fpu_register_float(fd_reg, fs - ft);
break;
case MUL_D:
case MUL_S:
set_fpu_register_float(fd_reg, fs * ft);
break;
case DIV_D:
case DIV_S:
set_fpu_register_float(fd_reg, fs / ft);
break;
case ABS_D:
case ABS_S:
set_fpu_register_float(fd_reg, fabs(fs));
break;
case MOV_D:
case MOV_S:
set_fpu_register_float(fd_reg, fs);
break;
case NEG_D:
case NEG_S:
set_fpu_register_float(fd_reg, -fs);
break;
case SQRT_D:
case SQRT_S:
set_fpu_register_float(fd_reg, fast_sqrt(fs));
break;
case RSQRT_S: {
float result = 1.0 / fast_sqrt(fs);
set_fpu_register_float(fd_reg, result);
break;
}
case RECIP_S: {
float result = 1.0 / fs;
set_fpu_register_float(fd_reg, result);
break;
}
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
@ -2411,6 +2534,202 @@ void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
case CVT_D_S:
set_fpu_register_double(fd_reg, static_cast<double>(fs));
break;
case TRUNC_W_S: { // Truncate single to word (round towards 0).
float rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case TRUNC_L_S: { // Mips64r2 instruction.
float rounded = trunc(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case ROUND_W_S: {
float rounded = std::floor(fs + 0.5);
int32_t result = static_cast<int32_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
break;
}
case ROUND_L_S: { // Mips64r2 instruction.
float rounded = std::floor(fs + 0.5);
int64_t result = static_cast<int64_t>(rounded);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
int64_t i64 = static_cast<int64_t>(result);
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case FLOOR_L_S: { // Mips64r2 instruction.
float rounded = floor(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case FLOOR_W_S: // Round double to word towards negative infinity.
{
float rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register_word(fd_reg, kFPUInvalidResult);
}
} break;
case CEIL_W_S: // Round double to word towards positive infinity.
{
float rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
} break;
case CEIL_L_S: { // Mips64r2 instruction.
float rounded = ceil(fs);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}
break;
}
case MINA:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
float result;
if (fabs(fs) > fabs(ft)) {
result = ft;
} else if (fabs(fs) < fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_float(fd_reg, result);
}
break;
case MAXA:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
float result;
if (fabs(fs) < fabs(ft)) {
result = ft;
} else if (fabs(fs) > fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_float(fd_reg, result);
}
break;
case MIN:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
set_fpu_register_float(fd_reg, (fs >= ft) ? ft : fs);
}
break;
case MAX:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_float(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_float(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_float(fd_reg, fs);
} else {
set_fpu_register_float(fd_reg, (fs <= ft) ? ft : fs);
}
break;
case SEL:
DCHECK(kArchVariant == kMips64r6);
set_fpu_register_float(fd_reg, (fd_int & 0x1) == 0 ? fs : ft);
break;
case SELEQZ_C:
DCHECK(kArchVariant == kMips64r6);
set_fpu_register_float(
fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_float(fs_reg) : 0.0);
break;
case SELNEZ_C:
DCHECK(kArchVariant == kMips64r6);
set_fpu_register_float(
fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_float(fs_reg) : 0.0);
break;
case MOVZ_C: {
DCHECK(kArchVariant == kMips64r2);
int32_t rt_reg = instr->RtValue();
int64_t rt = get_register(rt_reg);
if (rt == 0) {
set_fpu_register_float(fd_reg, fs);
}
break;
}
case MOVN_C: {
DCHECK(kArchVariant == kMips64r2);
int32_t rt_reg = instr->RtValue();
int64_t rt = get_register(rt_reg);
if (rt != 0) {
set_fpu_register_float(fd_reg, fs);
}
break;
}
case MOVF: {
// Same function field for MOVT.D and MOVF.D
uint32_t ft_cc = (ft_reg >> 2) & 0x7;
ft_cc = get_fcsr_condition_bit(ft_cc);
if (instr->Bit(16)) { // Read Tf bit.
// MOVT.D
if (test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs);
} else {
// MOVF.D
if (!test_fcsr_bit(ft_cc)) set_fpu_register_float(fd_reg, fs);
}
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.
@ -2426,7 +2745,9 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
double ft, fs, fd;
uint32_t cc, fcsr_cc;
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
if (instr->FunctionFieldRaw() != MOVF) {
ft = get_fpu_register_double(ft_reg);
}
fd = get_fpu_register_double(fd_reg);
cc = instr->FCccValue();
fcsr_cc = get_fcsr_condition_bit(cc);
@ -2438,7 +2759,7 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
double result, temp, temp_result;
double upper = std::ceil(fs);
double lower = std::floor(fs);
switch (FCSR_ & 0x3) {
switch (get_fcsr_rounding_mode()) {
case kRoundToNearest:
if (upper - fs < fs - lower) {
result = upper;
@ -2482,6 +2803,79 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
DCHECK(kArchVariant == kMips64r6);
set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0);
break;
case MOVZ_C: {
DCHECK(kArchVariant == kMips64r2);
int32_t rt_reg = instr->RtValue();
int64_t rt = get_register(rt_reg);
if (rt == 0) {
set_fpu_register_double(fd_reg, fs);
}
break;
}
case MOVN_C: {
DCHECK(kArchVariant == kMips64r2);
int32_t rt_reg = instr->RtValue();
int64_t rt = get_register(rt_reg);
if (rt != 0) {
set_fpu_register_double(fd_reg, fs);
}
break;
}
case MOVF: {
// Same function field for MOVT.D and MOVF.D
uint32_t ft_cc = (ft_reg >> 2) & 0x7;
ft_cc = get_fcsr_condition_bit(ft_cc);
if (instr->Bit(16)) { // Read Tf bit.
// MOVT.D
if (test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs);
} else {
// MOVF.D
if (!test_fcsr_bit(ft_cc)) set_fpu_register_double(fd_reg, fs);
}
break;
}
case MINA:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_double(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else {
double result;
if (fabs(fs) > fabs(ft)) {
result = ft;
} else if (fabs(fs) < fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_double(fd_reg, result);
}
break;
case MAXA:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_double(fs_reg);
if (std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else if (std::isnan(fs) && !std::isnan(ft)) {
set_fpu_register_double(fd_reg, ft);
} else if (!std::isnan(fs) && std::isnan(ft)) {
set_fpu_register_double(fd_reg, fs);
} else {
double result;
if (fabs(fs) < fabs(ft)) {
result = ft;
} else if (fabs(fs) > fabs(ft)) {
result = fs;
} else {
result = (fs > ft ? fs : ft);
}
set_fpu_register_double(fd_reg, result);
}
break;
case MIN:
DCHECK(kArchVariant == kMips64r6);
fs = get_fpu_register_double(fs_reg);
@ -2532,6 +2926,16 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
case SQRT_D:
set_fpu_register_double(fd_reg, fast_sqrt(fs));
break;
case RSQRT_D: {
double result = 1.0 / fast_sqrt(fs);
set_fpu_register_double(fd_reg, result);
break;
}
case RECIP_D: {
double result = 1.0 / fs;
set_fpu_register_double(fd_reg, result);
break;
}
case C_UN_D:
set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
break;
@ -2618,10 +3022,15 @@ void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
break;
}
case ROUND_L_D: { // Mips64r2 instruction.
// check error cases
double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
double rounded = std::floor(fs + 0.5);
int64_t result = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, result);
if ((result & 1) != 0 && result - fs == 0.5) {
// If the number is halfway between two integers,
// round to the even one.
result--;
}
int64_t i64 = static_cast<int64_t>(result);
set_fpu_register(fd_reg, i64);
if (set_fcsr_round64_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPU64InvalidResult);
}

View File

@ -207,11 +207,14 @@ class Simulator {
bool test_fcsr_bit(uint32_t cc);
bool set_fcsr_round_error(double original, double rounded);
bool set_fcsr_round64_error(double original, double rounded);
bool set_fcsr_round_error(float original, float rounded);
bool set_fcsr_round64_error(float original, float rounded);
void round_according_to_fcsr(double toRound, double& rounded,
int32_t& rounded_int, double fs);
void round64_according_to_fcsr(double toRound, double& rounded,
int64_t& rounded_int, double fs);
void set_fcsr_rounding_mode(FPURoundingMode mode);
unsigned int get_fcsr_rounding_mode();
// Special case of set_register and get_register to access the raw PC value.
void set_pc(int64_t value);
int64_t get_pc() const;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -525,21 +525,94 @@ TEST(Type0) {
}
// Tests only seleqz, selnez, seleqz.fmt and selnez.fmt
TEST(Type1) {
SET_UP();
if (IsMipsArchVariant(kMips32r6)) {
SET_UP();
COMPARE(seleqz(a0, a1, a2), "00a62035 seleqz a0, a1, a2");
COMPARE(selnez(a0, a1, a2), "00a62037 selnez a0, a1, a2");
COMPARE(seleqz(D, f3, f4, f5), "462520d4 seleqz.d f3, f4, f5");
COMPARE(selnez(D, f3, f4, f5), "462520d7 selnez.d f3, f4, f5");
COMPARE(seleqz_d(f3, f4, f5), "462520d4 seleqz.d f3, f4, f5");
COMPARE(selnez_d(f3, f4, f5), "462520d7 selnez.d f3, f4, f5");
COMPARE(seleqz_s(f3, f4, f5), "460520d4 seleqz.s f3, f4, f5");
COMPARE(selnez_s(f3, f4, f5), "460520d7 selnez.s f3, f4, f5");
COMPARE(min_d(f3, f4, f5), "462520dc min.d f3, f4, f5");
COMPARE(max_d(f3, f4, f5), "462520de max.d f3, f4, f5");
COMPARE(rint_d(f8, f6), "4620321a rint.d f8, f6");
VERIFY_RUN();
COMPARE(sel_s(f3, f4, f5), "460520d0 sel.s f3, f4, f5");
COMPARE(sel_d(f3, f4, f5), "462520d0 sel.d f3, f4, f5");
COMPARE(rint_d(f8, f6), "4620321a rint.d f8, f6");
COMPARE(rint_s(f8, f6), "4600321a rint.s f8, f6");
COMPARE(min_s(f3, f4, f5), "460520dc min.s f3, f4, f5");
COMPARE(max_s(f3, f4, f5), "460520de max.s f3, f4, f5");
COMPARE(mina_d(f3, f4, f5), "462520dd mina.d f3, f4, f5");
COMPARE(mina_s(f3, f4, f5), "460520dd mina.s f3, f4, f5");
COMPARE(maxa_d(f3, f4, f5), "462520df maxa.d f3, f4, f5");
COMPARE(maxa_s(f3, f4, f5), "460520df maxa.s f3, f4, f5");
}
COMPARE(trunc_w_d(f8, f6), "4620320d trunc.w.d f8, f6");
COMPARE(trunc_w_s(f8, f6), "4600320d trunc.w.s f8, f6");
COMPARE(round_w_s(f8, f6), "4600320c round.w.s f8, f6");
COMPARE(round_w_d(f8, f6), "4620320c round.w.d f8, f6");
COMPARE(round_l_s(f8, f6), "46003208 round.l.s f8, f6");
COMPARE(round_l_d(f8, f6), "46203208 round.l.d f8, f6");
COMPARE(floor_w_s(f8, f6), "4600320f floor.w.s f8, f6");
COMPARE(floor_w_d(f8, f6), "4620320f floor.w.d f8, f6");
COMPARE(floor_l_s(f8, f6), "4600320b floor.l.s f8, f6");
COMPARE(floor_l_d(f8, f6), "4620320b floor.l.d f8, f6");
COMPARE(ceil_w_s(f8, f6), "4600320e ceil.w.s f8, f6");
COMPARE(ceil_w_d(f8, f6), "4620320e ceil.w.d f8, f6");
COMPARE(ceil_l_s(f8, f6), "4600320a ceil.l.s f8, f6");
COMPARE(ceil_l_d(f8, f6), "4620320a ceil.l.d f8, f6");
COMPARE(sub_s(f10, f8, f6), "46064281 sub.s f10, f8, f6");
COMPARE(sub_d(f10, f8, f6), "46264281 sub.d f10, f8, f6");
COMPARE(sqrt_s(f8, f6), "46003204 sqrt.s f8, f6");
COMPARE(sqrt_d(f8, f6), "46203204 sqrt.d f8, f6");
COMPARE(neg_s(f8, f6), "46003207 neg.s f8, f6");
COMPARE(neg_d(f8, f6), "46203207 neg.d f8, f6");
COMPARE(mul_s(f8, f6, f4), "46043202 mul.s f8, f6, f4");
COMPARE(mul_d(f8, f6, f4), "46243202 mul.d f8, f6, f4");
COMPARE(rsqrt_s(f8, f6), "46003216 rsqrt.s f8, f6");
COMPARE(rsqrt_d(f8, f6), "46203216 rsqrt.d f8, f6");
COMPARE(recip_s(f8, f6), "46003215 recip.s f8, f6");
COMPARE(recip_d(f8, f6), "46203215 recip.d f8, f6");
COMPARE(mov_s(f6, f4), "46002186 mov.s f6, f4");
COMPARE(mov_d(f6, f4), "46202186 mov.d f6, f4");
if (IsMipsArchVariant(kMips32r2)) {
COMPARE(trunc_l_d(f8, f6), "46203209 trunc.l.d f8, f6");
COMPARE(trunc_l_s(f8, f6), "46003209 trunc.l.s f8, f6");
COMPARE(movz_s(f6, f4, t0), "46082192 movz.s f6, f4, t0");
COMPARE(movz_d(f6, f4, t0), "46282192 movz.d f6, f4, t0");
COMPARE(movt_s(f6, f4, 4), "46112191 movt.s f6, f4, cc(1)");
COMPARE(movt_d(f6, f4, 4), "46312191 movt.d f6, f4, cc(1)");
COMPARE(movf_s(f6, f4, 4), "46102191 movf.s f6, f4, cc(1)");
COMPARE(movf_d(f6, f4, 4), "46302191 movf.d f6, f4, cc(1)");
COMPARE(movn_s(f6, f4, t0), "46082193 movn.s f6, f4, t0");
COMPARE(movn_d(f6, f4, t0), "46282193 movn.d f6, f4, t0");
}
VERIFY_RUN();
}

View File

@ -674,18 +674,92 @@ TEST(Type0) {
TEST(Type1) {
SET_UP();
if (kArchVariant == kMips64r6) {
SET_UP();
COMPARE(seleqz(a0, a1, a2), "00a62035 seleqz a0, a1, a2");
COMPARE(selnez(a0, a1, a2), "00a62037 selnez a0, a1, a2");
COMPARE(seleqz(D, f3, f4, f5), "462520d4 seleqz.d f3, f4, f5");
COMPARE(selnez(D, f3, f4, f5), "462520d7 selnez.d f3, f4, f5");
COMPARE(seleqz(S, f3, f4, f5), "460520d4 seleqz.s f3, f4, f5");
COMPARE(selnez(S, f3, f4, f5), "460520d7 selnez.s f3, f4, f5");
COMPARE(min_d(f3, f4, f5), "462520dc min.d f3, f4, f5");
COMPARE(max_d(f3, f4, f5), "462520de max.d f3, f4, f5");
COMPARE(sel(S, f3, f4, f5), "460520d0 sel.s f3, f4, f5");
COMPARE(sel(D, f3, f4, f5), "462520d0 sel.d f3, f4, f5");
COMPARE(rint_d(f8, f6), "4620321a rint.d f8, f6");
VERIFY_RUN();
COMPARE(min_s(f3, f4, f5), "460520dc min.s f3, f4, f5");
COMPARE(max_s(f3, f4, f5), "460520de max.s f3, f4, f5");
COMPARE(rint(S, f8, f6), "4600321a rint.s f8, f6");
COMPARE(mina_d(f3, f4, f5), "462520dd mina.d f3, f4, f5");
COMPARE(mina_s(f3, f4, f5), "460520dd mina.s f3, f4, f5");
COMPARE(maxa_d(f3, f4, f5), "462520df maxa.d f3, f4, f5");
COMPARE(maxa_s(f3, f4, f5), "460520df maxa.s f3, f4, f5");
}
COMPARE(trunc_w_d(f8, f6), "4620320d trunc.w.d f8, f6");
COMPARE(trunc_w_s(f8, f6), "4600320d trunc.w.s f8, f6");
COMPARE(round_w_s(f8, f6), "4600320c round.w.s f8, f6");
COMPARE(round_w_d(f8, f6), "4620320c round.w.d f8, f6");
COMPARE(round_l_s(f8, f6), "46003208 round.l.s f8, f6");
COMPARE(round_l_d(f8, f6), "46203208 round.l.d f8, f6");
COMPARE(floor_w_s(f8, f6), "4600320f floor.w.s f8, f6");
COMPARE(floor_w_d(f8, f6), "4620320f floor.w.d f8, f6");
COMPARE(floor_l_s(f8, f6), "4600320b floor.l.s f8, f6");
COMPARE(floor_l_d(f8, f6), "4620320b floor.l.d f8, f6");
COMPARE(ceil_w_s(f8, f6), "4600320e ceil.w.s f8, f6");
COMPARE(ceil_w_d(f8, f6), "4620320e ceil.w.d f8, f6");
COMPARE(ceil_l_s(f8, f6), "4600320a ceil.l.s f8, f6");
COMPARE(ceil_l_d(f8, f6), "4620320a ceil.l.d f8, f6");
COMPARE(sub_s(f10, f8, f6), "46064281 sub.s f10, f8, f6");
COMPARE(sub_d(f10, f8, f6), "46264281 sub.d f10, f8, f6");
COMPARE(sqrt_s(f8, f6), "46003204 sqrt.s f8, f6");
COMPARE(sqrt_d(f8, f6), "46203204 sqrt.d f8, f6");
COMPARE(neg_s(f8, f6), "46003207 neg.s f8, f6");
COMPARE(neg_d(f8, f6), "46203207 neg.d f8, f6");
COMPARE(mul_s(f8, f6, f4), "46043202 mul.s f8, f6, f4");
COMPARE(mul_d(f8, f6, f4), "46243202 mul.d f8, f6, f4");
COMPARE(rsqrt_s(f8, f6), "46003216 rsqrt.s f8, f6");
COMPARE(rsqrt_d(f8, f6), "46203216 rsqrt.d f8, f6");
COMPARE(recip_s(f8, f6), "46003215 recip.s f8, f6");
COMPARE(recip_d(f8, f6), "46203215 recip.d f8, f6");
COMPARE(mov_s(f6, f4), "46002186 mov.s f6, f4");
COMPARE(mov_d(f6, f4), "46202186 mov.d f6, f4");
if (kArchVariant == kMips64r2) {
COMPARE(trunc_l_d(f8, f6), "46203209 trunc.l.d f8, f6");
COMPARE(trunc_l_s(f8, f6), "46003209 trunc.l.s f8, f6");
COMPARE(movz_s(f6, f4, t0), "460c2192 movz.s f6, f4, t0");
COMPARE(movz_d(f6, f4, t0), "462c2192 movz.d f6, f4, t0");
COMPARE(movt_s(f6, f4, 4), "46112191 movt.s f6, f4, cc(1)");
COMPARE(movt_d(f6, f4, 4), "46312191 movt.d f6, f4, cc(1)");
COMPARE(movf_s(f6, f4, 4), "46102191 movf.s f6, f4, cc(1)");
COMPARE(movf_d(f6, f4, 4), "46302191 movf.d f6, f4, cc(1)");
COMPARE(movn_s(f6, f4, t0), "460c2193 movn.s f6, f4, t0");
COMPARE(movn_d(f6, f4, t0), "462c2193 movn.d f6, f4, t0");
}
VERIFY_RUN();
}