Commit d4a391bb authored by balazs.kilvady's avatar balazs.kilvady Committed by Commit bot

MIPS: Support r6 min, max floating point instructions.

Use macro instructions for min, max ops to get the same functionality on
pre-r6 and r6 targets.

BUG=
TEST=mjsunit/math-min-max, cctest/test-macro-assembler-mips64/min_max_nan, cctest/test-macro-assembler-mips/min_max_nan, cctest/test-assembler-mips64/min_max, cctest/test-assembler-mips/min_max

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

Cr-Commit-Position: refs/heads/master@{#35073}
parent ed2b3158
......@@ -2140,7 +2140,6 @@ void Assembler::lwc1(FPURegister fd, const MemOperand& src) {
void Assembler::ldc1(FPURegister fd, const MemOperand& src) {
// Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
// load to two 32-bit loads.
DCHECK(!src.rm().is(at));
if (IsFp32Mode()) { // fp32 mode.
if (is_int16(src.offset_) && is_int16(src.offset_ + kIntSize)) {
GenInstrImmediate(LWC1, src.rm(), fd,
......
......@@ -149,17 +149,15 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
// -- sp[(argc - n) * 8] : arg[n] (zero-based)
// -- sp[(argc + 1) * 8] : receiver
// -----------------------------------
Condition const cc = (kind == MathMaxMinKind::kMin) ? ge : le;
Heap::RootListIndex const root_index =
(kind == MathMaxMinKind::kMin) ? Heap::kInfinityValueRootIndex
: Heap::kMinusInfinityValueRootIndex;
DoubleRegister const reg = (kind == MathMaxMinKind::kMin) ? f2 : f0;
// Load the accumulator with the default return value (either -Infinity or
// +Infinity), with the tagged value in a1 and the double value in f0.
__ LoadRoot(a1, root_index);
__ ldc1(f0, FieldMemOperand(a1, HeapNumber::kValueOffset));
__ mov(a3, a0);
__ Addu(a3, a0, Operand(1));
Label done_loop, loop;
__ bind(&loop);
......@@ -182,8 +180,6 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
{
// Parameter is not a Number, use the ToNumberStub to convert it.
FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(a0);
__ SmiTag(a3);
__ Push(a0, a1, a3);
__ mov(a0, a2);
ToNumberStub stub(masm->isolate());
......@@ -200,8 +196,6 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
__ SmiToDoubleFPURegister(a1, f0, t0);
__ bind(&done_restore);
}
__ SmiUntag(a3);
__ SmiUntag(a0);
}
__ jmp(&convert);
__ bind(&convert_number);
......@@ -211,21 +205,24 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
__ SmiToDoubleFPURegister(a2, f2, t0);
__ bind(&done_convert);
// Perform the actual comparison with the accumulator value on the left hand
// side (f0) and the next parameter value on the right hand side (f2).
Label compare_equal, compare_nan, compare_swap;
__ BranchF(&compare_equal, &compare_nan, eq, f0, f2);
__ BranchF(&compare_swap, nullptr, cc, f0, f2);
__ Branch(&loop);
// Left and right hand side are equal, check for -0 vs. +0.
__ bind(&compare_equal);
__ FmoveHigh(t0, reg);
__ Branch(&loop, ne, t0, Operand(0x80000000));
// Result is on the right hand side.
__ bind(&compare_swap);
__ mov_d(f0, f2);
// Perform the actual comparison with using Min/Max macro instructions the
// accumulator value on the left hand side (f0) and the next parameter value
// on the right hand side (f2).
// We need to work out which HeapNumber (or smi) the result came from.
Label compare_nan, set_value;
__ BranchF(nullptr, &compare_nan, eq, f0, f2);
__ Move(t0, t1, f0);
if (kind == MathMaxMinKind::kMin) {
__ MinNaNCheck_d(f0, f0, f2);
} else {
DCHECK(kind == MathMaxMinKind::kMax);
__ MaxNaNCheck_d(f0, f0, f2);
}
__ Move(at, t8, f0);
__ Branch(&set_value, ne, t0, Operand(at));
__ Branch(&set_value, ne, t1, Operand(t8));
__ jmp(&loop);
__ bind(&set_value);
__ mov(a1, a2);
__ jmp(&loop);
......@@ -238,8 +235,8 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
__ bind(&done_loop);
__ Lsa(sp, sp, a3, kPointerSizeLog2);
__ mov(v0, a1);
__ DropAndRet(1);
__ Ret(USE_DELAY_SLOT);
__ mov(v0, a1); // In delay slot.
}
// static
......
......@@ -1881,6 +1881,185 @@ void MacroAssembler::Movf(Register rd, Register rs, uint16_t cc) {
}
}
#define __ masm->
static bool ZeroHelper_d(MacroAssembler* masm, MaxMinKind kind, FPURegister dst,
FPURegister src1, FPURegister src2, Label* equal) {
if (src1.is(src2)) {
__ Move(dst, src1);
return true;
}
Label other, compare_not_equal;
FPURegister left, right;
if (kind == MaxMinKind::kMin) {
left = src1;
right = src2;
} else {
left = src2;
right = src1;
}
__ BranchF64(&compare_not_equal, nullptr, ne, src1, src2);
// Left and right hand side are equal, check for -0 vs. +0.
__ FmoveHigh(t8, src1);
__ Branch(&other, eq, t8, Operand(0x80000000));
__ Move_d(dst, right);
__ Branch(equal);
__ bind(&other);
__ Move_d(dst, left);
__ Branch(equal);
__ bind(&compare_not_equal);
return false;
}
static bool ZeroHelper_s(MacroAssembler* masm, MaxMinKind kind, FPURegister dst,
FPURegister src1, FPURegister src2, Label* equal) {
if (src1.is(src2)) {
__ Move(dst, src1);
return true;
}
Label other, compare_not_equal;
FPURegister left, right;
if (kind == MaxMinKind::kMin) {
left = src1;
right = src2;
} else {
left = src2;
right = src1;
}
__ BranchF32(&compare_not_equal, nullptr, ne, src1, src2);
// Left and right hand side are equal, check for -0 vs. +0.
__ FmoveLow(t8, src1);
__ Branch(&other, eq, t8, Operand(0x80000000));
__ Move_s(dst, right);
__ Branch(equal);
__ bind(&other);
__ Move_s(dst, left);
__ Branch(equal);
__ bind(&compare_not_equal);
return false;
}
#undef __
void MacroAssembler::MinNaNCheck_d(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF64(nullptr, nan, eq, src1, src2);
}
if (IsMipsArchVariant(kMips32r6)) {
min_d(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_d(this, MaxMinKind::kMin, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF64(&skip, nullptr, le, src1, src2);
Move_d(dst, src2);
} else if (dst.is(src2)) {
BranchF64(&skip, nullptr, ge, src1, src2);
Move_d(dst, src1);
} else {
Label right;
BranchF64(&right, nullptr, gt, src1, src2);
Move_d(dst, src1);
Branch(&skip);
bind(&right);
Move_d(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::MaxNaNCheck_d(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF64(nullptr, nan, eq, src1, src2);
}
if (IsMipsArchVariant(kMips32r6)) {
max_d(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_d(this, MaxMinKind::kMax, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF64(&skip, nullptr, ge, src1, src2);
Move_d(dst, src2);
} else if (dst.is(src2)) {
BranchF64(&skip, nullptr, le, src1, src2);
Move_d(dst, src1);
} else {
Label right;
BranchF64(&right, nullptr, lt, src1, src2);
Move_d(dst, src1);
Branch(&skip);
bind(&right);
Move_d(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::MinNaNCheck_s(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF32(nullptr, nan, eq, src1, src2);
}
if (IsMipsArchVariant(kMips32r6)) {
min_s(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_s(this, MaxMinKind::kMin, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF32(&skip, nullptr, le, src1, src2);
Move_s(dst, src2);
} else if (dst.is(src2)) {
BranchF32(&skip, nullptr, ge, src1, src2);
Move_s(dst, src1);
} else {
Label right;
BranchF32(&right, nullptr, gt, src1, src2);
Move_s(dst, src1);
Branch(&skip);
bind(&right);
Move_s(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::MaxNaNCheck_s(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF32(nullptr, nan, eq, src1, src2);
}
if (IsMipsArchVariant(kMips32r6)) {
max_s(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_s(this, MaxMinKind::kMax, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF32(&skip, nullptr, ge, src1, src2);
Move_s(dst, src2);
} else if (dst.is(src2)) {
BranchF32(&skip, nullptr, le, src1, src2);
Move_s(dst, src1);
} else {
Label right;
BranchF32(&right, nullptr, lt, src1, src2);
Move_s(dst, src1);
Branch(&skip);
bind(&right);
Move_s(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::Clz(Register rd, Register rs) {
if (IsMipsArchVariant(kLoongson)) {
......
......@@ -237,8 +237,8 @@ class MacroAssembler: public Assembler {
void Call(Label* target);
void Move(Register dst, Handle<Object> handle) { li(dst, handle); }
void Move(Register dst, Smi* smi) { li(dst, Operand(smi)); }
inline void Move(Register dst, Handle<Object> handle) { li(dst, handle); }
inline void Move(Register dst, Smi* smi) { li(dst, Operand(smi)); }
inline void Move(Register dst, Register src) {
if (!dst.is(src)) {
......@@ -246,12 +246,20 @@ class MacroAssembler: public Assembler {
}
}
inline void Move(FPURegister dst, FPURegister src) {
inline void Move_d(FPURegister dst, FPURegister src) {
if (!dst.is(src)) {
mov_d(dst, src);
}
}
inline void Move_s(FPURegister dst, FPURegister src) {
if (!dst.is(src)) {
mov_s(dst, src);
}
}
inline void Move(FPURegister dst, FPURegister src) { Move_d(dst, src); }
inline void Move(Register dst_low, Register dst_high, FPURegister src) {
mfc1(dst_low, src);
Mfhc1(dst_high, src);
......@@ -285,6 +293,17 @@ class MacroAssembler: public Assembler {
void Movt(Register rd, Register rs, uint16_t cc = 0);
void Movf(Register rd, Register rs, uint16_t cc = 0);
// Min, Max macros.
// On pre-r6 these functions may modify at and t8 registers.
void MinNaNCheck_d(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void MaxNaNCheck_d(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void MinNaNCheck_s(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void MaxNaNCheck_s(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void Clz(Register rd, Register rs);
// Jump unconditionally to given label.
......
......@@ -2503,7 +2503,6 @@ void Assembler::lwc1(FPURegister fd, const MemOperand& src) {
void Assembler::ldc1(FPURegister fd, const MemOperand& src) {
DCHECK(!src.rm().is(at));
if (is_int16(src.offset_)) {
GenInstrImmediate(LDC1, src.rm(), fd, src.offset_);
} else { // Offset > 16 bits, use multiple instructions to load.
......
......@@ -148,17 +148,15 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
// -- sp[(argc - n) * 8] : arg[n] (zero-based)
// -- sp[(argc + 1) * 8] : receiver
// -----------------------------------
Condition const cc = (kind == MathMaxMinKind::kMin) ? ge : le;
Heap::RootListIndex const root_index =
(kind == MathMaxMinKind::kMin) ? Heap::kInfinityValueRootIndex
: Heap::kMinusInfinityValueRootIndex;
DoubleRegister const reg = (kind == MathMaxMinKind::kMin) ? f2 : f0;
// Load the accumulator with the default return value (either -Infinity or
// +Infinity), with the tagged value in a1 and the double value in f0.
__ LoadRoot(a1, root_index);
__ ldc1(f0, FieldMemOperand(a1, HeapNumber::kValueOffset));
__ mov(a3, a0);
__ Addu(a3, a0, 1);
Label done_loop, loop;
__ bind(&loop);
......@@ -181,8 +179,6 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
{
// Parameter is not a Number, use the ToNumberStub to convert it.
FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(a0);
__ SmiTag(a3);
__ Push(a0, a1, a3);
__ mov(a0, a2);
ToNumberStub stub(masm->isolate());
......@@ -199,8 +195,6 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
__ SmiToDoubleFPURegister(a1, f0, a4);
__ bind(&done_restore);
}
__ SmiUntag(a3);
__ SmiUntag(a0);
}
__ jmp(&convert);
__ bind(&convert_number);
......@@ -210,23 +204,21 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
__ SmiToDoubleFPURegister(a2, f2, a4);
__ bind(&done_convert);
// Perform the actual comparison with the accumulator value on the left hand
// side (f0) and the next parameter value on the right hand side (f2).
Label compare_equal, compare_nan, compare_swap;
__ BranchF(&compare_equal, &compare_nan, eq, f0, f2);
__ BranchF(&compare_swap, nullptr, cc, f0, f2);
__ Branch(&loop);
// Left and right hand side are equal, check for -0 vs. +0.
__ bind(&compare_equal);
__ FmoveHigh(a4, reg);
// Make a4 unsigned.
__ dsll32(a4, a4, 0);
__ Branch(&loop, ne, a4, Operand(0x8000000000000000));
// Result is on the right hand side.
__ bind(&compare_swap);
__ mov_d(f0, f2);
// Perform the actual comparison with using Min/Max macro instructions the
// accumulator value on the left hand side (f0) and the next parameter value
// on the right hand side (f2).
// We need to work out which HeapNumber (or smi) the result came from.
Label compare_nan;
__ BranchF(nullptr, &compare_nan, eq, f0, f2);
__ Move(a4, f0);
if (kind == MathMaxMinKind::kMin) {
__ MinNaNCheck_d(f0, f0, f2);
} else {
DCHECK(kind == MathMaxMinKind::kMax);
__ MaxNaNCheck_d(f0, f0, f2);
}
__ Move(at, f0);
__ Branch(&loop, eq, a4, Operand(at));
__ mov(a1, a2);
__ jmp(&loop);
......@@ -239,8 +231,8 @@ void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
__ bind(&done_loop);
__ Dlsa(sp, sp, a3, kPointerSizeLog2);
__ mov(v0, a1);
__ DropAndRet(1);
__ Ret(USE_DELAY_SLOT);
__ mov(v0, a1); // In delay slot.
}
// static
......
......@@ -2362,6 +2362,186 @@ void MacroAssembler::Movf(Register rd, Register rs, uint16_t cc) {
movf(rd, rs, cc);
}
#define __ masm->
static bool ZeroHelper_d(MacroAssembler* masm, MaxMinKind kind, FPURegister dst,
FPURegister src1, FPURegister src2, Label* equal) {
if (src1.is(src2)) {
__ Move(dst, src1);
return true;
}
Label other, compare_not_equal;
FPURegister left, right;
if (kind == MaxMinKind::kMin) {
left = src1;
right = src2;
} else {
left = src2;
right = src1;
}
__ BranchF64(&compare_not_equal, nullptr, ne, src1, src2);
// Left and right hand side are equal, check for -0 vs. +0.
__ dmfc1(t8, src1);
__ Branch(&other, eq, t8, Operand(0x8000000000000000));
__ Move_d(dst, right);
__ Branch(equal);
__ bind(&other);
__ Move_d(dst, left);
__ Branch(equal);
__ bind(&compare_not_equal);
return false;
}
static bool ZeroHelper_s(MacroAssembler* masm, MaxMinKind kind, FPURegister dst,
FPURegister src1, FPURegister src2, Label* equal) {
if (src1.is(src2)) {
__ Move(dst, src1);
return true;
}
Label other, compare_not_equal;
FPURegister left, right;
if (kind == MaxMinKind::kMin) {
left = src1;
right = src2;
} else {
left = src2;
right = src1;
}
__ BranchF32(&compare_not_equal, nullptr, ne, src1, src2);
// Left and right hand side are equal, check for -0 vs. +0.
__ FmoveLow(t8, src1);
__ dsll32(t8, t8, 0);
__ Branch(&other, eq, t8, Operand(0x8000000000000000));
__ Move_s(dst, right);
__ Branch(equal);
__ bind(&other);
__ Move_s(dst, left);
__ Branch(equal);
__ bind(&compare_not_equal);
return false;
}
#undef __
void MacroAssembler::MinNaNCheck_d(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF64(nullptr, nan, eq, src1, src2);
}
if (kArchVariant >= kMips64r6) {
min_d(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_d(this, MaxMinKind::kMin, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF64(&skip, nullptr, le, src1, src2);
Move_d(dst, src2);
} else if (dst.is(src2)) {
BranchF64(&skip, nullptr, ge, src1, src2);
Move_d(dst, src1);
} else {
Label right;
BranchF64(&right, nullptr, gt, src1, src2);
Move_d(dst, src1);
Branch(&skip);
bind(&right);
Move_d(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::MaxNaNCheck_d(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF64(nullptr, nan, eq, src1, src2);
}
if (kArchVariant >= kMips64r6) {
max_d(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_d(this, MaxMinKind::kMax, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF64(&skip, nullptr, ge, src1, src2);
Move_d(dst, src2);
} else if (dst.is(src2)) {
BranchF64(&skip, nullptr, le, src1, src2);
Move_d(dst, src1);
} else {
Label right;
BranchF64(&right, nullptr, lt, src1, src2);
Move_d(dst, src1);
Branch(&skip);
bind(&right);
Move_d(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::MinNaNCheck_s(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF32(nullptr, nan, eq, src1, src2);
}
if (kArchVariant >= kMips64r6) {
min_s(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_s(this, MaxMinKind::kMin, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF32(&skip, nullptr, le, src1, src2);
Move_s(dst, src2);
} else if (dst.is(src2)) {
BranchF32(&skip, nullptr, ge, src1, src2);
Move_s(dst, src1);
} else {
Label right;
BranchF32(&right, nullptr, gt, src1, src2);
Move_s(dst, src1);
Branch(&skip);
bind(&right);
Move_s(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::MaxNaNCheck_s(FPURegister dst, FPURegister src1,
FPURegister src2, Label* nan) {
if (nan) {
BranchF32(nullptr, nan, eq, src1, src2);
}
if (kArchVariant >= kMips64r6) {
max_s(dst, src1, src2);
} else {
Label skip;
if (!ZeroHelper_s(this, MaxMinKind::kMax, dst, src1, src2, &skip)) {
if (dst.is(src1)) {
BranchF32(&skip, nullptr, ge, src1, src2);
Move_s(dst, src2);
} else if (dst.is(src2)) {
BranchF32(&skip, nullptr, le, src1, src2);
Move_s(dst, src1);
} else {
Label right;
BranchF32(&right, nullptr, lt, src1, src2);
Move_s(dst, src1);
Branch(&skip);
bind(&right);
Move_s(dst, src2);
}
}
bind(&skip);
}
}
void MacroAssembler::Clz(Register rd, Register rs) {
clz(rd, rs);
......
......@@ -265,8 +265,8 @@ class MacroAssembler: public Assembler {
void Call(Label* target);
void Move(Register dst, Handle<Object> handle) { li(dst, handle); }
void Move(Register dst, Smi* smi) { li(dst, Operand(smi)); }
inline void Move(Register dst, Handle<Object> handle) { li(dst, handle); }
inline void Move(Register dst, Smi* smi) { li(dst, Operand(smi)); }
inline void Move(Register dst, Register src) {
if (!dst.is(src)) {
......@@ -274,17 +274,29 @@ class MacroAssembler: public Assembler {
}
}
inline void Move(FPURegister dst, FPURegister src) {
inline void Move_d(FPURegister dst, FPURegister src) {
if (!dst.is(src)) {
mov_d(dst, src);
}
}
inline void Move_s(FPURegister dst, FPURegister src) {
if (!dst.is(src)) {
mov_s(dst, src);
}
}
inline void Move(FPURegister dst, FPURegister src) { Move_d(dst, src); }
inline void Move(Register dst_low, Register dst_high, FPURegister src) {
mfc1(dst_low, src);
mfhc1(dst_high, src);
}
inline void Move(Register dst, FPURegister src) { dmfc1(dst, src); }
inline void Move(FPURegister dst, Register src) { dmtc1(src, dst); }
inline void FmoveHigh(Register dst_high, FPURegister src) {
mfhc1(dst_high, src);
}
......@@ -313,6 +325,17 @@ class MacroAssembler: public Assembler {
void Movt(Register rd, Register rs, uint16_t cc = 0);
void Movf(Register rd, Register rs, uint16_t cc = 0);
// Min, Max macros.
// On pre-r6 these functions may modify at and t8 registers.
void MinNaNCheck_d(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void MaxNaNCheck_d(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void MinNaNCheck_s(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void MaxNaNCheck_s(FPURegister dst, FPURegister src1, FPURegister src2,
Label* nan = nullptr);
void Clz(Register rd, Register rs);
// Jump unconditionally to given label.
......
......@@ -1490,7 +1490,7 @@ TEST(min_max) {
float inputse[kTableLength] = {2.0, 3.0, fnan, 3.0, -0.0, 0.0, finf,
fnan, 42.0, finf, fminf, finf, fnan};
float inputsf[kTableLength] = {3.0, 2.0, 3.0, fnan, -0.0, 0.0, fnan,
float inputsf[kTableLength] = {3.0, 2.0, 3.0, fnan, 0.0, -0.0, fnan,
finf, finf, 42.0, finf, fminf, fnan};
float outputsfmin[kTableLength] = {2.0, 2.0, 3.0, 3.0, -0.0,
-0.0, finf, finf, 42.0, 42.0,
......@@ -1524,19 +1524,12 @@ TEST(min_max) {
test.e = inputse[i];
test.f = inputsf[i];
(CALL_GENERATED_CODE(isolate, f, &test, 0, 0, 0, 0));
CALL_GENERATED_CODE(isolate, f, &test, 0, 0, 0, 0);
if (i < kTableLength - 1) {
CHECK_EQ(test.c, outputsdmin[i]);
CHECK_EQ(test.d, outputsdmax[i]);
CHECK_EQ(test.g, outputsfmin[i]);
CHECK_EQ(test.h, outputsfmax[i]);
} else {
CHECK(std::isnan(test.c));
CHECK(std::isnan(test.d));
CHECK(std::isnan(test.g));
CHECK(std::isnan(test.h));
}
CHECK_EQ(0, memcmp(&test.c, &outputsdmin[i], sizeof(test.c)));
CHECK_EQ(0, memcmp(&test.d, &outputsdmax[i], sizeof(test.d)));
CHECK_EQ(0, memcmp(&test.g, &outputsfmin[i], sizeof(test.g)));
CHECK_EQ(0, memcmp(&test.h, &outputsfmax[i], sizeof(test.h)));
}
}
}
......
......@@ -1620,7 +1620,7 @@ TEST(min_max) {
float inputse[kTableLength] = {2.0, 3.0, fnan, 3.0, -0.0, 0.0, finf,
fnan, 42.0, finf, fminf, finf, fnan};
float inputsf[kTableLength] = {3.0, 2.0, 3.0, fnan, -0.0, 0.0, fnan,
float inputsf[kTableLength] = {3.0, 2.0, 3.0, fnan, 0.0, -0.0, fnan,
finf, finf, 42.0, finf, fminf, fnan};
float outputsfmin[kTableLength] = {2.0, 2.0, 3.0, 3.0, -0.0,
-0.0, finf, finf, 42.0, 42.0,
......@@ -1648,25 +1648,18 @@ TEST(min_max) {
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
F3 f = FUNCTION_CAST<F3>(code->entry());
for (int i = 0; i < kTableLength; i++) {
for (int i = 4; i < kTableLength; i++) {
test.a = inputsa[i];
test.b = inputsb[i];
test.e = inputse[i];
test.f = inputsf[i];
(CALL_GENERATED_CODE(isolate, f, &test, 0, 0, 0, 0));
CALL_GENERATED_CODE(isolate, f, &test, 0, 0, 0, 0);
if (i < kTableLength - 1) {
CHECK_EQ(test.c, outputsdmin[i]);
CHECK_EQ(test.d, outputsdmax[i]);
CHECK_EQ(test.g, outputsfmin[i]);
CHECK_EQ(test.h, outputsfmax[i]);
} else {
CHECK(std::isnan(test.c));
CHECK(std::isnan(test.d));
CHECK(std::isnan(test.g));
CHECK(std::isnan(test.h));
}
CHECK_EQ(0, memcmp(&test.c, &outputsdmin[i], sizeof(test.c)));
CHECK_EQ(0, memcmp(&test.d, &outputsdmax[i], sizeof(test.d)));
CHECK_EQ(0, memcmp(&test.g, &outputsfmin[i], sizeof(test.g)));
CHECK_EQ(0, memcmp(&test.h, &outputsfmax[i], sizeof(test.h)));
}
}
}
......
......@@ -40,6 +40,7 @@ using namespace v8::internal;
typedef void* (*F)(int x, int y, int p2, int p3, int p4);
typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4);
typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4);
#define __ masm->
......@@ -466,4 +467,114 @@ TEST(cvt_d_w_Trunc_w_d) {
}
}
TEST(min_max_nan) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assembler(isolate, nullptr, 0,
v8::internal::CodeObjectRequired::kYes);
MacroAssembler* masm = &assembler;
struct TestFloat {
double a;
double b;
double c;
double d;
float e;
float f;
float g;
float h;
};
TestFloat test;
const double dnan = std::numeric_limits<double>::quiet_NaN();
const double dinf = std::numeric_limits<double>::infinity();
const double dminf = -std::numeric_limits<double>::infinity();
const float fnan = std::numeric_limits<float>::quiet_NaN();
const float finf = std::numeric_limits<float>::infinity();
const float fminf = std::numeric_limits<float>::infinity();
const int kTableLength = 13;
double inputsa[kTableLength] = {2.0, 3.0, -0.0, 0.0, 42.0, dinf, dminf,
dinf, dnan, 3.0, dinf, dnan, dnan};
double inputsb[kTableLength] = {3.0, 2.0, 0.0, -0.0, dinf, 42.0, dinf,
dminf, 3.0, dnan, dnan, dinf, dnan};
double outputsdmin[kTableLength] = {2.0, 2.0, -0.0, -0.0, 42.0,
42.0, dminf, dminf, dnan, dnan,
dnan, dnan, dnan};
double outputsdmax[kTableLength] = {3.0, 3.0, 0.0, 0.0, dinf, dinf, dinf,
dinf, dnan, dnan, dnan, dnan, dnan};
float inputse[kTableLength] = {2.0, 3.0, -0.0, 0.0, 42.0, finf, fminf,
finf, fnan, 3.0, finf, fnan, fnan};
float inputsf[kTableLength] = {3.0, 2.0, 0.0, -0.0, finf, 42.0, finf,
fminf, 3.0, fnan, fnan, finf, fnan};
float outputsfmin[kTableLength] = {2.0, 2.0, -0.0, -0.0, 42.0, 42.0, fminf,
fminf, fnan, fnan, fnan, fnan, fnan};
float outputsfmax[kTableLength] = {3.0, 3.0, 0.0, 0.0, finf, finf, finf,
finf, fnan, fnan, fnan, fnan, fnan};
auto handle_dnan = [masm](FPURegister dst, Label* nan, Label* back) {
__ bind(nan);
__ LoadRoot(at, Heap::kNanValueRootIndex);
__ ldc1(dst, FieldMemOperand(at, HeapNumber::kValueOffset));
__ Branch(back);
};
auto handle_snan = [masm, fnan](FPURegister dst, Label* nan, Label* back) {
__ bind(nan);
__ Move(dst, fnan);
__ Branch(back);
};
Label handle_mind_nan, handle_maxd_nan, handle_mins_nan, handle_maxs_nan;
Label back_mind_nan, back_maxd_nan, back_mins_nan, back_maxs_nan;
__ push(s6);
__ InitializeRootRegister();
__ ldc1(f4, MemOperand(a0, offsetof(TestFloat, a)));
__ ldc1(f8, MemOperand(a0, offsetof(TestFloat, b)));
__ lwc1(f2, MemOperand(a0, offsetof(TestFloat, e)));
__ lwc1(f6, MemOperand(a0, offsetof(TestFloat, f)));
__ MinNaNCheck_d(f10, f4, f8, &handle_mind_nan);
__ bind(&back_mind_nan);
__ MaxNaNCheck_d(f12, f4, f8, &handle_maxd_nan);
__ bind(&back_maxd_nan);
__ MinNaNCheck_s(f14, f2, f6, &handle_mins_nan);
__ bind(&back_mins_nan);
__ MaxNaNCheck_s(f16, f2, f6, &handle_maxs_nan);
__ bind(&back_maxs_nan);
__ sdc1(f10, MemOperand(a0, offsetof(TestFloat, c)));
__ sdc1(f12, MemOperand(a0, offsetof(TestFloat, d)));
__ swc1(f14, MemOperand(a0, offsetof(TestFloat, g)));
__ swc1(f16, MemOperand(a0, offsetof(TestFloat, h)));
__ pop(s6);
__ jr(ra);
__ nop();
handle_dnan(f10, &handle_mind_nan, &back_mind_nan);
handle_dnan(f12, &handle_maxd_nan, &back_maxd_nan);
handle_snan(f14, &handle_mins_nan, &back_mins_nan);
handle_snan(f16, &handle_maxs_nan, &back_maxs_nan);
CodeDesc desc;
masm->GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
::F3 f = FUNCTION_CAST<::F3>(code->entry());
for (int i = 0; i < kTableLength; i++) {
test.a = inputsa[i];
test.b = inputsb[i];
test.e = inputse[i];
test.f = inputsf[i];
CALL_GENERATED_CODE(isolate, f, &test, 0, 0, 0, 0);
CHECK_EQ(0, memcmp(&test.c, &outputsdmin[i], sizeof(test.c)));
CHECK_EQ(0, memcmp(&test.d, &outputsdmax[i], sizeof(test.d)));
CHECK_EQ(0, memcmp(&test.g, &outputsfmin[i], sizeof(test.g)));
CHECK_EQ(0, memcmp(&test.h, &outputsfmax[i], sizeof(test.h)));
}
}
#undef __
......@@ -41,6 +41,7 @@ using namespace v8::internal;
typedef void* (*F)(int64_t x, int64_t y, int p2, int p3, int p4);
typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4);
typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4);
#define __ masm->
......@@ -676,4 +677,114 @@ TEST(cvt_d_w_Trunc_w_d) {
}
}
TEST(min_max_nan) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assembler(isolate, nullptr, 0,
v8::internal::CodeObjectRequired::kYes);
MacroAssembler* masm = &assembler;
struct TestFloat {
double a;
double b;
double c;
double d;
float e;
float f;
float g;
float h;
};
TestFloat test;
const double dnan = std::numeric_limits<double>::quiet_NaN();
const double dinf = std::numeric_limits<double>::infinity();
const double dminf = -std::numeric_limits<double>::infinity();
const float fnan = std::numeric_limits<float>::quiet_NaN();
const float finf = std::numeric_limits<float>::infinity();
const float fminf = std::numeric_limits<float>::infinity();
const int kTableLength = 13;
double inputsa[kTableLength] = {2.0, 3.0, -0.0, 0.0, 42.0, dinf, dminf,
dinf, dnan, 3.0, dinf, dnan, dnan};
double inputsb[kTableLength] = {3.0, 2.0, 0.0, -0.0, dinf, 42.0, dinf,
dminf, 3.0, dnan, dnan, dinf, dnan};
double outputsdmin[kTableLength] = {2.0, 2.0, -0.0, -0.0, 42.0,
42.0, dminf, dminf, dnan, dnan,
dnan, dnan, dnan};
double outputsdmax[kTableLength] = {3.0, 3.0, 0.0, 0.0, dinf, dinf, dinf,
dinf, dnan, dnan, dnan, dnan, dnan};
float inputse[kTableLength] = {2.0, 3.0, -0.0, 0.0, 42.0, finf, fminf,
finf, fnan, 3.0, finf, fnan, fnan};
float inputsf[kTableLength] = {3.0, 2.0, 0.0, -0.0, finf, 42.0, finf,
fminf, 3.0, fnan, fnan, finf, fnan};
float outputsfmin[kTableLength] = {2.0, 2.0, -0.0, -0.0, 42.0, 42.0, fminf,
fminf, fnan, fnan, fnan, fnan, fnan};
float outputsfmax[kTableLength] = {3.0, 3.0, 0.0, 0.0, finf, finf, finf,
finf, fnan, fnan, fnan, fnan, fnan};
auto handle_dnan = [masm](FPURegister dst, Label* nan, Label* back) {
__ bind(nan);
__ LoadRoot(at, Heap::kNanValueRootIndex);
__ ldc1(dst, FieldMemOperand(at, HeapNumber::kValueOffset));
__ Branch(back);
};
auto handle_snan = [masm, fnan](FPURegister dst, Label* nan, Label* back) {
__ bind(nan);
__ Move(dst, fnan);
__ Branch(back);
};
Label handle_mind_nan, handle_maxd_nan, handle_mins_nan, handle_maxs_nan;
Label back_mind_nan, back_maxd_nan, back_mins_nan, back_maxs_nan;
__ push(s6);
__ InitializeRootRegister();
__ ldc1(f4, MemOperand(a0, offsetof(TestFloat, a)));
__ ldc1(f8, MemOperand(a0, offsetof(TestFloat, b)));
__ lwc1(f2, MemOperand(a0, offsetof(TestFloat, e)));
__ lwc1(f6, MemOperand(a0, offsetof(TestFloat, f)));
__ MinNaNCheck_d(f10, f4, f8, &handle_mind_nan);
__ bind(&back_mind_nan);
__ MaxNaNCheck_d(f12, f4, f8, &handle_maxd_nan);
__ bind(&back_maxd_nan);
__ MinNaNCheck_s(f14, f2, f6, &handle_mins_nan);
__ bind(&back_mins_nan);
__ MaxNaNCheck_s(f16, f2, f6, &handle_maxs_nan);
__ bind(&back_maxs_nan);
__ sdc1(f10, MemOperand(a0, offsetof(TestFloat, c)));
__ sdc1(f12, MemOperand(a0, offsetof(TestFloat, d)));
__ swc1(f14, MemOperand(a0, offsetof(TestFloat, g)));
__ swc1(f16, MemOperand(a0, offsetof(TestFloat, h)));
__ pop(s6);
__ jr(ra);
__ nop();
handle_dnan(f10, &handle_mind_nan, &back_mind_nan);
handle_dnan(f12, &handle_maxd_nan, &back_maxd_nan);
handle_snan(f14, &handle_mins_nan, &back_mins_nan);
handle_snan(f16, &handle_maxs_nan, &back_maxs_nan);
CodeDesc desc;
masm->GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
::F3 f = FUNCTION_CAST<::F3>(code->entry());
for (int i = 0; i < kTableLength; i++) {
test.a = inputsa[i];
test.b = inputsb[i];
test.e = inputse[i];
test.f = inputsf[i];
CALL_GENERATED_CODE(isolate, f, &test, 0, 0, 0, 0);
CHECK_EQ(0, memcmp(&test.c, &outputsdmin[i], sizeof(test.c)));
CHECK_EQ(0, memcmp(&test.d, &outputsdmax[i], sizeof(test.d)));
CHECK_EQ(0, memcmp(&test.g, &outputsfmin[i], sizeof(test.g)));
CHECK_EQ(0, memcmp(&test.h, &outputsfmax[i], sizeof(test.h)));
}
}
#undef __
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment