diff options
| author | lizzie <lizzie@eden-emu.dev> | 2026-05-09 05:41:41 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-05-09 05:41:41 +0200 |
| commit | eec460ec2ef13dee7376f2854ba00da9a98b0cd5 (patch) | |
| tree | c0f19f04bac33f2a7f53f0b0fadf3f167305a472 | |
| parent | a6423a88cc068f8213db8707900a1d1b38b9566d (diff) | |
[dynarmic] remove decode matcher function handlers using std::function<>, use raw function pointers (#3920)
issues:
- std::function<> is used, which is famously bad
- storage of tehse in tables makes big fucking tables for no good reason
- lets just store a normal pointer and stuff! :)
this pr attempts to address that
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3920
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
15 files changed, 187 insertions, 276 deletions
diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp index 077a696cc8..ec2b155fe4 100644 --- a/src/core/arm/nce/interpreter_visitor.cpp +++ b/src/core/arm/nce/interpreter_visitor.cpp @@ -761,8 +761,7 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3 return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt); } -std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, - fpsimd_context* fpsimd_context) { +std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, fpsimd_context* fpsimd_context) { std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31); std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32); u64& sp = *reinterpret_cast<u64*>(&context->sp); @@ -772,9 +771,9 @@ std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, m u32 instruction = memory.Read32(pc); bool was_executed = false; - auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction); + auto decoder = Dynarmic::A64::Decode<VisitorBase, bool>(visitor, instruction); if (decoder) { - was_executed = decoder->get().call(visitor, instruction); + was_executed = *decoder; } else { was_executed = false; } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h index d1cd9a923d..a9309ef5a8 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h @@ -26,48 +26,41 @@ namespace Dynarmic::A32 { template<typename Visitor> using ArmMatcher = Decoder::Matcher<Visitor, u32>; -template<typename Visitor> -using ArmDecodeTable = std::array<std::vector<ArmMatcher<Visitor>>, 0x1000>; - -namespace detail { -inline size_t ToFastLookupIndexArm(u32 instruction) noexcept { - return ((instruction >> 4) & 0x00F) | ((instruction >> 16) & 0xFF0); -} -} // namespace detail - -template<typename V> -static ArmDecodeTable<V> GetArmDecodeTable() noexcept { - ArmDecodeTable<V> table{}; - for (size_t i = 0; i < table.size(); ++i) { - // PLEASE HEAP ELLIDE - for (auto const& e : std::vector<ArmMatcher<V>>{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template<typename V, typename ReturnType> +static std::optional<ReturnType> DecodeArm(V& visitor, u32 instruction) noexcept { + auto const make_fast_index = [](u32 a) { + return ((a >> 4) & 0x00F) | ((a >> 16) & 0xFF0); + }; + struct Handler { + bool (*fn)(V&, u32); + u32 mask; + u32 expect; + }; + alignas(64) static const std::array<std::vector<Handler>, 0x1000> table = [&] { + std::array<std::vector<Handler>, 0x1000> t{}; + for (size_t i = 0; i < t.size(); ++i) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((i & make_fast_index(mask)) == make_fast_index(expect)) { \ + t[i].emplace_back([](V& visitor, u32 instruction) -> bool { \ + return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + }, mask, expect); \ + } \ + } while (0); #include "./arm.inc" #undef INST - }) { - auto const expect = detail::ToFastLookupIndexArm(e.GetExpected()); - auto const mask = detail::ToFastLookupIndexArm(e.GetMask()); - if ((i & mask) == expect) { - table[i].push_back(e); - } } - } - return table; -} - -template<typename V> -static std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) noexcept { - alignas(64) static const auto table = GetArmDecodeTable<V>(); - const auto matches_instruction = [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }; - const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)]; - auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction); - return iter != subtable.end() ? std::optional<std::reference_wrapper<const ArmMatcher<V>>>(*iter) : std::nullopt; + return t; + }(); + for (auto const& e : table[make_fast_index(instruction)]) + if ((instruction & e.mask) == e.expect) + return e.fn(visitor, instruction); + return std::nullopt; } template<typename V> -static std::optional<std::string_view> GetNameARM(u32 inst) noexcept { +static std::optional<std::string_view> GetNameArm(u32 inst) noexcept { std::vector<std::pair<std::string_view, ArmMatcher<V>>> list = { #define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) }, #include "./arm.inc" diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h index 5b0336e59f..4feae6f478 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h @@ -26,17 +26,16 @@ namespace Dynarmic::A32 { template<typename Visitor> using ASIMDMatcher = Decoder::Matcher<Visitor, u32>; -template<typename V> -static std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) noexcept { - alignas(64) static const auto table = std::array{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template<typename V, typename ReturnType> +static std::optional<ReturnType> DecodeASIMD(V& visitor, u32 instruction) noexcept { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + } while (0); #include "./asimd.inc" #undef INST - }; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional<std::reference_wrapper<const ASIMDMatcher<V>>>(*iter) : std::nullopt; + return std::nullopt; } template<typename V> diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h index 590878a6de..465159a00d 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h @@ -23,17 +23,16 @@ namespace Dynarmic::A32 { template<typename Visitor> using Thumb16Matcher = Decoder::Matcher<Visitor, u16>; -template<typename V> -static std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) { - alignas(64) static const auto table = std::array{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)), +template<typename V, typename ReturnType> +static std::optional<ReturnType> DecodeThumb16(V& visitor, u16 instruction) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)); \ + if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)); \ + } while (0); #include "./thumb16.inc" #undef INST - }; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb16Matcher<V>>>(*iter) : std::nullopt; + return std::nullopt; } template<typename V> diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h index 6a09c66afb..b3588c7a1d 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h @@ -22,17 +22,16 @@ namespace Dynarmic::A32 { template<typename Visitor> using Thumb32Matcher = Decoder::Matcher<Visitor, u32>; -template<typename V> -static std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) { - alignas(64) static const auto table = std::array{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template<typename V, typename ReturnType> +static std::optional<ReturnType> DecodeThumb32(V& visitor, u32 instruction) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + } while (0); #include "./thumb32.inc" #undef INST - }; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb32Matcher<V>>>(*iter) : std::nullopt; + return std::nullopt; } template<typename V> diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h index e02cc03a6b..8703aa24b4 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h @@ -23,32 +23,18 @@ namespace Dynarmic::A32 { template<typename Visitor> using VFPMatcher = Decoder::Matcher<Visitor, u32>; -template<typename V> -static std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) { - using Table = std::vector<VFPMatcher<V>>; - alignas(64) static const struct Tables { - Table unconditional; - Table conditional; - } tables = []() { - Table list = { -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template<typename V, typename ReturnType> +static std::optional<ReturnType> DecodeVFP(V& visitor, u32 instruction) { + bool const i_uncond = (instruction & 0xF0000000) == 0xF0000000; +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + bool const m_uncond = (mask & 0xF0000000) == 0xF0000000; \ + if ((instruction & mask) == expect && m_uncond == i_uncond) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + } while (0); #include "./vfp.inc" #undef INST - }; - auto const it = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) { - return (matcher.GetMask() & 0xF0000000) == 0xF0000000; - }); - return Tables{ - Table{list.begin(), it}, - Table{it, list.end()}, - }; - }(); - const bool is_unconditional = (instruction & 0xF0000000) == 0xF0000000; - const Table& table = is_unconditional ? tables.unconditional : tables.conditional; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional<std::reference_wrapper<const VFPMatcher<V>>>(*iter) : std::nullopt; + return std::nullopt; } template<typename V> diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp index 3fd475d074..6c89a4af36 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp @@ -41,12 +41,12 @@ void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCall tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir); ticks_for_instruction = tcb->GetTicksForCode(false, arm_pc, *arm_instruction); - if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(*arm_instruction)) { - should_continue = vfp_decoder->get().call(visitor, *arm_instruction); - } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(*arm_instruction)) { - should_continue = asimd_decoder->get().call(visitor, *arm_instruction); - } else if (const auto decoder = DecodeArm<TranslatorVisitor>(*arm_instruction)) { - should_continue = decoder->get().call(visitor, *arm_instruction); + if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, *arm_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, *arm_instruction)) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeArm<TranslatorVisitor, bool>(visitor, *arm_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.arm_UDF(); } @@ -88,12 +88,12 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript const u64 ticks_for_instruction = 1; - if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(arm_instruction)) { - should_continue = vfp_decoder->get().call(visitor, arm_instruction); - } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(arm_instruction)) { - should_continue = asimd_decoder->get().call(visitor, arm_instruction); - } else if (const auto decoder = DecodeArm<TranslatorVisitor>(arm_instruction)) { - should_continue = decoder->get().call(visitor, arm_instruction); + if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, arm_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, arm_instruction)) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeArm<TranslatorVisitor, bool>(visitor, arm_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.arm_UDF(); } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp index 332860a95c..2a2fbea63f 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp @@ -126,24 +126,24 @@ void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCa if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) { if (is_thumb_16) { - if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) { - should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction)); + if (const auto decoder = DecodeThumb16<TranslatorVisitor, bool>(visitor, u16(thumb_instruction))) { + should_continue = *decoder; } else { should_continue = visitor.thumb16_UDF(); } } else { if (MaybeVFPOrASIMDInstruction(thumb_instruction)) { - if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) { - should_continue = vfp_decoder->get().call(visitor, thumb_instruction); - } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) { - should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction)); - } else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, thumb_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, ConvertASIMDInstruction(thumb_instruction))) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } - } else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + } else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } @@ -187,25 +187,25 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri const u64 ticks_for_instruction = 1; if (is_thumb_16) { - if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) { - should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction)); + if (const auto decoder = DecodeThumb16<TranslatorVisitor, bool>(visitor, u16(thumb_instruction))) { + should_continue = *decoder; } else { should_continue = visitor.thumb16_UDF(); } } else { thumb_instruction = mcl::bit::swap_halves_32(thumb_instruction); if (MaybeVFPOrASIMDInstruction(thumb_instruction)) { - if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) { - should_continue = vfp_decoder->get().call(visitor, thumb_instruction); - } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) { - should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction)); - } else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, thumb_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, ConvertASIMDInstruction(thumb_instruction))) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } - } else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + } else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } diff --git a/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h b/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h index ccf890cbcd..f0b7baba7b 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h +++ b/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h @@ -26,45 +26,37 @@ namespace Dynarmic::A64 { template<typename Visitor> using Matcher = Decoder::Matcher<Visitor, u32>; -template<typename Visitor> -using DecodeTable = std::array<std::vector<Matcher<Visitor>>, 0x1000>; - -namespace detail { -inline size_t ToFastLookupIndex(u32 instruction) { - return ((instruction >> 10) & 0x00F) | ((instruction >> 18) & 0xFF0); -} -} // namespace detail - -template<typename V> -inline DecodeTable<V> GetDecodeTable() { - DecodeTable<V> table{}; - for (size_t i = 0; i < table.size(); ++i) { - // PLEASE HEAP ELLIDE - for (auto const& e : std::vector<Matcher<V>>{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template<typename V, typename ReturnType> +static std::optional<ReturnType> Decode(V& visitor, u32 instruction) noexcept { + auto const make_fast_index = [](u32 a) { + return ((a >> 10) & 0x00F) | ((a >> 18) & 0xFF0); + }; + struct Handler { + bool (*fn)(V&, u32); + u32 mask; + u32 expect; + }; + alignas(64) static const std::array<std::vector<Handler>, 0x1000> table = [&] { + std::array<std::vector<Handler>, 0x1000> t{}; + for (size_t i = 0; i < t.size(); ++i) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((i & make_fast_index(mask)) == make_fast_index(expect)) { \ + t[i].emplace_back([](V& visitor, u32 instruction) -> bool { \ + return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + }, mask, expect); \ + } \ + } while (0); #include "./a64.inc" #undef INST - }) { - const auto expect = detail::ToFastLookupIndex(e.GetExpected()); - const auto mask = detail::ToFastLookupIndex(e.GetMask()); - if ((i & mask) == expect) - table[i].push_back(e); } - } - return table; -} - -/// In practice it must always suceed, otherwise something else unrelated would have gone awry -template<typename V> -inline std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) { - alignas(64) static const auto table = GetDecodeTable<V>(); - const auto& subtable = table[detail::ToFastLookupIndex(instruction)]; - auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != subtable.end() - ? std::optional{ std::reference_wrapper<const Matcher<V>>(*iter) } - : std::nullopt; + return t; + }(); + for (auto const& e : table[make_fast_index(instruction)]) + if ((instruction & e.mask) == e.expect) + return e.fn(visitor, instruction); + return std::nullopt; } template<typename V> diff --git a/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp b/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp index 4afce6bd29..7840c63d79 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp @@ -24,9 +24,8 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu do { const u64 pc = visitor.ir.current_location->PC(); if (const auto instruction = memory_read_code(pc)) { - auto decoder = Decode<TranslatorVisitor>(*instruction); - if (decoder) { - should_continue = decoder->get().call(visitor, *instruction); + if (auto decoder = Decode<TranslatorVisitor, bool>(visitor, *instruction)) { + should_continue = *decoder; } else { should_continue = visitor.RaiseException(Exception::UnallocatedEncoding); } @@ -47,13 +46,9 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) { TranslatorVisitor visitor{block, descriptor, {}}; - bool should_continue = true; - auto const decoder = Decode<TranslatorVisitor>(instruction); - if (decoder) { - should_continue = decoder->get().call(visitor, instruction); - } else { - should_continue = false; - } + bool should_continue = false; + if (auto const decoder = Decode<TranslatorVisitor, bool>(visitor, instruction)) + should_continue = *decoder; visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4); block.CycleCount()++; diff --git a/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h b/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h index c22e585c26..e383fb0262 100644 --- a/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h +++ b/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h @@ -8,31 +8,25 @@ #pragma once -#include <algorithm> #include <array> #include <tuple> - +#include <utility> #include "common/assert.h" #include "dynarmic/mcl/bit.hpp" -#include "dynarmic/mcl/function_info.hpp" namespace Dynarmic::Decoder { namespace detail { template<size_t N> -inline consteval 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; +inline consteval std::array<char, N> StringToArray(const char (&s)[N + 1]) { + std::array<char, N> r{}; + for (size_t i = 0; i < N; i++) + r[i] = s[i]; + return r; } -/** - * Helper functions for the decoders. - * - * @tparam MatcherT The type of the Matcher to use. - */ +/// @brief Helper functions for the decoders. +/// @tparam MatcherT The type of the Matcher to use. template<class MatcherT> struct detail { using opcode_type = typename MatcherT::opcode_type; @@ -40,17 +34,15 @@ struct detail { static constexpr size_t opcode_bitsize = mcl::bitsizeof<opcode_type>; - /** - * Generates the mask and the expected value after masking from a given bitstring. - * 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. - */ + /// @brief Generates the mask and the expected value after masking from a given bitstring. + /// 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. #ifdef __clang__ static constexpr auto GetMaskAndExpect(std::array<char, opcode_bitsize> bitstring) { #else static consteval auto GetMaskAndExpect(std::array<char, opcode_bitsize> bitstring) { #endif - const auto one = static_cast<opcode_type>(1); + const auto one = opcode_type(1); opcode_type mask = 0, expect = 0; for (size_t i = 0; i < opcode_bitsize; i++) { const size_t bit_position = opcode_bitsize - i - 1; @@ -101,7 +93,7 @@ struct detail { } #if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__) // Avoids a MSVC ICE, and avoids Android NDK issue. - ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; })); + DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; })); #endif return std::make_tuple(masks, shifts); } @@ -109,65 +101,32 @@ struct detail { /// @brief This struct's Make member function generates a lambda which decodes an instruction /// based on the provided arg_masks and arg_shifts. The Visitor member function to call is /// provided as a template argument. - template<typename FnT> - struct VisitorCaller; - #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning) #endif - template<typename V, typename... Args, typename ReturnType> - struct VisitorCaller<ReturnType (V::*)(Args...)> { - template<size_t... iota> - static constexpr auto Make(std::integer_sequence<size_t, iota...>, - ReturnType (V::*const fn)(Args...), - const std::array<opcode_type, sizeof...(iota)> arg_masks, - const std::array<size_t, sizeof...(iota)> arg_shifts) { - static_assert(std::is_same_v<visitor_type, V>, "Member function is not from Matcher's Visitor"); - return [fn, arg_masks, arg_shifts](V& v, opcode_type instruction) { - (void)instruction; - (void)arg_masks; - (void)arg_shifts; - return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...); - }; - } - }; - - template<typename V, typename... Args, typename ReturnType> - struct VisitorCaller<ReturnType (V::*)(Args...) const> { - template<size_t... iota> - static constexpr auto Make(std::integer_sequence<size_t, iota...>, - ReturnType (V::*const fn)(Args...) const, - const std::array<opcode_type, sizeof...(iota)> arg_masks, - const std::array<size_t, sizeof...(iota)> arg_shifts) { - static_assert(std::is_same_v<visitor_type, const V>, "Member function is not from Matcher's Visitor"); - return [fn, arg_masks, arg_shifts](const V& v, opcode_type instruction) { - (void)instruction; - (void)arg_masks; - (void)arg_shifts; - return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...); - }; + template<typename V, typename ReturnType, typename... Args> + struct VisitorCaller { + template<std::size_t... iota> + static inline constexpr auto Invoke(std::index_sequence<iota...>, V& visitor, [[maybe_unused]] opcode_type instruction, ReturnType (V::*const fn)(Args...), [[maybe_unused]] const std::array<opcode_type, sizeof...(Args)> arg_masks, [[maybe_unused]] const std::array<size_t, sizeof...(Args)> arg_shifts) { + return (visitor.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...); } }; #ifdef _MSC_VER # pragma warning(pop) #endif - /// @brief Creates a matcher that can match and parse instructions based on bitstring. - /// See also: GetMaskAndExpect and GetArgInfo for format of bitstring. - template<auto bitstring, typename F> - static constexpr auto GetMatcher(F fn) { - constexpr size_t args_count = mcl::parameter_count_v<F>; - constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring)); - constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring)); - constexpr auto arg_masks = std::get<0>(GetArgInfo<args_count>(bitstring)); - constexpr auto arg_shifts = std::get<1>(GetArgInfo<args_count>(bitstring)); - const auto proxy_fn = VisitorCaller<F>::Make(std::make_index_sequence<args_count>(), fn, arg_masks, arg_shifts); - return MatcherT(mask, expect, proxy_fn); + template<auto bitstring, typename V, typename ReturnType, typename... Args> + static inline constexpr auto GetMatcherFunction(V& visitor, [[maybe_unused]] opcode_type instruction, ReturnType (V::*const fn)(Args...)) { + constexpr auto arg_masks = std::get<0>(GetArgInfo<sizeof...(Args)>(bitstring)); + constexpr auto arg_shifts = std::get<1>(GetArgInfo<sizeof...(Args)>(bitstring)); + return VisitorCaller<V, ReturnType, Args...>::Invoke(std::index_sequence_for<Args...>(), visitor, instruction, fn, arg_masks, arg_shifts); } }; -#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn) +#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::GetMaskAndExpect(bitstring) + +#define DYNARMIC_DECODER_GET_MATCHER_FUNCTION(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcherFunction<bitstring, V>(visitor, instruction, &V::fn) } // namespace detail } // namespace Dynarmic::Decoder diff --git a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h index 194f646ed2..49095f67e3 100644 --- a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h +++ b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h @@ -15,50 +15,41 @@ namespace Dynarmic::Decoder { /// Generic instruction handling construct. -/// @tparam Visitor An arbitrary visitor type that will be passed through -/// to the function being handled. This type must be the -/// type of the first parameter in a handler function. -/// @tparam OpcodeType Type representing an opcode. This must be the -/// type of the second parameter in a handler function. -template<typename Visitor, typename OpcodeType> +/// @tparam V An arbitrary visitor type that will be passed through +/// to the function being handled. This type must be the +/// type of the first parameter in a handler function. +/// @tparam T Type representing an opcode. This must be the +/// type of the second parameter in a handler function. +template<typename V, typename T> class Matcher { public: - using opcode_type = OpcodeType; - using visitor_type = Visitor; - using handler_return_type = typename Visitor::instruction_return_type; - using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>; - Matcher(opcode_type mask, opcode_type expected, handler_function func) - : mask{mask}, expected{expected}, fn{std::move(func)} {} + using opcode_type = T; + using visitor_type = V; - /// Gets the mask for this instruction. - inline opcode_type GetMask() const noexcept { + constexpr Matcher(T mask, T expected) noexcept + : mask{mask} + , expected{expected} + {} + + /// @brief Gets the mask for this instruction. + constexpr inline T GetMask() const noexcept { return mask; } - /// Gets the expected value after masking for this instruction. - inline opcode_type GetExpected() const noexcept { + /// @brief Gets the expected value after masking for this instruction. + constexpr inline T GetExpected() const noexcept { return expected; } - /// Tests to see if the given instruction is the instruction this matcher represents. + /// @brief Tests to see if the given instruction is the instruction this matcher represents. /// @param instruction The instruction to test /// @returns true if the given instruction matches. - inline bool Matches(opcode_type instruction) const noexcept { + constexpr inline bool Matches(T instruction) const noexcept { return (instruction & mask) == expected; } - /// Calls the corresponding instruction handler on visitor for this type of instruction. - /// @param v The visitor to use - /// @param instruction The instruction to decode. - inline handler_return_type call(Visitor& v, opcode_type instruction) const noexcept { - ASSERT(Matches(instruction)); - return fn(v, instruction); - } - -private: - opcode_type mask; - opcode_type expected; - handler_function fn; + T mask; + T expected; }; } // namespace Dynarmic::Decoder diff --git a/src/dynarmic/src/dynarmic/frontend/imm.h b/src/dynarmic/src/dynarmic/frontend/imm.h index 56fbcbda0e..f305b5bfc8 100644 --- a/src/dynarmic/src/dynarmic/frontend/imm.h +++ b/src/dynarmic/src/dynarmic/frontend/imm.h @@ -27,9 +27,8 @@ class Imm { public: static constexpr size_t bit_size = bit_size_; - explicit Imm(u32 value) - : value(value) { - ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected"); + explicit Imm(u32 value) : value(value) { + DEBUG_ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected"); } template<typename T = u32> diff --git a/src/dynarmic/tests/decoder_tests.cpp b/src/dynarmic/tests/decoder_tests.cpp index 2a6c0f6a4f..17ed2c0ce5 100644 --- a/src/dynarmic/tests/decoder_tests.cpp +++ b/src/dynarmic/tests/decoder_tests.cpp @@ -51,7 +51,7 @@ TEST_CASE("ASIMD Decoder: Ensure table order correctness", "[decode][a32][.]") { const bool iserr = is_decode_error(*iter, instruction); const auto alternative = std::find_if(table.cbegin(), iter, [instruction](const auto& m) { - return m.Matches(instruction); + return (instruction & mask) == expect; }); const bool altiserr = is_decode_error(*alternative, instruction); diff --git a/src/dynarmic/tests/print_info.cpp b/src/dynarmic/tests/print_info.cpp index 6d6109d849..988a8d294e 100644 --- a/src/dynarmic/tests/print_info.cpp +++ b/src/dynarmic/tests/print_info.cpp @@ -43,12 +43,12 @@ using namespace Dynarmic; std::string_view GetNameOfA32Instruction(u32 instruction) { - if (auto const vfp_decoder = A32::DecodeVFP<A32::TranslatorVisitor>(instruction)) - return *A32::GetNameVFP<A32::TranslatorVisitor>(instruction); - else if (auto const asimd_decoder = A32::DecodeASIMD<A32::TranslatorVisitor>(instruction)) - return *A32::GetNameASIMD<A32::TranslatorVisitor>(instruction); - else if (auto const decoder = A32::DecodeArm<A32::TranslatorVisitor>(instruction)) - return *A32::GetNameARM<A32::TranslatorVisitor>(instruction); + if (auto const vfp_decoder = A32::GetNameVFP<A32::TranslatorVisitor>(instruction)) + return *vfp_decoder; + else if (auto const asimd_decoder = A32::GetNameASIMD<A32::TranslatorVisitor>(instruction)) + return *asimd_decoder; + else if (auto const decoder = A32::GetNameArm<A32::TranslatorVisitor>(instruction)) + return *decoder; return "<null>"; } |
