From da33af5abeb63ee77bfa9022fa2d87a0055d9ed0 Mon Sep 17 00:00:00 2001
From: MerryMage <MerryMage@users.noreply.github.com>
Date: Sun, 7 Aug 2016 11:38:30 +0100
Subject: [PATCH] VFP: Implement VMLA, VMLS, VNMLA, VNMLS

---
 src/frontend/decoder/vfp2.h                   | 11 ++-
 .../disassembler/disassembler_arm.cpp         | 24 +++++-
 .../translate/translate_arm/translate_arm.h   |  6 +-
 src/frontend/translate/translate_arm/vfp2.cpp | 80 +++++++++++++++++++
 4 files changed, 112 insertions(+), 9 deletions(-)

diff --git a/src/frontend/decoder/vfp2.h b/src/frontend/decoder/vfp2.h
index be67f81a..ec2e8e80 100644
--- a/src/frontend/decoder/vfp2.h
+++ b/src/frontend/decoder/vfp2.h
@@ -64,10 +64,10 @@ boost::optional<const VFP2Matcher<V>&> DecodeVFP2(u32 instruction) {
     // cccc1110________----101-__-0----
 
     // Floating-point three-register data processing instructions
-    // VMLA
-    // VMLS
-    // VNMLA
-    // VNMLS
+    INST(&V::vfp2_VMLA,       "VMLA",                "cccc11100D00nnnndddd101zN0M0mmmm"),
+    INST(&V::vfp2_VMLS,       "VMLS",                "cccc11100D00nnnndddd101zN1M0mmmm"),
+    INST(&V::vfp2_VNMLS,      "VNMLS",               "cccc11100D01nnnndddd101zN0M0mmmm"),
+    INST(&V::vfp2_VNMLA,      "VNMLA",               "cccc11100D01nnnndddd101zN1M0mmmm"),
     INST(&V::vfp2_VMUL,       "VMUL",                "cccc11100D10nnnndddd101zN0M0mmmm"),
     INST(&V::vfp2_VNMUL,      "VNMUL",               "cccc11100D10nnnndddd101zN1M0mmmm"),
     INST(&V::vfp2_VADD,       "VADD",                "cccc11100D11nnnndddd101zN0M0mmmm"),
@@ -99,6 +99,9 @@ boost::optional<const VFP2Matcher<V>&> DecodeVFP2(u32 instruction) {
 
     };
 
+    if ((instruction & 0xF0000000) == 0xF0000000)
+        return boost::none; // Don't try matching any unconditional instructions.
+
     const auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
 
     auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
diff --git a/src/frontend/disassembler/disassembler_arm.cpp b/src/frontend/disassembler/disassembler_arm.cpp
index 0be58d6f..a09ecc83 100644
--- a/src/frontend/disassembler/disassembler_arm.cpp
+++ b/src/frontend/disassembler/disassembler_arm.cpp
@@ -560,10 +560,6 @@ public:
     std::string arm_SRS() { return "ice"; }
 
     // Floating point arithmetic instructions
-    std::string vfp2_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
-        return Common::StringFromFormat("vmul%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
-    }
-
     std::string vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
         return Common::StringFromFormat("vadd%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
     }
@@ -572,10 +568,30 @@ public:
         return Common::StringFromFormat("vsub%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
     }
 
+    std::string vfp2_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+        return Common::StringFromFormat("vmul%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
+    }
+
+    std::string vfp2_VMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+        return Common::StringFromFormat("vmla%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
+    }
+
+    std::string vfp2_VMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+        return Common::StringFromFormat("vmls%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
+    }
+
     std::string vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
         return Common::StringFromFormat("vnmul%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
     }
 
+    std::string vfp2_VNMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+        return Common::StringFromFormat("vnmla%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
+    }
+
+    std::string vfp2_VNMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+        return Common::StringFromFormat("vnmls%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
+    }
+
     std::string vfp2_VDIV(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
         return Common::StringFromFormat("vdiv%s.%s %s, %s, %s", CondToString(cond), sz ? "f64" : "f32", FPRegStr(sz, Vd, D).c_str(), FPRegStr(sz, Vn, N).c_str(), FPRegStr(sz, Vm, M).c_str());
     }
diff --git a/src/frontend/translate/translate_arm/translate_arm.h b/src/frontend/translate/translate_arm/translate_arm.h
index 5107b96c..9cf68973 100644
--- a/src/frontend/translate/translate_arm/translate_arm.h
+++ b/src/frontend/translate/translate_arm/translate_arm.h
@@ -321,8 +321,12 @@ struct ArmTranslatorVisitor final {
     // Floating-point three-register data processing instructions
     bool vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
     bool vfp2_VSUB(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
-    bool vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
     bool vfp2_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
+    bool vfp2_VMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
+    bool vfp2_VMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
+    bool vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
+    bool vfp2_VNMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
+    bool vfp2_VNMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
     bool vfp2_VDIV(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
 
     // Floating-point misc instructions
diff --git a/src/frontend/translate/translate_arm/vfp2.cpp b/src/frontend/translate/translate_arm/vfp2.cpp
index cad47750..d9a1c0e6 100644
--- a/src/frontend/translate/translate_arm/vfp2.cpp
+++ b/src/frontend/translate/translate_arm/vfp2.cpp
@@ -74,6 +74,46 @@ bool ArmTranslatorVisitor::vfp2_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bo
     return true;
 }
 
+bool ArmTranslatorVisitor::vfp2_VMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+    if (ir.current_location.FPSCR_Len() != 1 || ir.current_location.FPSCR_Stride() != 1)
+        return InterpretThisInstruction(); // TODO: Vectorised floating point instructions
+
+    ExtReg d = ToExtReg(sz, Vd, D);
+    ExtReg n = ToExtReg(sz, Vn, N);
+    ExtReg m = ToExtReg(sz, Vm, M);
+    // VMLA.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
+    if (ConditionPassed(cond)) {
+        auto a = ir.GetExtendedRegister(n);
+        auto b = ir.GetExtendedRegister(m);
+        auto c = ir.GetExtendedRegister(d);
+        auto result = sz
+                      ? ir.FPAdd64(c, ir.FPMul64(a, b, true), true)
+                      : ir.FPAdd32(c, ir.FPMul32(a, b, true), true);
+        ir.SetExtendedRegister(d, result);
+    }
+    return true;
+}
+
+bool ArmTranslatorVisitor::vfp2_VMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+    if (ir.current_location.FPSCR_Len() != 1 || ir.current_location.FPSCR_Stride() != 1)
+        return InterpretThisInstruction(); // TODO: Vectorised floating point instructions
+
+    ExtReg d = ToExtReg(sz, Vd, D);
+    ExtReg n = ToExtReg(sz, Vn, N);
+    ExtReg m = ToExtReg(sz, Vm, M);
+    // VMLS.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
+    if (ConditionPassed(cond)) {
+        auto a = ir.GetExtendedRegister(n);
+        auto b = ir.GetExtendedRegister(m);
+        auto c = ir.GetExtendedRegister(d);
+        auto result = sz
+                      ? ir.FPAdd64(c, ir.FPNeg64(ir.FPMul64(a, b, true)), true)
+                      : ir.FPAdd32(c, ir.FPNeg32(ir.FPMul32(a, b, true)), true);
+        ir.SetExtendedRegister(d, result);
+    }
+    return true;
+}
+
 bool ArmTranslatorVisitor::vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
     if (ir.current_location.FPSCR_Len() != 1 || ir.current_location.FPSCR_Stride() != 1)
         return InterpretThisInstruction(); // TODO: Vectorised floating point instructions
@@ -93,6 +133,46 @@ bool ArmTranslatorVisitor::vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, b
     return true;
 }
 
+bool ArmTranslatorVisitor::vfp2_VNMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+    if (ir.current_location.FPSCR_Len() != 1 || ir.current_location.FPSCR_Stride() != 1)
+        return InterpretThisInstruction(); // TODO: Vectorised floating point instructions
+
+    ExtReg d = ToExtReg(sz, Vd, D);
+    ExtReg n = ToExtReg(sz, Vn, N);
+    ExtReg m = ToExtReg(sz, Vm, M);
+    // VNMLA.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
+    if (ConditionPassed(cond)) {
+        auto a = ir.GetExtendedRegister(n);
+        auto b = ir.GetExtendedRegister(m);
+        auto c = ir.GetExtendedRegister(d);
+        auto result = sz
+                      ? ir.FPAdd64(ir.FPNeg64(c), ir.FPNeg64(ir.FPMul64(a, b, true)), true)
+                      : ir.FPAdd32(ir.FPNeg32(c), ir.FPNeg32(ir.FPMul32(a, b, true)), true);
+        ir.SetExtendedRegister(d, result);
+    }
+    return true;
+}
+
+bool ArmTranslatorVisitor::vfp2_VNMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
+    if (ir.current_location.FPSCR_Len() != 1 || ir.current_location.FPSCR_Stride() != 1)
+        return InterpretThisInstruction(); // TODO: Vectorised floating point instructions
+
+    ExtReg d = ToExtReg(sz, Vd, D);
+    ExtReg n = ToExtReg(sz, Vn, N);
+    ExtReg m = ToExtReg(sz, Vm, M);
+    // VNMLS.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
+    if (ConditionPassed(cond)) {
+        auto a = ir.GetExtendedRegister(n);
+        auto b = ir.GetExtendedRegister(m);
+        auto c = ir.GetExtendedRegister(d);
+        auto result = sz
+                      ? ir.FPAdd64(ir.FPNeg64(c), ir.FPMul64(a, b, true), true)
+                      : ir.FPAdd32(ir.FPNeg32(c), ir.FPMul32(a, b, true), true);
+        ir.SetExtendedRegister(d, result);
+    }
+    return true;
+}
+
 bool ArmTranslatorVisitor::vfp2_VDIV(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
     if (ir.current_location.FPSCR_Len() != 1 || ir.current_location.FPSCR_Stride() != 1)
         return InterpretThisInstruction(); // TODO: Vectorised floating point instructions