A32: Implement ASIMD VCEQ, VCGE, VCGT, VCLE, VCLT with zero

Fairly self-explanatory, we can leverage the existing IR functions for
the purpose of these instructions.

In the integer case, we can just insert function pointers
into an array and index it, given all comparison primitives exist
already for the integer side of things.
This commit is contained in:
Lioncash 2020-06-20 00:05:58 +01:00 committed by MerryMage
parent 656419286c
commit ed6ca58058
4 changed files with 93 additions and 8 deletions

View file

@ -85,11 +85,11 @@ INST(asimd_VMVN_reg, "VMVN_reg", "111100111D11zz00dddd010
//INST(asimd_VPADAL, "VPADAL", "111100111-11--00----0110xx-0----") // ASIMD
INST(asimd_VQABS, "VQABS", "111100111D11zz00dddd01110QM0mmmm") // ASIMD
INST(asimd_VQNEG, "VQNEG", "111100111D11zz00dddd01111QM0mmmm") // ASIMD
//INST(asimd_VCGT_zero, "VCGT (zero)", "111100111-11--01----0x000x-0----") // ASIMD
//INST(asimd_VCGE_zero, "VCGE (zero)", "111100111-11--01----0x001x-0----") // ASIMD
//INST(asimd_VCEQ_zero, "VCEQ (zero)", "111100111-11--01----0x010x-0----") // ASIMD
//INST(asimd_VCLE_zero, "VCLE (zero)", "111100111-11--01----0x011x-0----") // ASIMD
//INST(asimd_VCLT_zero, "VCLT (zero)", "111100111-11--01----0x100x-0----") // ASIMD
INST(asimd_VCGT_zero, "VCGT (zero)", "111100111D11zz01dddd0F000QM0mmmm") // ASIMD
INST(asimd_VCGE_zero, "VCGE (zero)", "111100111D11zz01dddd0F001QM0mmmm") // ASIMD
INST(asimd_VCEQ_zero, "VCEQ (zero)", "111100111D11zz01dddd0F010QM0mmmm") // ASIMD
INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm") // ASIMD
INST(asimd_VCLT_zero, "VCLT (zero)", "111100111D11zz01dddd0F100QM0mmmm") // ASIMD
INST(asimd_VABS, "VABS", "111100111D11zz01dddd0F110QM0mmmm") // ASIMD
INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm") // ASIMD
INST(asimd_VSWP, "VSWP", "111100111D110010dddd00000QM0mmmm") // ASIMD

View file

@ -5,9 +5,69 @@
#include "common/bit_util.h"
#include <array>
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
namespace {
enum class Comparison {
EQ,
GE,
GT,
LE,
LT,
};
bool CompareWithZero(ArmTranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm, Comparison type) {
if (sz == 0b11 || (F && sz != 0b10)) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [&] {
const auto reg_m = v.ir.GetVector(m);
const auto zero = v.ir.ZeroVector();
if (F) {
switch (type) {
case Comparison::EQ:
return v.ir.FPVectorEqual(32, reg_m, zero, false);
case Comparison::GE:
return v.ir.FPVectorGreaterEqual(32, reg_m, zero, false);
case Comparison::GT:
return v.ir.FPVectorGreater(32, reg_m, zero, false);
case Comparison::LE:
return v.ir.FPVectorGreaterEqual(32, zero, reg_m, false);
case Comparison::LT:
return v.ir.FPVectorGreater(32, zero, reg_m, false);
}
return IR::U128{};
} else {
static constexpr std::array fns{
&IREmitter::VectorEqual,
&IREmitter::VectorGreaterEqualSigned,
&IREmitter::VectorGreaterSigned,
&IREmitter::VectorLessEqualSigned,
&IREmitter::VectorLessSigned,
};
const size_t esize = 8U << sz;
return (v.ir.*fns[static_cast<size_t>(type)])(esize, reg_m, zero);
}
}();
v.ir.SetVector(d, result);
return true;
}
} // Anonymous namespace
bool ArmTranslatorVisitor::asimd_VREV(bool D, size_t sz, size_t Vd, size_t op, bool Q, bool M, size_t Vm) {
if (op + sz >= 3) {
@ -192,6 +252,26 @@ bool ArmTranslatorVisitor::asimd_VQNEG(bool D, size_t sz, size_t Vd, bool Q, boo
return true;
}
bool ArmTranslatorVisitor::asimd_VCGT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GT);
}
bool ArmTranslatorVisitor::asimd_VCGE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GE);
}
bool ArmTranslatorVisitor::asimd_VCEQ_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::EQ);
}
bool ArmTranslatorVisitor::asimd_VCLE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LE);
}
bool ArmTranslatorVisitor::asimd_VCLT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LT);
}
bool ArmTranslatorVisitor::asimd_VABS(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
if (sz == 0b11 || (F && sz != 0b10)) {
return UndefinedInstruction();

View file

@ -479,6 +479,11 @@ struct ArmTranslatorVisitor final {
bool asimd_VMVN_reg(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VQABS(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VQNEG(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VCGT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCGE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCEQ_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCLE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCLT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VABS(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VNEG(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VSWP(bool D, size_t Vd, bool Q, bool M, size_t Vm);

View file

@ -466,7 +466,7 @@ TEST_CASE("arm: PackedAbsDiffSumS8", "[arm][A32]") {
REQUIRE(jit.Cpsr() == 0xb0000010);
}
TEST_CASE("arm: vclt.f32 with zero", "[arm][A32][.]") {
TEST_CASE("arm: vclt.f32 with zero", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {