From 6d292e3eac44478ae36d766e1e8f745ede38410b Mon Sep 17 00:00:00 2001
From: MerryMage <MerryMage@users.noreply.github.com>
Date: Mon, 3 May 2021 13:00:40 +0100
Subject: [PATCH] decoder: Ensure more compiler-time computation

Replace with consteval when C++20 hits
---
 src/frontend/A32/decoder/arm.h        |  2 +-
 src/frontend/A32/decoder/asimd.h      |  2 +-
 src/frontend/A32/decoder/thumb16.h    |  2 +-
 src/frontend/A32/decoder/thumb32.h    |  2 +-
 src/frontend/A32/decoder/vfp.h        |  2 +-
 src/frontend/A64/decoder/a64.h        | 18 ++++++++--------
 src/frontend/decoder/decoder_detail.h | 31 +++++++++++++++++----------
 7 files changed, 34 insertions(+), 25 deletions(-)

diff --git a/src/frontend/A32/decoder/arm.h b/src/frontend/A32/decoder/arm.h
index 184ae30a..442fe67d 100644
--- a/src/frontend/A32/decoder/arm.h
+++ b/src/frontend/A32/decoder/arm.h
@@ -26,7 +26,7 @@ template <typename V>
 std::vector<ArmMatcher<V>> GetArmDecodeTable() {
     std::vector<ArmMatcher<V>> table = {
 
-#define INST(fn, name, bitstring) Decoder::detail::detail<ArmMatcher<V>>::GetMatcher(&V::fn, name, bitstring),
+#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
 #include "arm.inc"
 #undef INST
 
diff --git a/src/frontend/A32/decoder/asimd.h b/src/frontend/A32/decoder/asimd.h
index 5316430a..07fb0f71 100644
--- a/src/frontend/A32/decoder/asimd.h
+++ b/src/frontend/A32/decoder/asimd.h
@@ -25,7 +25,7 @@ template <typename V>
 std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
     std::vector<ASIMDMatcher<V>> table = {
 
-#define INST(fn, name, bitstring) Decoder::detail::detail<ASIMDMatcher<V>>::GetMatcher(&V::fn, name, bitstring),
+#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
 #include "asimd.inc"
 #undef INST
 
diff --git a/src/frontend/A32/decoder/thumb16.h b/src/frontend/A32/decoder/thumb16.h
index d2dfb863..b1ce7446 100644
--- a/src/frontend/A32/decoder/thumb16.h
+++ b/src/frontend/A32/decoder/thumb16.h
@@ -23,7 +23,7 @@ template<typename V>
 std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
     static const std::vector<Thumb16Matcher<V>> table = {
 
-#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(&V::fn, name, bitstring),
+#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)),
 #include "thumb16.inc"
 #undef INST
 
diff --git a/src/frontend/A32/decoder/thumb32.h b/src/frontend/A32/decoder/thumb32.h
index a0dd4333..dc370a31 100644
--- a/src/frontend/A32/decoder/thumb32.h
+++ b/src/frontend/A32/decoder/thumb32.h
@@ -22,7 +22,7 @@ template<typename V>
 std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
     static const std::vector<Thumb32Matcher<V>> table = {
 
-#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(&V::fn, name, bitstring),
+#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
 #include "thumb32.inc"
 #undef INST
 
diff --git a/src/frontend/A32/decoder/vfp.h b/src/frontend/A32/decoder/vfp.h
index 2b78c66e..1abfc67a 100644
--- a/src/frontend/A32/decoder/vfp.h
+++ b/src/frontend/A32/decoder/vfp.h
@@ -30,7 +30,7 @@ std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruc
     } tables = []{
         Table list = {
 
-#define INST(fn, name, bitstring) Decoder::detail::detail<VFPMatcher<V>>::GetMatcher(&V::fn, name, bitstring),
+#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
 #include "vfp.inc"
 #undef INST
 
diff --git a/src/frontend/A64/decoder/a64.h b/src/frontend/A64/decoder/a64.h
index 0379c32c..d3dc0cf6 100644
--- a/src/frontend/A64/decoder/a64.h
+++ b/src/frontend/A64/decoder/a64.h
@@ -31,10 +31,10 @@ inline size_t ToFastLookupIndex(u32 instruction) {
 }
 } // namespace detail
 
-template <typename Visitor>
-DecodeTable<Visitor> GetDecodeTable() {
-    std::vector<Matcher<Visitor>> list = {
-#define INST(fn, name, bitstring) Decoder::detail::detail<Matcher<Visitor>>::GetMatcher(&Visitor::fn, name, bitstring),
+template <typename V>
+DecodeTable<V> GetDecodeTable() {
+    std::vector<Matcher<V>> list = {
+#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
 #include "a64.inc"
 #undef INST
     };
@@ -55,7 +55,7 @@ DecodeTable<Visitor> GetDecodeTable() {
         return comes_first.count(matcher.GetName()) > 0;
     });
 
-    DecodeTable<Visitor> table{};
+    DecodeTable<V> table{};
     for (size_t i = 0; i < table.size(); ++i) {
         for (auto matcher : list) {
             const auto expect = detail::ToFastLookupIndex(matcher.GetExpected());
@@ -68,15 +68,15 @@ DecodeTable<Visitor> GetDecodeTable() {
     return table;
 }
 
-template<typename Visitor>
-std::optional<std::reference_wrapper<const Matcher<Visitor>>> Decode(u32 instruction) {
-    static const auto table = GetDecodeTable<Visitor>();
+template<typename V>
+std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
+    static const auto table = GetDecodeTable<V>();
 
     const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
 
     const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
     auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
-    return iter != subtable.end() ? std::optional<std::reference_wrapper<const Matcher<Visitor>>>(*iter) : std::nullopt;
+    return iter != subtable.end() ? std::optional<std::reference_wrapper<const Matcher<V>>>(*iter) : std::nullopt;
 }
 
 } // namespace Dynarmic::A64
diff --git a/src/frontend/decoder/decoder_detail.h b/src/frontend/decoder/decoder_detail.h
index 4f41929e..124b1f57 100644
--- a/src/frontend/decoder/decoder_detail.h
+++ b/src/frontend/decoder/decoder_detail.h
@@ -17,6 +17,15 @@
 namespace Dynarmic::Decoder {
 namespace detail {
 
+template <size_t N>
+inline constexpr std::array<char, N> StringToArray(const char (&str)[N + 1]) {
+    std::array<char, N> result;
+    for (size_t i = 0; i < N; i++) {
+        result[i] = str[i];
+    }
+    return result;
+}
+
 /**
  * Helper functions for the decoders.
  *
@@ -24,7 +33,6 @@ namespace detail {
  */
 template<class MatcherT>
 struct detail {
-private:
     using opcode_type  = typename MatcherT::opcode_type;
     using visitor_type = typename MatcherT::visitor_type;
 
@@ -35,7 +43,7 @@ private:
      * A '0' in a bitstring indicates that a zero must be present at that bit position.
      * A '1' in a bitstring indicates that a one must be present at that bit position.
      */
-    static constexpr auto GetMaskAndExpect(const char* const bitstring) {
+    static constexpr auto GetMaskAndExpect(std::array<char, opcode_bitsize> bitstring) {
         const auto one = static_cast<opcode_type>(1);
         opcode_type mask = 0, expect = 0;
         for (size_t i = 0; i < opcode_bitsize; i++) {
@@ -62,7 +70,7 @@ private:
      * An argument is specified by a continuous string of the same character.
      */
     template<size_t N>
-    static auto GetArgInfo(const char* const bitstring) {
+    static constexpr auto GetArgInfo(std::array<char, opcode_bitsize> bitstring) {
         std::array<opcode_type, N> masks = {};
         std::array<size_t, N> shifts = {};
         size_t arg_index = 0;
@@ -85,11 +93,12 @@ private:
                 if constexpr (N > 0) {
                     const size_t bit_position = opcode_bitsize - i - 1;
 
-                    ASSERT(arg_index < N);
+                    if (arg_index >= N) throw std::out_of_range("Unexpected field");
+
                     masks[arg_index] |= static_cast<opcode_type>(1) << bit_position;
                     shifts[arg_index] = bit_position;
                 } else {
-                    ASSERT_FALSE();
+                    throw std::out_of_range("Unexpected field");
                 }
             }
         }
@@ -148,23 +157,23 @@ private:
 #pragma warning(pop)
 #endif
 
-public:
     /**
      * Creates a matcher that can match and parse instructions based on bitstring.
      * See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
      */
-    template<typename FnT>
-    static auto GetMatcher(FnT fn, const char* const name, const char* const bitstring) {
-        constexpr size_t args_count = mp::parameter_count_v<FnT>;
+    template<typename FnT, size_t args_count = mp::parameter_count_v<FnT>>
+    static auto GetMatcher(FnT fn, const char* const name, std::tuple<opcode_type, opcode_type> mask_and_expect, std::tuple<std::array<opcode_type, args_count>, std::array<size_t, args_count>> masks_and_shifts) {
         using Iota = std::make_index_sequence<args_count>;
 
-        const auto [mask, expect] = GetMaskAndExpect(bitstring);
-        const auto [arg_masks, arg_shifts] = GetArgInfo<args_count>(bitstring);
+        const auto [mask, expect] = mask_and_expect;
+        const auto [arg_masks, arg_shifts] = masks_and_shifts;
         const auto proxy_fn = VisitorCaller<FnT>::Make(Iota(), fn, arg_masks, arg_shifts);
 
         return MatcherT(name, mask, expect, proxy_fn);
     }
 };
 
+#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::GetMatcher(&V::fn, name, Decoder::detail::detail<MatcherT<V>>::GetMaskAndExpect(bitstring), Decoder::detail::detail<MatcherT<V>>::template GetArgInfo<mp::parameter_count_v<decltype(&V::fn)>>(bitstring))
+
 } // namespace detail
 } // namespace Dynarmic::Decoder