A32: Implement ASIMD VQRDMULH (scalar)

This commit is contained in:
Lioncash 2020-06-23 01:47:34 -04:00 committed by merry
parent ab5efe8632
commit 20a2bf29fc
3 changed files with 49 additions and 26 deletions

View file

@ -75,8 +75,8 @@ INST(asimd_VMLAL_scalar, "VMLAL (scalar)", "1111001U1dzznnnndddd0o1
INST(asimd_VMUL_scalar, "VMUL (scalar)", "1111001Q1Dzznnnndddd100FN1M0mmmm") // ASIMD INST(asimd_VMUL_scalar, "VMUL (scalar)", "1111001Q1Dzznnnndddd100FN1M0mmmm") // ASIMD
INST(asimd_VMULL_scalar, "VMULL (scalar)", "1111001U1Dzznnnndddd1010N1M0mmmm") // ASIMD INST(asimd_VMULL_scalar, "VMULL (scalar)", "1111001U1Dzznnnndddd1010N1M0mmmm") // ASIMD
//INST(asimd_VQDMULL_scalar, "VQDMULL (scalar)", "111100101-BB--------1011-1-0----") // ASIMD //INST(asimd_VQDMULL_scalar, "VQDMULL (scalar)", "111100101-BB--------1011-1-0----") // ASIMD
INST(asimd_VQDMULH_scalar, "VQDMULH", "1111001Q1Dzznnnndddd1100N1M0mmmm") // ASIMD INST(asimd_VQDMULH_scalar, "VQDMULH (scalar)", "1111001Q1Dzznnnndddd1100N1M0mmmm") // ASIMD
//INST(asimd_VQRDMULH_scalar, "VQRDMULH", "1111001U1-BB--------1101-1-0----") // ASIMD INST(asimd_VQRDMULH_scalar, "VQRDMULH (scalar)", "1111001Q1Dzznnnndddd1101N1M0mmmm") // ASIMD
// Two registers and a shift amount // Two registers and a shift amount
INST(asimd_SHR, "SHR", "1111001U1Diiiiiidddd0000LQM1mmmm") // ASIMD INST(asimd_SHR, "SHR", "1111001U1Diiiiiidddd0000LQM1mmmm") // ASIMD

View file

@ -24,6 +24,11 @@ enum class MultiplyBehavior {
MultiplySubtract, MultiplySubtract,
}; };
enum class Rounding {
None,
Round,
};
bool ScalarMultiply(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm, bool ScalarMultiply(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm,
MultiplyBehavior multiply) { MultiplyBehavior multiply) {
if (sz == 0b11) { if (sz == 0b11) {
@ -105,6 +110,43 @@ bool ScalarMultiplyLong(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size
v.ir.SetVector(d, result); v.ir.SetVector(d, result);
return true; return true;
} }
bool ScalarMultiplyReturnHigh(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm,
Rounding round) {
if (sz == 0b11) {
// TODO: This should be a decode error.
return v.UndefinedInstruction();
}
if (sz == 0b00) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn))) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto n = ToVector(Q, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = v.ir.VectorGetElement(esize, v.ir.GetVector(m), index);
const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.VectorBroadcast(esize, scalar);
const auto result = [&] {
const auto tmp = v.ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m);
if (round == Rounding::Round) {
return v.ir.VectorAdd(esize, tmp.upper, v.ir.VectorLogicalShiftRight(esize, tmp.lower, static_cast<u8>(esize - 1)));
}
return tmp.upper;
}();
v.ir.SetVector(d, result);
return true;
}
} // Anonymous namespace } // Anonymous namespace
bool ArmTranslatorVisitor::asimd_VMLA_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool F, bool N, bool M, size_t Vm) { bool ArmTranslatorVisitor::asimd_VMLA_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool F, bool N, bool M, size_t Vm) {
@ -128,31 +170,11 @@ bool ArmTranslatorVisitor::asimd_VMULL_scalar(bool U, bool D, size_t sz, size_t
} }
bool ArmTranslatorVisitor::asimd_VQDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) { bool ArmTranslatorVisitor::asimd_VQDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
if (sz == 0b11) { return ScalarMultiplyReturnHigh(*this, Q, D, sz, Vn, Vd, N, M, Vm, Rounding::None);
// TODO: This should be a decode error.
return UndefinedInstruction();
} }
if (sz == 0b00) { bool ArmTranslatorVisitor::asimd_VQRDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
return UndefinedInstruction(); return ScalarMultiplyReturnHigh(*this, Q, D, sz, Vn, Vd, N, M, Vm, Rounding::Round);
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto n = ToVector(Q, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = ir.VectorGetElement(esize, ir.GetVector(m), index);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.VectorBroadcast(esize, scalar);
const auto result = ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m).upper;
ir.SetVector(d, result);
return true;
} }
} // namespace Dynarmic::A32 } // namespace Dynarmic::A32

View file

@ -511,6 +511,7 @@ struct ArmTranslatorVisitor final {
bool asimd_VMUL_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm); bool asimd_VMUL_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm);
bool asimd_VMULL_scalar(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm); bool asimd_VMULL_scalar(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VQDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm); bool asimd_VQDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VQRDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
// Two registers and a shift amount // Two registers and a shift amount
bool asimd_SHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm); bool asimd_SHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);