Commit 3396bb29 authored by jacob.bramley's avatar jacob.bramley Committed by Commit bot

[arm] Improve Float(32|64)(Max|Min).

ARMv8 can use vminnm and vmaxnm to handle most inputs. Other platforms
use an implementation similar to what was there before, except that
out-of-line code is used for the uncommon cases.

BUG=

Review-Url: https://codereview.chromium.org/2313863003
Cr-Commit-Position: refs/heads/master@{#39202}
parent 70429e7a
...@@ -250,15 +250,17 @@ void MacroAssembler::Move(Register dst, Register src, Condition cond) { ...@@ -250,15 +250,17 @@ void MacroAssembler::Move(Register dst, Register src, Condition cond) {
} }
} }
void MacroAssembler::Move(SwVfpRegister dst, SwVfpRegister src) { void MacroAssembler::Move(SwVfpRegister dst, SwVfpRegister src,
Condition cond) {
if (!dst.is(src)) { if (!dst.is(src)) {
vmov(dst, src); vmov(dst, src, cond);
} }
} }
void MacroAssembler::Move(DwVfpRegister dst, DwVfpRegister src) { void MacroAssembler::Move(DwVfpRegister dst, DwVfpRegister src,
Condition cond) {
if (!dst.is(src)) { if (!dst.is(src)) {
vmov(dst, src); vmov(dst, src, cond);
} }
} }
...@@ -3428,6 +3430,144 @@ void MacroAssembler::RestoreFPRegs(Register location, Register scratch) { ...@@ -3428,6 +3430,144 @@ void MacroAssembler::RestoreFPRegs(Register location, Register scratch) {
add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq); add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
} }
template <typename T>
void MacroAssembler::FloatMaxHelper(T result, T left, T right,
Label* out_of_line) {
// This trivial case is caught sooner, so that the out-of-line code can be
// completely avoided.
DCHECK(!left.is(right));
if (CpuFeatures::IsSupported(ARMv8)) {
CpuFeatureScope scope(this, ARMv8);
VFPCompareAndSetFlags(left, right);
b(vs, out_of_line);
vmaxnm(result, left, right);
} else {
Label done;
VFPCompareAndSetFlags(left, right);
b(vs, out_of_line);
// Avoid a conditional instruction if the result register is unique.
bool aliased_result_reg = result.is(left) || result.is(right);
Move(result, right, aliased_result_reg ? mi : al);
Move(result, left, gt);
b(ne, &done);
// Left and right are equal, but check for +/-0.
VFPCompareAndSetFlags(left, 0.0);
b(eq, out_of_line);
// The arguments are equal and not zero, so it doesn't matter which input we
// pick. We have already moved one input into the result (if it didn't
// already alias) so there's nothing more to do.
bind(&done);
}
}
template <typename T>
void MacroAssembler::FloatMaxOutOfLineHelper(T result, T left, T right) {
DCHECK(!left.is(right));
// ARMv8: At least one of left and right is a NaN.
// Anything else: At least one of left and right is a NaN, or both left and
// right are zeroes with unknown sign.
// If left and right are +/-0, select the one with the most positive sign.
// If left or right are NaN, vadd propagates the appropriate one.
vadd(result, left, right);
}
template <typename T>
void MacroAssembler::FloatMinHelper(T result, T left, T right,
Label* out_of_line) {
// This trivial case is caught sooner, so that the out-of-line code can be
// completely avoided.
DCHECK(!left.is(right));
if (CpuFeatures::IsSupported(ARMv8)) {
CpuFeatureScope scope(this, ARMv8);
VFPCompareAndSetFlags(left, right);
b(vs, out_of_line);
vminnm(result, left, right);
} else {
Label done;
VFPCompareAndSetFlags(left, right);
b(vs, out_of_line);
// Avoid a conditional instruction if the result register is unique.
bool aliased_result_reg = result.is(left) || result.is(right);
Move(result, left, aliased_result_reg ? mi : al);
Move(result, right, gt);
b(ne, &done);
// Left and right are equal, but check for +/-0.
VFPCompareAndSetFlags(left, 0.0);
// If the arguments are equal and not zero, it doesn't matter which input we
// pick. We have already moved one input into the result (if it didn't
// already alias) so there's nothing more to do.
b(ne, &done);
// At this point, both left and right are either 0 or -0.
// We could use a single 'vorr' instruction here if we had NEON support.
// The algorithm used is -((-L) + (-R)), which is most efficiently expressed
// as -((-L) - R).
if (left.is(result)) {
DCHECK(!right.is(result));
vneg(result, left);
vsub(result, result, right);
vneg(result, result);
} else {
DCHECK(!left.is(result));
vneg(result, right);
vsub(result, result, left);
vneg(result, result);
}
bind(&done);
}
}
template <typename T>
void MacroAssembler::FloatMinOutOfLineHelper(T result, T left, T right) {
DCHECK(!left.is(right));
// At least one of left and right is a NaN. Use vadd to propagate the NaN
// appropriately. +/-0 is handled inline.
vadd(result, left, right);
}
void MacroAssembler::FloatMax(SwVfpRegister result, SwVfpRegister left,
SwVfpRegister right, Label* out_of_line) {
FloatMaxHelper(result, left, right, out_of_line);
}
void MacroAssembler::FloatMin(SwVfpRegister result, SwVfpRegister left,
SwVfpRegister right, Label* out_of_line) {
FloatMinHelper(result, left, right, out_of_line);
}
void MacroAssembler::FloatMax(DwVfpRegister result, DwVfpRegister left,
DwVfpRegister right, Label* out_of_line) {
FloatMaxHelper(result, left, right, out_of_line);
}
void MacroAssembler::FloatMin(DwVfpRegister result, DwVfpRegister left,
DwVfpRegister right, Label* out_of_line) {
FloatMinHelper(result, left, right, out_of_line);
}
void MacroAssembler::FloatMaxOutOfLine(SwVfpRegister result, SwVfpRegister left,
SwVfpRegister right) {
FloatMaxOutOfLineHelper(result, left, right);
}
void MacroAssembler::FloatMinOutOfLine(SwVfpRegister result, SwVfpRegister left,
SwVfpRegister right) {
FloatMinOutOfLineHelper(result, left, right);
}
void MacroAssembler::FloatMaxOutOfLine(DwVfpRegister result, DwVfpRegister left,
DwVfpRegister right) {
FloatMaxOutOfLineHelper(result, left, right);
}
void MacroAssembler::FloatMinOutOfLine(DwVfpRegister result, DwVfpRegister left,
DwVfpRegister right) {
FloatMinOutOfLineHelper(result, left, right);
}
void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialOneByte( void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialOneByte(
Register first, Register second, Register scratch1, Register scratch2, Register first, Register second, Register scratch1, Register scratch2,
......
...@@ -170,8 +170,8 @@ class MacroAssembler: public Assembler { ...@@ -170,8 +170,8 @@ class MacroAssembler: public Assembler {
mov(dst, src, sbit, cond); mov(dst, src, sbit, cond);
} }
} }
void Move(SwVfpRegister dst, SwVfpRegister src); void Move(SwVfpRegister dst, SwVfpRegister src, Condition cond = al);
void Move(DwVfpRegister dst, DwVfpRegister src); void Move(DwVfpRegister dst, DwVfpRegister src, Condition cond = al);
void Load(Register dst, const MemOperand& src, Representation r); void Load(Register dst, const MemOperand& src, Representation r);
void Store(Register src, const MemOperand& dst, Representation r); void Store(Register src, const MemOperand& dst, Representation r);
...@@ -1082,6 +1082,32 @@ class MacroAssembler: public Assembler { ...@@ -1082,6 +1082,32 @@ class MacroAssembler: public Assembler {
// values to location, restoring [d0..(d15|d31)]. // values to location, restoring [d0..(d15|d31)].
void RestoreFPRegs(Register location, Register scratch); void RestoreFPRegs(Register location, Register scratch);
// Perform a floating-point min or max operation with the
// (IEEE-754-compatible) semantics of ARM64's fmin/fmax. Some cases, typically
// NaNs or +/-0.0, are expected to be rare and are handled in out-of-line
// code. The specific behaviour depends on supported instructions.
//
// These functions assume (and assert) that !left.is(right). It is permitted
// for the result to alias either input register.
void FloatMax(SwVfpRegister result, SwVfpRegister left, SwVfpRegister right,
Label* out_of_line);
void FloatMin(SwVfpRegister result, SwVfpRegister left, SwVfpRegister right,
Label* out_of_line);
void FloatMax(DwVfpRegister result, DwVfpRegister left, DwVfpRegister right,
Label* out_of_line);
void FloatMin(DwVfpRegister result, DwVfpRegister left, DwVfpRegister right,
Label* out_of_line);
// Generate out-of-line cases for the macros above.
void FloatMaxOutOfLine(SwVfpRegister result, SwVfpRegister left,
SwVfpRegister right);
void FloatMinOutOfLine(SwVfpRegister result, SwVfpRegister left,
SwVfpRegister right);
void FloatMaxOutOfLine(DwVfpRegister result, DwVfpRegister left,
DwVfpRegister right);
void FloatMinOutOfLine(DwVfpRegister result, DwVfpRegister left,
DwVfpRegister right);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Runtime calls // Runtime calls
...@@ -1513,6 +1539,16 @@ class MacroAssembler: public Assembler { ...@@ -1513,6 +1539,16 @@ class MacroAssembler: public Assembler {
MemOperand SafepointRegisterSlot(Register reg); MemOperand SafepointRegisterSlot(Register reg);
MemOperand SafepointRegistersAndDoublesSlot(Register reg); MemOperand SafepointRegistersAndDoublesSlot(Register reg);
// Implementation helpers for FloatMin and FloatMax.
template <typename T>
void FloatMaxHelper(T result, T left, T right, Label* out_of_line);
template <typename T>
void FloatMinHelper(T result, T left, T right, Label* out_of_line);
template <typename T>
void FloatMaxOutOfLineHelper(T result, T left, T right);
template <typename T>
void FloatMinOutOfLineHelper(T result, T left, T right);
bool generating_stub_; bool generating_stub_;
bool has_frame_; bool has_frame_;
// This handle will be patched with the code object on installation. // This handle will be patched with the code object on installation.
......
...@@ -271,6 +271,37 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -271,6 +271,37 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
UnwindingInfoWriter* const unwinding_info_writer_; UnwindingInfoWriter* const unwinding_info_writer_;
}; };
template <typename T>
class OutOfLineFloatMin final : public OutOfLineCode {
public:
OutOfLineFloatMin(CodeGenerator* gen, T result, T left, T right)
: OutOfLineCode(gen), result_(result), left_(left), right_(right) {}
void Generate() final { __ FloatMinOutOfLine(result_, left_, right_); }
private:
T const result_;
T const left_;
T const right_;
};
typedef OutOfLineFloatMin<SwVfpRegister> OutOfLineFloat32Min;
typedef OutOfLineFloatMin<DwVfpRegister> OutOfLineFloat64Min;
template <typename T>
class OutOfLineFloatMax final : public OutOfLineCode {
public:
OutOfLineFloatMax(CodeGenerator* gen, T result, T left, T right)
: OutOfLineCode(gen), result_(result), left_(left), right_(right) {}
void Generate() final { __ FloatMaxOutOfLine(result_, left_, right_); }
private:
T const result_;
T const left_;
T const right_;
};
typedef OutOfLineFloatMax<SwVfpRegister> OutOfLineFloat32Max;
typedef OutOfLineFloatMax<DwVfpRegister> OutOfLineFloat64Max;
Condition FlagsConditionToCondition(FlagsCondition condition) { Condition FlagsConditionToCondition(FlagsCondition condition) {
switch (condition) { switch (condition) {
...@@ -1377,145 +1408,59 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -1377,145 +1408,59 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
break; break;
case kArmFloat32Max: { case kArmFloat32Max: {
FloatRegister left_reg = i.InputFloat32Register(0); SwVfpRegister result = i.OutputFloat32Register();
FloatRegister right_reg = i.InputFloat32Register(1); SwVfpRegister left = i.InputFloat32Register(0);
FloatRegister result_reg = i.OutputFloat32Register(); SwVfpRegister right = i.InputFloat32Register(1);
Label result_is_nan, return_left, return_right, check_zero, done; if (left.is(right)) {
__ VFPCompareAndSetFlags(left_reg, right_reg); __ Move(result, left);
__ b(mi, &return_right);
__ b(gt, &return_left);
__ b(vs, &result_is_nan);
// Left equals right => check for -0.
__ VFPCompareAndSetFlags(left_reg, 0.0);
if (left_reg.is(result_reg) || right_reg.is(result_reg)) {
__ b(ne, &done); // left == right != 0.
} else { } else {
__ b(ne, &return_left); // left == right != 0. auto ool = new (zone()) OutOfLineFloat32Max(this, result, left, right);
} __ FloatMax(result, left, right, ool->entry());
// At this point, both left and right are either 0 or -0. __ bind(ool->exit());
// Since we operate on +0 and/or -0, vadd and vand have the same effect; }
// the decision for vadd is easy because vand is a NEON instruction. DCHECK_EQ(LeaveCC, i.OutputSBit());
__ vadd(result_reg, left_reg, right_reg);
__ b(&done);
__ bind(&result_is_nan);
__ vadd(result_reg, left_reg, right_reg);
__ b(&done);
__ bind(&return_right);
__ Move(result_reg, right_reg);
if (!left_reg.is(result_reg)) __ b(&done);
__ bind(&return_left);
__ Move(result_reg, left_reg);
__ bind(&done);
break; break;
} }
case kArmFloat64Max: { case kArmFloat64Max: {
DwVfpRegister left_reg = i.InputDoubleRegister(0); DwVfpRegister result = i.OutputDoubleRegister();
DwVfpRegister right_reg = i.InputDoubleRegister(1); DwVfpRegister left = i.InputDoubleRegister(0);
DwVfpRegister result_reg = i.OutputDoubleRegister(); DwVfpRegister right = i.InputDoubleRegister(1);
Label result_is_nan, return_left, return_right, check_zero, done; if (left.is(right)) {
__ VFPCompareAndSetFlags(left_reg, right_reg); __ Move(result, left);
__ b(mi, &return_right);
__ b(gt, &return_left);
__ b(vs, &result_is_nan);
// Left equals right => check for -0.
__ VFPCompareAndSetFlags(left_reg, 0.0);
if (left_reg.is(result_reg) || right_reg.is(result_reg)) {
__ b(ne, &done); // left == right != 0.
} else { } else {
__ b(ne, &return_left); // left == right != 0. auto ool = new (zone()) OutOfLineFloat64Max(this, result, left, right);
} __ FloatMax(result, left, right, ool->entry());
// At this point, both left and right are either 0 or -0. __ bind(ool->exit());
// Since we operate on +0 and/or -0, vadd and vand have the same effect; }
// the decision for vadd is easy because vand is a NEON instruction. DCHECK_EQ(LeaveCC, i.OutputSBit());
__ vadd(result_reg, left_reg, right_reg);
__ b(&done);
__ bind(&result_is_nan);
__ vadd(result_reg, left_reg, right_reg);
__ b(&done);
__ bind(&return_right);
__ Move(result_reg, right_reg);
if (!left_reg.is(result_reg)) __ b(&done);
__ bind(&return_left);
__ Move(result_reg, left_reg);
__ bind(&done);
break; break;
} }
case kArmFloat32Min: { case kArmFloat32Min: {
FloatRegister left_reg = i.InputFloat32Register(0); SwVfpRegister result = i.OutputFloat32Register();
FloatRegister right_reg = i.InputFloat32Register(1); SwVfpRegister left = i.InputFloat32Register(0);
FloatRegister result_reg = i.OutputFloat32Register(); SwVfpRegister right = i.InputFloat32Register(1);
Label result_is_nan, return_left, return_right, check_zero, done; if (left.is(right)) {
__ VFPCompareAndSetFlags(left_reg, right_reg); __ Move(result, left);
__ b(mi, &return_left);
__ b(gt, &return_right);
__ b(vs, &result_is_nan);
// Left equals right => check for -0.
__ VFPCompareAndSetFlags(left_reg, 0.0);
if (left_reg.is(result_reg) || right_reg.is(result_reg)) {
__ b(ne, &done); // left == right != 0.
} else { } else {
__ b(ne, &return_left); // left == right != 0. auto ool = new (zone()) OutOfLineFloat32Min(this, result, left, right);
} __ FloatMin(result, left, right, ool->entry());
// At this point, both left and right are either 0 or -0. __ bind(ool->exit());
// We could use a single 'vorr' instruction here if we had NEON support. }
// The algorithm is: -((-L) + (-R)), which in case of L and R being DCHECK_EQ(LeaveCC, i.OutputSBit());
// different registers is most efficiently expressed as -((-L) - R).
__ vneg(left_reg, left_reg);
if (left_reg.is(right_reg)) {
__ vadd(result_reg, left_reg, right_reg);
} else {
__ vsub(result_reg, left_reg, right_reg);
}
__ vneg(result_reg, result_reg);
__ b(&done);
__ bind(&result_is_nan);
__ vadd(result_reg, left_reg, right_reg);
__ b(&done);
__ bind(&return_right);
__ Move(result_reg, right_reg);
if (!left_reg.is(result_reg)) __ b(&done);
__ bind(&return_left);
__ Move(result_reg, left_reg);
__ bind(&done);
break; break;
} }
case kArmFloat64Min: { case kArmFloat64Min: {
DwVfpRegister left_reg = i.InputDoubleRegister(0); DwVfpRegister result = i.OutputDoubleRegister();
DwVfpRegister right_reg = i.InputDoubleRegister(1); DwVfpRegister left = i.InputDoubleRegister(0);
DwVfpRegister result_reg = i.OutputDoubleRegister(); DwVfpRegister right = i.InputDoubleRegister(1);
Label result_is_nan, return_left, return_right, check_zero, done; if (left.is(right)) {
__ VFPCompareAndSetFlags(left_reg, right_reg); __ Move(result, left);
__ b(mi, &return_left);
__ b(gt, &return_right);
__ b(vs, &result_is_nan);
// Left equals right => check for -0.
__ VFPCompareAndSetFlags(left_reg, 0.0);
if (left_reg.is(result_reg) || right_reg.is(result_reg)) {
__ b(ne, &done); // left == right != 0.
} else {
__ b(ne, &return_left); // left == right != 0.
}
// At this point, both left and right are either 0 or -0.
// We could use a single 'vorr' instruction here if we had NEON support.
// The algorithm is: -((-L) + (-R)), which in case of L and R being
// different registers is most efficiently expressed as -((-L) - R).
__ vneg(left_reg, left_reg);
if (left_reg.is(right_reg)) {
__ vadd(result_reg, left_reg, right_reg);
} else { } else {
__ vsub(result_reg, left_reg, right_reg); auto ool = new (zone()) OutOfLineFloat64Min(this, result, left, right);
} __ FloatMin(result, left, right, ool->entry());
__ vneg(result_reg, result_reg); __ bind(ool->exit());
__ b(&done); }
__ bind(&result_is_nan); DCHECK_EQ(LeaveCC, i.OutputSBit());
__ vadd(result_reg, left_reg, right_reg);
__ b(&done);
__ bind(&return_right);
__ Move(result_reg, right_reg);
if (!left_reg.is(result_reg)) __ b(&done);
__ bind(&return_left);
__ Move(result_reg, left_reg);
__ bind(&done);
break; break;
} }
case kArmFloat64SilenceNaN: { case kArmFloat64SilenceNaN: {
......
...@@ -30,11 +30,11 @@ ...@@ -30,11 +30,11 @@
#include "src/v8.h" #include "src/v8.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
#include "src/arm/assembler-arm-inl.h"
#include "src/arm/simulator-arm.h" #include "src/arm/simulator-arm.h"
#include "src/base/utils/random-number-generator.h" #include "src/base/utils/random-number-generator.h"
#include "src/disassembler.h" #include "src/disassembler.h"
#include "src/factory.h" #include "src/factory.h"
#include "src/macro-assembler.h"
#include "src/ostreams.h" #include "src/ostreams.h"
using namespace v8::base; using namespace v8::base;
...@@ -2382,7 +2382,7 @@ TEST(ARMv8_vsel) { ...@@ -2382,7 +2382,7 @@ TEST(ARMv8_vsel) {
} }
TEST(ARMv8_vminmax_f64) { TEST(ARMv8_vminmax_f64) {
// Test the vsel floating point instructions. // Test the vminnm and vmaxnm floating point instructions.
CcTest::InitializeVM(); CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate(); Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -2464,7 +2464,7 @@ TEST(ARMv8_vminmax_f64) { ...@@ -2464,7 +2464,7 @@ TEST(ARMv8_vminmax_f64) {
} }
TEST(ARMv8_vminmax_f32) { TEST(ARMv8_vminmax_f32) {
// Test the vsel floating point instructions. // Test the vminnm and vmaxnm floating point instructions.
CcTest::InitializeVM(); CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate(); Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -2545,6 +2545,236 @@ TEST(ARMv8_vminmax_f32) { ...@@ -2545,6 +2545,236 @@ TEST(ARMv8_vminmax_f32) {
} }
} }
template <typename T, typename Inputs, typename Results>
static F4 GenerateMacroFloatMinMax(MacroAssembler& assm) {
T a = T::from_code(0); // d0/s0
T b = T::from_code(1); // d1/s1
T c = T::from_code(2); // d2/s2
// Create a helper function:
// void TestFloatMinMax(const Inputs* inputs,
// Results* results);
Label ool_min_abc, ool_min_aab, ool_min_aba;
Label ool_max_abc, ool_max_aab, ool_max_aba;
Label done_min_abc, done_min_aab, done_min_aba;
Label done_max_abc, done_max_aab, done_max_aba;
// a = min(b, c);
__ vldr(b, r0, offsetof(Inputs, left_));
__ vldr(c, r0, offsetof(Inputs, right_));
__ FloatMin(a, b, c, &ool_min_abc);
__ bind(&done_min_abc);
__ vstr(a, r1, offsetof(Results, min_abc_));
// a = min(a, b);
__ vldr(a, r0, offsetof(Inputs, left_));
__ vldr(b, r0, offsetof(Inputs, right_));
__ FloatMin(a, a, b, &ool_min_aab);
__ bind(&done_min_aab);
__ vstr(a, r1, offsetof(Results, min_aab_));
// a = min(b, a);
__ vldr(b, r0, offsetof(Inputs, left_));
__ vldr(a, r0, offsetof(Inputs, right_));
__ FloatMin(a, b, a, &ool_min_aba);
__ bind(&done_min_aba);
__ vstr(a, r1, offsetof(Results, min_aba_));
// a = max(b, c);
__ vldr(b, r0, offsetof(Inputs, left_));
__ vldr(c, r0, offsetof(Inputs, right_));
__ FloatMax(a, b, c, &ool_max_abc);
__ bind(&done_max_abc);
__ vstr(a, r1, offsetof(Results, max_abc_));
// a = max(a, b);
__ vldr(a, r0, offsetof(Inputs, left_));
__ vldr(b, r0, offsetof(Inputs, right_));
__ FloatMax(a, a, b, &ool_max_aab);
__ bind(&done_max_aab);
__ vstr(a, r1, offsetof(Results, max_aab_));
// a = max(b, a);
__ vldr(b, r0, offsetof(Inputs, left_));
__ vldr(a, r0, offsetof(Inputs, right_));
__ FloatMax(a, b, a, &ool_max_aba);
__ bind(&done_max_aba);
__ vstr(a, r1, offsetof(Results, max_aba_));
__ bx(lr);
// Generate out-of-line cases.
__ bind(&ool_min_abc);
__ FloatMinOutOfLine(a, b, c);
__ b(&done_min_abc);
__ bind(&ool_min_aab);
__ FloatMinOutOfLine(a, a, b);
__ b(&done_min_aab);
__ bind(&ool_min_aba);
__ FloatMinOutOfLine(a, b, a);
__ b(&done_min_aba);
__ bind(&ool_max_abc);
__ FloatMaxOutOfLine(a, b, c);
__ b(&done_max_abc);
__ bind(&ool_max_aab);
__ FloatMaxOutOfLine(a, a, b);
__ b(&done_max_aab);
__ bind(&ool_max_aba);
__ FloatMaxOutOfLine(a, b, a);
__ b(&done_max_aba);
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = assm.isolate()->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef DEBUG
OFStream os(stdout);
code->Print(os);
#endif
return FUNCTION_CAST<F4>(code->entry());
}
TEST(macro_float_minmax_f64) {
// Test the FloatMin and FloatMax macros.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, NULL, 0, CodeObjectRequired::kYes);
struct Inputs {
double left_;
double right_;
};
struct Results {
// Check all register aliasing possibilities in order to exercise all
// code-paths in the macro assembler.
double min_abc_;
double min_aab_;
double min_aba_;
double max_abc_;
double max_aab_;
double max_aba_;
};
F4 f = GenerateMacroFloatMinMax<DwVfpRegister, Inputs, Results>(assm);
Object* dummy = nullptr;
USE(dummy);
#define CHECK_MINMAX(left, right, min, max) \
do { \
Inputs inputs = {left, right}; \
Results results; \
dummy = CALL_GENERATED_CODE(isolate, f, &inputs, &results, 0, 0, 0); \
/* Use a bit_cast to correctly identify -0.0 and NaNs. */ \
CHECK_EQ(bit_cast<uint64_t>(min), bit_cast<uint64_t>(results.min_abc_)); \
CHECK_EQ(bit_cast<uint64_t>(min), bit_cast<uint64_t>(results.min_aab_)); \
CHECK_EQ(bit_cast<uint64_t>(min), bit_cast<uint64_t>(results.min_aba_)); \
CHECK_EQ(bit_cast<uint64_t>(max), bit_cast<uint64_t>(results.max_abc_)); \
CHECK_EQ(bit_cast<uint64_t>(max), bit_cast<uint64_t>(results.max_aab_)); \
CHECK_EQ(bit_cast<uint64_t>(max), bit_cast<uint64_t>(results.max_aba_)); \
} while (0)
double nan_a = bit_cast<double>(UINT64_C(0x7ff8000000000001));
double nan_b = bit_cast<double>(UINT64_C(0x7ff8000000000002));
CHECK_MINMAX(1.0, -1.0, -1.0, 1.0);
CHECK_MINMAX(-1.0, 1.0, -1.0, 1.0);
CHECK_MINMAX(0.0, -1.0, -1.0, 0.0);
CHECK_MINMAX(-1.0, 0.0, -1.0, 0.0);
CHECK_MINMAX(-0.0, -1.0, -1.0, -0.0);
CHECK_MINMAX(-1.0, -0.0, -1.0, -0.0);
CHECK_MINMAX(0.0, 1.0, 0.0, 1.0);
CHECK_MINMAX(1.0, 0.0, 0.0, 1.0);
CHECK_MINMAX(0.0, 0.0, 0.0, 0.0);
CHECK_MINMAX(-0.0, -0.0, -0.0, -0.0);
CHECK_MINMAX(-0.0, 0.0, -0.0, 0.0);
CHECK_MINMAX(0.0, -0.0, -0.0, 0.0);
CHECK_MINMAX(0.0, nan_a, nan_a, nan_a);
CHECK_MINMAX(nan_a, 0.0, nan_a, nan_a);
CHECK_MINMAX(nan_a, nan_b, nan_a, nan_a);
CHECK_MINMAX(nan_b, nan_a, nan_b, nan_b);
#undef CHECK_MINMAX
}
TEST(macro_float_minmax_f32) {
// Test the FloatMin and FloatMax macros.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
MacroAssembler assm(isolate, NULL, 0, CodeObjectRequired::kYes);
struct Inputs {
float left_;
float right_;
};
struct Results {
// Check all register aliasing possibilities in order to exercise all
// code-paths in the macro assembler.
float min_abc_;
float min_aab_;
float min_aba_;
float max_abc_;
float max_aab_;
float max_aba_;
};
F4 f = GenerateMacroFloatMinMax<SwVfpRegister, Inputs, Results>(assm);
Object* dummy = nullptr;
USE(dummy);
#define CHECK_MINMAX(left, right, min, max) \
do { \
Inputs inputs = {left, right}; \
Results results; \
dummy = CALL_GENERATED_CODE(isolate, f, &inputs, &results, 0, 0, 0); \
/* Use a bit_cast to correctly identify -0.0 and NaNs. */ \
CHECK_EQ(bit_cast<uint32_t>(min), bit_cast<uint32_t>(results.min_abc_)); \
CHECK_EQ(bit_cast<uint32_t>(min), bit_cast<uint32_t>(results.min_aab_)); \
CHECK_EQ(bit_cast<uint32_t>(min), bit_cast<uint32_t>(results.min_aba_)); \
CHECK_EQ(bit_cast<uint32_t>(max), bit_cast<uint32_t>(results.max_abc_)); \
CHECK_EQ(bit_cast<uint32_t>(max), bit_cast<uint32_t>(results.max_aab_)); \
CHECK_EQ(bit_cast<uint32_t>(max), bit_cast<uint32_t>(results.max_aba_)); \
} while (0)
float nan_a = bit_cast<float>(UINT32_C(0x7fc00001));
float nan_b = bit_cast<float>(UINT32_C(0x7fc00002));
CHECK_MINMAX(1.0f, -1.0f, -1.0f, 1.0f);
CHECK_MINMAX(-1.0f, 1.0f, -1.0f, 1.0f);
CHECK_MINMAX(0.0f, -1.0f, -1.0f, 0.0f);
CHECK_MINMAX(-1.0f, 0.0f, -1.0f, 0.0f);
CHECK_MINMAX(-0.0f, -1.0f, -1.0f, -0.0f);
CHECK_MINMAX(-1.0f, -0.0f, -1.0f, -0.0f);
CHECK_MINMAX(0.0f, 1.0f, 0.0f, 1.0f);
CHECK_MINMAX(1.0f, 0.0f, 0.0f, 1.0f);
CHECK_MINMAX(0.0f, 0.0f, 0.0f, 0.0f);
CHECK_MINMAX(-0.0f, -0.0f, -0.0f, -0.0f);
CHECK_MINMAX(-0.0f, 0.0f, -0.0f, 0.0f);
CHECK_MINMAX(0.0f, -0.0f, -0.0f, 0.0f);
CHECK_MINMAX(0.0f, nan_a, nan_a, nan_a);
CHECK_MINMAX(nan_a, 0.0f, nan_a, nan_a);
CHECK_MINMAX(nan_a, nan_b, nan_a, nan_a);
CHECK_MINMAX(nan_b, nan_a, nan_b, nan_b);
#undef CHECK_MINMAX
}
TEST(unaligned_loads) { TEST(unaligned_loads) {
// All supported ARM targets allow unaligned accesses. // All supported ARM targets allow unaligned accesses.
CcTest::InitializeVM(); CcTest::InitializeVM();
......
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