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) {
}
}
void MacroAssembler::Move(SwVfpRegister dst, SwVfpRegister src) {
void MacroAssembler::Move(SwVfpRegister dst, SwVfpRegister src,
Condition cond) {
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)) {
vmov(dst, src);
vmov(dst, src, cond);
}
}
......@@ -3428,6 +3430,144 @@ void MacroAssembler::RestoreFPRegs(Register location, Register scratch) {
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(
Register first, Register second, Register scratch1, Register scratch2,
......
......@@ -170,8 +170,8 @@ class MacroAssembler: public Assembler {
mov(dst, src, sbit, cond);
}
}
void Move(SwVfpRegister dst, SwVfpRegister src);
void Move(DwVfpRegister dst, DwVfpRegister src);
void Move(SwVfpRegister dst, SwVfpRegister src, Condition cond = al);
void Move(DwVfpRegister dst, DwVfpRegister src, Condition cond = al);
void Load(Register dst, const MemOperand& src, Representation r);
void Store(Register src, const MemOperand& dst, Representation r);
......@@ -1082,6 +1082,32 @@ class MacroAssembler: public Assembler {
// values to location, restoring [d0..(d15|d31)].
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
......@@ -1513,6 +1539,16 @@ class MacroAssembler: public Assembler {
MemOperand SafepointRegisterSlot(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 has_frame_;
// This handle will be patched with the code object on installation.
......
......@@ -271,6 +271,37 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
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) {
switch (condition) {
......@@ -1377,145 +1408,59 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArmFloat32Max: {
FloatRegister left_reg = i.InputFloat32Register(0);
FloatRegister right_reg = i.InputFloat32Register(1);
FloatRegister result_reg = i.OutputFloat32Register();
Label result_is_nan, return_left, return_right, check_zero, done;
__ VFPCompareAndSetFlags(left_reg, right_reg);
__ 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.
SwVfpRegister result = i.OutputFloat32Register();
SwVfpRegister left = i.InputFloat32Register(0);
SwVfpRegister right = i.InputFloat32Register(1);
if (left.is(right)) {
__ Move(result, left);
} else {
__ b(ne, &return_left); // left == right != 0.
auto ool = new (zone()) OutOfLineFloat32Max(this, result, left, right);
__ FloatMax(result, left, right, ool->entry());
__ bind(ool->exit());
}
// At this point, both left and right are either 0 or -0.
// 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.
__ 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);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmFloat64Max: {
DwVfpRegister left_reg = i.InputDoubleRegister(0);
DwVfpRegister right_reg = i.InputDoubleRegister(1);
DwVfpRegister result_reg = i.OutputDoubleRegister();
Label result_is_nan, return_left, return_right, check_zero, done;
__ VFPCompareAndSetFlags(left_reg, right_reg);
__ 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.
DwVfpRegister result = i.OutputDoubleRegister();
DwVfpRegister left = i.InputDoubleRegister(0);
DwVfpRegister right = i.InputDoubleRegister(1);
if (left.is(right)) {
__ Move(result, left);
} else {
__ b(ne, &return_left); // left == right != 0.
auto ool = new (zone()) OutOfLineFloat64Max(this, result, left, right);
__ FloatMax(result, left, right, ool->entry());
__ bind(ool->exit());
}
// At this point, both left and right are either 0 or -0.
// 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.
__ 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);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmFloat32Min: {
FloatRegister left_reg = i.InputFloat32Register(0);
FloatRegister right_reg = i.InputFloat32Register(1);
FloatRegister result_reg = i.OutputFloat32Register();
Label result_is_nan, return_left, return_right, check_zero, done;
__ VFPCompareAndSetFlags(left_reg, right_reg);
__ 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);
SwVfpRegister result = i.OutputFloat32Register();
SwVfpRegister left = i.InputFloat32Register(0);
SwVfpRegister right = i.InputFloat32Register(1);
if (left.is(right)) {
__ Move(result, left);
} else {
__ vsub(result_reg, left_reg, right_reg);
auto ool = new (zone()) OutOfLineFloat32Min(this, result, left, right);
__ FloatMin(result, left, right, ool->entry());
__ bind(ool->exit());
}
__ 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);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmFloat64Min: {
DwVfpRegister left_reg = i.InputDoubleRegister(0);
DwVfpRegister right_reg = i.InputDoubleRegister(1);
DwVfpRegister result_reg = i.OutputDoubleRegister();
Label result_is_nan, return_left, return_right, check_zero, done;
__ VFPCompareAndSetFlags(left_reg, right_reg);
__ 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);
DwVfpRegister result = i.OutputDoubleRegister();
DwVfpRegister left = i.InputDoubleRegister(0);
DwVfpRegister right = i.InputDoubleRegister(1);
if (left.is(right)) {
__ Move(result, left);
} else {
__ vsub(result_reg, left_reg, right_reg);
auto ool = new (zone()) OutOfLineFloat64Min(this, result, left, right);
__ FloatMin(result, left, right, ool->entry());
__ bind(ool->exit());
}
__ 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);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmFloat64SilenceNaN: {
......
......@@ -30,11 +30,11 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
#include "src/arm/assembler-arm-inl.h"
#include "src/arm/simulator-arm.h"
#include "src/base/utils/random-number-generator.h"
#include "src/disassembler.h"
#include "src/factory.h"
#include "src/macro-assembler.h"
#include "src/ostreams.h"
using namespace v8::base;
......@@ -2382,7 +2382,7 @@ TEST(ARMv8_vsel) {
}
TEST(ARMv8_vminmax_f64) {
// Test the vsel floating point instructions.
// Test the vminnm and vmaxnm floating point instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
......@@ -2464,7 +2464,7 @@ TEST(ARMv8_vminmax_f64) {
}
TEST(ARMv8_vminmax_f32) {
// Test the vsel floating point instructions.
// Test the vminnm and vmaxnm floating point instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
......@@ -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) {
// All supported ARM targets allow unaligned accesses.
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