[arm] Implement Float(32|64)(Min|Max) using vsel.
BUG= Review URL: https://codereview.chromium.org/1862993002 Cr-Commit-Position: refs/heads/master@{#35292}
This commit is contained in:
parent
d2eb555ee1
commit
141324cfdc
@ -3371,6 +3371,69 @@ void Assembler::vcmp(const SwVfpRegister src1, const float src2,
|
||||
0x5 * B9 | B6);
|
||||
}
|
||||
|
||||
void Assembler::vsel(Condition cond, const DwVfpRegister dst,
|
||||
const DwVfpRegister src1, const DwVfpRegister src2) {
|
||||
// cond=kSpecialCondition(31-28) | 11100(27-23) | D(22) |
|
||||
// vsel_cond=XX(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);
|
||||
int sz = 1;
|
||||
|
||||
// VSEL has a special (restricted) condition encoding.
|
||||
// eq(0b0000)... -> 0b00
|
||||
// ge(0b1010)... -> 0b10
|
||||
// gt(0b1100)... -> 0b11
|
||||
// vs(0b0110)... -> 0b01
|
||||
// No other conditions are supported.
|
||||
int vsel_cond = (cond >> 30) & 0x3;
|
||||
if ((cond != eq) && (cond != ge) && (cond != gt) && (cond != vs)) {
|
||||
// We can implement some other conditions by swapping the inputs.
|
||||
DCHECK((cond == ne) | (cond == lt) | (cond == le) | (cond == vc));
|
||||
std::swap(vn, vm);
|
||||
std::swap(n, m);
|
||||
}
|
||||
|
||||
emit(kSpecialCondition | 0x1C * B23 | d * B22 | vsel_cond * B20 | vn * B16 |
|
||||
vd * B12 | 0x5 * B9 | sz * B8 | n * B7 | m * B5 | vm);
|
||||
}
|
||||
|
||||
void Assembler::vsel(Condition cond, const SwVfpRegister dst,
|
||||
const SwVfpRegister src1, const SwVfpRegister src2) {
|
||||
// cond=kSpecialCondition(31-28) | 11100(27-23) | D(22) |
|
||||
// vsel_cond=XX(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);
|
||||
int sz = 0;
|
||||
|
||||
// VSEL has a special (restricted) condition encoding.
|
||||
// eq(0b0000)... -> 0b00
|
||||
// ge(0b1010)... -> 0b10
|
||||
// gt(0b1100)... -> 0b11
|
||||
// vs(0b0110)... -> 0b01
|
||||
// No other conditions are supported.
|
||||
int vsel_cond = (cond >> 30) & 0x3;
|
||||
if ((cond != eq) && (cond != ge) && (cond != gt) && (cond != vs)) {
|
||||
// We can implement some other conditions by swapping the inputs.
|
||||
DCHECK((cond == ne) | (cond == lt) | (cond == le) | (cond == vc));
|
||||
std::swap(vn, vm);
|
||||
std::swap(n, m);
|
||||
}
|
||||
|
||||
emit(kSpecialCondition | 0x1C * B23 | d * B22 | vsel_cond * B20 | vn * B16 |
|
||||
vd * B12 | 0x5 * B9 | sz * B8 | n * B7 | m * B5 | vm);
|
||||
}
|
||||
|
||||
void Assembler::vsqrt(const DwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
|
@ -1225,6 +1225,17 @@ class Assembler : public AssemblerBase {
|
||||
const Condition cond = al);
|
||||
void vcmp(const SwVfpRegister src1, const float src2,
|
||||
const Condition cond = al);
|
||||
|
||||
// VSEL supports cond in {eq, ne, ge, lt, gt, le, vs, vc}.
|
||||
void vsel(const Condition cond,
|
||||
const DwVfpRegister dst,
|
||||
const DwVfpRegister src1,
|
||||
const DwVfpRegister src2);
|
||||
void vsel(const Condition cond,
|
||||
const SwVfpRegister dst,
|
||||
const SwVfpRegister src1,
|
||||
const SwVfpRegister src2);
|
||||
|
||||
void vsqrt(const DwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
const Condition cond = al);
|
||||
|
@ -1869,6 +1869,48 @@ void Decoder::DecodeSpecialCondition(Instruction* instr) {
|
||||
Unknown(instr);
|
||||
}
|
||||
break;
|
||||
case 0x1C:
|
||||
if ((instr->Bits(11, 9) == 0x5) && (instr->Bit(6) == 0) &&
|
||||
(instr->Bit(4) == 0)) {
|
||||
// VSEL* (floating-point)
|
||||
bool dp_operation = (instr->SzValue() == 1);
|
||||
switch (instr->Bits(21, 20)) {
|
||||
case 0x0:
|
||||
if (dp_operation) {
|
||||
Format(instr, "vseleq.f64 'Dd, 'Dn, 'Dm");
|
||||
} else {
|
||||
Format(instr, "vseleq.f32 'Sd, 'Sn, 'Sm");
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
if (dp_operation) {
|
||||
Format(instr, "vselvs.f64 'Dd, 'Dn, 'Dm");
|
||||
} else {
|
||||
Format(instr, "vselvs.f32 'Sd, 'Sn, 'Sm");
|
||||
}
|
||||
break;
|
||||
case 0x2:
|
||||
if (dp_operation) {
|
||||
Format(instr, "vselge.f64 'Dd, 'Dn, 'Dm");
|
||||
} else {
|
||||
Format(instr, "vselge.f32 'Sd, 'Sn, 'Sm");
|
||||
}
|
||||
break;
|
||||
case 0x3:
|
||||
if (dp_operation) {
|
||||
Format(instr, "vselgt.f64 'Dd, 'Dn, 'Dm");
|
||||
} else {
|
||||
Format(instr, "vselgt.f32 'Sd, 'Sn, 'Sm");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE(); // Case analysis is exhaustive.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Unknown(instr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Unknown(instr);
|
||||
break;
|
||||
|
@ -4028,6 +4028,45 @@ void Simulator::DecodeSpecialCondition(Instruction* instr) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
break;
|
||||
case 0x1C:
|
||||
if ((instr->Bits(11, 9) == 0x5) && (instr->Bit(6) == 0) &&
|
||||
(instr->Bit(4) == 0)) {
|
||||
// VSEL* (floating-point)
|
||||
bool condition_holds;
|
||||
switch (instr->Bits(21, 20)) {
|
||||
case 0x0: // VSELEQ
|
||||
condition_holds = (z_flag_ == 1);
|
||||
break;
|
||||
case 0x1: // VSELVS
|
||||
condition_holds = (v_flag_ == 1);
|
||||
break;
|
||||
case 0x2: // VSELGE
|
||||
condition_holds = (n_flag_ == v_flag_);
|
||||
break;
|
||||
case 0x3: // VSELGT
|
||||
condition_holds = ((z_flag_ == 0) && (n_flag_ == v_flag_));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE(); // Case analysis is exhaustive.
|
||||
break;
|
||||
}
|
||||
if (instr->SzValue() == 0x1) {
|
||||
int n = instr->VFPNRegValue(kDoublePrecision);
|
||||
int m = instr->VFPMRegValue(kDoublePrecision);
|
||||
int d = instr->VFPDRegValue(kDoublePrecision);
|
||||
double result = get_double_from_d_register(condition_holds ? n : m);
|
||||
set_d_register_from_double(d, result);
|
||||
} else {
|
||||
int n = instr->VFPNRegValue(kSinglePrecision);
|
||||
int m = instr->VFPMRegValue(kSinglePrecision);
|
||||
int d = instr->VFPDRegValue(kSinglePrecision);
|
||||
float result = get_float_from_s_register(condition_holds ? n : m);
|
||||
set_s_register_from_float(d, result);
|
||||
}
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
|
@ -1146,6 +1146,46 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmFloat32Max: {
|
||||
CpuFeatureScope scope(masm(), ARMv8);
|
||||
// (b < a) ? a : b
|
||||
SwVfpRegister a = i.InputFloat32Register(0);
|
||||
SwVfpRegister b = i.InputFloat32Register(1);
|
||||
SwVfpRegister result = i.OutputFloat32Register(0);
|
||||
__ VFPCompareAndSetFlags(a, b);
|
||||
__ vsel(gt, result, a, b);
|
||||
break;
|
||||
}
|
||||
case kArmFloat32Min: {
|
||||
CpuFeatureScope scope(masm(), ARMv8);
|
||||
// (a < b) ? a : b
|
||||
SwVfpRegister a = i.InputFloat32Register(0);
|
||||
SwVfpRegister b = i.InputFloat32Register(1);
|
||||
SwVfpRegister result = i.OutputFloat32Register(0);
|
||||
__ VFPCompareAndSetFlags(b, a);
|
||||
__ vsel(gt, result, a, b);
|
||||
break;
|
||||
}
|
||||
case kArmFloat64Max: {
|
||||
CpuFeatureScope scope(masm(), ARMv8);
|
||||
// (b < a) ? a : b
|
||||
DwVfpRegister a = i.InputFloat64Register(0);
|
||||
DwVfpRegister b = i.InputFloat64Register(1);
|
||||
DwVfpRegister result = i.OutputFloat64Register(0);
|
||||
__ VFPCompareAndSetFlags(a, b);
|
||||
__ vsel(gt, result, a, b);
|
||||
break;
|
||||
}
|
||||
case kArmFloat64Min: {
|
||||
CpuFeatureScope scope(masm(), ARMv8);
|
||||
// (a < b) ? a : b
|
||||
DwVfpRegister a = i.InputFloat64Register(0);
|
||||
DwVfpRegister b = i.InputFloat64Register(1);
|
||||
DwVfpRegister result = i.OutputFloat64Register(0);
|
||||
__ VFPCompareAndSetFlags(b, a);
|
||||
__ vsel(gt, result, a, b);
|
||||
break;
|
||||
}
|
||||
case kArmPush:
|
||||
if (instr->InputAt(0)->IsDoubleRegister()) {
|
||||
__ vpush(i.InputDoubleRegister(0));
|
||||
|
@ -101,6 +101,10 @@ namespace compiler {
|
||||
V(ArmVstrF32) \
|
||||
V(ArmVldrF64) \
|
||||
V(ArmVstrF64) \
|
||||
V(ArmFloat32Max) \
|
||||
V(ArmFloat32Min) \
|
||||
V(ArmFloat64Max) \
|
||||
V(ArmFloat64Min) \
|
||||
V(ArmLdrb) \
|
||||
V(ArmLdrsb) \
|
||||
V(ArmStrb) \
|
||||
|
@ -99,6 +99,10 @@ int InstructionScheduler::GetTargetInstructionFlags(
|
||||
case kArmVmovHighU32F64:
|
||||
case kArmVmovHighF64U32:
|
||||
case kArmVmovF64U32U32:
|
||||
case kArmFloat64Max:
|
||||
case kArmFloat64Min:
|
||||
case kArmFloat32Max:
|
||||
case kArmFloat32Min:
|
||||
return kNoOpcodeFlags;
|
||||
|
||||
case kArmVldrF32:
|
||||
|
@ -1285,18 +1285,25 @@ void InstructionSelector::VisitFloat64Mod(Node* node) {
|
||||
g.UseFixed(node->InputAt(1), d1))->MarkAsCall();
|
||||
}
|
||||
|
||||
void InstructionSelector::VisitFloat32Max(Node* node) {
|
||||
DCHECK(IsSupported(ARMv8));
|
||||
VisitRRR(this, kArmFloat32Max, node);
|
||||
}
|
||||
|
||||
void InstructionSelector::VisitFloat32Max(Node* node) { UNREACHABLE(); }
|
||||
void InstructionSelector::VisitFloat64Max(Node* node) {
|
||||
DCHECK(IsSupported(ARMv8));
|
||||
VisitRRR(this, kArmFloat64Max, node);
|
||||
}
|
||||
|
||||
void InstructionSelector::VisitFloat32Min(Node* node) {
|
||||
DCHECK(IsSupported(ARMv8));
|
||||
VisitRRR(this, kArmFloat32Min, node);
|
||||
}
|
||||
|
||||
void InstructionSelector::VisitFloat64Max(Node* node) { UNREACHABLE(); }
|
||||
|
||||
|
||||
void InstructionSelector::VisitFloat32Min(Node* node) { UNREACHABLE(); }
|
||||
|
||||
|
||||
void InstructionSelector::VisitFloat64Min(Node* node) { UNREACHABLE(); }
|
||||
|
||||
void InstructionSelector::VisitFloat64Min(Node* node) {
|
||||
DCHECK(IsSupported(ARMv8));
|
||||
VisitRRR(this, kArmFloat64Min, node);
|
||||
}
|
||||
|
||||
void InstructionSelector::VisitFloat32Abs(Node* node) {
|
||||
VisitRR(this, kArmVabsF32, node);
|
||||
@ -1826,7 +1833,11 @@ InstructionSelector::SupportedMachineOperatorFlags() {
|
||||
MachineOperatorBuilder::kFloat64RoundTruncate |
|
||||
MachineOperatorBuilder::kFloat64RoundTiesAway |
|
||||
MachineOperatorBuilder::kFloat32RoundTiesEven |
|
||||
MachineOperatorBuilder::kFloat64RoundTiesEven;
|
||||
MachineOperatorBuilder::kFloat64RoundTiesEven |
|
||||
MachineOperatorBuilder::kFloat32Min |
|
||||
MachineOperatorBuilder::kFloat32Max |
|
||||
MachineOperatorBuilder::kFloat64Min |
|
||||
MachineOperatorBuilder::kFloat64Max;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
@ -2231,6 +2231,158 @@ TEST(ARMv8_vrintX) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ARMv8_vsel) {
|
||||
// Test the vsel floating point instructions.
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
HandleScope scope(isolate);
|
||||
|
||||
Assembler assm(isolate, NULL, 0);
|
||||
|
||||
// Used to indicate whether a condition passed or failed.
|
||||
static constexpr float kResultPass = 1.0f;
|
||||
static constexpr float kResultFail = -kResultPass;
|
||||
|
||||
struct ResultsF32 {
|
||||
float vseleq_;
|
||||
float vselge_;
|
||||
float vselgt_;
|
||||
float vselvs_;
|
||||
|
||||
// The following conditions aren't architecturally supported, but the
|
||||
// assembler implements them by swapping the inputs.
|
||||
float vselne_;
|
||||
float vsellt_;
|
||||
float vselle_;
|
||||
float vselvc_;
|
||||
};
|
||||
|
||||
struct ResultsF64 {
|
||||
double vseleq_;
|
||||
double vselge_;
|
||||
double vselgt_;
|
||||
double vselvs_;
|
||||
|
||||
// The following conditions aren't architecturally supported, but the
|
||||
// assembler implements them by swapping the inputs.
|
||||
double vselne_;
|
||||
double vsellt_;
|
||||
double vselle_;
|
||||
double vselvc_;
|
||||
};
|
||||
|
||||
if (CpuFeatures::IsSupported(ARMv8)) {
|
||||
CpuFeatureScope scope(&assm, ARMv8);
|
||||
|
||||
// Create a helper function:
|
||||
// void TestVsel(uint32_t nzcv,
|
||||
// ResultsF32* results_f32,
|
||||
// ResultsF64* results_f64);
|
||||
__ msr(CPSR_f, Operand(r0));
|
||||
|
||||
__ vmov(s1, kResultPass);
|
||||
__ vmov(s2, kResultFail);
|
||||
|
||||
__ vsel(eq, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vseleq_));
|
||||
__ vsel(ge, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vselge_));
|
||||
__ vsel(gt, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vselgt_));
|
||||
__ vsel(vs, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vselvs_));
|
||||
|
||||
__ vsel(ne, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vselne_));
|
||||
__ vsel(lt, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vsellt_));
|
||||
__ vsel(le, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vselle_));
|
||||
__ vsel(vc, s0, s1, s2);
|
||||
__ vstr(s0, r1, offsetof(ResultsF32, vselvc_));
|
||||
|
||||
__ vmov(d1, kResultPass);
|
||||
__ vmov(d2, kResultFail);
|
||||
|
||||
__ vsel(eq, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vseleq_));
|
||||
__ vsel(ge, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vselge_));
|
||||
__ vsel(gt, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vselgt_));
|
||||
__ vsel(vs, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vselvs_));
|
||||
|
||||
__ vsel(ne, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vselne_));
|
||||
__ vsel(lt, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vsellt_));
|
||||
__ vsel(le, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vselle_));
|
||||
__ vsel(vc, d0, d1, d2);
|
||||
__ vstr(d0, r2, offsetof(ResultsF64, vselvc_));
|
||||
|
||||
__ 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
|
||||
F5 f = FUNCTION_CAST<F5>(code->entry());
|
||||
Object* dummy = nullptr;
|
||||
USE(dummy);
|
||||
|
||||
STATIC_ASSERT(kResultPass == -kResultFail);
|
||||
#define CHECK_VSEL(n, z, c, v, vseleq, vselge, vselgt, vselvs) \
|
||||
do { \
|
||||
ResultsF32 results_f32; \
|
||||
ResultsF64 results_f64; \
|
||||
uint32_t nzcv = (n << 31) | (z << 30) | (c << 29) | (v << 28); \
|
||||
dummy = CALL_GENERATED_CODE(isolate, f, nzcv, &results_f32, &results_f64, \
|
||||
0, 0); \
|
||||
CHECK_EQ(vseleq, results_f32.vseleq_); \
|
||||
CHECK_EQ(vselge, results_f32.vselge_); \
|
||||
CHECK_EQ(vselgt, results_f32.vselgt_); \
|
||||
CHECK_EQ(vselvs, results_f32.vselvs_); \
|
||||
CHECK_EQ(-vseleq, results_f32.vselne_); \
|
||||
CHECK_EQ(-vselge, results_f32.vsellt_); \
|
||||
CHECK_EQ(-vselgt, results_f32.vselle_); \
|
||||
CHECK_EQ(-vselvs, results_f32.vselvc_); \
|
||||
CHECK_EQ(vseleq, results_f64.vseleq_); \
|
||||
CHECK_EQ(vselge, results_f64.vselge_); \
|
||||
CHECK_EQ(vselgt, results_f64.vselgt_); \
|
||||
CHECK_EQ(vselvs, results_f64.vselvs_); \
|
||||
CHECK_EQ(-vseleq, results_f64.vselne_); \
|
||||
CHECK_EQ(-vselge, results_f64.vsellt_); \
|
||||
CHECK_EQ(-vselgt, results_f64.vselle_); \
|
||||
CHECK_EQ(-vselvs, results_f64.vselvc_); \
|
||||
} while (0);
|
||||
|
||||
// N Z C V vseleq vselge vselgt vselvs
|
||||
CHECK_VSEL(0, 0, 0, 0, kResultFail, kResultPass, kResultPass, kResultFail);
|
||||
CHECK_VSEL(0, 0, 0, 1, kResultFail, kResultFail, kResultFail, kResultPass);
|
||||
CHECK_VSEL(0, 0, 1, 0, kResultFail, kResultPass, kResultPass, kResultFail);
|
||||
CHECK_VSEL(0, 0, 1, 1, kResultFail, kResultFail, kResultFail, kResultPass);
|
||||
CHECK_VSEL(0, 1, 0, 0, kResultPass, kResultPass, kResultFail, kResultFail);
|
||||
CHECK_VSEL(0, 1, 0, 1, kResultPass, kResultFail, kResultFail, kResultPass);
|
||||
CHECK_VSEL(0, 1, 1, 0, kResultPass, kResultPass, kResultFail, kResultFail);
|
||||
CHECK_VSEL(0, 1, 1, 1, kResultPass, kResultFail, kResultFail, kResultPass);
|
||||
CHECK_VSEL(1, 0, 0, 0, kResultFail, kResultFail, kResultFail, kResultFail);
|
||||
CHECK_VSEL(1, 0, 0, 1, kResultFail, kResultPass, kResultPass, kResultPass);
|
||||
CHECK_VSEL(1, 0, 1, 0, kResultFail, kResultFail, kResultFail, kResultFail);
|
||||
CHECK_VSEL(1, 0, 1, 1, kResultFail, kResultPass, kResultPass, kResultPass);
|
||||
CHECK_VSEL(1, 1, 0, 0, kResultPass, kResultFail, kResultFail, kResultFail);
|
||||
CHECK_VSEL(1, 1, 0, 1, kResultPass, kResultPass, kResultFail, kResultPass);
|
||||
CHECK_VSEL(1, 1, 1, 0, kResultPass, kResultFail, kResultFail, kResultFail);
|
||||
CHECK_VSEL(1, 1, 1, 1, kResultPass, kResultPass, kResultFail, kResultPass);
|
||||
|
||||
#undef CHECK_VSEL
|
||||
}
|
||||
}
|
||||
|
||||
TEST(regress4292_b) {
|
||||
CcTest::InitializeVM();
|
||||
|
@ -860,6 +860,51 @@ TEST(ARMv8_vrintX_disasm) {
|
||||
}
|
||||
|
||||
|
||||
TEST(ARMv8_vselX_disasm) {
|
||||
SET_UP();
|
||||
|
||||
if (CpuFeatures::IsSupported(ARMv8)) {
|
||||
// Native instructions.
|
||||
COMPARE(vsel(eq, d0, d1, d2),
|
||||
"fe010b02 vseleq.f64 d0, d1, d2");
|
||||
COMPARE(vsel(eq, s0, s1, s2),
|
||||
"fe000a81 vseleq.f32 s0, s1, s2");
|
||||
COMPARE(vsel(ge, d0, d1, d2),
|
||||
"fe210b02 vselge.f64 d0, d1, d2");
|
||||
COMPARE(vsel(ge, s0, s1, s2),
|
||||
"fe200a81 vselge.f32 s0, s1, s2");
|
||||
COMPARE(vsel(gt, d0, d1, d2),
|
||||
"fe310b02 vselgt.f64 d0, d1, d2");
|
||||
COMPARE(vsel(gt, s0, s1, s2),
|
||||
"fe300a81 vselgt.f32 s0, s1, s2");
|
||||
COMPARE(vsel(vs, d0, d1, d2),
|
||||
"fe110b02 vselvs.f64 d0, d1, d2");
|
||||
COMPARE(vsel(vs, s0, s1, s2),
|
||||
"fe100a81 vselvs.f32 s0, s1, s2");
|
||||
|
||||
// Inverted conditions (and swapped inputs).
|
||||
COMPARE(vsel(ne, d0, d1, d2),
|
||||
"fe020b01 vseleq.f64 d0, d2, d1");
|
||||
COMPARE(vsel(ne, s0, s1, s2),
|
||||
"fe010a20 vseleq.f32 s0, s2, s1");
|
||||
COMPARE(vsel(lt, d0, d1, d2),
|
||||
"fe220b01 vselge.f64 d0, d2, d1");
|
||||
COMPARE(vsel(lt, s0, s1, s2),
|
||||
"fe210a20 vselge.f32 s0, s2, s1");
|
||||
COMPARE(vsel(le, d0, d1, d2),
|
||||
"fe320b01 vselgt.f64 d0, d2, d1");
|
||||
COMPARE(vsel(le, s0, s1, s2),
|
||||
"fe310a20 vselgt.f32 s0, s2, s1");
|
||||
COMPARE(vsel(vc, d0, d1, d2),
|
||||
"fe120b01 vselvs.f64 d0, d2, d1");
|
||||
COMPARE(vsel(vc, s0, s1, s2),
|
||||
"fe110a20 vselvs.f32 s0, s2, s1");
|
||||
}
|
||||
|
||||
VERIFY_RUN();
|
||||
}
|
||||
|
||||
|
||||
TEST(Neon) {
|
||||
SET_UP();
|
||||
|
||||
|
@ -2954,6 +2954,78 @@ TEST_F(InstructionSelectorTest, Word32Clz) {
|
||||
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
||||
}
|
||||
|
||||
TEST_F(InstructionSelectorTest, Float32Max) {
|
||||
StreamBuilder m(this, MachineType::Float32(), MachineType::Float32(),
|
||||
MachineType::Float32());
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* const n = m.Float32Max(p0, p1);
|
||||
m.Return(n);
|
||||
Stream s = m.Build(ARMv8);
|
||||
// Float32Max is `(b < a) ? a : b`.
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArmFloat32Max, s[0]->arch_opcode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
||||
}
|
||||
|
||||
TEST_F(InstructionSelectorTest, Float32Min) {
|
||||
StreamBuilder m(this, MachineType::Float32(), MachineType::Float32(),
|
||||
MachineType::Float32());
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* const n = m.Float32Min(p0, p1);
|
||||
m.Return(n);
|
||||
Stream s = m.Build(ARMv8);
|
||||
// Float32Min is `(a < b) ? a : b`.
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArmFloat32Min, s[0]->arch_opcode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
||||
}
|
||||
|
||||
TEST_F(InstructionSelectorTest, Float64Max) {
|
||||
StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(),
|
||||
MachineType::Float64());
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* const n = m.Float64Max(p0, p1);
|
||||
m.Return(n);
|
||||
Stream s = m.Build(ARMv8);
|
||||
// Float64Max is `(b < a) ? a : b`.
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArmFloat64Max, s[0]->arch_opcode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
||||
}
|
||||
|
||||
TEST_F(InstructionSelectorTest, Float64Min) {
|
||||
StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(),
|
||||
MachineType::Float64());
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* const n = m.Float64Min(p0, p1);
|
||||
m.Return(n);
|
||||
Stream s = m.Build(ARMv8);
|
||||
// Float64Min is `(a < b) ? a : b`.
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArmFloat64Min, s[0]->arch_opcode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user