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:
parent
943487ecee
commit
f75acd6cfb
11 changed files with 116 additions and 172 deletions
|
@ -59,6 +59,7 @@ set(HEADERS
|
|||
frontend/arm/types.h
|
||||
frontend/decoder/arm.h
|
||||
frontend/decoder/decoder_detail.h
|
||||
frontend/decoder/matcher.h
|
||||
frontend/decoder/thumb16.h
|
||||
frontend/decoder/thumb32.h
|
||||
frontend/decoder/vfp2.h
|
||||
|
|
|
@ -16,60 +16,19 @@
|
|||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend/decoder/decoder_detail.h"
|
||||
#include "frontend/decoder/matcher.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
template <typename Visitor>
|
||||
struct ArmMatcher {
|
||||
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;
|
||||
};
|
||||
using ArmMatcher = Matcher<Visitor, u32>;
|
||||
|
||||
template <typename V>
|
||||
std::vector<ArmMatcher<V>> GetArmDecodeTable() {
|
||||
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
|
||||
INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), // v5
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <tuple>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/mp.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
@ -19,22 +20,25 @@ namespace detail {
|
|||
|
||||
/**
|
||||
* 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 instruction_bit_size Bit-size for an instruction. (ARM: 32, Thumb16: 16)
|
||||
*
|
||||
* @tparam MatcherT The type of the Matcher to use.
|
||||
*/
|
||||
template<template<typename> class MatcherT, typename InstructionT, size_t instruction_bit_size>
|
||||
template<class MatcherT>
|
||||
struct detail {
|
||||
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.
|
||||
* 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 auto GetMaskAndExpect(const char* const bitstring) {
|
||||
InstructionT mask = 0, expect = 0;
|
||||
for (size_t i = 0; i < instruction_bit_size; i++) {
|
||||
const size_t bit_position = instruction_bit_size - i - 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;
|
||||
switch (bitstring[i]) {
|
||||
case '0':
|
||||
mask |= 1 << bit_position;
|
||||
|
@ -58,13 +62,13 @@ private:
|
|||
*/
|
||||
template<size_t N>
|
||||
static auto GetArgInfo(const char* const bitstring) {
|
||||
std::array<InstructionT, N> masks = {};
|
||||
std::array<opcode_type, N> masks = {};
|
||||
std::array<size_t, N> shifts = {};
|
||||
size_t arg_index = 0;
|
||||
char ch = 0;
|
||||
|
||||
for (size_t i = 0; i < instruction_bit_size; i++) {
|
||||
const size_t bit_position = instruction_bit_size - i - 1;
|
||||
for (size_t i = 0; i < opcode_bitsize; i++) {
|
||||
const size_t bit_position = opcode_bitsize - i - 1;
|
||||
|
||||
if (bitstring[i] == '0' || bitstring[i] == '1' || bitstring[i] == '-') {
|
||||
if (ch != 0) {
|
||||
|
@ -107,9 +111,9 @@ private:
|
|||
template<size_t ...iota>
|
||||
static auto Make(std::integer_sequence<size_t, iota...>,
|
||||
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) {
|
||||
return [fn, arg_masks, arg_shifts](Visitor& v, InstructionT instruction) {
|
||||
return [fn, arg_masks, arg_shifts](Visitor& v, opcode_type instruction) {
|
||||
(void)instruction;
|
||||
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
||||
};
|
||||
|
@ -126,7 +130,6 @@ public:
|
|||
*/
|
||||
template<typename FnT>
|
||||
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;
|
||||
using Iota = std::make_index_sequence<args_count>;
|
||||
|
||||
|
@ -134,7 +137,7 @@ public:
|
|||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
78
src/frontend/decoder/matcher.h
Normal file
78
src/frontend/decoder/matcher.h
Normal 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
|
|
@ -6,60 +6,25 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend/decoder/decoder_detail.h"
|
||||
#include "frontend/decoder/matcher.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
template <typename Visitor>
|
||||
struct Thumb16Matcher {
|
||||
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;
|
||||
};
|
||||
using Thumb16Matcher = Matcher<Visitor, u16>;
|
||||
|
||||
template<typename V>
|
||||
boost::optional<const Thumb16Matcher<V>&> DecodeThumb16(u16 instruction) {
|
||||
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
|
||||
INST(&V::thumb16_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd"),
|
||||
|
|
|
@ -6,60 +6,25 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend/decoder/decoder_detail.h"
|
||||
#include "frontend/decoder/matcher.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
template <typename Visitor>
|
||||
struct Thumb32Matcher {
|
||||
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;
|
||||
};
|
||||
using Thumb32Matcher = Matcher<Visitor, u32>;
|
||||
|
||||
template<typename V>
|
||||
boost::optional<const Thumb32Matcher<V>&> DecodeThumb32(u32 instruction) {
|
||||
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
|
||||
INST(&V::thumb32_BL_imm, "BL (imm)", "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T
|
||||
|
|
|
@ -6,60 +6,25 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend/decoder/decoder_detail.h"
|
||||
#include "frontend/decoder/matcher.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
template <typename Visitor>
|
||||
struct VFP2Matcher {
|
||||
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;
|
||||
};
|
||||
using VFP2Matcher = Matcher<Visitor, u32>;
|
||||
|
||||
template<typename V>
|
||||
boost::optional<const VFP2Matcher<V>&> DecodeVFP2(u32 instruction) {
|
||||
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----
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ namespace Arm {
|
|||
|
||||
class DisassemblerVisitor {
|
||||
public:
|
||||
using instruction_return_type = std::string;
|
||||
|
||||
u32 rotr(u32 x, int shift) {
|
||||
shift &= 31;
|
||||
if (!shift) return x;
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace Arm {
|
|||
|
||||
class DisassemblerVisitor {
|
||||
public:
|
||||
using instruction_return_type = std::string;
|
||||
|
||||
std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
||||
return fmt::format("lsls {}, {}, #{}", d, m, imm5);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ enum class ConditionalState {
|
|||
};
|
||||
|
||||
struct ArmTranslatorVisitor final {
|
||||
using instruction_return_type = bool;
|
||||
|
||||
explicit ArmTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) {
|
||||
ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode");
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ namespace Arm {
|
|||
namespace {
|
||||
|
||||
struct ThumbTranslatorVisitor final {
|
||||
using instruction_return_type = bool;
|
||||
|
||||
explicit ThumbTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) {
|
||||
ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue