decoder: Generify the matcher interface (#33)

Gets rid of a bit of duplication while remaining compatible
with the current interfaces in place.
This commit is contained in:
Mat M 2016-09-17 04:48:18 -04:00 committed by Merry
parent 943487ecee
commit f75acd6cfb
11 changed files with 116 additions and 172 deletions

View file

@ -59,6 +59,7 @@ set(HEADERS
frontend/arm/types.h frontend/arm/types.h
frontend/decoder/arm.h frontend/decoder/arm.h
frontend/decoder/decoder_detail.h frontend/decoder/decoder_detail.h
frontend/decoder/matcher.h
frontend/decoder/thumb16.h frontend/decoder/thumb16.h
frontend/decoder/thumb32.h frontend/decoder/thumb32.h
frontend/decoder/vfp2.h frontend/decoder/vfp2.h

View file

@ -16,60 +16,19 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h" #include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {
template <typename Visitor> template <typename Visitor>
struct ArmMatcher { using ArmMatcher = Matcher<Visitor, u32>;
using CallRetT = mp::return_type_t<decltype(&Visitor::arm_UDF)>;
ArmMatcher(const char* const name, u32 mask, u32 expect, std::function<CallRetT(Visitor&, u32)> fn)
: name(name), mask(mask), expect(expect), fn(fn) {}
/// Gets the name of this type of instruction.
const char* GetName() const {
return name;
}
/**
* Tests to see if the instruction is this type of instruction.
* @param instruction The instruction to test
* @returns true if the instruction is
*/
bool Matches(u32 instruction) const {
return (instruction & mask) == expect;
}
/**
* Calls the corresponding instruction handler on visitor for this type of instruction.
* @param v The visitor to use
* @param instruction The instruction to decode.
*/
CallRetT call(Visitor& v, u32 instruction) const {
ASSERT(Matches(instruction));
return fn(v, instruction);
}
u32 GetMask() const {
return mask;
}
u32 GetExpect() const {
return expect;
}
private:
const char* name;
u32 mask, expect;
std::function<CallRetT(Visitor&, u32)> fn;
};
template <typename V> template <typename V>
std::vector<ArmMatcher<V>> GetArmDecodeTable() { std::vector<ArmMatcher<V>> GetArmDecodeTable() {
std::vector<ArmMatcher<V>> table = { std::vector<ArmMatcher<V>> table = {
#define INST(fn, name, bitstring) detail::detail<ArmMatcher, u32, 32>::GetMatcher<decltype(fn)>(fn, name, bitstring) #define INST(fn, name, bitstring) detail::detail<ArmMatcher<V>>::GetMatcher(fn, name, bitstring)
// Branch instructions // Branch instructions
INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), // v5 INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), // v5

View file

@ -11,6 +11,7 @@
#include <tuple> #include <tuple>
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_util.h"
#include "common/mp.h" #include "common/mp.h"
namespace Dynarmic { namespace Dynarmic {
@ -19,22 +20,25 @@ namespace detail {
/** /**
* Helper functions for the decoders. * Helper functions for the decoders.
* @tparam MatcherT The type of the matcher. (ARM: ArmMatcher, Thumb16: Thumb16Matcher) *
* @tparam InstructionT The type that represents an instruction. (ARM: u32, Thumb16: u16) * @tparam MatcherT The type of the Matcher to use.
* @tparam instruction_bit_size Bit-size for an instruction. (ARM: 32, Thumb16: 16)
*/ */
template<template<typename> class MatcherT, typename InstructionT, size_t instruction_bit_size> template<class MatcherT>
struct detail { struct detail {
private: private:
using opcode_type = typename MatcherT::opcode_type;
static constexpr size_t opcode_bitsize = Common::BitSize<opcode_type>();
/** /**
* Generates the mask and the expected value after masking from a given bitstring. * 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 '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. * A '1' in a bitstring indicates that a one must be present at that bit position.
*/ */
static auto GetMaskAndExpect(const char* const bitstring) { static auto GetMaskAndExpect(const char* const bitstring) {
InstructionT mask = 0, expect = 0; opcode_type mask = 0, expect = 0;
for (size_t i = 0; i < instruction_bit_size; i++) { for (size_t i = 0; i < opcode_bitsize; i++) {
const size_t bit_position = instruction_bit_size - i - 1; const size_t bit_position = opcode_bitsize - i - 1;
switch (bitstring[i]) { switch (bitstring[i]) {
case '0': case '0':
mask |= 1 << bit_position; mask |= 1 << bit_position;
@ -58,13 +62,13 @@ private:
*/ */
template<size_t N> template<size_t N>
static auto GetArgInfo(const char* const bitstring) { static auto GetArgInfo(const char* const bitstring) {
std::array<InstructionT, N> masks = {}; std::array<opcode_type, N> masks = {};
std::array<size_t, N> shifts = {}; std::array<size_t, N> shifts = {};
size_t arg_index = 0; size_t arg_index = 0;
char ch = 0; char ch = 0;
for (size_t i = 0; i < instruction_bit_size; i++) { for (size_t i = 0; i < opcode_bitsize; i++) {
const size_t bit_position = instruction_bit_size - i - 1; const size_t bit_position = opcode_bitsize - i - 1;
if (bitstring[i] == '0' || bitstring[i] == '1' || bitstring[i] == '-') { if (bitstring[i] == '0' || bitstring[i] == '1' || bitstring[i] == '-') {
if (ch != 0) { if (ch != 0) {
@ -107,9 +111,9 @@ private:
template<size_t ...iota> template<size_t ...iota>
static auto Make(std::integer_sequence<size_t, iota...>, static auto Make(std::integer_sequence<size_t, iota...>,
CallRetT (Visitor::* const fn)(Args...), CallRetT (Visitor::* const fn)(Args...),
const std::array<InstructionT, sizeof...(iota)> arg_masks, const std::array<opcode_type, sizeof...(iota)> arg_masks,
const std::array<size_t, sizeof...(iota)> arg_shifts) { const std::array<size_t, sizeof...(iota)> arg_shifts) {
return [fn, arg_masks, arg_shifts](Visitor& v, InstructionT instruction) { return [fn, arg_masks, arg_shifts](Visitor& v, opcode_type instruction) {
(void)instruction; (void)instruction;
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...); return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
}; };
@ -126,7 +130,6 @@ public:
*/ */
template<typename FnT> template<typename FnT>
static auto GetMatcher(FnT fn, const char* const name, const char* const bitstring) { static auto GetMatcher(FnT fn, const char* const name, const char* const bitstring) {
using Visitor = typename mp::FunctionInfo<FnT>::class_type;
constexpr size_t args_count = mp::FunctionInfo<FnT>::args_count; constexpr size_t args_count = mp::FunctionInfo<FnT>::args_count;
using Iota = std::make_index_sequence<args_count>; using Iota = std::make_index_sequence<args_count>;
@ -134,7 +137,7 @@ public:
const auto arg_info = GetArgInfo<args_count>(bitstring); const auto arg_info = GetArgInfo<args_count>(bitstring);
const auto proxy_fn = VisitorCaller<FnT>::Make(Iota(), fn, std::get<0>(arg_info), std::get<1>(arg_info)); const auto proxy_fn = VisitorCaller<FnT>::Make(Iota(), fn, std::get<0>(arg_info), std::get<1>(arg_info));
return MatcherT<Visitor>(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn); return MatcherT(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn);
} }
}; };

View file

@ -0,0 +1,78 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <functional>
#include "common/assert.h"
namespace Dynarmic {
namespace Arm {
/**
* 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>
class Matcher {
public:
using opcode_type = OpcodeType;
using handler_return_type = typename Visitor::instruction_return_type;
using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>;
Matcher(const char* const name, opcode_type mask, opcode_type expected, handler_function 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.
opcode_type GetMask() const {
return mask;
}
/// Gets the expected value after masking for this instruction.
opcode_type GetExpected() const {
return expected;
}
/**
* 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.
*/
bool Matches(opcode_type instruction) const {
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.
*/
handler_return_type call(Visitor& v, opcode_type instruction) const {
ASSERT(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
opcode_type mask;
opcode_type expected;
handler_function fn;
};
} // namespace Arm
} // namespace Dynarmic

View file

@ -6,60 +6,25 @@
#pragma once #pragma once
#include <array>
#include <functional>
#include <vector> #include <vector>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h" #include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {
template <typename Visitor> template <typename Visitor>
struct Thumb16Matcher { using Thumb16Matcher = Matcher<Visitor, u16>;
using CallRetT = mp::return_type_t<decltype(&Visitor::thumb16_UDF)>;
Thumb16Matcher(const char* const name, u16 mask, u16 expect, std::function<CallRetT(Visitor&, u16)> fn)
: name(name), mask(mask), expect(expect), fn(fn) {}
/// Gets the name of this type of instruction.
const char* GetName() const {
return name;
}
/**
* Tests to see if the instruction is this type of instruction.
* @param instruction The instruction to test
* @returns true if the instruction is
*/
bool Matches(u16 instruction) const {
return (instruction & mask) == expect;
}
/**
* Calls the corresponding instruction handler on visitor for this type of instruction.
* @param v The visitor to use
* @param instruction The instruction to decode.
*/
CallRetT call(Visitor& v, u16 instruction) const {
ASSERT(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
u16 mask, expect;
std::function<CallRetT(Visitor&, u16)> fn;
};
template<typename V> template<typename V>
boost::optional<const Thumb16Matcher<V>&> DecodeThumb16(u16 instruction) { boost::optional<const Thumb16Matcher<V>&> DecodeThumb16(u16 instruction) {
const static std::vector<Thumb16Matcher<V>> table = { const static std::vector<Thumb16Matcher<V>> table = {
#define INST(fn, name, bitstring) detail::detail<Thumb16Matcher, u16, 16>::GetMatcher<decltype(fn)>(fn, name, bitstring) #define INST(fn, name, bitstring) detail::detail<Thumb16Matcher<V>>::GetMatcher(fn, name, bitstring)
// Shift (immediate), add, subtract, move and compare instructions // Shift (immediate), add, subtract, move and compare instructions
INST(&V::thumb16_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd"), INST(&V::thumb16_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd"),

View file

@ -6,60 +6,25 @@
#pragma once #pragma once
#include <array>
#include <functional>
#include <vector> #include <vector>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h" #include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {
template <typename Visitor> template <typename Visitor>
struct Thumb32Matcher { using Thumb32Matcher = Matcher<Visitor, u32>;
using CallRetT = mp::return_type_t<decltype(&Visitor::thumb32_UDF)>;
Thumb32Matcher(const char* const name, u32 mask, u32 expect, std::function<CallRetT(Visitor&, u32)> fn)
: name(name), mask(mask), expect(expect), fn(fn) {}
/// Gets the name of this type of instruction.
const char* GetName() const {
return name;
}
/**
* Tests to see if the instruction is this type of instruction.
* @param instruction The instruction to test
* @returns true if the instruction is
*/
bool Matches(u32 instruction) const {
return (instruction & mask) == expect;
}
/**
* Calls the corresponding instruction handler on visitor for this type of instruction.
* @param v The visitor to use
* @param instruction The instruction to decode.
*/
CallRetT call(Visitor& v, u32 instruction) const {
ASSERT(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
u32 mask, expect;
std::function<CallRetT(Visitor&, u32)> fn;
};
template<typename V> template<typename V>
boost::optional<const Thumb32Matcher<V>&> DecodeThumb32(u32 instruction) { boost::optional<const Thumb32Matcher<V>&> DecodeThumb32(u32 instruction) {
const static std::vector<Thumb32Matcher<V>> table = { const static std::vector<Thumb32Matcher<V>> table = {
#define INST(fn, name, bitstring) detail::detail<Thumb32Matcher, u32, 32>::GetMatcher<decltype(fn)>(fn, name, bitstring) #define INST(fn, name, bitstring) detail::detail<Thumb32Matcher<V>>::GetMatcher(fn, name, bitstring)
// Branch instructions // Branch instructions
INST(&V::thumb32_BL_imm, "BL (imm)", "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T INST(&V::thumb32_BL_imm, "BL (imm)", "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T

View file

@ -6,60 +6,25 @@
#pragma once #pragma once
#include <array>
#include <functional>
#include <vector> #include <vector>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h" #include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {
template <typename Visitor> template <typename Visitor>
struct VFP2Matcher { using VFP2Matcher = Matcher<Visitor, u32>;
using CallRetT = mp::return_type_t<decltype(&Visitor::vfp2_VADD)>;
VFP2Matcher(const char* const name, u32 mask, u32 expect, std::function<CallRetT(Visitor&, u32)> fn)
: name(name), mask(mask), expect(expect), fn(fn) {}
/// Gets the name of this type of instruction.
const char* GetName() const {
return name;
}
/**
* Tests to see if the instruction is this type of instruction.
* @param instruction The instruction to test
* @returns true if the instruction is
*/
bool Matches(u32 instruction) const {
return (instruction & mask) == expect;
}
/**
* Calls the corresponding instruction handler on visitor for this type of instruction.
* @param v The visitor to use
* @param instruction The instruction to decode.
*/
CallRetT call(Visitor& v, u32 instruction) const {
assert(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
u32 mask, expect;
std::function<CallRetT(Visitor&, u32)> fn;
};
template<typename V> template<typename V>
boost::optional<const VFP2Matcher<V>&> DecodeVFP2(u32 instruction) { boost::optional<const VFP2Matcher<V>&> DecodeVFP2(u32 instruction) {
const static std::vector<VFP2Matcher<V>> table = { const static std::vector<VFP2Matcher<V>> table = {
#define INST(fn, name, bitstring) detail::detail<VFP2Matcher, u32, 32>::GetMatcher<decltype(fn)>(fn, name, bitstring) #define INST(fn, name, bitstring) detail::detail<VFP2Matcher<V>>::GetMatcher(fn, name, bitstring)
// cccc1110________----101-__-0---- // cccc1110________----101-__-0----

View file

@ -21,6 +21,8 @@ namespace Arm {
class DisassemblerVisitor { class DisassemblerVisitor {
public: public:
using instruction_return_type = std::string;
u32 rotr(u32 x, int shift) { u32 rotr(u32 x, int shift) {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift) return x;

View file

@ -20,6 +20,8 @@ namespace Arm {
class DisassemblerVisitor { class DisassemblerVisitor {
public: public:
using instruction_return_type = std::string;
std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) { std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
return fmt::format("lsls {}, {}, #{}", d, m, imm5); return fmt::format("lsls {}, {}, #{}", d, m, imm5);
} }

View file

@ -24,6 +24,8 @@ enum class ConditionalState {
}; };
struct ArmTranslatorVisitor final { struct ArmTranslatorVisitor final {
using instruction_return_type = bool;
explicit ArmTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) { explicit ArmTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) {
ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode"); ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode");
} }

View file

@ -21,6 +21,8 @@ namespace Arm {
namespace { namespace {
struct ThumbTranslatorVisitor final { struct ThumbTranslatorVisitor final {
using instruction_return_type = bool;
explicit ThumbTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) { explicit ThumbTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) {
ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode"); ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode");
} }