[dynarmic] reduce matcher table noise and cache misses
All checks were successful
eden-license / license-header (pull_request) Successful in 26s
All checks were successful
eden-license / license-header (pull_request) Successful in 26s
Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
parent
5a5a8b044d
commit
d60c072016
6 changed files with 38 additions and 62 deletions
|
@ -9,12 +9,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "dynarmic/common/assert.h"
|
#include "dynarmic/common/assert.h"
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
|
|
||||||
#include "dynarmic/interface/A32/coprocessor_util.h"
|
#include "dynarmic/interface/A32/coprocessor_util.h"
|
||||||
#include "dynarmic/ir/cond.h"
|
#include "dynarmic/ir/cond.h"
|
||||||
|
|
||||||
|
@ -89,23 +86,17 @@ constexpr bool IsQuadExtReg(ExtReg reg) {
|
||||||
|
|
||||||
inline size_t RegNumber(Reg reg) {
|
inline size_t RegNumber(Reg reg) {
|
||||||
ASSERT(reg != Reg::INVALID_REG);
|
ASSERT(reg != Reg::INVALID_REG);
|
||||||
return static_cast<size_t>(reg);
|
return size_t(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t RegNumber(ExtReg reg) {
|
inline size_t RegNumber(ExtReg reg) {
|
||||||
if (IsSingleExtReg(reg)) {
|
if (IsSingleExtReg(reg)) {
|
||||||
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::S0);
|
return size_t(reg) - size_t(ExtReg::S0);
|
||||||
|
} else if (IsDoubleExtReg(reg)) {
|
||||||
|
return size_t(reg) - size_t(ExtReg::D0);
|
||||||
}
|
}
|
||||||
|
ASSERT(IsQuadExtReg(reg));
|
||||||
if (IsDoubleExtReg(reg)) {
|
return size_t(reg) - size_t(ExtReg::Q0);
|
||||||
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::D0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsQuadExtReg(reg)) {
|
|
||||||
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::Q0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_MSG(false, "Invalid extended register");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Reg operator+(Reg reg, size_t number) {
|
inline Reg operator+(Reg reg, size_t number) {
|
||||||
|
|
|
@ -27,14 +27,11 @@ using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
|
std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
|
||||||
std::vector<ASIMDMatcher<V>> table = {
|
std::vector<std::pair<const char*, ASIMDMatcher<V>>> table = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
|
||||||
#include "./asimd.inc"
|
#include "./asimd.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Exceptions to the rule of thumb.
|
// Exceptions to the rule of thumb.
|
||||||
const std::set<std::string> comes_first{
|
const std::set<std::string> comes_first{
|
||||||
"VBIC, VMOV, VMVN, VORR (immediate)",
|
"VBIC, VMOV, VMVN, VORR (immediate)",
|
||||||
|
@ -53,19 +50,21 @@ std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
|
||||||
"VQDMULH (scalar)",
|
"VQDMULH (scalar)",
|
||||||
"VQRDMULH (scalar)",
|
"VQRDMULH (scalar)",
|
||||||
};
|
};
|
||||||
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
|
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& e) {
|
||||||
return comes_first.count(matcher.GetName()) > 0;
|
return comes_first.count(e.first) > 0;
|
||||||
});
|
});
|
||||||
const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
|
const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& e) {
|
||||||
return comes_last.count(matcher.GetName()) == 0;
|
return comes_last.count(e.first) == 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||||
std::stable_sort(sort_begin, sort_end, [](const auto& matcher1, const auto& matcher2) {
|
std::stable_sort(sort_begin, sort_end, [](const auto& a, const auto& b) {
|
||||||
return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask());
|
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
|
||||||
});
|
});
|
||||||
|
std::vector<ASIMDMatcher<V>> final_table;
|
||||||
return table;
|
std::transform(table.cbegin(), table.cend(), final_table.begin(), [](auto const& e) {
|
||||||
|
return e.second;
|
||||||
|
});
|
||||||
|
return final_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
|
|
|
@ -30,22 +30,18 @@ std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruc
|
||||||
static const struct Tables {
|
static const struct Tables {
|
||||||
Table unconditional;
|
Table unconditional;
|
||||||
Table conditional;
|
Table conditional;
|
||||||
} tables = [] {
|
} tables = []() {
|
||||||
Table list = {
|
Table list = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||||
#include "./vfp.inc"
|
#include "./vfp.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
|
|
||||||
};
|
};
|
||||||
|
auto const it = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
|
||||||
const auto division = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
|
|
||||||
return (matcher.GetMask() & 0xF0000000) == 0xF0000000;
|
return (matcher.GetMask() & 0xF0000000) == 0xF0000000;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Tables{
|
return Tables{
|
||||||
Table{list.begin(), division},
|
Table{list.begin(), it},
|
||||||
Table{division, list.end()},
|
Table{it, list.end()},
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
|
@ -37,34 +37,31 @@ inline size_t ToFastLookupIndex(u32 instruction) {
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
constexpr DecodeTable<V> GetDecodeTable() {
|
constexpr DecodeTable<V> GetDecodeTable() {
|
||||||
std::vector<Matcher<V>> list = {
|
std::vector<std::pair<const char*, Matcher<V>>> list = {
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
#include "./a64.inc"
|
#include "./a64.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
};
|
};
|
||||||
|
|
||||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||||
std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) {
|
std::stable_sort(list.begin(), list.end(), [](const auto& a, const auto& b) {
|
||||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||||
return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask());
|
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Exceptions to the above rule of thumb.
|
// Exceptions to the above rule of thumb.
|
||||||
std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
|
std::stable_partition(list.begin(), list.end(), [&](const auto& e) {
|
||||||
return std::set<std::string>{
|
return std::set<std::string>{
|
||||||
"MOVI, MVNI, ORR, BIC (vector, immediate)",
|
"MOVI, MVNI, ORR, BIC (vector, immediate)",
|
||||||
"FMOV (vector, immediate)",
|
"FMOV (vector, immediate)",
|
||||||
"Unallocated SIMD modified immediate",
|
"Unallocated SIMD modified immediate",
|
||||||
}.count(matcher.GetName()) > 0;
|
}.count(e.first) > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
DecodeTable<V> table{};
|
DecodeTable<V> table{};
|
||||||
for (size_t i = 0; i < table.size(); ++i) {
|
for (size_t i = 0; i < table.size(); ++i) {
|
||||||
for (auto matcher : list) {
|
for (auto const& e : list) {
|
||||||
const auto expect = detail::ToFastLookupIndex(matcher.GetExpected());
|
const auto expect = detail::ToFastLookupIndex(e.second.GetExpected());
|
||||||
const auto mask = detail::ToFastLookupIndex(matcher.GetMask());
|
const auto mask = detail::ToFastLookupIndex(e.second.GetMask());
|
||||||
if ((i & mask) == expect) {
|
if ((i & mask) == expect) {
|
||||||
table[i].push_back(matcher);
|
table[i].push_back(e.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,18 +163,18 @@ struct detail {
|
||||||
/// @brief Creates a matcher that can match and parse instructions based on bitstring.
|
/// @brief Creates a matcher that can match and parse instructions based on bitstring.
|
||||||
/// See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
|
/// See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
|
||||||
template<auto bitstring, typename F>
|
template<auto bitstring, typename F>
|
||||||
static constexpr auto GetMatcher(F fn, const char* const name) {
|
static constexpr auto GetMatcher(F fn) {
|
||||||
constexpr size_t args_count = mcl::parameter_count_v<F>;
|
constexpr size_t args_count = mcl::parameter_count_v<F>;
|
||||||
constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring));
|
constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring));
|
||||||
constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring));
|
constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring));
|
||||||
constexpr auto arg_masks = std::get<0>(GetArgInfo<args_count>(bitstring));
|
constexpr auto arg_masks = std::get<0>(GetArgInfo<args_count>(bitstring));
|
||||||
constexpr auto arg_shifts = std::get<1>(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);
|
const auto proxy_fn = VisitorCaller<F>::Make(std::make_index_sequence<args_count>(), fn, arg_masks, arg_shifts);
|
||||||
return MatcherT(name, mask, expect, proxy_fn);
|
return MatcherT(mask, expect, proxy_fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn, name)
|
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn)
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace Dynarmic::Decoder
|
} // namespace Dynarmic::Decoder
|
||||||
|
|
|
@ -31,14 +31,8 @@ public:
|
||||||
using visitor_type = Visitor;
|
using visitor_type = Visitor;
|
||||||
using handler_return_type = typename Visitor::instruction_return_type;
|
using handler_return_type = typename Visitor::instruction_return_type;
|
||||||
using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>;
|
using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>;
|
||||||
|
Matcher(opcode_type mask, opcode_type expected, handler_function func)
|
||||||
Matcher(const char* const name, opcode_type mask, opcode_type expected, handler_function func)
|
: mask{mask}, expected{expected}, fn{std::move(func)} {}
|
||||||
: name{name}, mask{mask}, expected{expected}, fn{std::move(func)} {}
|
|
||||||
|
|
||||||
/// Gets the name of this type of instruction.
|
|
||||||
const char* GetName() const {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the mask for this instruction.
|
/// Gets the mask for this instruction.
|
||||||
opcode_type GetMask() const {
|
opcode_type GetMask() const {
|
||||||
|
@ -70,7 +64,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* name;
|
|
||||||
opcode_type mask;
|
opcode_type mask;
|
||||||
opcode_type expected;
|
opcode_type expected;
|
||||||
handler_function fn;
|
handler_function fn;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue