Use TRUNC.W instead of CVT.W on mips to convert from double to int as to not

depend on the current rounding mode (fixes issue 25900).
Add support for TRUNC.W in assembler, disassembler, and simulator.
Fix disassembler to understand CVT.S.
Remove unused instructions.
Update co19 status file.

R=rmacnak@google.com

Review URL: https://codereview.chromium.org/1765623002 .
This commit is contained in:
Regis Crelier 2016-03-03 17:05:09 -08:00
parent 3b16570e07
commit 6219753e18
8 changed files with 137 additions and 124 deletions

View file

@ -585,6 +585,12 @@ class Assembler : public ValueObject {
EmitRType(SPECIAL2, rs, rd, rd, 0, CLZ);
}
// Convert a double in ds to a 32-bit signed int in fd rounding towards 0.
void truncwd(FRegister fd, DRegister ds) {
FRegister fs = static_cast<FRegister>(ds * 2);
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_TRUNC_W);
}
// Convert a 32-bit float in fs to a 64-bit double in dd.
void cvtds(DRegister dd, FRegister fs) {
FRegister fd = static_cast<FRegister>(dd * 2);
@ -597,23 +603,12 @@ class Assembler : public ValueObject {
EmitFpuRType(COP1, FMT_W, F0, fs, fd, COP1_CVT_D);
}
// Converts a 64-bit signed int in fs to a double in fd.
void cvtdl(DRegister dd, DRegister ds) {
FRegister fs = static_cast<FRegister>(ds * 2);
FRegister fd = static_cast<FRegister>(dd * 2);
EmitFpuRType(COP1, FMT_L, F0, fs, fd, COP1_CVT_D);
}
// Convert a 64-bit double in ds to a 32-bit float in fd.
void cvtsd(FRegister fd, DRegister ds) {
FRegister fs = static_cast<FRegister>(ds * 2);
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_CVT_S);
}
void cvtwd(FRegister fd, DRegister ds) {
FRegister fs = static_cast<FRegister>(ds * 2);
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_CVT_W);
}
void div(Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, R0, 0, DIV);
}

View file

@ -1976,6 +1976,101 @@ ASSEMBLER_TEST_RUN(Cop1COLE_not_taken, test) {
}
ASSEMBLER_TEST_GENERATE(Cop1TruncWD, assembler) {
__ LoadImmediate(D1, 42.9);
__ truncwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1TruncWD, test) {
typedef int (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1TruncWD_neg, assembler) {
__ LoadImmediate(D1, -42.9);
__ truncwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1TruncWD_neg, test) {
typedef int (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(-42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1TruncWD_NaN, assembler) {
// Double non-signaling NaN is 0x7FF8000000000000.
__ LoadImmediate(T0, 0x7FF80000);
__ mtc1(ZR, F2); // Load upper bits of NaN.
__ mtc1(T0, F3); // Load lower bits of NaN.
__ truncwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1TruncWD_NaN, test) {
typedef double (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(kMaxInt32, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1TruncWD_Inf, assembler) {
__ LoadImmediate(T0, 0x7FF00000); // +inf
__ mtc1(ZR, F2);
__ mtc1(T0, F3);
__ truncwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1TruncWD_Inf, test) {
typedef double (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(kMaxInt32, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1TruncWD_Overflow, assembler) {
__ LoadImmediate(D1, 2.0*kMaxInt32);
__ truncwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1TruncWD_Overflow, test) {
typedef double (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(kMaxInt32, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1TruncWD_Underflow, assembler) {
__ LoadImmediate(D1, 2.0*kMinInt32);
__ truncwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1TruncWD_Underflow, test) {
typedef double (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(kMaxInt32, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1CvtDW, assembler) {
__ LoadImmediate(T0, 42);
__ mtc1(T0, F2);
@ -2008,78 +2103,6 @@ ASSEMBLER_TEST_RUN(Cop1CvtDW_neg, test) {
}
ASSEMBLER_TEST_GENERATE(Cop1CvtDL, assembler) {
if (TargetCPUFeatures::mips_version() == MIPS32r2) {
__ LoadImmediate(T0, 0x1);
__ mtc1(ZR, F2);
__ mtc1(T0, F3); // D0 <- 0x100000000 = 4294967296
__ cvtdl(D0, D1);
} else {
__ LoadImmediate(D0, 4294967296.0);
}
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1CvtDL, test) {
typedef double (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
double res = EXECUTE_TEST_CODE_DOUBLE(SimpleCode, test->entry());
EXPECT_FLOAT_EQ(4294967296.0, res, 0.001);
}
ASSEMBLER_TEST_GENERATE(Cop1CvtDL_neg, assembler) {
if (TargetCPUFeatures::mips_version() == MIPS32r2) {
__ LoadImmediate(T0, 0xffffffff);
__ mtc1(T0, F2);
__ mtc1(T0, F3); // D0 <- 0xffffffffffffffff = -1
__ cvtdl(D0, D1);
} else {
__ LoadImmediate(D0, -1.0);
}
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1CvtDL_neg, test) {
typedef double (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
double res = EXECUTE_TEST_CODE_DOUBLE(SimpleCode, test->entry());
EXPECT_FLOAT_EQ(-1.0, res, 0.001);
}
ASSEMBLER_TEST_GENERATE(Cop1CvtWD, assembler) {
__ LoadImmediate(D1, 42.0);
__ cvtwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1CvtWD, test) {
typedef int (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1CvtWD_neg, assembler) {
__ LoadImmediate(D1, -42.0);
__ cvtwd(F0, D1);
__ mfc1(V0, F0);
__ Ret();
}
ASSEMBLER_TEST_RUN(Cop1CvtWD_neg, test) {
typedef int (*SimpleCode)() DART_UNUSED;
EXPECT(test != NULL);
EXPECT_EQ(-42, EXECUTE_TEST_CODE_INT32(SimpleCode, test->entry()));
}
ASSEMBLER_TEST_GENERATE(Cop1CvtSD, assembler) {
__ LoadImmediate(D2, -42.42);
__ cvtsd(F2, D2);

View file

@ -440,9 +440,9 @@ enum Cop1Function {
COP1_SQRT = 0x04,
COP1_MOV = 0x06,
COP1_NEG = 0x07,
COP1_TRUNC_W = 0x0d,
COP1_CVT_S = 0x20,
COP1_CVT_D = 0x21,
COP1_CVT_W = 0x24,
COP1_C_F = 0x30,
COP1_C_UN = 0x31,
COP1_C_EQ = 0x32,

View file

@ -581,12 +581,16 @@ void MIPSDecoder::DecodeCop1(Instr* instr) {
Format(instr, "c.ule.'fmt 'fs, 'ft");
break;
}
case COP1_CVT_D: {
Format(instr, "cvt.d.'fmt 'fd, 'fs");
case COP1_TRUNC_W: {
Format(instr, "trunc.w.'fmt 'fd, 'fs");
break;
}
case COP1_CVT_W: {
Format(instr, "cvt.w.'fmt 'fd, 'fs");
case COP1_CVT_S: {
Format(instr, "cvt.s.'fmt 'fd, 'fs");
break;
}
case COP1_CVT_D: {
Format(instr, "cvt.d.'fmt 'fd, 'fs");
break;
}
default: {

View file

@ -4206,7 +4206,7 @@ void DoubleToIntegerInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(result == V0);
ASSERT(result != value_obj);
__ LoadDFromOffset(DTMP, value_obj, Double::value_offset() - kHeapObjectTag);
__ cvtwd(STMP1, DTMP);
__ truncwd(STMP1, DTMP);
__ mfc1(result, STMP1);
// Overflow is signaled with minint.
@ -4229,7 +4229,7 @@ void DoubleToIntegerInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
instance_call()->token_pos(),
target,
kNumberOfArguments,
Object::null_array(), // No argument names.,
Object::null_array(), // No argument names.
locs(),
ICData::Handle());
__ Bind(&done);
@ -4252,7 +4252,7 @@ void DoubleToSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Label* deopt = compiler->AddDeoptStub(deopt_id(), ICData::kDeoptDoubleToSmi);
Register result = locs()->out(0).reg();
DRegister value = locs()->in(0).fpu_reg();
__ cvtwd(STMP1, value);
__ truncwd(STMP1, value);
__ mfc1(result, STMP1);
// Check for overflow and that it fits into Smi.

View file

@ -1538,7 +1538,7 @@ void Intrinsifier::DoubleToInteger(Assembler* assembler) {
__ lw(T0, Address(SP, 0 * kWordSize));
__ LoadDFromOffset(D0, T0, Double::value_offset() - kHeapObjectTag);
__ cvtwd(F2, D0);
__ truncwd(F2, D0);
__ mfc1(V0, F2);
// Overflow is signaled with minint.

View file

@ -1888,6 +1888,28 @@ void Simulator::DecodeCop1(Instr* instr) {
(fs_val <= ft_val) || isnan(fs_val) || isnan(ft_val));
break;
}
case COP1_TRUNC_W: {
switch (instr->FormatField()) {
case FMT_D: {
double fs_dbl = get_fregister_double(instr->FsField());
int32_t fs_int;
if (isnan(fs_dbl) || isinf(fs_dbl) || (fs_dbl > kMaxInt32) ||
(fs_dbl < kMinInt32)) {
fs_int = kMaxInt32;
} else {
fs_int = static_cast<int32_t>(fs_dbl);
}
set_fregister(instr->FdField(), fs_int);
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
break;
}
case COP1_CVT_D: {
switch (instr->FormatField()) {
case FMT_W: {
@ -1902,34 +1924,6 @@ void Simulator::DecodeCop1(Instr* instr) {
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
case FMT_L: {
int64_t fs_int = get_fregister_long(instr->FsField());
double fs_dbl = static_cast<double>(fs_int);
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
break;
}
case COP1_CVT_W: {
switch (instr->FormatField()) {
case FMT_D: {
double fs_dbl = get_fregister_double(instr->FsField());
int32_t fs_int;
if (isnan(fs_dbl) || isinf(fs_dbl) || (fs_dbl > INT_MAX) ||
(fs_dbl < INT_MIN)) {
fs_int = INT_MIN;
} else {
fs_int = static_cast<int32_t>(fs_dbl);
}
set_fregister(instr->FdField(), fs_int);
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);

View file

@ -59,9 +59,6 @@ LibTest/core/List/List_class_A01_t02: Pass, Slow
[ ($runtime == vm || $runtime == dart_precompiled || $runtime == dart_product) && ($arch != x64 && $arch != simarm64 && $arch != arm64) ]
LibTest/core/int/operator_left_shift_A01_t02: Fail # co19 issue 129
[ ($runtime == vm || $runtime == dart_precompiled || $runtime == dart_product) && $arch == mips ]
LibTest/core/double/toInt_A01_t01: Fail # Issue 25900
[ ($compiler == none || $compiler == precompiler) && ($runtime == vm || $runtime == dart_precompiled) && ($arch == mips || $arch == arm64) ]
# These tests take too much memory (300 MB) for our 1 GB test machine.
# co19 issue 673. http://code.google.com/p/co19/issues/detail?id=673