Commit a0ff620f authored by jacob.bramley's avatar jacob.bramley Committed by Commit bot

[arm] Add support for vminnm and vmaxnm.

These are ARMv8 instructions that will be used in a follow-up patch.

BUG=

Review-Url: https://codereview.chromium.org/2273003002
Cr-Commit-Position: refs/heads/master@{#39193}
parent 77c4ba07
......@@ -3528,6 +3528,70 @@ void Assembler::vcmp(const SwVfpRegister src1, const float src2,
0x5 * B9 | B6);
}
void Assembler::vmaxnm(const DwVfpRegister dst, const DwVfpRegister src1,
const DwVfpRegister src2) {
// kSpecialCondition(31-28) | 11101(27-23) | D(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
DCHECK(CpuFeatures::IsSupported(ARMv8));
int vd, d;
dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(kSpecialCondition | 0x1D * B23 | d * B22 | vn * B16 | vd * B12 |
0x5 * B9 | B8 | n * B7 | m * B5 | vm);
}
void Assembler::vmaxnm(const SwVfpRegister dst, const SwVfpRegister src1,
const SwVfpRegister src2) {
// kSpecialCondition(31-28) | 11101(27-23) | D(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz=0(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
DCHECK(CpuFeatures::IsSupported(ARMv8));
int vd, d;
dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(kSpecialCondition | 0x1D * B23 | d * B22 | vn * B16 | vd * B12 |
0x5 * B9 | n * B7 | m * B5 | vm);
}
void Assembler::vminnm(const DwVfpRegister dst, const DwVfpRegister src1,
const DwVfpRegister src2) {
// kSpecialCondition(31-28) | 11101(27-23) | D(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
DCHECK(CpuFeatures::IsSupported(ARMv8));
int vd, d;
dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(kSpecialCondition | 0x1D * B23 | d * B22 | vn * B16 | vd * B12 |
0x5 * B9 | B8 | n * B7 | B6 | m * B5 | vm);
}
void Assembler::vminnm(const SwVfpRegister dst, const SwVfpRegister src1,
const SwVfpRegister src2) {
// kSpecialCondition(31-28) | 11101(27-23) | D(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz=0(8) | N(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
DCHECK(CpuFeatures::IsSupported(ARMv8));
int vd, d;
dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(kSpecialCondition | 0x1D * B23 | d * B22 | vn * B16 | vd * B12 |
0x5 * B9 | n * B7 | B6 | m * B5 | vm);
}
void Assembler::vsel(Condition cond, const DwVfpRegister dst,
const DwVfpRegister src1, const DwVfpRegister src2) {
// cond=kSpecialCondition(31-28) | 11100(27-23) | D(22) |
......
......@@ -1258,6 +1258,19 @@ class Assembler : public AssemblerBase {
void vcmp(const SwVfpRegister src1, const float src2,
const Condition cond = al);
void vmaxnm(const DwVfpRegister dst,
const DwVfpRegister src1,
const DwVfpRegister src2);
void vmaxnm(const SwVfpRegister dst,
const SwVfpRegister src1,
const SwVfpRegister src2);
void vminnm(const DwVfpRegister dst,
const DwVfpRegister src1,
const DwVfpRegister src2);
void vminnm(const SwVfpRegister dst,
const SwVfpRegister src1,
const SwVfpRegister src2);
// VSEL supports cond in {eq, ne, ge, lt, gt, le, vs, vc}.
void vsel(const Condition cond,
const DwVfpRegister dst,
......
......@@ -1896,6 +1896,22 @@ void Decoder::DecodeSpecialCondition(Instruction* instr) {
UNREACHABLE(); // Case analysis is exhaustive.
break;
}
} else if ((instr->Opc1Value() == 0x4) && (instr->Bits(11, 9) == 0x5) &&
(instr->Bit(4) == 0x0)) {
// VMAXNM, VMINNM (floating-point)
if (instr->SzValue() == 0x1) {
if (instr->Bit(6) == 0x1) {
Format(instr, "vminnm.f64 'Dd, 'Dn, 'Dm");
} else {
Format(instr, "vmaxnm.f64 'Dd, 'Dn, 'Dm");
}
} else {
if (instr->Bit(6) == 0x1) {
Format(instr, "vminnm.f32 'Sd, 'Sn, 'Sm");
} else {
Format(instr, "vmaxnm.f32 'Sd, 'Sn, 'Sm");
}
}
} else {
Unknown(instr);
}
......
......@@ -3906,6 +3906,69 @@ void Simulator::DecodeSpecialCondition(Instruction* instr) {
sd_value = canonicalizeNaN(sd_value);
set_s_register_from_float(d, sd_value);
}
} else if ((instr->Opc1Value() == 0x4) && (instr->Bits(11, 9) == 0x5) &&
(instr->Bit(4) == 0x0)) {
if (instr->SzValue() == 0x1) {
int m = instr->VFPMRegValue(kDoublePrecision);
int n = instr->VFPNRegValue(kDoublePrecision);
int d = instr->VFPDRegValue(kDoublePrecision);
double dn_value = get_double_from_d_register(n);
double dm_value = get_double_from_d_register(m);
double dd_value;
if (instr->Bit(6) == 0x1) { // vminnm
if ((dn_value < dm_value) || std::isnan(dm_value)) {
dd_value = dn_value;
} else if ((dm_value < dn_value) || std::isnan(dn_value)) {
dd_value = dm_value;
} else {
DCHECK_EQ(dn_value, dm_value);
// Make sure that we pick the most negative sign for +/-0.
dd_value = std::signbit(dn_value) ? dn_value : dm_value;
}
} else { // vmaxnm
if ((dn_value > dm_value) || std::isnan(dm_value)) {
dd_value = dn_value;
} else if ((dm_value > dn_value) || std::isnan(dn_value)) {
dd_value = dm_value;
} else {
DCHECK_EQ(dn_value, dm_value);
// Make sure that we pick the most positive sign for +/-0.
dd_value = std::signbit(dn_value) ? dm_value : dn_value;
}
}
dd_value = canonicalizeNaN(dd_value);
set_d_register_from_double(d, dd_value);
} else {
int m = instr->VFPMRegValue(kSinglePrecision);
int n = instr->VFPNRegValue(kSinglePrecision);
int d = instr->VFPDRegValue(kSinglePrecision);
float sn_value = get_float_from_s_register(n);
float sm_value = get_float_from_s_register(m);
float sd_value;
if (instr->Bit(6) == 0x1) { // vminnm
if ((sn_value < sm_value) || std::isnan(sm_value)) {
sd_value = sn_value;
} else if ((sm_value < sn_value) || std::isnan(sn_value)) {
sd_value = sm_value;
} else {
DCHECK_EQ(sn_value, sm_value);
// Make sure that we pick the most negative sign for +/-0.
sd_value = std::signbit(sn_value) ? sn_value : sm_value;
}
} else { // vmaxnm
if ((sn_value > sm_value) || std::isnan(sm_value)) {
sd_value = sn_value;
} else if ((sm_value > sn_value) || std::isnan(sn_value)) {
sd_value = sm_value;
} else {
DCHECK_EQ(sn_value, sm_value);
// Make sure that we pick the most positive sign for +/-0.
sd_value = std::signbit(sn_value) ? sm_value : sn_value;
}
}
sd_value = canonicalizeNaN(sd_value);
set_s_register_from_float(d, sd_value);
}
} else {
UNIMPLEMENTED();
}
......
......@@ -2381,6 +2381,170 @@ TEST(ARMv8_vsel) {
}
}
TEST(ARMv8_vminmax_f64) {
// Test the vsel floating point instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Assembler assm(isolate, NULL, 0);
struct Inputs {
double left_;
double right_;
};
struct Results {
double vminnm_;
double vmaxnm_;
};
if (CpuFeatures::IsSupported(ARMv8)) {
CpuFeatureScope scope(&assm, ARMv8);
// Create a helper function:
// void TestVminmax(const Inputs* inputs,
// Results* results);
__ vldr(d1, r0, offsetof(Inputs, left_));
__ vldr(d2, r0, offsetof(Inputs, right_));
__ vminnm(d0, d1, d2);
__ vstr(d0, r1, offsetof(Results, vminnm_));
__ vmaxnm(d0, d1, d2);
__ vstr(d0, r1, offsetof(Results, vmaxnm_));
__ bx(lr);
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef DEBUG
OFStream os(stdout);
code->Print(os);
#endif
F4 f = FUNCTION_CAST<F4>(code->entry());
Object* dummy = nullptr;
USE(dummy);
#define CHECK_VMINMAX(left, right, vminnm, vmaxnm) \
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>(vminnm), bit_cast<uint64_t>(results.vminnm_)); \
CHECK_EQ(bit_cast<uint64_t>(vmaxnm), bit_cast<uint64_t>(results.vmaxnm_)); \
} while (0);
double nan_a = bit_cast<double>(UINT64_C(0x7ff8000000000001));
double nan_b = bit_cast<double>(UINT64_C(0x7ff8000000000002));
CHECK_VMINMAX(1.0, -1.0, -1.0, 1.0);
CHECK_VMINMAX(-1.0, 1.0, -1.0, 1.0);
CHECK_VMINMAX(0.0, -1.0, -1.0, 0.0);
CHECK_VMINMAX(-1.0, 0.0, -1.0, 0.0);
CHECK_VMINMAX(-0.0, -1.0, -1.0, -0.0);
CHECK_VMINMAX(-1.0, -0.0, -1.0, -0.0);
CHECK_VMINMAX(0.0, 1.0, 0.0, 1.0);
CHECK_VMINMAX(1.0, 0.0, 0.0, 1.0);
CHECK_VMINMAX(0.0, 0.0, 0.0, 0.0);
CHECK_VMINMAX(-0.0, -0.0, -0.0, -0.0);
CHECK_VMINMAX(-0.0, 0.0, -0.0, 0.0);
CHECK_VMINMAX(0.0, -0.0, -0.0, 0.0);
CHECK_VMINMAX(0.0, nan_a, 0.0, 0.0);
CHECK_VMINMAX(nan_a, 0.0, 0.0, 0.0);
CHECK_VMINMAX(nan_a, nan_b, nan_a, nan_a);
CHECK_VMINMAX(nan_b, nan_a, nan_b, nan_b);
#undef CHECK_VMINMAX
}
}
TEST(ARMv8_vminmax_f32) {
// Test the vsel floating point instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Assembler assm(isolate, NULL, 0);
struct Inputs {
float left_;
float right_;
};
struct Results {
float vminnm_;
float vmaxnm_;
};
if (CpuFeatures::IsSupported(ARMv8)) {
CpuFeatureScope scope(&assm, ARMv8);
// Create a helper function:
// void TestVminmax(const Inputs* inputs,
// Results* results);
__ vldr(s1, r0, offsetof(Inputs, left_));
__ vldr(s2, r0, offsetof(Inputs, right_));
__ vminnm(s0, s1, s2);
__ vstr(s0, r1, offsetof(Results, vminnm_));
__ vmaxnm(s0, s1, s2);
__ vstr(s0, r1, offsetof(Results, vmaxnm_));
__ bx(lr);
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef DEBUG
OFStream os(stdout);
code->Print(os);
#endif
F4 f = FUNCTION_CAST<F4>(code->entry());
Object* dummy = nullptr;
USE(dummy);
#define CHECK_VMINMAX(left, right, vminnm, vmaxnm) \
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>(vminnm), bit_cast<uint32_t>(results.vminnm_)); \
CHECK_EQ(bit_cast<uint32_t>(vmaxnm), bit_cast<uint32_t>(results.vmaxnm_)); \
} while (0);
float nan_a = bit_cast<float>(UINT32_C(0x7fc00001));
float nan_b = bit_cast<float>(UINT32_C(0x7fc00002));
CHECK_VMINMAX(1.0f, -1.0f, -1.0f, 1.0f);
CHECK_VMINMAX(-1.0f, 1.0f, -1.0f, 1.0f);
CHECK_VMINMAX(0.0f, -1.0f, -1.0f, 0.0f);
CHECK_VMINMAX(-1.0f, 0.0f, -1.0f, 0.0f);
CHECK_VMINMAX(-0.0f, -1.0f, -1.0f, -0.0f);
CHECK_VMINMAX(-1.0f, -0.0f, -1.0f, -0.0f);
CHECK_VMINMAX(0.0f, 1.0f, 0.0f, 1.0f);
CHECK_VMINMAX(1.0f, 0.0f, 0.0f, 1.0f);
CHECK_VMINMAX(0.0f, 0.0f, 0.0f, 0.0f);
CHECK_VMINMAX(-0.0f, -0.0f, -0.0f, -0.0f);
CHECK_VMINMAX(-0.0f, 0.0f, -0.0f, 0.0f);
CHECK_VMINMAX(0.0f, -0.0f, -0.0f, 0.0f);
CHECK_VMINMAX(0.0f, nan_a, 0.0f, 0.0f);
CHECK_VMINMAX(nan_a, 0.0f, 0.0f, 0.0f);
CHECK_VMINMAX(nan_a, nan_b, nan_a, nan_a);
CHECK_VMINMAX(nan_b, nan_a, nan_b, nan_b);
#undef CHECK_VMINMAX
}
}
TEST(unaligned_loads) {
// All supported ARM targets allow unaligned accesses.
CcTest::InitializeVM();
......
......@@ -860,6 +860,20 @@ TEST(ARMv8_vrintX_disasm) {
}
TEST(ARMv8_vminmax_disasm) {
SET_UP();
if (CpuFeatures::IsSupported(ARMv8)) {
COMPARE(vmaxnm(d0, d1, d2), "fe810b02 vmaxnm.f64 d0, d1, d2");
COMPARE(vminnm(d3, d4, d5), "fe843b45 vminnm.f64 d3, d4, d5");
COMPARE(vmaxnm(s6, s7, s8), "fe833a84 vmaxnm.f32 s6, s7, s8");
COMPARE(vminnm(s9, s10, s11), "fec54a65 vminnm.f32 s9, s10, s11");
}
VERIFY_RUN();
}
TEST(ARMv8_vselX_disasm) {
SET_UP();
......
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