CPU JIT Part 2 (#20)

* ARM/Decoder: Initial implementation

* JitX64: Implement register allocator

* JitX64: Initial implementation of JitX64 compiler

* JitX64: Add accessors for ARMul_State::exclusive_tag and ARMul_State::exclusive_state

* JitX64: Implement ARM_Jit

ARM_Jit is the ARM_Interface for the x64 recompiler.

* tests: Infrastructure for unit tests

* dyncom: Implement Arm_DynCom::ClearCache()

* tests/JitX64: Fuzz ARM data processing instructions

* JitX64: Implement ARM_Jit::ClearCache()

* JitX64: Implement ADC_imm

* JitX64: Implement immediate data processing instructions

* fixup! Arm/ArmDecoder: Address comments regarding the arm decoder and check versions of all arm instructions

* fixup! Builds on OS X now

* fixup! Fix bugs in thumb decoder & have it use boost::optional

* JitX64/RegAlloc: Improve documentation and improve method names

* tests/JitX64: Improve tests

* tests/JitX64: Add tests for testing data processing instructions with Rd=R15

* fixup! JitX64/RegAlloc: Rename member functions to (Lock|Bind)ArmFor(Read|ReadWrite|Write).

* fixup! Addressed comments regarding JitX64::JitX64

* fixup! tests/JitX64: Address comments

* tests/JitX64: Initial thumb tests

* Common: Add MathUtil::SignExtend

* ARM_Disasm: Disassemble BLX

* JitX64: Implement branch instructions

* JitX64: Implement 32-bit thumb BL(X) instruction

* JitX64: Data Processing: Implement shift-by-immediate (_reg) instructions

* JitX64: Data Processing: Implement shift-by-register (_rsr) instructions

* x64 ABI: Be explicit about Gen:: namespace

* MMIO: UnmapRegion

* JitX64: Implement load/store instructions

* JitX64: Implement load and store multiple

* JitX64: Implement SETEND

* tests/JitX64: Improve thumb instruction test coverage

* fixup! Fix non-MSVC builds

* JitX64: Load/Store: Add UNPREDICTABLE ASSERTs, correct LDR R15 behavior

* tests/JitX64: Fix thumb tests

* JitX64: Implement synchronisation instructions

* JitX64: Implement exception-generating instructions

* ARM_Jit: ClearCache upon construct

* ARM/Decoder: Fill out more instructions

* JitX64: Removed old page table code

* fixup! ARM/Decoder: Various fixes

* Use C++14 std::integer_sequence.
* Split ArmDecoder::Instruction into ArmInstruction and ThumbInstruction
* Make Cond, Register, ShiftType and SignExtendRotation enum classes.
* ThumbMatcher is no longer allocated on the heap.
* JitX64::CondManager now uses Cond instead of SkyEye's ConditionCode.
* Add utility functions IsArmRegValid and MakeArmRegList.

* fixup! ARM/Decoder: Misc fixes

* fixup! Common: Move SignExtend and bits into BitUtil

* fixup! arm_dyncom_interpreter

* fixup! JitState: Should be final and should have initial values

* fixup! Make JitX64::CondManager::CurrentCond() const

* fixup! JitX64: Move thumb_BLX_* variables below ArmDecoder::Visitor functions

* fixup! RegAlloc: Simplify code

* fixup! MMIO: UnmapRegion

* fixup! Tests/JitX64: Use std::generate_n and factor pass-testing into DoesBehaviorMatch

* fixup! JitX64::LocationDescriptorHash: Use static_cast

* fixup! JitX64: Default values for primitive members

* fixup! JitX64: Add const to relevant member functions, add initialisers to members of CondManager

* fixup! Jit: Remove unnecessary headers

* fixup! RunJittedCode: Remove C-style casts

* fixup! Common: Common::make_unique -> std::make_unique

* fixup! BitUtils

* fixup! JitX64: reinterpret_cast<const void* const> -> reinterpret_cast<const void*>

* fixup! static -> inline in headers, moved instruction/helper/load_store.h function bodies into .cpp file

* fixup! Move public interface of JitX64 class to the top

* fixup! Rename JitX64::UpdateFlags* so flag names are in order (N, Z, C, V)

* fixup! BitUtil: Correct error messages, independent impl of Bit, correct SignExtend logic
This commit is contained in:
Dragios 2016-04-16 01:49:30 +08:00
parent 58934192df
commit 7d74f8cf47
61 changed files with 18094 additions and 32 deletions

10445
externals/catch/catch.hpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@ add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(video_core)
add_subdirectory(audio_core)
add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(citra)
endif()

View file

@ -26,6 +26,7 @@ set(HEADERS
assert.h
bit_field.h
bit_set.h
bit_util.h
break_points.h
chunk_file.h
code_block.h

47
src/common/bit_util.h Normal file
View file

@ -0,0 +1,47 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <climits>
#include <cstddef>
namespace BitUtil {
/// The size of a type in terms of bits
template <typename T>
constexpr size_t BitSize() {
return sizeof(T) * CHAR_BIT;
}
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
template<size_t begin_bit, size_t end_bit, typename T>
constexpr T Bits(const T value) {
static_assert(begin_bit < end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
static_assert(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
static_assert(end_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
return (value >> begin_bit) & ((1 << (end_bit - begin_bit + 1)) - 1);
}
/// Extracts a single bit at bit_position from value of type T.
template<size_t bit_position, typename T>
constexpr T Bit(const T value) {
static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
return (value >> bit_position) & 1;
}
/// Sign-extends a value that has NBits bits to the full bitwidth of type T.
template<size_t NBits, typename T>
inline T SignExtend(const T value) {
static_assert(NBits <= BitSize<T>(), "NBits larger than bitsize of T");
constexpr T mask = static_cast<T>(1ULL << NBits) - 1;
const T signbit = Bit<NBits - 1>(value);
if (signbit != 0) {
return value | ~mask;
}
return value;
}
} // namespace BitUtil

View file

@ -68,18 +68,22 @@ inline u64 _rotr64(u64 x, unsigned int shift){
}
#else // _MSC_VER
#if (_MSC_VER < 1900)
#if (_MSC_VER < 1900)
// Function Cross-Compatibility
#define snprintf _snprintf
#endif
#endif
// Locale Cross-Compatibility
#define locale_t _locale_t
// Locale Cross-Compatibility
#define locale_t _locale_t
extern "C" {
extern "C" {
__declspec(dllimport) void __stdcall DebugBreak(void);
}
#define Crash() {DebugBreak();}
}
#define Crash() {DebugBreak();}
#define rotr _rotr
#define rotl _rotl
#endif // _MSC_VER ndef
// Generic function to get last error message.

View file

@ -5,6 +5,7 @@
#pragma once
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <type_traits>
@ -16,14 +17,12 @@ inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start
}
template<typename T>
inline T Clamp(const T val, const T& min, const T& max)
{
inline T Clamp(const T val, const T& min, const T& max) {
return std::max(min, std::min(max, val));
}
template<class T>
struct Rectangle
{
struct Rectangle {
T left;
T top;
T right;

View file

@ -29,31 +29,31 @@
#ifdef _WIN32 // 64-bit Windows - the really exotic calling convention
#define ABI_PARAM1 RCX
#define ABI_PARAM2 RDX
#define ABI_PARAM3 R8
#define ABI_PARAM4 R9
#define ABI_PARAM1 ::Gen::RCX
#define ABI_PARAM2 ::Gen::RDX
#define ABI_PARAM3 ::Gen::R8
#define ABI_PARAM4 ::Gen::R9
// xmm0-xmm15 use the upper 16 bits in the functions that push/pop registers.
#define ABI_ALL_CALLER_SAVED \
(BitSet32 { RAX, RCX, RDX, R8, R9, R10, R11, \
XMM0+16, XMM1+16, XMM2+16, XMM3+16, XMM4+16, XMM5+16 })
(BitSet32 { ::Gen::RAX, ::Gen::RCX, ::Gen::RDX, ::Gen::R8, ::Gen::R9, ::Gen::R10, ::Gen::R11, \
::Gen::XMM0+16, ::Gen::XMM1+16, ::Gen::XMM2+16, ::Gen::XMM3+16, ::Gen::XMM4+16, ::Gen::XMM5+16 })
#else //64-bit Unix / OS X
#define ABI_PARAM1 RDI
#define ABI_PARAM2 RSI
#define ABI_PARAM3 RDX
#define ABI_PARAM4 RCX
#define ABI_PARAM5 R8
#define ABI_PARAM6 R9
#define ABI_PARAM1 ::Gen::RDI
#define ABI_PARAM2 ::Gen::RSI
#define ABI_PARAM3 ::Gen::RDX
#define ABI_PARAM4 ::Gen::RCX
#define ABI_PARAM5 ::Gen::R8
#define ABI_PARAM6 ::Gen::R9
// TODO: Avoid pushing all 16 XMM registers when possible. Most functions we call probably
// don't actually clobber them.
#define ABI_ALL_CALLER_SAVED \
(BitSet32 { RAX, RCX, RDX, RDI, RSI, R8, R9, R10, R11 } | \
(BitSet32 { ::Gen::RAX, ::Gen::RCX, ::Gen::RDX, ::Gen::RDI, ::Gen::RSI, ::Gen::R8, ::Gen::R9, ::Gen::R10, ::Gen::R11 } | \
ABI_ALL_FPRS)
#endif // WIN32
#define ABI_ALL_CALLEE_SAVED (~ABI_ALL_CALLER_SAVED)
#define ABI_RETURN RAX
#define ABI_RETURN ::Gen::RAX

View file

@ -1,6 +1,8 @@
set(SRCS
arm/disassembler/arm_disasm.cpp
arm/disassembler/load_symbol_map.cpp
arm/decoder/arm.cpp
arm/decoder/thumb.cpp
arm/dyncom/arm_dyncom.cpp
arm/dyncom/arm_dyncom_dec.cpp
arm/dyncom/arm_dyncom_interpreter.cpp
@ -129,6 +131,7 @@ set(HEADERS
arm/arm_interface.h
arm/disassembler/arm_disasm.h
arm/disassembler/load_symbol_map.h
arm/decoder/decoder.h
arm/dyncom/arm_dyncom.h
arm/dyncom/arm_dyncom_dec.h
arm/dyncom/arm_dyncom_interpreter.h
@ -261,6 +264,46 @@ set(HEADERS
system.h
)
if(ARCHITECTURE_x86_64)
set(SRCS ${SRCS}
arm/jit_x64/cond.cpp
arm/jit_x64/instructions/branch.cpp
arm/jit_x64/instructions/coprocessor.cpp
arm/jit_x64/instructions/data_processing.cpp
arm/jit_x64/instructions/exception_generating.cpp
arm/jit_x64/instructions/extension.cpp
arm/jit_x64/instructions/hint.cpp
arm/jit_x64/instructions/load_store.cpp
arm/jit_x64/instructions/misc.cpp
arm/jit_x64/instructions/usad.cpp
arm/jit_x64/instructions/packing.cpp
arm/jit_x64/instructions/reversal.cpp
arm/jit_x64/instructions/saturation.cpp
arm/jit_x64/instructions/multiply.cpp
arm/jit_x64/instructions/parallel_add_subtract_modulo.cpp
arm/jit_x64/instructions/parallel_add_subtract_saturating.cpp
arm/jit_x64/instructions/parallel_add_subtract_halving.cpp
arm/jit_x64/instructions/qadd_qsub.cpp
arm/jit_x64/instructions/synchronisation.cpp
arm/jit_x64/instructions/status_register.cpp
arm/jit_x64/instructions/thumb.cpp
arm/jit_x64/instructions/helper/load_store.cpp
arm/jit_x64/interface.cpp
arm/jit_x64/interpret.cpp
arm/jit_x64/jit_x64.cpp
arm/jit_x64/reg_alloc.cpp
)
set(HEADERS ${HEADERS}
arm/jit_x64/common.h
arm/jit_x64/instructions/helper/load_store.h
arm/jit_x64/interface.h
arm/jit_x64/jit_x64.h
arm/jit_x64/reg_alloc.h
)
endif()
create_directory_groups(${SRCS} ${HEADERS})
add_library(core STATIC ${SRCS} ${HEADERS})

View file

@ -148,6 +148,9 @@ public:
s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
/// Clears any cached state. Call when instructions in memory change to avoid executing stale cached instructions (e.g.: CROs).
virtual void ClearCache() = 0;
protected:
/**

View file

@ -0,0 +1,393 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
#include <memory>
#include <utility>
#include <boost/optional.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/arm/decoder/decoder.h"
namespace ArmDecoder {
namespace Impl {
// Internal implementation for call
template<size_t ...seq, typename Container, typename ...Args>
void call_impl(std::integer_sequence<size_t, seq...>, Visitor* v, void (Visitor::*fn)(Args...), const Container& list) {
using FunctionArgTypes = typename std::tuple<Args...>;
// Here we static_cast each element in list to the corresponding argument type for fn.
(v->*fn)(static_cast<typename std::tuple_element<seq, FunctionArgTypes>::type>(std::get<seq>(list))...);
}
/**
* This function takes a member function of Visitor and calls it with the parameters specified in list.
* @tparam NumArgs Number of arguments that the member function fn has.
* @param v The Visitor.
* @param fn Member function to call on v.
* @param list List of arguments that will be splatted.
*/
template<size_t NumArgs, typename Container, typename ...Args>
void call(Visitor* v, void (Visitor::*fn)(Args...), const Container& list) {
call_impl(typename std::index_sequence_for<Args...>{}, v, fn, list);
}
/// Function has NumArgs arguments
template<size_t NumArgs, typename Function>
struct MatcherImpl : ArmMatcher {
std::array<u32, NumArgs> masks = {};
std::array<size_t, NumArgs> shifts = {};
Function fn = nullptr;
void visit(Visitor *v, u32 inst) override {
std::array<u32, NumArgs> values;
std::transform(masks.begin(), masks.begin() + NumArgs, shifts.begin(), values.begin(),
[inst](u32 mask, size_t shift) { return (inst & mask) >> shift; });
call<NumArgs>(v, fn, values);
}
};
}
template<size_t NumArgs, typename Function>
static std::unique_ptr<ArmMatcher> MakeMatcher(const char* const format, Function fn) {
ASSERT(strlen(format) == 32);
auto ret = std::make_unique<Impl::MatcherImpl<NumArgs, Function>>();
ret->fn = fn;
ret->masks.fill(0);
ret->shifts.fill(0);
char ch = 0;
int arg = -1;
for (size_t i = 0; i < 32; i++) {
const size_t bit_position = 31 - i;
const u32 bit = 1 << bit_position;
if (format[i] == '0') {
// 0: A zero must be found here
ret->bit_mask |= bit;
ch = 0;
continue;
} else if (format[i] == '1') {
// 1: A one must be found here
ret->bit_mask |= bit;
ret->expected |= bit;
ch = 0;
continue;
} else if (format[i] == '-') {
// -: Ignore this bit
ch = 0;
continue;
}
// Ban some characters
ASSERT(format[i] != 'I');
ASSERT(format[i] != 'l');
ASSERT(format[i] != 'O');
// otherwise: This bit is part of a field to extract
// strings of the same character make up a field
if (format[i] != ch) {
arg++;
ASSERT(arg < NumArgs);
ch = format[i];
}
ret->masks[arg] |= bit;
ret->shifts[arg] = bit_position;
}
ASSERT(arg == NumArgs - 1);
return std::unique_ptr<ArmMatcher>(std::move(ret));
}
static const std::array<ArmInstruction, 221> arm_instruction_table = {{
// Branch instructions
{ "BLX (immediate)", MakeMatcher<2>("1111101hvvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::BLX_imm) }, // ARMv5
{ "BLX (register)", MakeMatcher<2>("cccc000100101111111111110011mmmm", &Visitor::BLX_reg) }, // ARMv5
{ "B", MakeMatcher<2>("cccc1010vvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::B) }, // all
{ "BL", MakeMatcher<2>("cccc1011vvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::BL) }, // all
{ "BX", MakeMatcher<2>("cccc000100101111111111110001mmmm", &Visitor::BX) }, // ARMv4T
{ "BXJ", MakeMatcher<2>("cccc000100101111111111110010mmmm", &Visitor::BXJ) }, // ARMv5J
// Coprocessor instructions
{ "CDP2", MakeMatcher<0>("11111110-------------------1----", &Visitor::CDP) }, // ARMv5 (Generic Coprocessor)
{ "CDP", MakeMatcher<0>("----1110-------------------0----", &Visitor::CDP) }, // ARMv2 (Generic Coprocessor)
{ "LDC2", MakeMatcher<0>("1111110----1--------------------", &Visitor::LDC) }, // ARMv5 (Generic Coprocessor)
{ "LDC", MakeMatcher<0>("----110----1--------------------", &Visitor::LDC) }, // ARMv2 (Generic Coprocessor)
{ "MCR2", MakeMatcher<0>("----1110---0---------------1----", &Visitor::MCR) }, // ARMv5 (Generic Coprocessor)
{ "MCR", MakeMatcher<0>("----1110---0---------------1----", &Visitor::MCR) }, // ARMv2 (Generic Coprocessor)
{ "MCRR2", MakeMatcher<0>("111111000100--------------------", &Visitor::MCRR) }, // ARMv6 (Generic Coprocessor)
{ "MCRR", MakeMatcher<0>("----11000100--------------------", &Visitor::MCRR) }, // ARMv5E (Generic Coprocessor)
{ "MRC2", MakeMatcher<0>("11111110---1---------------1----", &Visitor::MRC) }, // ARMv5 (Generic Coprocessor)
{ "MRC", MakeMatcher<0>("----1110---1---------------1----", &Visitor::MRC) }, // ARMv2 (Generic Coprocessor)
{ "MRRC2", MakeMatcher<0>("111111000101--------------------", &Visitor::MRRC) }, // ARMv6 (Generic Coprocessor)
{ "MRRC", MakeMatcher<0>("----11000101--------------------", &Visitor::MRRC) }, // ARMv5E (Generic Coprocessor)
{ "STC2", MakeMatcher<0>("1111110----0--------------------", &Visitor::STC) }, // ARMv5 (Generic Coprocessor)
{ "STC", MakeMatcher<0>("----110----0--------------------", &Visitor::STC) }, // ARMv2 (Generic Coprocessor)
// Data Processing instructions
{ "ADC (imm)", MakeMatcher<6>("cccc0010101Snnnnddddrrrrvvvvvvvv", &Visitor::ADC_imm) }, // all
{ "ADC (reg)", MakeMatcher<7>("cccc0000101Snnnnddddvvvvvrr0mmmm", &Visitor::ADC_reg) }, // all
{ "ADC (rsr)", MakeMatcher<7>("cccc0000101Snnnnddddssss0rr1mmmm", &Visitor::ADC_rsr) }, // all
{ "ADD (imm)", MakeMatcher<6>("cccc0010100Snnnnddddrrrrvvvvvvvv", &Visitor::ADD_imm) }, // all
{ "ADD (reg)", MakeMatcher<7>("cccc0000100Snnnnddddvvvvvrr0mmmm", &Visitor::ADD_reg) }, // all
{ "ADD (rsr)", MakeMatcher<7>("cccc0000100Snnnnddddssss0rr1mmmm", &Visitor::ADD_rsr) }, // all
{ "AND (imm)", MakeMatcher<6>("cccc0010000Snnnnddddrrrrvvvvvvvv", &Visitor::AND_imm) }, // all
{ "AND (reg)", MakeMatcher<7>("cccc0000000Snnnnddddvvvvvrr0mmmm", &Visitor::AND_reg) }, // all
{ "AND (rsr)", MakeMatcher<7>("cccc0000000Snnnnddddssss0rr1mmmm", &Visitor::AND_rsr) }, // all
{ "BIC (imm)", MakeMatcher<6>("cccc0011110Snnnnddddrrrrvvvvvvvv", &Visitor::BIC_imm) }, // all
{ "BIC (reg)", MakeMatcher<7>("cccc0001110Snnnnddddvvvvvrr0mmmm", &Visitor::BIC_reg) }, // all
{ "BIC (rsr)", MakeMatcher<7>("cccc0001110Snnnnddddssss0rr1mmmm", &Visitor::BIC_rsr) }, // all
{ "CMN (imm)", MakeMatcher<4>("cccc00110111nnnn0000rrrrvvvvvvvv", &Visitor::CMN_imm) }, // all
{ "CMN (reg)", MakeMatcher<5>("cccc00010111nnnn0000vvvvvrr0mmmm", &Visitor::CMN_reg) }, // all
{ "CMN (rsr)", MakeMatcher<5>("cccc00010111nnnn0000ssss0rr1mmmm", &Visitor::CMN_rsr) }, // all
{ "CMP (imm)", MakeMatcher<4>("cccc00110101nnnn0000rrrrvvvvvvvv", &Visitor::CMP_imm) }, // all
{ "CMP (reg)", MakeMatcher<5>("cccc00010101nnnn0000vvvvvrr0mmmm", &Visitor::CMP_reg) }, // all
{ "CMP (rsr)", MakeMatcher<5>("cccc00010101nnnn0000ssss0rr1mmmm", &Visitor::CMP_rsr) }, // all
{ "EOR (imm)", MakeMatcher<6>("cccc0010001Snnnnddddrrrrvvvvvvvv", &Visitor::EOR_imm) }, // all
{ "EOR (reg)", MakeMatcher<7>("cccc0000001Snnnnddddvvvvvrr0mmmm", &Visitor::EOR_reg) }, // all
{ "EOR (rsr)", MakeMatcher<7>("cccc0000001Snnnnddddssss0rr1mmmm", &Visitor::EOR_rsr) }, // all
{ "MOV (imm)", MakeMatcher<5>("cccc0011101S0000ddddrrrrvvvvvvvv", &Visitor::MOV_imm) }, // all
{ "MOV (reg)", MakeMatcher<6>("cccc0001101S0000ddddvvvvvrr0mmmm", &Visitor::MOV_reg) }, // all
{ "MOV (rsr)", MakeMatcher<6>("cccc0001101S0000ddddssss0rr1mmmm", &Visitor::MOV_rsr) }, // all
{ "MVN (imm)", MakeMatcher<5>("cccc0011111S0000ddddrrrrvvvvvvvv", &Visitor::MVN_imm) }, // all
{ "MVN (reg)", MakeMatcher<6>("cccc0001111S0000ddddvvvvvrr0mmmm", &Visitor::MVN_reg) }, // all
{ "MVN (rsr)", MakeMatcher<6>("cccc0001111S0000ddddssss0rr1mmmm", &Visitor::MVN_rsr) }, // all
{ "ORR (imm)", MakeMatcher<6>("cccc0011100Snnnnddddrrrrvvvvvvvv", &Visitor::ORR_imm) }, // all
{ "ORR (reg)", MakeMatcher<7>("cccc0001100Snnnnddddvvvvvrr0mmmm", &Visitor::ORR_reg) }, // all
{ "ORR (rsr)", MakeMatcher<7>("cccc0001100Snnnnddddssss0rr1mmmm", &Visitor::ORR_rsr) }, // all
{ "RSB (imm)", MakeMatcher<6>("cccc0010011Snnnnddddrrrrvvvvvvvv", &Visitor::RSB_imm) }, // all
{ "RSB (reg)", MakeMatcher<7>("cccc0000011Snnnnddddvvvvvrr0mmmm", &Visitor::RSB_reg) }, // all
{ "RSB (rsr)", MakeMatcher<7>("cccc0000011Snnnnddddssss0rr1mmmm", &Visitor::RSB_rsr) }, // all
{ "RSC (imm)", MakeMatcher<6>("cccc0010111Snnnnddddrrrrvvvvvvvv", &Visitor::RSC_imm) }, // all
{ "RSC (reg)", MakeMatcher<7>("cccc0000111Snnnnddddvvvvvrr0mmmm", &Visitor::RSC_reg) }, // all
{ "RSC (rsr)", MakeMatcher<7>("cccc0000111Snnnnddddssss0rr1mmmm", &Visitor::RSC_rsr) }, // all
{ "SBC (imm)", MakeMatcher<6>("cccc0010110Snnnnddddrrrrvvvvvvvv", &Visitor::SBC_imm) }, // all
{ "SBC (reg)", MakeMatcher<7>("cccc0000110Snnnnddddvvvvvrr0mmmm", &Visitor::SBC_reg) }, // all
{ "SBC (rsr)", MakeMatcher<7>("cccc0000110Snnnnddddssss0rr1mmmm", &Visitor::SBC_rsr) }, // all
{ "SUB (imm)", MakeMatcher<6>("cccc0010010Snnnnddddrrrrvvvvvvvv", &Visitor::SUB_imm) }, // all
{ "SUB (reg)", MakeMatcher<7>("cccc0000010Snnnnddddvvvvvrr0mmmm", &Visitor::SUB_reg) }, // all
{ "SUB (rsr)", MakeMatcher<7>("cccc0000010Snnnnddddssss0rr1mmmm", &Visitor::SUB_rsr) }, // all
{ "TEQ (imm)", MakeMatcher<4>("cccc00110011nnnn0000rrrrvvvvvvvv", &Visitor::TEQ_imm) }, // all
{ "TEQ (reg)", MakeMatcher<5>("cccc00010011nnnn0000vvvvvrr0mmmm", &Visitor::TEQ_reg) }, // all
{ "TEQ (rsr)", MakeMatcher<5>("cccc00010011nnnn0000ssss0rr1mmmm", &Visitor::TEQ_rsr) }, // all
{ "TST (imm)", MakeMatcher<4>("cccc00110001nnnn0000rrrrvvvvvvvv", &Visitor::TST_imm) }, // all
{ "TST (reg)", MakeMatcher<5>("cccc00010001nnnn0000vvvvvrr0mmmm", &Visitor::TST_reg) }, // all
{ "TST (rsr)", MakeMatcher<5>("cccc00010001nnnn0000ssss0rr1mmmm", &Visitor::TST_rsr) }, // all
// Exception Generating instructions
{ "BKPT", MakeMatcher<3>("cccc00010010vvvvvvvvvvvv0111vvvv", &Visitor::BKPT) }, // ARMv5
{ "SVC", MakeMatcher<2>("cccc1111vvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::SVC) }, // all
{ "UDF", MakeMatcher<0>("111001111111------------1111----", &Visitor::UDF) }, // all
// Extension instructions
{ "SXTB", MakeMatcher<4>("cccc011010101111ddddrr000111mmmm", &Visitor::SXTB) }, // ARMv6
{ "SXTB16", MakeMatcher<4>("cccc011010001111ddddrr000111mmmm", &Visitor::SXTB16) }, // ARMv6
{ "SXTH", MakeMatcher<4>("cccc011010111111ddddrr000111mmmm", &Visitor::SXTH) }, // ARMv6
{ "SXTAB", MakeMatcher<5>("cccc01101010nnnnddddrr000111mmmm", &Visitor::SXTAB) }, // ARMv6
{ "SXTAB16", MakeMatcher<5>("cccc01101000nnnnddddrr000111mmmm", &Visitor::SXTAB16) }, // ARMv6
{ "SXTAH", MakeMatcher<5>("cccc01101011nnnnddddrr000111mmmm", &Visitor::SXTAH) }, // ARMv6
{ "UXTB", MakeMatcher<4>("cccc011011101111ddddrr000111mmmm", &Visitor::UXTB) }, // ARMv6
{ "UXTB16", MakeMatcher<4>("cccc011011001111ddddrr000111mmmm", &Visitor::UXTB16) }, // ARMv6
{ "UXTH", MakeMatcher<4>("cccc011011111111ddddrr000111mmmm", &Visitor::UXTH) }, // ARMv6
{ "UXTAB", MakeMatcher<5>("cccc01101110nnnnddddrr000111mmmm", &Visitor::UXTAB) }, // ARMv6
{ "UXTAB16", MakeMatcher<5>("cccc01101100nnnnddddrr000111mmmm", &Visitor::UXTAB16) }, // ARMv6
{ "UXTAH", MakeMatcher<5>("cccc01101111nnnnddddrr000111mmmm", &Visitor::UXTAH) }, // ARMv6
// Hint instructions
{ "PLD", MakeMatcher<0>("111101---101----1111------------", &Visitor::PLD) }, // ARMv5E
{ "SEV", MakeMatcher<0>("----0011001000001111000000000100", &Visitor::SEV) }, // ARMv6K
{ "WFE", MakeMatcher<0>("----0011001000001111000000000010", &Visitor::WFE) }, // ARMv6K
{ "WFI", MakeMatcher<0>("----0011001000001111000000000011", &Visitor::WFI) }, // ARMv6K
{ "YIELD", MakeMatcher<0>("----0011001000001111000000000001", &Visitor::YIELD) }, // ARMv6K
// Synchronization Primitive instructions
{ "CLREX", MakeMatcher<0>("11110101011111111111000000011111", &Visitor::CLREX) }, // ARMv6K
{ "LDREX", MakeMatcher<3>("cccc00011001nnnndddd111110011111", &Visitor::LDREX) }, // ARMv6
{ "LDREXB", MakeMatcher<3>("cccc00011101nnnndddd111110011111", &Visitor::LDREXB) }, // ARMv6K
{ "LDREXD", MakeMatcher<3>("cccc00011011nnnndddd111110011111", &Visitor::LDREXD) }, // ARMv6K
{ "LDREXH", MakeMatcher<3>("cccc00011111nnnndddd111110011111", &Visitor::LDREXH) }, // ARMv6K
{ "STREX", MakeMatcher<4>("cccc00011000nnnndddd11111001mmmm", &Visitor::STREX) }, // ARMv6
{ "STREXB", MakeMatcher<4>("cccc00011100nnnndddd11111001mmmm", &Visitor::STREXB) }, // ARMv6K
{ "STREXD", MakeMatcher<4>("cccc00011010nnnndddd11111001mmmm", &Visitor::STREXD) }, // ARMv6K
{ "STREXH", MakeMatcher<4>("cccc00011110nnnndddd11111001mmmm", &Visitor::STREXH) }, // ARMv6K
{ "SWP", MakeMatcher<4>("cccc00010000nnnndddd00001001mmmm", &Visitor::SWP) }, // ARMv2S (Deprecated in ARMv6)
{ "SWPB", MakeMatcher<4>("cccc00010100nnnndddd00001001mmmm", &Visitor::SWPB) }, // ARMv2S (Deprecated in ARMv6)
// Load/Store instructions
{ "LDR (imm)", MakeMatcher<7>("cccc010pu0w1nnnnddddvvvvvvvvvvvv", &Visitor::LDR_imm) },
{ "LDR (reg)", MakeMatcher<9>("cccc011pu0w1nnnnddddvvvvvrr0mmmm", &Visitor::LDR_reg) },
{ "LDRB (imm)", MakeMatcher<7>("cccc010pu1w1nnnnddddvvvvvvvvvvvv", &Visitor::LDRB_imm) },
{ "LDRB (reg)", MakeMatcher<9>("cccc011pu1w1nnnnddddvvvvvrr0mmmm", &Visitor::LDRB_reg) },
{ "LDRBT (A1)", MakeMatcher<0>("----0100-111--------------------", &Visitor::LDRBT) },
{ "LDRBT (A2)", MakeMatcher<0>("----0110-111---------------0----", &Visitor::LDRBT) },
{ "LDRD (imm)", MakeMatcher<8>("cccc000pu1w0nnnnddddvvvv1101vvvv", &Visitor::LDRD_imm) }, // ARMv5E
{ "LDRD (reg)", MakeMatcher<7>("cccc000pu0w0nnnndddd00001101mmmm", &Visitor::LDRD_reg) }, // ARMv5E
{ "LDRH (imm)", MakeMatcher<8>("cccc000pu1w1nnnnddddvvvv1011vvvv", &Visitor::LDRH_imm) },
{ "LDRH (reg)", MakeMatcher<7>("cccc000pu0w1nnnndddd00001011mmmm", &Visitor::LDRH_reg) },
{ "LDRHT (A1)", MakeMatcher<0>("----0000-111------------1011----", &Visitor::LDRHT) },
{ "LDRHT (A2)", MakeMatcher<0>("----0000-011--------00001011----", &Visitor::LDRHT) },
{ "LDRSB (imm)", MakeMatcher<8>("cccc000pu1w1nnnnddddvvvv1101vvvv", &Visitor::LDRSB_imm) },
{ "LDRSB (reg)", MakeMatcher<7>("cccc000pu0w1nnnndddd00001101mmmm", &Visitor::LDRSB_reg) },
{ "LDRSBT (A1)", MakeMatcher<0>("----0000-111------------1101----", &Visitor::LDRSBT) },
{ "LDRSBT (A2)", MakeMatcher<0>("----0000-011--------00001101----", &Visitor::LDRSBT) },
{ "LDRSH (imm)", MakeMatcher<8>("cccc000pu1w1nnnnddddvvvv1111vvvv", &Visitor::LDRSH_imm) },
{ "LDRSH (reg)", MakeMatcher<7>("cccc000pu0w1nnnndddd00001111mmmm", &Visitor::LDRSH_reg) },
{ "LDRSHT (A1)", MakeMatcher<0>("----0000-111------------1111----", &Visitor::LDRSHT) },
{ "LDRSHT (A2)", MakeMatcher<0>("----0000-011--------00001111----", &Visitor::LDRSHT) },
{ "LDRT (A1)", MakeMatcher<0>("----0100-011--------------------", &Visitor::LDRT) },
{ "LDRT (A2)", MakeMatcher<0>("----0110-011---------------0----", &Visitor::LDRT) },
{ "STR (imm)", MakeMatcher<7>("cccc010pu0w0nnnnddddvvvvvvvvvvvv", &Visitor::STR_imm) },
{ "STR (reg)", MakeMatcher<9>("cccc011pu0w0nnnnddddvvvvvrr0mmmm", &Visitor::STR_reg) },
{ "STRB (imm)", MakeMatcher<7>("cccc010pu1w0nnnnddddvvvvvvvvvvvv", &Visitor::STRB_imm) },
{ "STRB (reg)", MakeMatcher<9>("cccc011pu1w0nnnnddddvvvvvrr0mmmm", &Visitor::STRB_reg) },
{ "STRBT (A1)", MakeMatcher<0>("----0100-110--------------------", &Visitor::STRBT) },
{ "STRBT (A2)", MakeMatcher<0>("----0110-110---------------0----", &Visitor::STRBT) },
{ "STRD (imm)", MakeMatcher<8>("cccc000pu1w0nnnnddddvvvv1111vvvv", &Visitor::STRD_imm) }, // ARMv5E
{ "STRD (reg)", MakeMatcher<7>("cccc000pu0w0nnnndddd00001111mmmm", &Visitor::STRD_reg) }, // ARMv5E
{ "STRH (imm)", MakeMatcher<8>("cccc000pu1w0nnnnddddvvvv1011vvvv", &Visitor::STRH_imm) },
{ "STRH (reg)", MakeMatcher<7>("cccc000pu0w0nnnndddd00001011mmmm", &Visitor::STRH_reg) },
{ "STRHT (A1)", MakeMatcher<0>("----0000-110------------1011----", &Visitor::STRHT) },
{ "STRHT (A2)", MakeMatcher<0>("----0000-010--------00001011----", &Visitor::STRHT) },
{ "STRT (A1)", MakeMatcher<0>("----0100-010--------------------", &Visitor::STRT) },
{ "STRT (A2)", MakeMatcher<0>("----0110-010---------------0----", &Visitor::STRT) },
// Load/Store Multiple instructions
{ "LDM", MakeMatcher<6>("cccc100pu0w1nnnnxxxxxxxxxxxxxxxx", &Visitor::LDM) }, // all
{ "LDM (usr reg)", MakeMatcher<0>("----100--101--------------------", &Visitor::LDM_usr) }, // all
{ "LDM (exce ret)", MakeMatcher<0>("----100--1-1----1---------------", &Visitor::LDM_eret) }, // all
{ "STM", MakeMatcher<6>("cccc100pu0w0nnnnxxxxxxxxxxxxxxxx", &Visitor::STM) }, // all
{ "STM (usr reg)", MakeMatcher<0>("----100--100--------------------", &Visitor::STM_usr) }, // all
// Miscellaneous instructions
{ "CLZ", MakeMatcher<3>("cccc000101101111dddd11110001mmmm", &Visitor::CLZ) }, // ARMv5
{ "NOP", MakeMatcher<0>("----001100100000111100000000----", &Visitor::NOP) }, // ARMv6K
{ "SEL", MakeMatcher<4>("cccc01101000nnnndddd11111011mmmm", &Visitor::SEL) }, // ARMv6
// Unsigned Sum of Absolute Differences instructions
{ "USAD8", MakeMatcher<4>("cccc01111000dddd1111mmmm0001nnnn", &Visitor::USAD8) }, // ARMv6
{ "USADA8", MakeMatcher<5>("cccc01111000ddddaaaammmm0001nnnn", &Visitor::USADA8) }, // ARMv6
// Packing instructions
{ "PKHBT", MakeMatcher<5>("cccc01101000nnnnddddvvvvv001mmmm", &Visitor::PKHBT) }, // ARMv6K
{ "PKHTB", MakeMatcher<5>("cccc01101000nnnnddddvvvvv101mmmm", &Visitor::PKHTB) }, // ARMv6K
// Reversal instructions
{ "REV", MakeMatcher<3>("cccc011010111111dddd11110011mmmm", &Visitor::REV) }, // ARMv6
{ "REV16", MakeMatcher<3>("cccc011010111111dddd11111011mmmm", &Visitor::REV16) }, // ARMv6
{ "REVSH", MakeMatcher<3>("cccc011011111111dddd11111011mmmm", &Visitor::REVSH) }, // ARMv6
// Saturation instructions
{ "SSAT", MakeMatcher<6>("cccc0110101vvvvvddddvvvvvr01nnnn", &Visitor::SSAT) }, // ARMv6
{ "SSAT16", MakeMatcher<4>("cccc01101010vvvvdddd11110011nnnn", &Visitor::SSAT16) }, // ARMv6
{ "USAT", MakeMatcher<6>("cccc0110111vvvvvddddvvvvvr01nnnn", &Visitor::USAT) }, // ARMv6
{ "USAT16", MakeMatcher<4>("cccc01101110vvvvdddd11110011nnnn", &Visitor::USAT16) }, // ARMv6
// Multiply (Normal) instructions
{ "MLA", MakeMatcher<6>("cccc0000001Sddddaaaammmm1001nnnn", &Visitor::MLA) }, // ARMv2
{ "MUL", MakeMatcher<5>("cccc0000000Sdddd0000mmmm1001nnnn", &Visitor::MUL) }, // ARMv2
// Multiply (Long) instructions
{ "SMLAL", MakeMatcher<6>("cccc0000111Sddddaaaammmm1001nnnn", &Visitor::SMLAL) }, // ARMv3M
{ "SMULL", MakeMatcher<6>("cccc0000110Sddddaaaammmm1001nnnn", &Visitor::SMULL) }, // ARMv3M
{ "UMAAL", MakeMatcher<5>("cccc00000100ddddaaaammmm1001nnnn", &Visitor::UMAAL) }, // ARMv6
{ "UMLAL", MakeMatcher<6>("cccc0000101Sddddaaaammmm1001nnnn", &Visitor::UMLAL) }, // ARMv3M
{ "UMULL", MakeMatcher<6>("cccc0000100Sddddaaaammmm1001nnnn", &Visitor::UMULL) }, // ARMv3M
// Multiply (Halfword) instructions
{ "SMLALXY", MakeMatcher<7>("cccc00010100ddddaaaammmm1xy0nnnn", &Visitor::SMLALxy) }, // ARMv5xP
{ "SMLAXY", MakeMatcher<7>("cccc00010000ddddaaaammmm1xy0nnnn", &Visitor::SMLAxy) }, // ARMv5xP
{ "SMULXY", MakeMatcher<6>("cccc00010110dddd0000mmmm1xy0nnnn", &Visitor::SMULxy) }, // ARMv5xP
// Multiply (Word by Halfword) instructions
{ "SMLAWY", MakeMatcher<6>("cccc00010010ddddaaaammmm1y00nnnn", &Visitor::SMLAWy) }, // ARMv5xP
{ "SMULWY", MakeMatcher<5>("cccc00010010dddd0000mmmm1y10nnnn", &Visitor::SMULWy) }, // ARMv5xP
// Multiply (Most Significant Word) instructions
{ "SMMUL", MakeMatcher<5>("cccc01110101dddd1111mmmm00R1nnnn", &Visitor::SMMUL) }, // ARMv6
{ "SMMLA", MakeMatcher<6>("cccc01110101ddddaaaammmm00R1nnnn", &Visitor::SMMLA) }, // ARMv6
{ "SMMLS", MakeMatcher<6>("cccc01110101ddddaaaammmm11R1nnnn", &Visitor::SMMLS) }, // ARMv6
// Multiply (Dual) instructions
{ "SMLAD", MakeMatcher<6>("cccc01110000ddddaaaammmm00M1nnnn", &Visitor::SMLAD) }, // ARMv6
{ "SMLALD", MakeMatcher<6>("cccc01110100ddddaaaammmm00M1nnnn", &Visitor::SMLALD) }, // ARMv6
{ "SMLSD", MakeMatcher<6>("cccc01110000ddddaaaammmm01M1nnnn", &Visitor::SMLSD) }, // ARMv6
{ "SMLSLD", MakeMatcher<6>("cccc01110100ddddaaaammmm01M1nnnn", &Visitor::SMLSLD) }, // ARMv6
{ "SMUAD", MakeMatcher<5>("cccc01110000dddd1111mmmm00M1nnnn", &Visitor::SMUAD) }, // ARMv6
{ "SMUSD", MakeMatcher<5>("cccc01110000dddd1111mmmm01M1nnnn", &Visitor::SMUSD) }, // ARMv6
// Parallel Add/Subtract (Modulo) instructions
{ "SADD8", MakeMatcher<4>("cccc01100001nnnndddd11111001mmmm", &Visitor::SADD8) }, // ARMv6
{ "SADD16", MakeMatcher<4>("cccc01100001nnnndddd11110001mmmm", &Visitor::SADD16) }, // ARMv6
{ "SASX", MakeMatcher<4>("cccc01100001nnnndddd11110011mmmm", &Visitor::SASX) }, // ARMv6
{ "SSAX", MakeMatcher<4>("cccc01100001nnnndddd11110101mmmm", &Visitor::SSAX) }, // ARMv6
{ "SSUB8", MakeMatcher<4>("cccc01100001nnnndddd11111111mmmm", &Visitor::SSUB8) }, // ARMv6
{ "SSUB16", MakeMatcher<4>("cccc01100001nnnndddd11110111mmmm", &Visitor::SSUB16) }, // ARMv6
{ "UADD8", MakeMatcher<4>("cccc01100101nnnndddd11111001mmmm", &Visitor::UADD8) }, // ARMv6
{ "UADD16", MakeMatcher<4>("cccc01100101nnnndddd11110001mmmm", &Visitor::UADD16) }, // ARMv6
{ "UASX", MakeMatcher<4>("cccc01100101nnnndddd11110011mmmm", &Visitor::UASX) }, // ARMv6
{ "USAX", MakeMatcher<4>("cccc01100101nnnndddd11110101mmmm", &Visitor::USAX) }, // ARMv6
{ "USUB8", MakeMatcher<4>("cccc01100101nnnndddd11111111mmmm", &Visitor::USUB8) }, // ARMv6
{ "USUB16", MakeMatcher<4>("cccc01100101nnnndddd11110111mmmm", &Visitor::USUB16) }, // ARMv6
// Parallel Add/Subtract (Saturating) instructions
{ "QADD8", MakeMatcher<4>("cccc01100010nnnndddd11111001mmmm", &Visitor::QADD8) }, // ARMv6
{ "QADD16", MakeMatcher<4>("cccc01100010nnnndddd11110001mmmm", &Visitor::QADD16) }, // ARMv6
{ "QASX", MakeMatcher<4>("cccc01100010nnnndddd11110011mmmm", &Visitor::QASX) }, // ARMv6
{ "QSAX", MakeMatcher<4>("cccc01100010nnnndddd11110101mmmm", &Visitor::QSAX) }, // ARMv6
{ "QSUB8", MakeMatcher<4>("cccc01100010nnnndddd11111111mmmm", &Visitor::QSUB8) }, // ARMv6
{ "QSUB16", MakeMatcher<4>("cccc01100010nnnndddd11110111mmmm", &Visitor::QSUB16) }, // ARMv6
{ "UQADD8", MakeMatcher<4>("cccc01100110nnnndddd11111001mmmm", &Visitor::UQADD8) }, // ARMv6
{ "UQADD16", MakeMatcher<4>("cccc01100110nnnndddd11110001mmmm", &Visitor::UQADD16) }, // ARMv6
{ "UQASX", MakeMatcher<4>("cccc01100110nnnndddd11110011mmmm", &Visitor::UQASX) }, // ARMv6
{ "UQSAX", MakeMatcher<4>("cccc01100110nnnndddd11110101mmmm", &Visitor::UQSAX) }, // ARMv6
{ "UQSUB8", MakeMatcher<4>("cccc01100110nnnndddd11111111mmmm", &Visitor::UQSUB8) }, // ARMv6
{ "UQSUB16", MakeMatcher<4>("cccc01100110nnnndddd11110111mmmm", &Visitor::UQSUB16) }, // ARMv6
// Parallel Add/Subtract (Halving) instructions
{ "SHADD8", MakeMatcher<4>("cccc01100011nnnndddd11111001mmmm", &Visitor::SHADD8) }, // ARMv6
{ "SHADD16", MakeMatcher<4>("cccc01100011nnnndddd11110001mmmm", &Visitor::SHADD16) }, // ARMv6
{ "SHASX", MakeMatcher<4>("cccc01100011nnnndddd11110011mmmm", &Visitor::SHASX) }, // ARMv6
{ "SHSAX", MakeMatcher<4>("cccc01100011nnnndddd11110101mmmm", &Visitor::SHSAX) }, // ARMv6
{ "SHSUB8", MakeMatcher<4>("cccc01100011nnnndddd11111111mmmm", &Visitor::SHSUB8) }, // ARMv6
{ "SHSUB16", MakeMatcher<4>("cccc01100011nnnndddd11110111mmmm", &Visitor::SHSUB16) }, // ARMv6
{ "UHADD8", MakeMatcher<4>("cccc01100111nnnndddd11111001mmmm", &Visitor::UHADD8) }, // ARMv6
{ "UHADD16", MakeMatcher<4>("cccc01100111nnnndddd11110001mmmm", &Visitor::UHADD16) }, // ARMv6
{ "UHASX", MakeMatcher<4>("cccc01100111nnnndddd11110011mmmm", &Visitor::UHASX) }, // ARMv6
{ "UHSAX", MakeMatcher<4>("cccc01100111nnnndddd11110101mmmm", &Visitor::UHSAX) }, // ARMv6
{ "UHSUB8", MakeMatcher<4>("cccc01100111nnnndddd11111111mmmm", &Visitor::UHSUB8) }, // ARMv6
{ "UHSUB16", MakeMatcher<4>("cccc01100111nnnndddd11110111mmmm", &Visitor::UHSUB16) }, // ARMv6
// Saturated Add/Subtract instructions
{ "QADD", MakeMatcher<4>("cccc00010000nnnndddd00000101mmmm", &Visitor::QADD) }, // ARMv5xP
{ "QSUB", MakeMatcher<4>("cccc00010010nnnndddd00000101mmmm", &Visitor::QSUB) }, // ARMv5xP
{ "QDADD", MakeMatcher<4>("cccc00010100nnnndddd00000101mmmm", &Visitor::QDADD) }, // ARMv5xP
{ "QDSUB", MakeMatcher<4>("cccc00010110nnnndddd00000101mmmm", &Visitor::QDSUB) }, // ARMv5xP
// Status Register Access instructions
{ "CPS", MakeMatcher<0>("111100010000---00000000---0-----", &Visitor::CPS) }, // ARMv6
{ "SETEND", MakeMatcher<1>("1111000100000001000000e000000000", &Visitor::SETEND) }, // ARMv6
{ "MRS", MakeMatcher<0>("----00010-00--------00--00000000", &Visitor::MRS) }, // ARMv3
{ "MSR", MakeMatcher<0>("----00-10-10----1111------------", &Visitor::MSR) }, // ARMv3
{ "RFE", MakeMatcher<0>("----0001101-0000---------110----", &Visitor::RFE) }, // ARMv6
{ "SRS", MakeMatcher<0>("0000011--0-00000000000000001----", &Visitor::SRS) }, // ARMv6
}};
boost::optional<const ArmInstruction&> DecodeArm(u32 i) {
auto iterator = std::find_if(arm_instruction_table.cbegin(), arm_instruction_table.cend(),
[i](const auto& instruction) { return instruction.Match(i); });
return iterator != arm_instruction_table.cend() ? boost::make_optional<const ArmInstruction&>(*iterator) : boost::none;
}
} // namespace ArmDecoder

View file

@ -0,0 +1,415 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <memory>
#include <boost/optional.hpp>
#include "common/assert.h"
#include "common/common_types.h"
namespace ArmDecoder {
// This is a generic ARMv6K decoder using double dispatch.
class ArmInstruction;
class ThumbInstruction;
class Visitor;
/**
* This function identifies an ARM instruction and returns the relevant ArmInstruction.
* Returns boost::none if the instruction was not recognised.
*/
boost::optional<const ArmInstruction&> DecodeArm(u32 instruction);
/**
* This function identifies a Thumb instruction and returns the relevant ThumbInstruction.
* Returns boost::none if the instruction was not recognised.
*/
boost::optional<const ThumbInstruction&> DecodeThumb(u16 instruction);
/// INTERNAL
struct ArmMatcher {
u32 bit_mask;
u32 expected;
bool Match(u32 x) const {
return (x & bit_mask) == expected;
}
virtual void visit(Visitor* v, u32 inst) = 0;
};
/**
* This structure represents a decoder for a specific ARM instruction.
* Calling Visit calls the relevant function on Visitor.
*/
class ArmInstruction final {
public:
ArmInstruction(const char* const name, std::unique_ptr<ArmMatcher> matcher) : name(name), matcher(std::move(matcher)) {}
const char* Name() const {
return name;
}
bool Match(u32 instruction) const {
return matcher->Match(instruction);
}
void Visit(Visitor* v, u32 instruction) const {
matcher->visit(v, instruction);
}
private:
const char* const name;
const std::unique_ptr<ArmMatcher> matcher;
};
/// INTERNAL
struct ThumbMatcher {
u16 bit_mask;
u16 expected;
bool Match(u16 x) const {
return (x & bit_mask) == expected;
}
std::function<void(Visitor*, u16 inst)> visit;
};
/**
* This structure represents a decoder for a specific Thumb instruction.
* Calling Visit calls the relevant function on Visitor.
*/
class ThumbInstruction final {
public:
ThumbInstruction(const char* const name, ThumbMatcher&& matcher) : name(name), matcher(std::move(matcher)) {}
const char* Name() const {
return name;
}
bool Match(u16 instruction) const {
return matcher.Match(instruction);
}
void Visit(Visitor* v, u16 instruction) const {
matcher.visit(v, instruction);
}
private:
const char* const name;
const ThumbMatcher matcher;
};
enum class Cond {
EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV
};
using Imm4 = u32;
using Imm5 = u32;
using Imm8 = u32;
using Imm11 = u32;
using Imm12 = u32;
using Imm24 = u32;
using RegisterList = u16;
enum class Register {
R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15,
SP = R13,
LR = R14,
PC = R15,
INVALID_REG = 99
};
inline Register operator+ (Register arm_reg, int number) {
ASSERT(arm_reg != Register::INVALID_REG);
int value = static_cast<int>(arm_reg) + number;
ASSERT(value >= 0 && value <= 15);
return static_cast<Register>(value);
}
enum class ShiftType {
LSL,
LSR,
ASR,
ROR ///< RRX falls under this too
};
enum class SignExtendRotation {
ROR_0, ///< ROR #0 or omitted
ROR_8, ///< ROR #8
ROR_16, ///< ROR #16
ROR_24 ///< ROR #24
};
class Visitor {
public:
virtual ~Visitor() = default;
// Branch instructions
virtual void B(Cond cond, Imm24 imm24) = 0;
virtual void BL(Cond cond, Imm24 imm24) = 0;
virtual void BLX_imm(bool H, Imm24 imm24) = 0;
virtual void BLX_reg(Cond cond, Register Rm) = 0;
virtual void BX(Cond cond, Register Rm) = 0;
virtual void BXJ(Cond cond, Register Rm) = 0;
// Coprocessor instructions
virtual void CDP() = 0;
virtual void LDC() = 0;
virtual void MCR() = 0;
virtual void MCRR() = 0;
virtual void MRC() = 0;
virtual void MRRC() = 0;
virtual void STC() = 0;
// Data processing instructions
virtual void ADC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void ADC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void ADC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void ADD_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void ADD_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void ADD_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void AND_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void AND_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void AND_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void BIC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void BIC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void BIC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void CMN_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
virtual void CMN_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void CMN_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void CMP_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
virtual void CMP_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void CMP_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void EOR_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void EOR_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void EOR_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void MOV_imm(Cond cond, bool S, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void MOV_reg(Cond cond, bool S, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void MOV_rsr(Cond cond, bool S, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void MVN_imm(Cond cond, bool S, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void MVN_reg(Cond cond, bool S, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void MVN_rsr(Cond cond, bool S, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void ORR_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void ORR_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void ORR_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void RSB_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void RSB_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void RSB_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void RSC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void RSC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void RSC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void SBC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void SBC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void SBC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void SUB_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
virtual void SUB_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void SUB_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void TEQ_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
virtual void TEQ_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void TEQ_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
virtual void TST_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
virtual void TST_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void TST_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
// Exception generation instructions
virtual void BKPT(Cond cond, Imm12 imm12, Imm4 imm4) = 0;
virtual void SVC(Cond cond, Imm24 imm24) = 0;
virtual void UDF() = 0;
// Extension functions
virtual void SXTAB(Cond cond, Register Rn, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void SXTAB16(Cond cond, Register Rn, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void SXTAH(Cond cond, Register Rn, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void SXTB(Cond cond, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void SXTB16(Cond cond, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void SXTH(Cond cond, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void UXTAB(Cond cond, Register Rn, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void UXTAB16(Cond cond, Register Rn, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void UXTAH(Cond cond, Register Rn, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void UXTB(Cond cond, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void UXTB16(Cond cond, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
virtual void UXTH(Cond cond, Register Rd, SignExtendRotation rotate, Register Rm) = 0;
// Hint instructions
virtual void PLD() = 0;
virtual void SEV() = 0;
virtual void WFE() = 0;
virtual void WFI() = 0;
virtual void YIELD() = 0;
// Load/Store instructions
virtual void LDR_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm12 imm12) = 0;
virtual void LDR_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void LDRB_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm12 imm12) = 0;
virtual void LDRB_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void LDRBT() = 0;
virtual void LDRD_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm4 imm8a, Imm4 imm8b) = 0;
virtual void LDRD_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Register Rm) = 0;
virtual void LDRH_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm4 imm8a, Imm4 imm8b) = 0;
virtual void LDRH_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Register Rm) = 0;
virtual void LDRHT() = 0;
virtual void LDRSB_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm4 imm8a, Imm4 imm8b) = 0;
virtual void LDRSB_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Register Rm) = 0;
virtual void LDRSBT() = 0;
virtual void LDRSH_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm4 imm8a, Imm4 imm8b) = 0;
virtual void LDRSH_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Register Rm) = 0;
virtual void LDRSHT() = 0;
virtual void LDRT() = 0;
virtual void STR_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm12 imm12) = 0;
virtual void STR_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void STRB_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm12 imm12) = 0;
virtual void STRB_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
virtual void STRBT() = 0;
virtual void STRD_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm4 imm8a, Imm4 imm8b) = 0;
virtual void STRD_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Register Rm) = 0;
virtual void STRH_imm(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Imm4 imm8a, Imm4 imm8b) = 0;
virtual void STRH_reg(Cond cond, bool P, bool U, bool W, Register Rn, Register Rd, Register Rm) = 0;
virtual void STRHT() = 0;
virtual void STRT() = 0;
// Load/Store multiple instructions
virtual void LDM(Cond cond, bool P, bool U, bool W, Register Rn, RegisterList list) = 0;
virtual void LDM_usr() = 0;
virtual void LDM_eret() = 0;
virtual void STM(Cond cond, bool P, bool U, bool W, Register Rn, RegisterList list) = 0;
virtual void STM_usr() = 0;
// Miscellaneous instructions
virtual void CLZ(Cond cond, Register Rd, Register Rm) = 0;
virtual void NOP() = 0;
virtual void SEL(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
// Unsigned sum of absolute difference functions
virtual void USAD8(Cond cond, Register Rd, Register Rm, Register Rn) = 0;
virtual void USADA8(Cond cond, Register Rd, Register Ra, Register Rm, Register Rn) = 0;
// Packing instructions
virtual void PKHBT(Cond cond, Register Rn, Register Rd, Imm5 imm5, Register Rm) = 0;
virtual void PKHTB(Cond cond, Register Rn, Register Rd, Imm5 imm5, Register Rm) = 0;
// Reversal instructions
virtual void REV(Cond cond, Register Rd, Register Rm) = 0;
virtual void REV16(Cond cond, Register Rd, Register Rm) = 0;
virtual void REVSH(Cond cond, Register Rd, Register Rm) = 0;
// Saturation instructions
virtual void SSAT(Cond cond, Imm5 sat_imm, Register Rd, Imm5 imm5, bool sh, Register Rn) = 0;
virtual void SSAT16(Cond cond, Imm4 sat_imm, Register Rd, Register Rn) = 0;
virtual void USAT(Cond cond, Imm5 sat_imm, Register Rd, Imm5 imm5, bool sh, Register Rn) = 0;
virtual void USAT16(Cond cond, Imm4 sat_imm, Register Rd, Register Rn) = 0;
// Multiply (Normal) instructions
virtual void MLA(Cond cond, bool S, Register Rd, Register Ra, Register Rm, Register Rn) = 0;
virtual void MUL(Cond cond, bool S, Register Rd, Register Rm, Register Rn) = 0;
// Multiply (Long) instructions
virtual void SMLAL(Cond cond, bool S, Register RdHi, Register RdLo, Register Rm, Register Rn) = 0;
virtual void SMULL(Cond cond, bool S, Register RdHi, Register RdLo, Register Rm, Register Rn) = 0;
virtual void UMAAL(Cond cond, Register RdHi, Register RdLo, Register Rm, Register Rn) = 0;
virtual void UMLAL(Cond cond, bool S, Register RdHi, Register RdLo, Register Rm, Register Rn) = 0;
virtual void UMULL(Cond cond, bool S, Register RdHi, Register RdLo, Register Rm, Register Rn) = 0;
// Multiply (Halfword) instructions
virtual void SMLALxy(Cond cond, Register RdHi, Register RdLo, Register Rm, bool M, bool N, Register Rn) = 0;
virtual void SMLAxy(Cond cond, Register Rd, Register Ra, Register Rm, bool M, bool N, Register Rn) = 0;
virtual void SMULxy(Cond cond, Register Rd, Register Rm, bool M, bool N, Register Rn) = 0;
// Multiply (word by halfword) instructions
virtual void SMLAWy(Cond cond, Register Rd, Register Ra, Register Rm, bool M, Register Rn) = 0;
virtual void SMULWy(Cond cond, Register Rd, Register Rm, bool M, Register Rn) = 0;
// Multiply (Most significant word) instructions
virtual void SMMLA(Cond cond, Register Rd, Register Ra, Register Rm, bool R, Register Rn) = 0;
virtual void SMMLS(Cond cond, Register Rd, Register Ra, Register Rm, bool R, Register Rn) = 0;
virtual void SMMUL(Cond cond, Register Rd, Register Rm, bool R, Register Rn) = 0;
// Multiply (Dual) instructions
virtual void SMLAD(Cond cond, Register Rd, Register Ra, Register Rm, bool M, Register Rn) = 0;
virtual void SMLALD(Cond cond, Register RdHi, Register RdLo, Register Rm, bool M, Register Rn) = 0;
virtual void SMLSD(Cond cond, Register Rd, Register Ra, Register Rm, bool M, Register Rn) = 0;
virtual void SMLSLD(Cond cond, Register RdHi, Register RdLo, Register Rm, bool M, Register Rn) = 0;
virtual void SMUAD(Cond cond, Register Rd, Register Rm, bool M, Register Rn) = 0;
virtual void SMUSD(Cond cond, Register Rd, Register Rm, bool M, Register Rn) = 0;
// Parallel Add/Subtract (Modulo arithmetic) instructions
virtual void SADD8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SADD16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SASX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SSAX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SSUB8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SSUB16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UADD8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UADD16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UASX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void USAX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void USUB8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void USUB16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
// Parallel Add/Subtract (Saturating) instructions
virtual void QADD8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QADD16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QASX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QSAX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QSUB8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QSUB16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UQADD8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UQADD16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UQASX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UQSAX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UQSUB8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UQSUB16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
// Parallel Add/Subtract (Halving) instructions
virtual void SHADD8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SHADD16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SHASX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SHSAX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SHSUB8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SHSUB16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UHADD8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UHADD16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UHASX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UHSAX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UHSUB8(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void UHSUB16(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
// Saturated Add/Subtract instructions
virtual void QADD(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QSUB(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QDADD(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void QDSUB(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
// Synchronization Primitive instructions
virtual void CLREX() = 0;
virtual void LDREX(Cond cond, Register Rn, Register Rd) = 0;
virtual void LDREXB(Cond cond, Register Rn, Register Rd) = 0;
virtual void LDREXD(Cond cond, Register Rn, Register Rd) = 0;
virtual void LDREXH(Cond cond, Register Rn, Register Rd) = 0;
virtual void STREX(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void STREXB(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void STREXD(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void STREXH(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SWP(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
virtual void SWPB(Cond cond, Register Rn, Register Rd, Register Rm) = 0;
// Status register access instructions
virtual void CPS() = 0;
virtual void MRS() = 0;
virtual void MSR() = 0;
virtual void RFE() = 0;
virtual void SETEND(bool E) = 0;
virtual void SRS() = 0;
// Thumb specific instructions
virtual void thumb_B(Cond cond, Imm8 imm8) = 0;
virtual void thumb_B(Imm11 imm11) = 0;
virtual void thumb_BLX_prefix(Imm11 imm11) = 0;
virtual void thumb_BLX_suffix(bool X, Imm11 imm11) = 0;
};
} // namespace ArmDecoder

View file

@ -0,0 +1,438 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <boost/optional.hpp>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "core/arm/decoder/decoder.h"
namespace ArmDecoder {
ThumbMatcher MakeMatcher(const char* const str, std::function<void(Visitor* v, u16 instruction)> fn) {
ASSERT(strlen(str) == 16);
u16 mask = 0;
u16 expect = 0;
for (size_t i = 0; i < 16; i++) {
const size_t bit_position = 15 - i;
const u16 bit = 1 << bit_position;
switch (str[i]) {
case '0':
mask |= bit;
break;
case '1':
mask |= bit;
expect |= bit;
break;
default:
break;
}
}
return { mask, expect, fn };
}
using BitUtil::Bit;
using BitUtil::Bits;
static const std::array<ThumbInstruction, 27> thumb_instruction_table = { {
{ "LSL/LSR/ASR", MakeMatcher("000ooxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bits<11, 12>(instruction);
u32 imm5 = Bits<6, 10>(instruction);
Register Rm = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opcode) {
case 0: // LSL <Rd>, <Rm>, #<imm5>
v->MOV_reg(Cond::AL, /*S=*/true, Rd, imm5, ShiftType::LSL, Rm);
break;
case 1: // LSR <Rd>, <Rm>, #<imm5>
v->MOV_reg(Cond::AL, /*S=*/true, Rd, imm5, ShiftType::LSR, Rm);
break;
case 2: // ASR <Rd>, <Rm>, #<imm5>
v->MOV_reg(Cond::AL, /*S=*/true, Rd, imm5, ShiftType::ASR, Rm);
break;
default:
UNREACHABLE();
}
})},
{ "ADD/SUB_reg", MakeMatcher("000110oxxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bit<9>(instruction);
Register Rm = static_cast<Register>(Bits<6, 8>(instruction));
Register Rn = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opcode) {
case 0: // ADD <Rd>, <Rn>, <Rm>
v->ADD_reg(Cond::AL, /*S=*/true, Rn, Rd, 0, ShiftType::LSL, Rm);
break;
case 1: // SUB <Rd>, <Rn>, <Rm>
v->SUB_reg(Cond::AL, /*S=*/true, Rn, Rd, 0, ShiftType::LSL, Rm);
break;
default:
UNREACHABLE();
}
})},
{ "ADD/SUB_imm", MakeMatcher("000111oxxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bit<9>(instruction);
u32 imm3 = Bits<6, 8>(instruction);
Register Rn = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opcode) {
case 0: // ADD <Rd>, <Rn>, #<imm3>
v->ADD_imm(Cond::AL, /*S=*/true, Rn, Rd, 0, imm3);
break;
case 1: // SUB <Rd>, <Rn>, #<imm3>
v->SUB_imm(Cond::AL, /*S=*/true, Rn, Rd, 0, imm3);
break;
default:
UNREACHABLE();
}
})},
{ "add/sub/cmp/mov_imm", MakeMatcher("001ooxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bits<11, 12>(instruction);
Register Rd = static_cast<Register>(Bits<8, 10>(instruction));
u32 imm8 = Bits<0, 7>(instruction);
switch (opcode) {
case 0: // MOV Rd, #imm8
v->MOV_imm(Cond::AL, /*S=*/true, Rd, 0, imm8);
break;
case 1: // CMP Rn, #imm8
v->CMP_imm(Cond::AL, Rd, 0, imm8);
break;
case 2: // ADD Rd, #imm8
v->ADD_imm(Cond::AL, /*S=*/true, Rd, Rd, 0, imm8);
break;
case 3: // SUB Rd, #imm8
v->SUB_imm(Cond::AL, /*S=*/true, Rd, Rd, 0, imm8);
break;
default:
UNREACHABLE();
}
})},
{ "data processing reg", MakeMatcher("010000ooooxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bits<6, 9>(instruction);
Register Ra = static_cast<Register>(Bits<3, 5>(instruction));
Register Rb = static_cast<Register>(Bits<0, 2>(instruction));
switch (opcode) {
case 0: // AND Rd, Rm
v->AND_reg(Cond::AL, /*S=*/true, Rb, Rb, 0, ShiftType::LSL, Ra);
break;
case 1: // EOR Rd, Rm
v->EOR_reg(Cond::AL, /*S=*/true, Rb, Rb, 0, ShiftType::LSL, Ra);
break;
case 2: // LSL Rd, Rs
v->MOV_rsr(Cond::AL, /*S=*/true, Rb, Ra, ShiftType::LSL, Rb);
break;
case 3: // LSR Rd, Rs
v->MOV_rsr(Cond::AL, /*S=*/true, Rb, Ra, ShiftType::LSR, Rb);
break;
case 4: // ASR Rd, Rs
v->MOV_rsr(Cond::AL, /*S=*/true, Rb, Ra, ShiftType::ASR, Rb);
break;
case 5: // ADC Rd, Rm
v->ADC_reg(Cond::AL, /*S=*/true, Rb, Rb, 0, ShiftType::LSL, Ra);
break;
case 6: // SBC Rd, Rm
v->SBC_reg(Cond::AL, /*S=*/true, Rb, Rb, 0, ShiftType::LSL, Ra);
break;
case 7: // ROR Rd, Rs
v->MOV_rsr(Cond::AL, /*S=*/true, Rb, Ra, ShiftType::ROR, Rb);
break;
case 8: // TST Rm, Rn
v->TST_reg(Cond::AL, Rb, 0, ShiftType::LSL, Ra);
break;
case 9: // NEG Rd, Rm
v->RSB_imm(Cond::AL, /*S=*/true, Ra, Rb, 0, 0);
break;
case 10: // CMP Rm, Rn
v->CMP_reg(Cond::AL, Rb, 0, ShiftType::LSL, Ra);
break;
case 11: // CMN Rm, Rn
v->CMN_reg(Cond::AL, Rb, 0, ShiftType::LSL, Ra);
break;
case 12: // ORR Rd, Rm
v->ORR_reg(Cond::AL, /*S=*/true, Rb, Rb, 0, ShiftType::LSL, Ra);
break;
case 13: // MUL Rd, Rm
v->MUL(Cond::AL, /*S=*/true, Rb, Rb, Ra);
break;
case 14: // BIC Rm, Rd
v->BIC_reg(Cond::AL, /*S=*/true, Rb, Rb, 0, ShiftType::LSL, Ra);
break;
case 15: // MVN Rd, Rm
v->MVN_reg(Cond::AL, /*S=*/true, Rb, 0, ShiftType::LSL, Ra);
break;
default:
UNREACHABLE();
}
})},
{ "special data processing", MakeMatcher("010001ooxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bits<8, 9>(instruction);
Register Rm = static_cast<Register>(Bits<3, 6>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction) | (Bit<7>(instruction) << 3));
switch (opcode) {
case 0: // ADD Rd, Rm
v->ADD_reg(Cond::AL, /*S=*/false, Rd, Rd, 0, ShiftType::LSL, Rm);
break;
case 1: // CMP Rm, Rn
v->CMP_reg(Cond::AL, Rd, 0, ShiftType::LSL, Rm);
break;
case 2: // MOV Rd, Rm
v->MOV_reg(Cond::AL, /*S=*/false, Rd, 0, ShiftType::LSL, Rm);
break;
default:
UNREACHABLE();
}
})},
{ "BLX/BX", MakeMatcher("01000111xxxxx000", [](Visitor* v, u16 instruction) {
bool L = Bit<7>(instruction);
Register Rm = static_cast<Register>(Bits<3, 6>(instruction));
if (!L) { // BX Rm
v->BX(Cond::AL, Rm);
} else { // BLX Rm
v->BLX_reg(Cond::AL, Rm);
}
})},
{ "load from literal pool", MakeMatcher("01001xxxxxxxxxxx", [](Visitor* v, u16 instruction) {
// LDR Rd, [PC, #]
Register Rd = static_cast<Register>(Bits<8, 10>(instruction));
u32 imm8 = Bits<0, 7>(instruction);
v->LDR_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Register::PC, Rd, imm8 * 4);
})},
{ "load/store reg offset", MakeMatcher("0101oooxxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opcode = Bits<9, 11>(instruction);
Register Rm = static_cast<Register>(Bits<6, 8>(instruction));
Register Rn = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opcode) {
case 0: // STR Rd, [Rn, Rm]
v->STR_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, 0, ShiftType::LSL, Rm);
break;
case 1: // STRH Rd, [Rn, Rm]
v->STRH_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, Rm);
break;
case 2: // STRB Rd, [Rn, Rm]
v->STRB_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, 0, ShiftType::LSL, Rm);
break;
case 3: // LDRSB Rd, [Rn, Rm]
v->LDRSB_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, Rm);
break;
case 4: // LDR Rd, [Rn, Rm]
v->LDR_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, 0, ShiftType::LSL, Rm);
break;
case 5: // LDRH Rd, [Rn, Rm]
v->LDRH_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, Rm);
break;
case 6: // LDRB Rd, [Rn, Rm]
v->LDRB_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, 0, ShiftType::LSL, Rm);
break;
case 7: // LDRSH Rd, [Rn, Rm]
v->LDRSH_reg(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, Rm);
break;
default:
UNREACHABLE();
}
})},
{ "STR(B)/LDR(B)_imm", MakeMatcher("011xxxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
u32 opc = Bits<11, 12>(instruction);
u32 offset = Bits<6, 10>(instruction);
Register Rn = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opc) {
case 0: // STR Rd, [Rn, #offset]
v->STR_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, offset * 4);
break;
case 1: // LDR Rd, [Rn, #offset]
v->LDR_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, offset * 4);
break;
case 2: // STRB Rd, [Rn, #offset]
v->STRB_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, offset);
break;
case 3: // LDRB Rd, [Rn, #offset]
v->LDRB_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, offset);
break;
default:
UNREACHABLE();
}
})},
{ "STRH/LDRH_imm", MakeMatcher("1000xxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
bool L = Bit<11>(instruction);
u32 offset = Bits<6, 10>(instruction);
Register Rn = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
if (!L) { // STRH Rd, [Rn, #offset]
v->STRH_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, (offset * 2) >> 4, (offset * 2) & 0xF);
} else { // LDRH Rd, [Rn, #offset]
v->LDRH_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Rn, Rd, (offset * 2) >> 4, (offset * 2) & 0xF);
}
})},
{ "load/store stack", MakeMatcher("1001xxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
bool L = Bit<11>(instruction);
Register Rd = static_cast<Register>(Bits<8, 10>(instruction));
u32 offset = Bits<0, 7>(instruction);
if (!L) { // STR Rd, [SP, #offset]
v->STR_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Register::SP, Rd, offset * 4);
} else { // LDR Rd, [SP, #offset]
v->LDR_imm(Cond::AL, /*P=*/1, /*U=*/1, /*W=*/0, Register::SP, Rd, offset * 4);
}
})},
{ "add to sp/pc", MakeMatcher("1010oxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
// ADD Rd, PC/SP, #imm8
Register Rn = Bit<11>(instruction) ? Register::SP : Register::PC;
Register Rd = static_cast<Register>(Bits<8, 10>(instruction));
u32 imm8 = Bits<0, 7>(instruction);
v->ADD_imm(Cond::AL, /*S=*/false, Rn, Rd, 0xF, imm8);
})},
{ "adjust stack ptr", MakeMatcher("10110000oxxxxxxx", [](Visitor* v, u16 instruction) {
// SUB SP, SP, #<imm7*4>
u32 opc = Bit<7>(instruction);
u32 imm7 = Bits<0, 6>(instruction);
switch (opc) {
case 0:
v->ADD_imm(Cond::AL, /*S=*/false, Register::SP, Register::SP, 0xF, imm7);
break;
case 1:
v->SUB_imm(Cond::AL, /*S=*/false, Register::SP, Register::SP, 0xF, imm7);
break;
default:
UNREACHABLE();
}
})},
{ "sign/zero extend", MakeMatcher("10110010ooxxxxxx", [](Visitor* v, u16 instruction) {
u32 opc = Bits<6, 7>(instruction);
Register Rm = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opc) {
case 0: // SXTH Rd, Rm
v->SXTH(Cond::AL, Rd, SignExtendRotation::ROR_0, Rm);
break;
case 1: // SXTB Rd, Rm
v->SXTB(Cond::AL, Rd, SignExtendRotation::ROR_0, Rm);
break;
case 2: // UXTH Rd, Rm
v->UXTH(Cond::AL, Rd, SignExtendRotation::ROR_0, Rm);
break;
case 3: // UXTB Rd, Rm
v->UXTB(Cond::AL, Rd, SignExtendRotation::ROR_0, Rm);
break;
default:
UNREACHABLE();
}
})},
{ "PUSH/POP_reglist", MakeMatcher("1011x10xxxxxxxxx", [](Visitor* v, u16 instruction) {
bool L = Bit<11>(instruction);
u32 R = Bit<8>(instruction);
u32 reglist = Bits<0, 7>(instruction);
if (!L) { // PUSH {reglist, <R>=LR}
reglist |= R << 14;
// Equivalent to STMDB SP!, {reglist}
v->STM(Cond::AL, /*P=*/1, /*U=*/0, /*W=*/1, Register::SP, reglist);
} else { // POP {reglist, <R>=PC}
reglist |= R << 15;
// Equivalent to LDMIA SP!, {reglist}
v->LDM(Cond::AL, /*P=*/0, /*U=*/1, /*W=*/1, Register::SP, reglist);
}
})},
{ "SETEND", MakeMatcher("101101100101x000", [](Visitor* v, u16 instruction) {
u16 E = Bit<3>(instruction);
v->SETEND(E);
})},
{ "change processor state", MakeMatcher("10110110011x0xxx", [](Visitor* v, u16 instruction) {
u16 imod = Bit<4>(instruction);
u16 A = Bit<2>(instruction);
u16 I = Bit<1>(instruction);
u16 F = Bit<0>(instruction);
v->CPS();
})},
{ "reverse bytes", MakeMatcher("10111010ooxxxxxx", [](Visitor* v, u16 instruction) {
u32 opc = Bits<6, 7>(instruction);
Register Rn = static_cast<Register>(Bits<3, 5>(instruction));
Register Rd = static_cast<Register>(Bits<0, 2>(instruction));
switch (opc) {
case 0: // REV Rd, Rn
v->REV(Cond::AL, Rd, Rn);
break;
case 1: // REV16 Rd, Rn
v->REV16(Cond::AL, Rd, Rn);
break;
case 2: // undefined
v->UDF();
break;
case 3: // REVSH Rd, Rn
v->REVSH(Cond::AL, Rd, Rn);
break;
default:
UNREACHABLE();
}
})},
{ "BKPT", MakeMatcher("10111110xxxxxxxx", [](Visitor* v, u16 instruction) {
// BKPT #imm8
Imm8 imm8 = Bits<0, 7>(instruction);
v->BKPT(Cond::AL, imm8 >> 4, imm8 & 0xF);
})},
{ "STMIA/LDMIA", MakeMatcher("1100xxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
bool L = Bit<11>(instruction);
Register Rn = static_cast<Register>(Bits<8, 10>(instruction));
u32 reglist = Bits<0, 7>(instruction);
if (!L) { // STMIA Rn!, { reglist }
v->STM(Cond::AL, /*P=*/0, /*U=*/1, /*W=*/1, Rn, reglist);
} else { // LDMIA Rn!, { reglist }
RegisterList Rn_bit = 1 << static_cast<unsigned>(Rn);
bool w = (reglist & Rn_bit) == 0;
v->LDM(Cond::AL, /*P=*/0, /*U=*/1, /*W=*/w, Rn, reglist);
}
})},
{ "B<cond>", MakeMatcher("1101xxxxxxxxxxxx", [](Visitor* v, u16 instruction) {
// B<cond> <PC + #offset*2>
Cond cond = static_cast<Cond>(Bits<8, 11>(instruction));
s32 offset = Bits<0, 7>(instruction);
ASSERT_MSG(cond != Cond::AL, "UNDEFINED");
v->thumb_B(cond, offset);
})},
{ "SWI", MakeMatcher("11011111xxxxxxxx", [](Visitor* v, u16 instruction) {
// SWI #imm8
Imm8 imm8 = Bits<0, 7>(instruction);
v->SVC(Cond::AL, imm8);
})},
{ "B", MakeMatcher("11100xxxxxxxxxxx", [](Visitor* v, u16 instruction) {
// B <PC + #offset*2>
Imm11 imm11 = Bits<0, 10>(instruction);
v->thumb_B(imm11);
})},
{ "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u16 instruction) {
Imm11 imm11 = Bits<0, 10>(instruction);
v->thumb_BLX_suffix(/*X=*/true, imm11);
})},
{ "BL/BLX (prefix)", MakeMatcher("11110xxxxxxxxxxx", [](Visitor* v, u16 instruction) {
Imm11 imm11 = Bits<0, 10>(instruction);
v->thumb_BLX_prefix(imm11);
})},
{ "BL (suffix)", MakeMatcher("11111xxxxxxxxxxx", [](Visitor* v, u16 instruction) {
Imm11 imm11 = Bits<0, 10>(instruction);
v->thumb_BLX_suffix(/*X=*/false, imm11);
})}
}};
boost::optional<const ThumbInstruction&> DecodeThumb(u16 i) {
// NOTE: The reverse search direction is important. Searching forwards would result in incorrect behavior.
// This is because the entries in thumb_instruction_table have more specific matches coming after less specific ones.
// Example:
// 000ooxxxxxxxxxxx comes before 000110oxxxxxxxxx
// with a forward search direction notice how the first one will always be matched and the latter never will be.
auto iterator = std::find_if(thumb_instruction_table.crbegin(), thumb_instruction_table.crend(),
[i](const auto& instruction) { return instruction.Match(i); });
return (iterator != thumb_instruction_table.crend()) ? boost::make_optional<const ThumbInstruction&>(*iterator) : boost::none;
}
};

View file

@ -3,6 +3,8 @@
#include <string>
#include <unordered_set>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/string_util.h"
@ -252,8 +254,7 @@ std::string ARM_Disasm::Disassemble(u32 addr, u32 insn)
case OP_BKPT:
return DisassembleBKPT(insn);
case OP_BLX:
// not supported yet
break;
return DisassembleBLX(insn);
case OP_BX:
return DisassembleBX(insn);
case OP_CDP:
@ -513,6 +514,21 @@ std::string ARM_Disasm::DisassembleBX(u32 insn)
return Common::StringFromFormat("bx%s\tr%d", cond_to_str(cond), rn);
}
std::string ARM_Disasm::DisassembleBLX(u32 insn)
{
if ((insn & 0xFE000000) == 0xFA000000) {
u32 imm24 = insn & 0xFFFFFF;
u32 H = (insn >> 24) & 1;
s32 offset = BitUtil::SignExtend<26>((imm24 << 2) + (H << 1)) + 8;
return Common::StringFromFormat("blx\t#+%d", offset);
} else if ((insn & 0x0FFFFFF0) == 0x012FFF30) {
u8 cond = (insn >> 28) & 0xf;
u8 rn = insn & 0xf;
return Common::StringFromFormat("blx%s\tr%d", cond_to_str(cond), rn);
}
UNREACHABLE();
}
std::string ARM_Disasm::DisassembleBKPT(u32 insn)
{
u8 cond = (insn >> 28) & 0xf;
@ -1062,6 +1078,10 @@ Opcode ARM_Disasm::Decode00(u32 insn) {
// Bx instruction
return OP_BX;
}
if ((insn & 0x0ffffff0) == 0x012fff30) {
// Blx instruction
return OP_BLX;
}
if ((insn & 0x0ff000f0) == 0x01600010) {
// Clz instruction
return OP_CLZ;

View file

@ -213,6 +213,7 @@ class ARM_Disasm {
static std::string DisassembleALU(Opcode opcode, u32 insn);
static std::string DisassembleBranch(u32 addr, Opcode opcode, u32 insn);
static std::string DisassembleBX(u32 insn);
static std::string DisassembleBLX(u32 insn);
static std::string DisassembleBKPT(u32 insn);
static std::string DisassembleCLZ(u32 insn);
static std::string DisassembleMediaMulDiv(Opcode opcode, u32 insn);

View file

@ -18,6 +18,7 @@
ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
state = std::make_unique<ARMul_State>(initial_mode);
ClearCache();
}
ARM_DynCom::~ARM_DynCom() {
@ -125,3 +126,8 @@ void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
void ARM_DynCom::PrepareReschedule() {
state->NumInstrsToExecute = 0;
}
void ARM_DynCom::ClearCache() {
state->instruction_cache.clear();
InterpreterClearCache();
}

View file

@ -43,6 +43,8 @@ public:
void PrepareReschedule() override;
void ExecuteInstructions(int num_instructions) override;
void ClearCache() override;
private:
std::unique_ptr<ARMul_State> state;
};

View file

@ -1144,6 +1144,10 @@ static inline void *AllocBuffer(unsigned int size) {
return (void *)&inst_buf[start];
}
void InterpreterClearCache() {
top = 0;
}
static shtop_fp_t get_shtop(unsigned int inst) {
if (BIT(inst, 25)) {
return DPO(Immediate);
@ -3884,6 +3888,9 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
LOAD_NZCVT;
DISPATCH:
{
if (num_instrs >= cpu->NumInstrsToExecute)
goto END;
if (!cpu->NirqSig) {
if (!(cpu->Cpsr & 0x80)) {
goto END;

View file

@ -7,3 +7,4 @@
struct ARMul_State;
unsigned InterpreterMainLoop(ARMul_State* state);
void InterpreterClearCache();

View file

@ -0,0 +1,56 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/arm/decoder/decoder.h"
#include "core/arm/skyeye_common/armstate.h"
namespace JitX64 {
using ArmReg = ArmDecoder::Register;
using ArmRegList = ArmDecoder::RegisterList;
using ArmImm4 = ArmDecoder::Imm4;
using ArmImm5 = ArmDecoder::Imm5;
using ArmImm8 = ArmDecoder::Imm8;
using ArmImm11 = ArmDecoder::Imm11;
using ArmImm12 = ArmDecoder::Imm12;
using ArmImm24 = ArmDecoder::Imm24;
using Cond = ArmDecoder::Cond;
using ShiftType = ArmDecoder::ShiftType;
using SignExtendRotation = ArmDecoder::SignExtendRotation;
struct JitState final {
JitState() : cpu_state(PrivilegeMode::USER32MODE) {}
void Reset() {
cpu_state.Reset();
}
ARMul_State cpu_state;
/// This value should always be appropriately aligned for a CALL instruction to be made.
u64 save_host_RSP = 0;
/// Jitted code will JMP to this value when done. Should contain the an address which returns to the dispatcher.
u64 return_RIP = 0;
/// If this value becomes <= 0, jitted code will jump to return_RIP.
s32 cycles_remaining = 0;
};
constexpr bool IsValidArmReg(ArmReg arm_reg) {
return static_cast<unsigned>(arm_reg) <= 15;
}
inline bool IsEvenArmReg(ArmReg arm_reg) {
ASSERT(IsValidArmReg(arm_reg));
return static_cast<unsigned>(arm_reg) % 2 == 0;
}
/// Turns a ArmReg into an ArmRegList bitmap.
constexpr ArmRegList MakeRegList(ArmReg arm_reg) {
return 1 << static_cast<unsigned>(arm_reg);
}
}

View file

@ -0,0 +1,146 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
using namespace Gen;
void JitX64::CondManager::Init(JitX64* jit_) {
jit = jit_;
current_cond = Cond::AL;
flags_dirty = false;
current_cond_fixup = {};
}
void JitX64::CondManager::CompileCond(const Cond new_cond) {
if (current_cond == new_cond && !flags_dirty)
return;
if (current_cond != Cond::AL && current_cond != Cond::NV) {
jit->reg_alloc.FlushEverything();
jit->reg_alloc.AssertNoLocked();
ASSERT(current_cond_fixup.ptr);
jit->code->SetJumpTarget(current_cond_fixup);
current_cond_fixup.ptr = nullptr;
}
if (new_cond != Cond::AL && new_cond != Cond::NV) {
CCFlags cc;
switch (new_cond) {
case Cond::EQ: //z
jit->code->CMP(8, jit->MJitStateZFlag(), Imm8(0));
cc = CC_E;
break;
case Cond::NE: //!z
jit->code->CMP(8, jit->MJitStateZFlag(), Imm8(0));
cc = CC_NE;
break;
case Cond::CS: //c
jit->code->CMP(8, jit->MJitStateCFlag(), Imm8(0));
cc = CC_E;
break;
case Cond::CC: //!c
jit->code->CMP(8, jit->MJitStateCFlag(), Imm8(0));
cc = CC_NE;
break;
case Cond::MI: //n
jit->code->CMP(8, jit->MJitStateNFlag(), Imm8(0));
cc = CC_E;
break;
case Cond::PL: //!n
jit->code->CMP(8, jit->MJitStateNFlag(), Imm8(0));
cc = CC_NE;
break;
case Cond::VS: //v
jit->code->CMP(8, jit->MJitStateVFlag(), Imm8(0));
cc = CC_E;
break;
case Cond::VC: //!v
jit->code->CMP(8, jit->MJitStateVFlag(), Imm8(0));
cc = CC_NE;
break;
case Cond::HI: { //c & !z
const X64Reg tmp = jit->reg_alloc.AllocTemp();
jit->code->MOVZX(64, 8, tmp, jit->MJitStateZFlag());
jit->code->CMP(8, jit->MJitStateCFlag(), R(tmp));
cc = CC_BE;
jit->reg_alloc.UnlockTemp(tmp);
break;
}
case Cond::LS: { //!c | z
const X64Reg tmp = jit->reg_alloc.AllocTemp();
jit->code->MOVZX(64, 8, tmp, jit->MJitStateZFlag());
jit->code->CMP(8, jit->MJitStateCFlag(), R(tmp));
cc = CC_A;
jit->reg_alloc.UnlockTemp(tmp);
break;
}
case Cond::GE: { // n == v
const X64Reg tmp = jit->reg_alloc.AllocTemp();
jit->code->MOVZX(64, 8, tmp, jit->MJitStateVFlag());
jit->code->CMP(8, jit->MJitStateNFlag(), R(tmp));
cc = CC_NE;
jit->reg_alloc.UnlockTemp(tmp);
break;
}
case Cond::LT: { // n != v
const X64Reg tmp = jit->reg_alloc.AllocTemp();
jit->code->MOVZX(64, 8, tmp, jit->MJitStateVFlag());
jit->code->CMP(8, jit->MJitStateNFlag(), R(tmp));
cc = CC_E;
jit->reg_alloc.UnlockTemp(tmp);
break;
}
case Cond::GT: { // !z & (n == v)
const X64Reg tmp = jit->reg_alloc.AllocTemp();
jit->code->MOVZX(64, 8, tmp, jit->MJitStateNFlag());
jit->code->XOR(8, R(tmp), jit->MJitStateVFlag());
jit->code->OR(8, R(tmp), jit->MJitStateZFlag());
jit->code->TEST(8, R(tmp), R(tmp));
cc = CC_NZ;
jit->reg_alloc.UnlockTemp(tmp);
break;
}
case Cond::LE: { // z | (n != v)
X64Reg tmp = jit->reg_alloc.AllocTemp();
jit->code->MOVZX(64, 8, tmp, jit->MJitStateNFlag());
jit->code->XOR(8, R(tmp), jit->MJitStateVFlag());
jit->code->OR(8, R(tmp), jit->MJitStateZFlag());
jit->code->TEST(8, R(tmp), R(tmp));
cc = CC_Z;
jit->reg_alloc.UnlockTemp(tmp);
break;
}
default:
UNREACHABLE();
break;
}
jit->reg_alloc.FlushEverything();
jit->reg_alloc.AssertNoLocked();
this->current_cond_fixup = jit->code->J_CC(cc, true);
}
current_cond = new_cond;
flags_dirty = false;
}
void JitX64::CondManager::Always() {
CompileCond(Cond::AL);
}
void JitX64::CondManager::FlagsDirty() {
flags_dirty = true;
}
Cond JitX64::CondManager::CurrentCond() const {
return current_cond;
}
}

View file

@ -0,0 +1,120 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/bit_util.h"
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
using namespace Gen;
void JitX64::B(Cond cond, ArmImm24 imm24) {
cond_manager.CompileCond(cond);
const u32 new_pc = GetReg15Value() + BitUtil::SignExtend<26>(imm24 << 2);
reg_alloc.FlushEverything();
current.arm_pc += GetInstSize();
CompileUpdateCycles(false);
CompileJumpToBB(new_pc);
if (cond == Cond::AL) {
stop_compilation = true;
}
}
void JitX64::BL(Cond cond, ArmImm24 imm24) {
cond_manager.CompileCond(cond);
const u32 new_pc = GetReg15Value() + BitUtil::SignExtend<26>(imm24 << 2);
ASSERT(!current.TFlag);
const u32 link_pc = current.arm_pc + GetInstSize();
Gen::OpArg LR = reg_alloc.LockArmForWrite(ArmReg::LR);
code->MOV(32, LR, Imm32(link_pc));
reg_alloc.UnlockArm(ArmReg::LR);
reg_alloc.FlushEverything();
current.arm_pc += GetInstSize();
CompileUpdateCycles(false);
CompileJumpToBB(new_pc);
if (cond == Cond::AL) {
stop_compilation = true;
}
}
void JitX64::BLX_imm(bool H, ArmImm24 imm24) {
cond_manager.Always();
const u32 new_pc = GetReg15Value() + BitUtil::SignExtend<26>(imm24 << 2) + (H ? 2 : 0);
ASSERT(!current.TFlag);
const u32 link_pc = current.arm_pc + GetInstSize();
Gen::OpArg LR = reg_alloc.LockArmForWrite(ArmReg::LR);
code->MOV(32, LR, Imm32(link_pc));
reg_alloc.UnlockArm(ArmReg::LR);
current.TFlag = true;
code->MOV(32, MJitStateTFlag(), Imm32(1));
reg_alloc.FlushEverything();
current.arm_pc += GetInstSize();
CompileUpdateCycles(false);
CompileJumpToBB(new_pc);
stop_compilation = true;
}
void JitX64::BLX_reg(Cond cond, ArmReg Rm_index) {
cond_manager.CompileCond(cond);
ASSERT_MSG(Rm_index != ArmReg::PC, "UNPREDICTABLE");
const u32 link_pc = current.arm_pc + GetInstSize() + (current.TFlag ? 1 : 0);
Gen::OpArg LR = reg_alloc.LockArmForWrite(ArmReg::LR);
code->MOV(32, LR, Imm32(link_pc));
reg_alloc.UnlockArm(ArmReg::LR);
Gen::X64Reg Rm = reg_alloc.BindArmForRead(Rm_index);
code->MOV(32, MJitStateArmPC(), R(Rm));
code->AND(32, MJitStateArmPC(), Imm32(0xFFFFFFFE));
code->BT(32, R(Rm), Imm8(0));
code->SETcc(CC_C, MJitStateTFlag()); // NOTE: current.TFlag is now inaccurate
reg_alloc.UnlockArm(Rm_index);
current.arm_pc += GetInstSize();
CompileReturnToDispatch();
stop_compilation = true;
}
void JitX64::BX(Cond cond, ArmReg Rm_index) {
cond_manager.CompileCond(cond);
if (Rm_index == ArmReg::PC) {
code->MOV(32, MJitStateArmPC(), Imm32(GetReg15Value()));
code->MOV(32, MJitStateTFlag(), Imm32(0));
} else {
Gen::X64Reg Rm = reg_alloc.BindArmForRead(Rm_index);
code->MOV(32, MJitStateArmPC(), R(Rm));
code->AND(32, MJitStateArmPC(), Imm32(0xFFFFFFFE));
code->BT(32, R(Rm), Imm8(0));
code->SETcc(CC_C, MJitStateTFlag()); // NOTE: current.TFlag is now inaccurate
reg_alloc.UnlockArm(Rm_index);
}
current.arm_pc += GetInstSize();
CompileReturnToDispatch();
stop_compilation = true;
}
void JitX64::BXJ(Cond cond, ArmReg Rm) {
// BXJ behaves exactly as BX since Jazelle is not supported
BX(cond, Rm);
}
} // namespace JitX64

View file

@ -0,0 +1,17 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::CDP() { CompileInterpretInstruction(); }
void JitX64::LDC() { CompileInterpretInstruction(); }
void JitX64::MCR() { CompileInterpretInstruction(); }
void JitX64::MCRR() { CompileInterpretInstruction(); }
void JitX64::MRC() { CompileInterpretInstruction(); }
void JitX64::MRRC() { CompileInterpretInstruction(); }
void JitX64::STC() { CompileInterpretInstruction(); }
} // namespace JitX64

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/x64/abi.h"
#include "core/arm/jit_x64/jit_x64.h"
#include "core/hle/svc.h"
namespace JitX64 {
using namespace Gen;
static void Breakpoint(u32 imm) {
LOG_DEBUG(Core_ARM11, "Breakpoint instruction hit. Immediate: 0x%08X", imm);
}
void JitX64::BKPT(Cond cond, ArmImm12 imm12, ArmImm4 imm4) {
cond_manager.CompileCond(cond);
ASSERT_MSG(false, "BKPT instruction @ pc=0x%08X", current.arm_pc);
reg_alloc.FlushX64(ABI_PARAM1);
reg_alloc.LockX64(ABI_PARAM1);
code->MOV(32, R(ABI_PARAM1), Imm32((imm12 << 4) | imm4));
CompileCallHost(reinterpret_cast<const void*>(&Breakpoint));
reg_alloc.UnlockX64(ABI_PARAM1);
current.arm_pc += GetInstSize();
}
static void ServiceCall(u64 imm) {
SVC::CallSVC(imm & 0xFFFF);
}
void JitX64::SVC(Cond cond, ArmImm24 imm24) {
cond_manager.CompileCond(cond);
// Flush and write out absolutely everything.
code->MOV(32, MJitStateArmPC(), Imm32(current.arm_pc));
reg_alloc.FlushEverything();
reg_alloc.LockX64(ABI_PARAM1);
code->MOV(64, R(ABI_PARAM1), Imm32(imm24));
CompileCallHost(reinterpret_cast<const void*>(&ServiceCall));
reg_alloc.UnlockX64(ABI_PARAM1);
// Some service calls require a task switch, so go back to the dispatcher to check.
current.arm_pc += GetInstSize();
code->ADD(32, MJitStateArmPC(), Imm32(GetInstSize()));
CompileReturnToDispatch();
stop_compilation = true;
}
void JitX64::UDF() {
cond_manager.Always();
ASSERT_MSG(false, "UDF instruction @ pc=0x%08X", current.arm_pc);
current.arm_pc += GetInstSize();
}
} // namespace JitX64

View file

@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::SXTAB(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SXTAB16(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SXTAH(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SXTB(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SXTB16(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SXTH(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UXTAB(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UXTAB16(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UXTAH(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UXTB(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UXTB16(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UXTH(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,73 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/swap.h"
#include "core/arm/jit_x64/instructions/helper/load_store.h"
#include "core/memory.h"
namespace JitX64 {
u64 Load64LE(u32 addr) {
// TODO: Improve this.
return Memory::Read32(addr) | (static_cast<u64>(Memory::Read32(addr + 4)) << 32);
}
u64 Load64BE(u32 addr) {
// TODO: Improve this.
return Common::swap32(Memory::Read32(addr)) | (static_cast<u64>(Common::swap32(Memory::Read32(addr + 4))) << 32);
}
void Store64LE(u32 addr, u32 v1, u32 v2) {
Memory::Write32(addr, v1);
Memory::Write32(addr + 4, v2);
}
void Store64BE(u32 addr, u32 v1, u32 v2) {
Memory::Write32(addr, Common::swap32(v2));
Memory::Write32(addr + 4, Common::swap32(v1));
}
u32 Load32LE(u32 addr) {
return Memory::Read32(addr);
}
u32 Load32BE(u32 addr) {
return Common::swap32(Memory::Read32(addr));
}
void Store32LE(u32 addr, u32 value) {
Memory::Write32(addr, value);
}
void Store32BE(u32 addr, u32 value) {
Memory::Write32(addr, Common::swap32(value));
}
u16 Load16LE(u32 addr) {
return Memory::Read16(addr);
}
u16 Load16BE(u32 addr) {
return Common::swap16(Memory::Read16(addr));
}
void Store16LE(u32 addr, u16 value) {
Memory::Write16(addr, value);
}
void Store16BE(u32 addr, u16 value) {
Memory::Write16(addr, Common::swap16(value));
}
u32 Load8(u32 addr) {
return Memory::Read8(addr);
}
void Store8(u32 addr, u8 value) {
Memory::Write8(addr, value);
}
}

View file

@ -0,0 +1,33 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace JitX64 {
// TODO: Set up an appropriately mapped region of memory to use instead of compiling CALL instructions.
constexpr u32 RESERVATION_GRANULE_MASK = 0xFFFFFFF8;
u64 Load64LE(u32 addr);
u64 Load64BE(u32 addr);
void Store64LE(u32 addr, u32 v1, u32 v2);
void Store64BE(u32 addr, u32 v1, u32 v2);
u32 Load32LE(u32 addr);
u32 Load32BE(u32 addr);
void Store32LE(u32 addr, u32 value);
void Store32BE(u32 addr, u32 value);
u16 Load16LE(u32 addr);
u16 Load16BE(u32 addr);
void Store16LE(u32 addr, u16 value);
void Store16BE(u32 addr, u16 value);
u32 Load8(u32 addr);
void Store8(u32 addr, u8 value);
} // namespace JitX64

View file

@ -0,0 +1,15 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::SEV() { CompileInterpretInstruction(); }
void JitX64::PLD() { CompileInterpretInstruction(); }
void JitX64::WFI() { CompileInterpretInstruction(); }
void JitX64::WFE() { CompileInterpretInstruction(); }
void JitX64::YIELD() { CompileInterpretInstruction(); }
} // namespace JitX64

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::CLZ(Cond cond, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::NOP() { CompileInterpretInstruction(); }
void JitX64::SEL(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,42 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
// Multiply (Normal) instructions
void JitX64::MLA(Cond cond, bool S, ArmReg Rd, ArmReg Ra, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::MUL(Cond cond, bool S, ArmReg Rd, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
// Multiply (Long) instructions
void JitX64::SMLAL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMULL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::UMAAL(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::UMLAL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::UMULL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
// Multiply (Halfword) instructions
void JitX64::SMLALxy(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, bool M, bool N, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMLAxy(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, bool N, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMULxy(Cond cond, ArmReg Rd, ArmReg Rm, bool M, bool N, ArmReg Rn) { CompileInterpretInstruction(); }
// Multiply (word by halfword) instructions
void JitX64::SMLAWy(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMULWy(Cond cond, ArmReg Rd, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
// Multiply (Most significant word) instructions
void JitX64::SMMLA(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool R, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMMLS(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool R, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMMUL(Cond cond, ArmReg Rd, ArmReg Rm, bool R, ArmReg Rn) { CompileInterpretInstruction(); }
// Multiply (Dual) instructions
void JitX64::SMLAD(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMLALD(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMLSD(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMLSLD(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMUAD(Cond cond, ArmReg Rd, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SMUSD(Cond cond, ArmReg Rd, ArmReg Rm, bool M, ArmReg Rn) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,12 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::PKHBT(Cond cond, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::PKHTB(Cond cond, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::SHADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SHADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SHASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SHSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SHSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SHSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UHADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UHADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UHASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UHSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UHSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UHSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::SADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::SSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::USAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::USUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::USUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::QADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UQADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UQADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UQASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UQSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UQSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::UQSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,14 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::QADD(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QSUB(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QDADD(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::QDSUB(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,13 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::REV(Cond cond, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::REV16(Cond cond, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::REVSH(Cond cond, ArmReg Rd, ArmReg Rm) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,14 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::SSAT(Cond cond, ArmImm5 sat_imm, ArmReg Rd, ArmImm5 imm5, bool sh, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::SSAT16(Cond cond, ArmImm4 sat_imm, ArmReg Rd, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::USAT(Cond cond, ArmImm5 sat_imm, ArmReg Rd, ArmImm5 imm5, bool sh, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::USAT16(Cond cond, ArmImm4 sat_imm, ArmReg Rd, ArmReg Rn) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,32 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
using namespace Gen;
void JitX64::CPS() { CompileInterpretInstruction(); }
void JitX64::MRS() { CompileInterpretInstruction(); }
void JitX64::MSR() { CompileInterpretInstruction(); }
void JitX64::RFE() { CompileInterpretInstruction(); }
void JitX64::SETEND(bool E) {
cond_manager.Always();
if (E) {
code->OR(32, MJitStateCpsr(), Imm32(1 << 9));
current.EFlag = true;
} else {
code->AND(32, MJitStateCpsr(), Imm32(~(1 << 9)));
current.EFlag = false;
}
current.arm_pc += GetInstSize();
}
void JitX64::SRS() { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,297 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <functional>
#include "common/assert.h"
#include "common/x64/abi.h"
#include "core/arm/jit_x64/jit_x64.h"
#include "core/arm/jit_x64/instructions/helper/load_store.h"
namespace JitX64 {
using namespace Gen;
void JitX64::CLREX() {
cond_manager.Always();
code->MOV(32, MJitStateExclusiveTag(), Imm32(0xFFFFFFFF));
code->MOV(8, MJitStateExclusiveState(), Imm8(0));
current.arm_pc += GetInstSize();
}
void ExclusiveLoadCommon(XEmitter* code, RegAlloc& reg_alloc, OpArg exclusive_state, OpArg exclusive_tag, ArmReg Rn_index, ArmReg Rd_index) {
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC, "UNPREDICTABLE");
code->MOV(8, exclusive_state, Imm8(1));
reg_alloc.FlushX64(ABI_PARAM1);
reg_alloc.LockX64(ABI_PARAM1);
OpArg Rn = reg_alloc.LockArmForRead(Rn_index);
code->MOV(32, R(ABI_PARAM1), Rn);
reg_alloc.UnlockArm(Rn_index);
code->MOV(32, exclusive_tag, R(ABI_PARAM1));
code->AND(32, exclusive_tag, Imm32(RESERVATION_GRANULE_MASK));
reg_alloc.UnlockX64(ABI_PARAM1);
}
void JitX64::LDREX(Cond cond, ArmReg Rn_index, ArmReg Rd_index) {
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC, "UNPREDICTABLE");
ExclusiveLoadCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(), Rn_index, Rd_index);
CompileCallHost(reinterpret_cast<const void*>(!current.EFlag ? &Load32LE : &Load32BE));
reg_alloc.LockX64(ABI_RETURN);
X64Reg Rd = reg_alloc.BindArmForWrite(Rd_index);
code->MOV(32, R(Rd), R(ABI_RETURN));
reg_alloc.UnlockArm(Rd_index);
reg_alloc.UnlockX64(ABI_RETURN);
current.arm_pc += GetInstSize();
}
void JitX64::LDREXB(Cond cond, ArmReg Rn_index, ArmReg Rd_index) {
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC, "UNPREDICTABLE");
ExclusiveLoadCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(), Rn_index, Rd_index);
CompileCallHost(reinterpret_cast<const void*>(&Load8));
reg_alloc.LockX64(ABI_RETURN);
X64Reg Rd = reg_alloc.BindArmForWrite(Rd_index);
code->MOVZX(32, 8, Rd, R(ABI_RETURN));
reg_alloc.UnlockArm(Rd_index);
reg_alloc.UnlockX64(ABI_RETURN);
current.arm_pc += GetInstSize();
}
void JitX64::LDREXD(Cond cond, ArmReg Rn_index, ArmReg Rd_index) {
cond_manager.CompileCond(cond);
ASSERT_MSG(IsEvenArmReg(Rd_index), "UNPREDICTABLE");
ASSERT_MSG(Rd_index < ArmReg::R14, "UNPREDICTABLE");
ASSERT_MSG(Rn_index != ArmReg::PC, "UNPREDICTABLE");
ExclusiveLoadCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(), Rn_index, Rd_index);
CompileCallHost(reinterpret_cast<const void*>(!current.EFlag ? Load64LE : Load64BE));
reg_alloc.LockX64(ABI_RETURN);
X64Reg Rd0 = reg_alloc.BindArmForWrite(Rd_index + 0);
X64Reg Rd1 = reg_alloc.BindArmForWrite(Rd_index + 1);
code->MOV(64, R(Rd0), R(ABI_RETURN));
code->SHR(64, R(ABI_RETURN), Imm8(32));
code->MOV(32, R(Rd1), R(ABI_RETURN));
reg_alloc.UnlockArm(Rd_index + 0);
reg_alloc.UnlockArm(Rd_index + 1);
reg_alloc.UnlockX64(ABI_RETURN);
current.arm_pc += GetInstSize();
}
void JitX64::LDREXH(Cond cond, ArmReg Rn_index, ArmReg Rd_index) {
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC, "UNPREDICTABLE");
ExclusiveLoadCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(), Rn_index, Rd_index);
CompileCallHost(reinterpret_cast<const void*>(!current.EFlag ? Load16LE : Load16BE));
reg_alloc.LockX64(ABI_RETURN);
X64Reg Rd = reg_alloc.BindArmForWrite(Rd_index);
code->MOVZX(32, 16, Rd, R(ABI_RETURN));
reg_alloc.UnlockArm(Rd_index);
reg_alloc.UnlockX64(ABI_RETURN);
current.arm_pc += GetInstSize();
}
void ExclusiveStoreCommon(XEmitter* code, RegAlloc& reg_alloc, OpArg exclusive_state, OpArg exclusive_tag, ArmReg Rn_index, ArmReg Rd_index, std::function<void()> do_memory_access) {
OpArg Rn = reg_alloc.LockArmForRead(Rn_index);
OpArg Rd = reg_alloc.LockArmForWrite(Rd_index);
code->MOV(32, Rd, Imm8(1)); // First, assume we failed.
code->MOV(32, R(ABI_PARAM1), Rn);
code->BT(8, exclusive_state, Imm8(0));
auto jmp_to_end1 = code->J_CC(CC_NC);
code->MOV(32, R(ABI_PARAM2), R(ABI_PARAM1));
code->AND(32, R(ABI_PARAM2), Imm32(RESERVATION_GRANULE_MASK));
code->CMP(32, R(ABI_PARAM2), exclusive_tag);
auto jmp_to_end2 = code->J_CC(CC_NE);
// Exclsuive monitor pass
code->MOV(32, Rd, Imm8(0)); // Okay, actually we passed.
code->MOV(8, exclusive_state, Imm8(0)); // Unset exclusive memory acecss.
code->MOV(32, exclusive_tag, Imm32(0xFFFFFFFF)); // Unset exclusive memory acecss.
do_memory_access();
code->SetJumpTarget(jmp_to_end1);
code->SetJumpTarget(jmp_to_end2);
reg_alloc.UnlockArm(Rd_index);
reg_alloc.UnlockArm(Rn_index);
}
void JitX64::STREX(Cond cond, ArmReg Rn_index, ArmReg Rd_index, ArmReg Rm_index) {
CompileInterpretInstruction();
return;
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC && Rm_index != ArmReg::PC, "UNPREDICTABLE");
ASSERT_MSG(Rd_index != Rn_index && Rd_index != Rm_index, "UNPREDICTABLE");
reg_alloc.FlushX64(ABI_PARAM1);
reg_alloc.LockX64(ABI_PARAM1);
reg_alloc.FlushX64(ABI_PARAM2);
reg_alloc.LockX64(ABI_PARAM2);
OpArg Rm = reg_alloc.LockArmForRead(Rm_index);
ExclusiveStoreCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(),
Rn_index, Rd_index,
[&](){
code->MOV(32, R(ABI_PARAM2), Rm);
CompileCallHost(reinterpret_cast<const void*>(!current.EFlag ? &Store32LE : &Store32BE));
});
reg_alloc.UnlockArm(Rm_index);
reg_alloc.UnlockX64(ABI_PARAM2);
reg_alloc.UnlockX64(ABI_PARAM1);
current.arm_pc += GetInstSize();
}
void JitX64::STREXB(Cond cond, ArmReg Rn_index, ArmReg Rd_index, ArmReg Rm_index) {
CompileInterpretInstruction();
return;
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC && Rm_index != ArmReg::PC, "UNPREDICTABLE");
ASSERT_MSG(Rd_index != Rn_index && Rd_index != Rm_index, "UNPREDICTABLE");
reg_alloc.FlushX64(ABI_PARAM1);
reg_alloc.LockX64(ABI_PARAM1);
reg_alloc.FlushX64(ABI_PARAM2);
reg_alloc.LockX64(ABI_PARAM2);
OpArg Rm = reg_alloc.LockArmForRead(Rm_index);
ExclusiveStoreCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(),
Rn_index, Rd_index,
[&]() {
code->MOV(32, R(ABI_PARAM2), Rm);
CompileCallHost(reinterpret_cast<const void*>(&Store8));
});
reg_alloc.UnlockArm(Rm_index);
reg_alloc.UnlockX64(ABI_PARAM2);
reg_alloc.UnlockX64(ABI_PARAM1);
current.arm_pc += GetInstSize();
}
void JitX64::STREXD(Cond cond, ArmReg Rn_index, ArmReg Rd_index, ArmReg Rm_index) {
CompileInterpretInstruction();
return;
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC && Rm_index != ArmReg::PC, "UNPREDICTABLE");
ASSERT_MSG(Rm_index != ArmReg::R14, "UNPREDICTABLE");
ASSERT_MSG(Rd_index != Rn_index && Rd_index != Rm_index, "UNPREDICTABLE");
ASSERT_MSG(Rd_index != Rm_index + 1, "UNPREDICTABLE");
ASSERT_MSG(IsEvenArmReg(Rm_index), "UNPREDICTABLE");
reg_alloc.FlushX64(ABI_PARAM1);
reg_alloc.LockX64(ABI_PARAM1);
reg_alloc.FlushX64(ABI_PARAM2);
reg_alloc.LockX64(ABI_PARAM2);
reg_alloc.FlushX64(ABI_PARAM3);
reg_alloc.LockX64(ABI_PARAM3);
OpArg Rm0 = reg_alloc.LockArmForRead(Rm_index + 0);
OpArg Rm1 = reg_alloc.LockArmForRead(Rm_index + 1);
ExclusiveStoreCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(),
Rn_index, Rd_index,
[&]() {
code->MOV(32, R(ABI_PARAM2), Rm0);
code->MOV(32, R(ABI_PARAM3), Rm1);
CompileCallHost(reinterpret_cast<const void*>(!current.EFlag ? &Store64LE : &Store64BE));
});
reg_alloc.UnlockArm(Rm_index + 1);
reg_alloc.UnlockArm(Rm_index + 0);
reg_alloc.UnlockX64(ABI_PARAM3);
reg_alloc.UnlockX64(ABI_PARAM2);
reg_alloc.UnlockX64(ABI_PARAM1);
current.arm_pc += GetInstSize();
}
void JitX64::STREXH(Cond cond, ArmReg Rn_index, ArmReg Rd_index, ArmReg Rm_index) {
CompileInterpretInstruction();
return;
cond_manager.CompileCond(cond);
ASSERT_MSG(Rn_index != ArmReg::PC && Rd_index != ArmReg::PC && Rm_index != ArmReg::PC, "UNPREDICTABLE");
ASSERT_MSG(Rd_index != Rn_index && Rd_index != Rm_index, "UNPREDICTABLE");
reg_alloc.FlushX64(ABI_PARAM1);
reg_alloc.LockX64(ABI_PARAM1);
reg_alloc.FlushX64(ABI_PARAM2);
reg_alloc.LockX64(ABI_PARAM2);
OpArg Rm = reg_alloc.LockArmForRead(Rm_index);
ExclusiveStoreCommon(code, reg_alloc, MJitStateExclusiveState(), MJitStateExclusiveTag(),
Rn_index, Rd_index,
[&]() {
code->MOV(32, R(ABI_PARAM2), Rm);
CompileCallHost(reinterpret_cast<const void*>(!current.EFlag ? &Store16LE : &Store16BE));
});
reg_alloc.UnlockArm(Rm_index);
reg_alloc.UnlockX64(ABI_PARAM2);
reg_alloc.UnlockX64(ABI_PARAM1);
current.arm_pc += GetInstSize();
}
void JitX64::SWP(Cond cond, ArmReg Rn_index, ArmReg Rd_index, ArmReg Rm_index) {
CompileInterpretInstruction();
}
void JitX64::SWPB(Cond cond, ArmReg Rn_index, ArmReg Rd_index, ArmReg Rm_index) {
CompileInterpretInstruction();
}
} // namespace JitX64

View file

@ -0,0 +1,100 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/bit_util.h"
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
using namespace Gen;
void JitX64::thumb_B(Cond cond, ArmImm8 imm8) {
cond_manager.CompileCond(cond);
ASSERT_MSG(current.TFlag, "thumb_B may only be called in thumb mode");
const u32 new_pc = GetReg15Value() + BitUtil::SignExtend<9>(imm8 << 1);
reg_alloc.FlushEverything();
current.arm_pc += GetInstSize();
CompileUpdateCycles(false);
CompileJumpToBB(new_pc);
if (cond == Cond::AL) {
stop_compilation = true;
}
}
void JitX64::thumb_B(ArmImm11 imm11) {
cond_manager.Always();
ASSERT_MSG(current.TFlag, "thumb_B may only be called in thumb mode");
const u32 new_pc = GetReg15Value() + BitUtil::SignExtend<12>(imm11 << 1);
reg_alloc.FlushEverything();
current.arm_pc += GetInstSize();
CompileUpdateCycles(false);
CompileJumpToBB(new_pc);
stop_compilation = true;
}
void JitX64::thumb_BLX_prefix(ArmImm11 imm11) {
cond_manager.Always();
ASSERT_MSG(!thumb_BLX_prefix_executed, "two thumb BLX prefixes cannot occur in a row, pc = %u", current.arm_pc);
ASSERT_MSG(!thumb_BLX_suffix_executed, "thumb_BLX_prefix invalid state, pc = %u", current.arm_pc);
ASSERT_MSG(current.TFlag, "thumb_BLX_prefix should only be called in thumb mode, pc = %u", current.arm_pc);
thumb_BLX_prefix_imm11 = imm11;
thumb_BLX_prefix_executed = true;
current.arm_pc += GetInstSize();
// Compile the suffix, and make sure that it's compiled.
instructions_compiled++; // Has to be done to pass unit tests (same method of counting as interpreter).
CompileSingleThumbInstruction();
ASSERT_MSG(thumb_BLX_suffix_executed, "thumb BLX suffix did not come after thumb BLX prefix, pc = %u", current.arm_pc);
thumb_BLX_prefix_executed = false;
thumb_BLX_suffix_executed = false;
}
void JitX64::thumb_BLX_suffix(bool X, ArmImm11 imm11) {
cond_manager.Always();
// Must only be ever called right after the prefix.
ASSERT_MSG(thumb_BLX_prefix_executed, "thumb BLX suffix may only come after a thumb BLX prefix, pc = %u", current.arm_pc);
ASSERT_MSG(!thumb_BLX_suffix_executed, "thumb_BLX_suffix invalid state, pc = %u", current.arm_pc);
ASSERT_MSG(current.TFlag, "thumb_BLX_suffix should only be called in thumb mode, pc = %u", current.arm_pc);
u32 new_pc = (current.arm_pc + 2) +
BitUtil::SignExtend<23>(thumb_BLX_prefix_imm11 << 12) +
(imm11 << 1);
u32 new_lr = (current.arm_pc + 2) | 1;
OpArg LR = reg_alloc.LockArmForWrite(ArmReg::LR);
code->MOV(32, LR, Imm32(new_lr));
reg_alloc.UnlockArm(ArmReg::LR);
if (X) {
current.TFlag = false;
code->MOV(32, MJitStateTFlag(), Imm32(0));
new_pc &= 0xFFFFFFFC;
}
reg_alloc.FlushEverything();
current.arm_pc += GetInstSize();
CompileUpdateCycles();
CompileJumpToBB(new_pc);
stop_compilation = true;
thumb_BLX_suffix_executed = true;
}
} // namespace JitX64

View file

@ -0,0 +1,12 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 {
void JitX64::USAD8(Cond cond, ArmReg Rd, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
void JitX64::USADA8(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, ArmReg Rn) { CompileInterpretInstruction(); }
} // namespace JitX64

View file

@ -0,0 +1,238 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/assert.h"
#include "common/x64/abi.h"
#include "common/x64/emitter.h"
#include "core/arm/jit_x64/common.h"
#include "core/arm/jit_x64/interface.h"
#include "core/arm/jit_x64/jit_x64.h"
#include "core/core.h"
#include "core/core_timing.h"
namespace Gen {
struct RunJittedCode final : private Gen::XCodeBlock {
private:
using RunJitFuncType = void(*)(JitX64::JitState*, void*);
RunJitFuncType run_jit;
u64 return_from_run_jit;
public:
RunJittedCode() {
AllocCodeSpace(1024);
run_jit = RunJitFuncType(this->GetCodePtr());
// This serves two purposes:
// 1. It saves all the registers we as a callee need to save.
// 2. It aligns the stack so that the code the JIT emits can assume
// that the stack is appropriately aligned for CALLs.
ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
MOV(64, MDisp(ABI_PARAM1, offsetof(JitX64::JitState, save_host_RSP)), R(RSP));
MOV(64, R(R15), R(ABI_PARAM1));
JMPptr(R(ABI_PARAM2));
return_from_run_jit = reinterpret_cast<u64>(this->GetCodePtr());
MOV(64, R(RSP), MDisp(R15, offsetof(JitX64::JitState, save_host_RSP)));
ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
RET();
}
unsigned CallCode(JitX64::JitState* jit_state, void* bb, unsigned cycles_to_run, u32& new_pc) {
auto cpu = &jit_state->cpu_state;
cpu->NFlag = (cpu->Cpsr >> 31);
cpu->ZFlag = (cpu->Cpsr >> 30) & 1;
cpu->CFlag = (cpu->Cpsr >> 29) & 1;
cpu->VFlag = (cpu->Cpsr >> 28) & 1;
cpu->TFlag = (cpu->Cpsr >> 5) & 1;
jit_state->cycles_remaining = cycles_to_run;
jit_state->return_RIP = CallCodeReturnAddress();
run_jit(jit_state, bb);
new_pc = cpu->Reg[15];
cpu->Cpsr = (cpu->Cpsr & 0x0fffffdf) |
(cpu->NFlag << 31) |
(cpu->ZFlag << 30) |
(cpu->CFlag << 29) |
(cpu->VFlag << 28) |
(cpu->TFlag << 5);
return cycles_to_run - jit_state->cycles_remaining;
}
u64 CallCodeReturnAddress() const {
return return_from_run_jit;
}
};
struct BlockOfCode : Gen::XCodeBlock {
BlockOfCode() {
AllocCodeSpace(128 * 1024 * 1024);
}
};
}
namespace JitX64 {
struct ARM_Jit::Impl {
Gen::RunJittedCode run_jit = {};
Gen::BlockOfCode block_of_code = {};
JitX64 compiler{ &block_of_code };
};
ARM_Jit::ARM_Jit(PrivilegeMode initial_mode) : impl(std::make_unique<Impl>()), state(std::make_unique<JitState>()) {
ASSERT_MSG(initial_mode == PrivilegeMode::USER32MODE, "Unimplemented");
ClearCache();
}
ARM_Jit::~ARM_Jit() {
}
void ARM_Jit::SetPC(u32 pc) {
state->cpu_state.Reg[15] = pc;
}
u32 ARM_Jit::GetPC() const {
return state->cpu_state.Reg[15];
}
u32 ARM_Jit::GetReg(int index) const {
if (index == 15) return GetPC();
return state->cpu_state.Reg[index];
}
void ARM_Jit::SetReg(int index, u32 value) {
if (index == 15) return SetPC(value);
state->cpu_state.Reg[index] = value;
}
u32 ARM_Jit::GetVFPReg(int index) const {
return state->cpu_state.ExtReg[index];
}
void ARM_Jit::SetVFPReg(int index, u32 value) {
state->cpu_state.ExtReg[index] = value;
}
u32 ARM_Jit::GetVFPSystemReg(VFPSystemRegister reg) const {
return state->cpu_state.VFP[reg];
}
void ARM_Jit::SetVFPSystemReg(VFPSystemRegister reg, u32 value) {
state->cpu_state.VFP[reg] = value;
}
u32 ARM_Jit::GetCPSR() const {
return state->cpu_state.Cpsr;
}
void ARM_Jit::SetCPSR(u32 cpsr) {
state->cpu_state.Cpsr = cpsr;
}
u32 ARM_Jit::GetCP15Register(CP15Register reg) {
return state->cpu_state.CP15[reg];
}
void ARM_Jit::SetCP15Register(CP15Register reg, u32 value) {
state->cpu_state.CP15[reg] = value;
}
void ARM_Jit::AddTicks(u64 ticks) {
down_count -= ticks;
if (down_count < 0)
CoreTiming::Advance();
}
void ARM_Jit::ExecuteInstructions(int num_instructions) {
reschedule = false;
do {
bool EFlag = (state->cpu_state.Cpsr >> 9) & 1;
state->cpu_state.TFlag = (state->cpu_state.Cpsr >> 5) & 1;
if (!state->cpu_state.NirqSig) {
UNIMPLEMENTED();
}
if (state->cpu_state.TFlag)
state->cpu_state.Reg[15] &= 0xfffffffe;
else
state->cpu_state.Reg[15] &= 0xfffffffc;
u8* ptr = impl->compiler.GetBB(state->cpu_state.Reg[15], state->cpu_state.TFlag, EFlag);
unsigned ticks_executed = impl->run_jit.CallCode(state.get(), ptr, num_instructions, state->cpu_state.Reg[15]);
num_instructions -= ticks_executed;
AddTicks(ticks_executed);
} while (!reschedule && num_instructions > 0);
}
void ARM_Jit::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) {
memset(&context, 0, sizeof(Core::ThreadContext));
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
context.cpsr = 0x1F; // Usermode
}
void ARM_Jit::SaveContext(Core::ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->cpu_state.Reg.data(), sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->cpu_state.ExtReg.data(), sizeof(ctx.fpu_registers));
ctx.sp = state->cpu_state.Reg[13];
ctx.lr = state->cpu_state.Reg[14];
ctx.pc = state->cpu_state.Reg[15];
ctx.cpsr = GetCPSR();
ctx.fpscr = state->cpu_state.VFP[1];
ctx.fpexc = state->cpu_state.VFP[2];
}
void ARM_Jit::LoadContext(const Core::ThreadContext& ctx) {
memcpy(state->cpu_state.Reg.data(), ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->cpu_state.ExtReg.data(), ctx.fpu_registers, sizeof(ctx.fpu_registers));
state->cpu_state.Reg[13] = ctx.sp;
state->cpu_state.Reg[14] = ctx.lr;
state->cpu_state.Reg[15] = ctx.pc;
SetCPSR(ctx.cpsr);
state->cpu_state.VFP[1] = ctx.fpscr;
state->cpu_state.VFP[2] = ctx.fpexc;
}
void ARM_Jit::PrepareReschedule() {
reschedule = true;
state->cpu_state.NumInstrsToExecute = 0;
}
void ARM_Jit::ClearCache() {
impl->compiler.ClearCache();
impl->block_of_code.ClearCodeSpace();
state->cpu_state.instruction_cache.clear();
}
void ARM_Jit::FastClearCache() {
impl->compiler.ClearCache();
impl->block_of_code.ResetCodePtr();
state->cpu_state.instruction_cache.clear();
}
}

View file

@ -0,0 +1,54 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/arm/jit_x64/common.h"
namespace JitX64 {
class ARM_Jit final : virtual public ARM_Interface {
public:
ARM_Jit(PrivilegeMode initial_mode);
~ARM_Jit();
void SetPC(u32 pc) override;
u32 GetPC() const override;
u32 GetReg(int index) const override;
void SetReg(int index, u32 value) override;
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetVFPSystemReg(VFPSystemRegister reg) const override;
void SetVFPSystemReg(VFPSystemRegister reg, u32 value) override;
u32 GetCPSR() const override;
void SetCPSR(u32 cpsr) override;
u32 GetCP15Register(CP15Register reg) override;
void SetCP15Register(CP15Register reg, u32 value) override;
void AddTicks(u64 ticks) override;
void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) override;
void SaveContext(Core::ThreadContext& ctx) override;
void LoadContext(const Core::ThreadContext& ctx) override;
void PrepareReschedule() override;
void ExecuteInstructions(int num_instructions) override;
void ClearCache() override;
void FastClearCache();
private:
struct Impl;
std::unique_ptr<Impl> impl;
bool reschedule = false;
std::unique_ptr<JitState> state;
};
}

View file

@ -0,0 +1,63 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/x64/abi.h"
#include "core/arm/jit_x64/jit_x64.h"
extern unsigned InterpreterMainLoop(ARMul_State* cpu);
namespace JitX64 {
using namespace Gen;
static JitState* CallInterpreter(JitState* jit_state, u64 pc, u64 TFlag, u64 EFlag) {
ARMul_State* cpu = &jit_state->cpu_state;
cpu->Reg[15] = pc;
cpu->Cpsr = (cpu->Cpsr & 0x0fffffdf) |
(cpu->NFlag << 31) |
(cpu->ZFlag << 30) |
(cpu->CFlag << 29) |
(cpu->VFlag << 28) |
(cpu->TFlag << 5);
if (jit_state->cycles_remaining >= 0) {
#if 0
cpu->NumInstrsToExecute = jit_state->cycles_remaining + 1;
if (cpu->NumInstrsToExecute > 100) cpu->NumInstrsToExecute = 100;
jit_state->cycles_remaining -= InterpreterMainLoop(cpu) - 1;
#else
cpu->NumInstrsToExecute = 1;
jit_state->cycles_remaining -= InterpreterMainLoop(cpu) - 1;
#endif
}
return jit_state;
}
void JitX64::CompileInterpretInstruction() {
cond_manager.Always();
reg_alloc.FlushEverything();
CompileUpdateCycles();
code->MOV(64, R(ABI_PARAM1), R(reg_alloc.JitStateReg()));
code->MOV(64, R(ABI_PARAM2), Imm64(current.arm_pc));
code->MOV(64, R(ABI_PARAM3), Imm64(current.TFlag));
code->MOV(64, R(ABI_PARAM4), Imm64(current.EFlag));
CompileCallHost(reinterpret_cast<const void*>(&CallInterpreter));
code->MOV(64, R(reg_alloc.JitStateReg()), R(ABI_RETURN));
// Return to dispatch
code->JMPptr(MJitStateHostReturnRIP());
current.arm_pc += GetInstSize();
stop_compilation = true;
}
}

View file

@ -0,0 +1,265 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
#include "core/arm/jit_x64/jit_x64.h"
#include "core/memory.h"
namespace JitX64 {
using namespace Gen;
JitX64::JitX64(XEmitter* code) : code(code) {}
void JitX64::ClearCache() {
basic_blocks.clear();
patch_jmp_locations.clear();
InterpreterClearCache();
}
CodePtr JitX64::GetBB(u32 pc, bool TFlag, bool EFlag) {
const LocationDescriptor desc = { pc, TFlag, EFlag };
if (basic_blocks.find(desc) == basic_blocks.end()) {
return Compile(pc, TFlag, EFlag);
}
return basic_blocks[desc];
}
CodePtr JitX64::Compile(u32 pc, bool TFlag, bool EFlag) {
const CodePtr bb = code->GetWritableCodePtr();
const LocationDescriptor desc = { pc, TFlag, EFlag };
ASSERT(basic_blocks.find(desc) == basic_blocks.end());
basic_blocks[desc] = bb;
Patch(desc, bb);
reg_alloc.Init(code);
cond_manager.Init(this);
current = desc;
instructions_compiled = 0;
stop_compilation = false;
do {
instructions_compiled++;
if (current.TFlag) {
CompileSingleThumbInstruction();
} else {
CompileSingleArmInstruction();
}
reg_alloc.AssertNoLocked();
} while (!stop_compilation && ((current.arm_pc & 0xFFF) != 0));
if (!stop_compilation) {
// We're stopping compilation because we've reached a page boundary.
cond_manager.Always();
CompileUpdateCycles();
CompileJumpToBB(current.arm_pc);
}
// Insert easily searchable byte sequence for ease of lookup in memory dumps.
code->NOP();
code->INT3();
code->NOP();
return bb;
}
void JitX64::CompileUpdateCycles(bool reset_cycles) {
// We're just taking one instruction == one cycle.
if (instructions_compiled) {
code->SUB(32, MJitStateCycleCount(), Imm32(instructions_compiled));
}
if (reset_cycles) {
instructions_compiled = 0;
}
}
void JitX64::CompileReturnToDispatch() {
if (cond_manager.CurrentCond() == Cond::AL) {
reg_alloc.FlushEverything();
CompileUpdateCycles();
code->JMPptr(MJitStateHostReturnRIP());
stop_compilation = true;
return;
}
reg_alloc.FlushEverything();
CompileUpdateCycles(false);
code->JMPptr(MJitStateHostReturnRIP());
cond_manager.Always();
CompileUpdateCycles(true);
CompileJumpToBB(current.arm_pc);
code->MOV(32, MJitStateArmPC(), Imm32(current.arm_pc));
code->JMPptr(MJitStateHostReturnRIP());
stop_compilation = true;
return;
}
void JitX64::CompileJumpToBB(u32 new_pc) {
reg_alloc.FlushEverything();
code->CMP(32, MJitStateCycleCount(), Imm8(0));
const LocationDescriptor new_desc = { new_pc, current.TFlag, current.EFlag };
patch_jmp_locations[new_desc].emplace_back(code->GetWritableCodePtr());
if (basic_blocks.find(new_desc) == basic_blocks.end()) {
code->NOP(6); // Leave enough space for a jg instruction.
} else {
code->J_CC(CC_G, basic_blocks[new_desc], true);
}
code->MOV(32, MJitStateArmPC(), Imm32(new_pc));
code->JMPptr(MJitStateHostReturnRIP());
}
void JitX64::Patch(LocationDescriptor desc, CodePtr bb) {
const CodePtr save_code_ptr = code->GetWritableCodePtr();
for (CodePtr location : patch_jmp_locations[desc]) {
code->SetCodePtr(location);
code->J_CC(CC_G, bb, true);
ASSERT(code->GetCodePtr() - location == 6);
}
code->SetCodePtr(save_code_ptr);
}
void JitX64::CompileSingleArmInstruction() {
u32 inst = Memory::Read32(current.arm_pc & 0xFFFFFFFC);
auto inst_info = ArmDecoder::DecodeArm(inst);
if (!inst_info) {
// TODO: Log message
CompileInterpretInstruction();
} else {
inst_info->Visit(this, inst);
}
}
void JitX64::CompileSingleThumbInstruction() {
u32 inst_u32 = Memory::Read32(current.arm_pc & 0xFFFFFFFC);
if ((current.arm_pc & 0x2) != 0) {
inst_u32 >>= 16;
}
inst_u32 &= 0xFFFFF;
u16 inst = inst_u32;
auto inst_info = ArmDecoder::DecodeThumb(inst);
if (!inst_info) {
// TODO: Log message
CompileInterpretInstruction();
} else {
inst_info->Visit(this, inst);
}
}
void JitX64::CompileCallHost(const void* const fn) {
// There is no need to setup the stack as the stored RSP has already been properly aligned.
reg_alloc.FlushABICallerSaved();
ASSERT(reg_alloc.JitStateReg() != RSP);
code->MOV(64, R(RSP), MJitStateHostReturnRSP());
const uintptr_t distance = reinterpret_cast<uintptr_t>(fn) - (reinterpret_cast<uintptr_t>(code->GetCodePtr()) + 5);
if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) {
// Far call
code->MOV(64, R(RAX), ImmPtr(fn));
code->CALLptr(R(RAX));
} else {
code->CALL(fn);
}
}
// Convenience functions:
// We static_assert types because anything that calls these functions makes those assumptions.
// If the types of the variables are changed please update all code that calls these functions.
Gen::OpArg JitX64::MJitStateCycleCount() const {
static_assert(std::is_same<decltype(JitState::cycles_remaining), s32>::value, "JitState::cycles_remaining must be s32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cycles_remaining));
}
Gen::OpArg JitX64::MJitStateArmPC() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::Reg), std::array<u32, 16>>::value, "ARMul_State::Reg must be std::array<u32, 16>");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, Reg) + 15 * sizeof(u32));
}
Gen::OpArg JitX64::MJitStateTFlag() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::TFlag), u32>::value, "TFlag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, TFlag));
}
Gen::OpArg JitX64::MJitStateHostReturnRIP() const {
static_assert(std::is_same<decltype(JitState::return_RIP), u64>::value, "JitState::return_RIP must be u64");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, return_RIP));
}
Gen::OpArg JitX64::MJitStateHostReturnRSP() const {
static_assert(std::is_same<decltype(JitState::save_host_RSP), u64>::value, "JitState::save_host_RSP must be u64");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, save_host_RSP));
}
Gen::OpArg JitX64::MJitStateZFlag() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::ZFlag), u32>::value, "ZFlag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, ZFlag));
}
Gen::OpArg JitX64::MJitStateCFlag() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::CFlag), u32>::value, "CFlag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, CFlag));
}
Gen::OpArg JitX64::MJitStateNFlag() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::NFlag), u32>::value, "NFlag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, NFlag));
}
Gen::OpArg JitX64::MJitStateVFlag() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::VFlag), u32>::value, "VFlag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, VFlag));
}
Gen::OpArg JitX64::MJitStateCpsr() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::Cpsr), u32>::value, "Cpsr must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, Cpsr));
}
Gen::OpArg JitX64::MJitStateExclusiveTag() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::exclusive_tag), u32>::value, "exclusive_tag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, exclusive_tag));
}
Gen::OpArg JitX64::MJitStateExclusiveState() const {
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::exclusive_state), bool>::value, "exclusive_state must be bool");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, exclusive_state));
}
}

View file

@ -0,0 +1,432 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "common/x64/emitter.h"
#include "core/arm/decoder/decoder.h"
#include "core/arm/jit_x64/common.h"
#include "core/arm/jit_x64/reg_alloc.h"
namespace JitX64 {
using CodePtr = u8*;
struct LocationDescriptor {
LocationDescriptor() = delete;
LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag) : arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag) {}
u32 arm_pc;
bool TFlag; ///< Thumb / ARM
bool EFlag; ///< Big / Little Endian
bool operator == (const LocationDescriptor& o) const {
return std::tie(arm_pc, TFlag, EFlag) == std::tie(o.arm_pc, o.TFlag, o.EFlag);
}
};
struct LocationDescriptorHash {
size_t operator()(const LocationDescriptor& x) const {
return std::hash<u64>()(static_cast<u64>(x.arm_pc) ^ (static_cast<u64>(x.TFlag) << 32) ^ (static_cast<u64>(x.EFlag) << 33));
}
};
class JitX64 final : private ArmDecoder::Visitor {
public:
JitX64() = delete;
explicit JitX64(Gen::XEmitter* code);
~JitX64() override {}
void ClearCache();
CodePtr GetBB(u32 pc, bool TFlag, bool EFlag);
/// Returns a pointer to the compiled basic block.
CodePtr Compile(u32 pc, bool TFlag, bool EFlag);
private:
Gen::XEmitter* code = nullptr;
RegAlloc reg_alloc;
/// ARM pc -> x64 code block
std::unordered_map<LocationDescriptor, CodePtr, LocationDescriptorHash> basic_blocks;
private:
LocationDescriptor current = { 0, false, false };
unsigned instructions_compiled = 0;
bool stop_compilation = false;
size_t GetInstSize() const { return current.TFlag ? 2 : 4; }
void CompileSingleArmInstruction();
void CompileSingleThumbInstruction();
/// Updates the cycle count in JitState and sets instructions_compiled to zero.
void CompileUpdateCycles(bool reset_cycles = true);
/// Update MJitStateArmPC and current.TFlag before calling this function.
void CompileReturnToDispatch();
/// If a basic_block starting at ARM pc is compiled -> these locations need to be patched
std::unordered_map<LocationDescriptor, std::vector<CodePtr>, LocationDescriptorHash> patch_jmp_locations;
/// Update JitState cycle count before calling this function. This function may instead update JitState PC and return to dispatcher.
void CompileJumpToBB(u32 arm_pc);
/// Patch missing jumps (fill in after CompileJumpToBB).
void Patch(LocationDescriptor desc, CodePtr bb);
private:
/// Convenience functions
Gen::OpArg MJitStateCycleCount() const;
Gen::OpArg MJitStateArmPC() const;
Gen::OpArg MJitStateTFlag() const;
Gen::OpArg MJitStateHostReturnRIP() const;
Gen::OpArg MJitStateHostReturnRSP() const;
Gen::OpArg MJitStateZFlag() const;
Gen::OpArg MJitStateCFlag() const;
Gen::OpArg MJitStateNFlag() const;
Gen::OpArg MJitStateVFlag() const;
Gen::OpArg MJitStateCpsr() const;
Gen::OpArg MJitStateExclusiveTag() const;
Gen::OpArg MJitStateExclusiveState() const;
u32 GetReg15Value() const {
return (current.arm_pc & ~0x1) + static_cast<u32>(GetInstSize() * 2);
}
u32 GetReg15Value_WordAligned() const {
return (current.arm_pc & ~0x3) + static_cast<u32>(GetInstSize() * 2);
}
void UpdateFlagsNZCV() {
cond_manager.FlagsDirty();
code->SETcc(Gen::CC_S, MJitStateNFlag());
code->SETcc(Gen::CC_Z, MJitStateZFlag());
code->SETcc(Gen::CC_C, MJitStateCFlag());
code->SETcc(Gen::CC_O, MJitStateVFlag());
}
void UpdateFlagsNZV() {
cond_manager.FlagsDirty();
code->SETcc(Gen::CC_S, MJitStateNFlag());
code->SETcc(Gen::CC_Z, MJitStateZFlag());
code->SETcc(Gen::CC_O, MJitStateVFlag());
}
void UpdateFlagsNZ() {
cond_manager.FlagsDirty();
code->SETcc(Gen::CC_S, MJitStateNFlag());
code->SETcc(Gen::CC_Z, MJitStateZFlag());
}
void UpdateFlagsC_complement() {
cond_manager.FlagsDirty();
code->SETcc(Gen::CC_NC, MJitStateCFlag());
}
private:
struct CondManager {
private:
JitX64* jit = nullptr;
Cond current_cond = Cond::AL;
bool flags_dirty = true;
Gen::FixupBranch current_cond_fixup = {};
public:
void Init(JitX64* jit_);
void CompileCond(Cond cond);
void Always();
void FlagsDirty();
Cond CurrentCond() const;
} cond_manager;
private:
void CompileInterpretInstruction();
void CompileCallHost(const void* const fn);
/// dest must be a temporary that contains a copy of the value of Rm
void CompileShifter_imm(Gen::X64Reg dest, ArmImm5 imm5, ShiftType shift, bool do_shifter_carry_out);
// Branch instructions
void B(Cond cond, ArmImm24 imm24) override;
void BL(Cond cond, ArmImm24 imm24) override;
void BLX_imm(bool H, ArmImm24 imm24) override;
void BLX_reg(Cond cond, ArmReg Rm) override;
void BX(Cond cond, ArmReg Rm) override;
void BXJ(Cond cond, ArmReg Rm) override;
// Coprocessor instructions
void CDP() override;
void LDC() override;
void MCR() override;
void MCRR() override;
void MRC() override;
void MRRC() override;
void STC() override;
// Data processing instructions
void CompileDataProcessingHelper(ArmReg Rn, ArmReg Rd, std::function<void(Gen::X64Reg)> body);
void CompileDataProcessingHelper_Reverse(ArmReg Rn, ArmReg Rd, std::function<void(Gen::X64Reg)> body);
Gen::X64Reg CompileDataProcessingHelper_reg(ArmImm5 imm5, ShiftType shift, ArmReg Rm, bool do_shifter_carry_out);
Gen::X64Reg CompileDataProcessingHelper_rsr(ArmReg Rs, ShiftType shift, ArmReg Rm, bool do_shifter_carry_out);
void ADC_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void ADC_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void ADC_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void ADD_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void ADD_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void ADD_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void AND_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void AND_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void AND_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void BIC_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void BIC_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void BIC_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void CMN_imm(Cond cond, ArmReg Rn, int rotate, ArmImm8 imm8) override;
void CMN_reg(Cond cond, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void CMN_rsr(Cond cond, ArmReg Rn, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void CMP_imm(Cond cond, ArmReg Rn, int rotate, ArmImm8 imm8) override;
void CMP_reg(Cond cond, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void CMP_rsr(Cond cond, ArmReg Rn, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void EOR_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void EOR_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void EOR_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void MOV_imm(Cond cond, bool S, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void MOV_reg(Cond cond, bool S, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void MOV_rsr(Cond cond, bool S, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void MVN_imm(Cond cond, bool S, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void MVN_reg(Cond cond, bool S, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void MVN_rsr(Cond cond, bool S, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void ORR_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void ORR_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void ORR_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void RSB_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void RSB_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void RSB_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void RSC_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void RSC_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void RSC_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void SBC_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void SBC_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void SBC_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void SUB_imm(Cond cond, bool S, ArmReg Rn, ArmReg Rd, int rotate, ArmImm8 imm8) override;
void SUB_reg(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void SUB_rsr(Cond cond, bool S, ArmReg Rn, ArmReg Rd, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void TEQ_imm(Cond cond, ArmReg Rn, int rotate, ArmImm8 imm8) override;
void TEQ_reg(Cond cond, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void TEQ_rsr(Cond cond, ArmReg Rn, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
void TST_imm(Cond cond, ArmReg Rn, int rotate, ArmImm8 imm8) override;
void TST_reg(Cond cond, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void TST_rsr(Cond cond, ArmReg Rn, ArmReg Rs, ShiftType shift, ArmReg Rm) override;
// Exception generation instructions
void BKPT(Cond cond, ArmImm12 imm12, ArmImm4 imm4) override;
void SVC(Cond cond, ArmImm24 imm24) override;
void UDF() override;
// Extension functions
void SXTAB(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void SXTAB16(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void SXTAH(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void SXTB(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void SXTB16(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void SXTH(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void UXTAB(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void UXTAB16(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void UXTAH(Cond cond, ArmReg Rn, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void UXTB(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void UXTB16(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
void UXTH(Cond cond, ArmReg Rd, SignExtendRotation rotate, ArmReg Rm) override;
// Hint instructions
void PLD() override;
void SEV() override;
void WFE() override;
void WFI() override;
void YIELD() override;
// Load/Store instructions
void LoadAndStoreWordOrUnsignedByte_Immediate_Helper(Gen::X64Reg dest, bool U, ArmReg Rn, ArmImm12 imm12);
void LoadAndStoreWordOrUnsignedByte_Register_Helper(Gen::X64Reg dest, bool U, ArmReg Rn, ArmReg Rm);
void LoadAndStoreWordOrUnsignedByte_ScaledRegister_Helper(Gen::X64Reg dest, bool U, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm);
void LoadAndStoreWordOrUnsignedByte_ImmediateOffset(Gen::X64Reg dest, bool U, ArmReg Rn, ArmImm12 imm12);
void LoadAndStoreWordOrUnsignedByte_ImmediatePreIndexed(Gen::X64Reg dest, bool U, ArmReg Rn_index, ArmImm12 imm12);
void LoadAndStoreWordOrUnsignedByte_ImmediatePostIndexed(Gen::X64Reg dest, bool U, ArmReg Rn_index, ArmImm12 imm12);
void LoadAndStoreWordOrUnsignedByte_ScaledRegisterOffset(Gen::X64Reg dest, bool U, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm);
void LoadAndStoreWordOrUnsignedByte_ScaledRegisterPreIndexed(Gen::X64Reg dest, bool U, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm);
void LoadAndStoreWordOrUnsignedByte_ScaledRegisterPostIndexed(Gen::X64Reg dest, bool U, ArmReg Rn, ArmImm5 imm5, ShiftType shift, ArmReg Rm);
void LDR_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm11 imm11) override;
void LDR_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void LDRB_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm11 imm11) override;
void LDRB_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void LDRBT() override;
void LDRD_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm4 imm8a, ArmImm4 imm8b) override;
void LDRD_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void LDRH_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm4 imm8a, ArmImm4 imm8b) override;
void LDRH_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void LDRHT() override;
void LDRSB_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm4 imm8a, ArmImm4 imm8b) override;
void LDRSB_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void LDRSBT() override;
void LDRSH_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm4 imm8a, ArmImm4 imm8b) override;
void LDRSH_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void LDRSHT() override;
void LDRT() override;
void STR_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm11 imm11) override;
void STR_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void STRB_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm11 imm11) override;
void STRB_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ShiftType shift, ArmReg Rm) override;
void STRBT() override;
void STRD_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm4 imm8a, ArmImm4 imm8b) override;
void STRD_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void STRH_imm(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmImm4 imm8a, ArmImm4 imm8b) override;
void STRH_reg(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void STRHT() override;
void STRT() override;
// Load/Store multiple instructions
void LDM(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmRegList list) override;
void LDM_usr() override;
void LDM_eret() override;
void STM(Cond cond, bool P, bool U, bool W, ArmReg Rn, ArmRegList list) override;
void STM_usr() override;
// Miscellaneous instructions
void CLZ(Cond cond, ArmReg Rd, ArmReg Rm) override;
void NOP() override;
void SEL(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
// Unsigned sum of absolute difference functions
void USAD8(Cond cond, ArmReg Rd, ArmReg Rm, ArmReg Rn) override;
void USADA8(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, ArmReg Rn) override;
// Packing instructions
void PKHBT(Cond cond, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ArmReg Rm) override;
void PKHTB(Cond cond, ArmReg Rn, ArmReg Rd, ArmImm5 imm5, ArmReg Rm) override;
// Reversal instructions
void REV(Cond cond, ArmReg Rd, ArmReg Rm) override;
void REV16(Cond cond, ArmReg Rd, ArmReg Rm) override;
void REVSH(Cond cond, ArmReg Rd, ArmReg Rm) override;
// Saturation instructions
void SSAT(Cond cond, ArmImm5 sat_imm, ArmReg Rd, ArmImm5 imm5, bool sh, ArmReg Rn) override;
void SSAT16(Cond cond, ArmImm4 sat_imm, ArmReg Rd, ArmReg Rn) override;
void USAT(Cond cond, ArmImm5 sat_imm, ArmReg Rd, ArmImm5 imm5, bool sh, ArmReg Rn) override;
void USAT16(Cond cond, ArmImm4 sat_imm, ArmReg Rd, ArmReg Rn) override;
// Multiply (Normal) instructions
void MLA(Cond cond, bool S, ArmReg Rd, ArmReg Ra, ArmReg Rm, ArmReg Rn) override;
void MUL(Cond cond, bool S, ArmReg Rd, ArmReg Rm, ArmReg Rn) override;
// Multiply (Long) instructions
void SMLAL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) override;
void SMULL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) override;
void UMAAL(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) override;
void UMLAL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) override;
void UMULL(Cond cond, bool S, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, ArmReg Rn) override;
// Multiply (Halfword) instructions
void SMLALxy(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, bool M, bool N, ArmReg Rn) override;
void SMLAxy(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, bool N, ArmReg Rn) override;
void SMULxy(Cond cond, ArmReg Rd, ArmReg Rm, bool M, bool N, ArmReg Rn) override;
// Multiply (word by halfword) instructions
void SMLAWy(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, ArmReg Rn) override;
void SMULWy(Cond cond, ArmReg Rd, ArmReg Rm, bool M, ArmReg Rn) override;
// Multiply (Most significant word) instructions
void SMMLA(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool R, ArmReg Rn) override;
void SMMLS(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool R, ArmReg Rn) override;
void SMMUL(Cond cond, ArmReg Rd, ArmReg Rm, bool R, ArmReg Rn) override;
// Multiply (Dual) instructions
void SMLAD(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, ArmReg Rn) override;
void SMLALD(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, bool M, ArmReg Rn) override;
void SMLSD(Cond cond, ArmReg Rd, ArmReg Ra, ArmReg Rm, bool M, ArmReg Rn) override;
void SMLSLD(Cond cond, ArmReg RdHi, ArmReg RdLo, ArmReg Rm, bool M, ArmReg Rn) override;
void SMUAD(Cond cond, ArmReg Rd, ArmReg Rm, bool M, ArmReg Rn) override;
void SMUSD(Cond cond, ArmReg Rd, ArmReg Rm, bool M, ArmReg Rn) override;
// Parallel Add/Subtract (Modulo arithmetic) instructions
void SADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void USAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void USUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void USUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
// Parallel Add/Subtract (Saturating) instructions
void QADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UQADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UQADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UQASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UQSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UQSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UQSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
// Parallel Add/Subtract (Halving) instructions
void SHADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SHADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SHASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SHSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SHSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SHSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UHADD8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UHADD16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UHASX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UHSAX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UHSUB8(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void UHSUB16(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
// Saturated Add/Subtract instructions
void QADD(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QSUB(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QDADD(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void QDSUB(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
// Synchronization Primitive instructions
void CLREX() override;
void LDREX(Cond cond, ArmReg Rn, ArmReg Rd) override;
void LDREXB(Cond cond, ArmReg Rn, ArmReg Rd) override;
void LDREXD(Cond cond, ArmReg Rn, ArmReg Rd) override;
void LDREXH(Cond cond, ArmReg Rn, ArmReg Rd) override;
void STREX(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void STREXB(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void STREXD(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void STREXH(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SWP(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
void SWPB(Cond cond, ArmReg Rn, ArmReg Rd, ArmReg Rm) override;
// Status register access instructions
void CPS() override;
void MRS() override;
void MSR() override;
void RFE() override;
void SETEND(bool E) override;
void SRS() override;
// Thumb specific instructions
void thumb_B(Cond cond, ArmImm8 imm8) override;
void thumb_B(ArmImm11 imm11) override;
void thumb_BLX_prefix(ArmImm11 imm11) override;
void thumb_BLX_suffix(bool L, ArmImm11 imm11) override;
ArmImm11 thumb_BLX_prefix_imm11 = 0;
bool thumb_BLX_prefix_executed = false;
bool thumb_BLX_suffix_executed = false;
};
} // namespace JitX64

View file

@ -0,0 +1,383 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstddef>
#include <map>
#include "common/assert.h"
#include "common/x64/abi.h"
#include "core/arm/jit_x64/reg_alloc.h"
namespace JitX64 {
const std::map<Gen::X64Reg, size_t> RegAlloc::x64_reg_to_index = {
// Ordered such that the caller saved registers are on top,
// and registers often flushed at the bottom.
{ Gen::R10, 0 },
{ Gen::R11, 1 },
{ Gen::RBX, 2 },
{ Gen::RBP, 3 },
{ Gen::R12, 4 },
{ Gen::R13, 5 },
{ Gen::R14, 6 },
{ Gen::RAX, 7 },
{ Gen::R9, 8 },
{ Gen::R8, 9 },
{ Gen::RSI, 10 },
{ Gen::RDX, 11 },
{ Gen::RDI, 12 },
{ Gen::RCX, 13 },
{ Gen::RSP, 14 },
};
constexpr Gen::X64Reg jit_state_reg = Gen::R15;
Gen::X64Reg RegAlloc::JitStateReg() const {
return jit_state_reg;
}
static Gen::OpArg MJitStateCpuReg(ArmReg arm_reg) {
// The below pointer arithmetic assumes the following:
static_assert(std::is_same<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::Reg), std::array<u32, 16>>::value, "ARMul_State::Reg must be std::array<u32, 16>");
ASSERT(IsValidArmReg(arm_reg));
return Gen::MDisp(jit_state_reg, offsetof(JitState, cpu_state) + offsetof(ARMul_State, Reg) + static_cast<unsigned>(arm_reg) * sizeof(u32));
}
void RegAlloc::Init(Gen::XEmitter* emitter) {
code = emitter;
for (size_t i = 0; i < arm_gpr.size(); i++) {
const ArmReg arm_reg = static_cast<ArmReg>(i);
arm_gpr[i] = { arm_reg, MJitStateCpuReg(arm_reg), false };
}
for (const auto& entry : x64_reg_to_index) {
const Gen::X64Reg x64_reg = entry.first;
const size_t i = entry.second;
x64_gpr[i] = { x64_reg, false, X64State::State::Free, ArmReg::INVALID_REG };
}
}
void RegAlloc::FlushX64(Gen::X64Reg x64_reg) {
X64State& x64_state = GetState(x64_reg);
ASSERT(!x64_state.locked);
switch (x64_state.state) {
case X64State::State::Free:
case X64State::State::Temp:
x64_state.state = X64State::State::Free;
break;
case X64State::State::CleanArmReg: {
ArmState& arm_state = GetState(x64_state.arm_reg);
AssertStatesConsistent(x64_state, arm_state);
// We ignore the value in the x64 register since it's not dirty.
x64_state.state = X64State::State::Free;
arm_state.location = MJitStateCpuReg(x64_state.arm_reg);
break;
}
case X64State::State::DirtyArmReg: {
AssertStatesConsistent(x64_state, GetState(x64_state.arm_reg));
// Flush the value in the x64 register back into ARMul_State since it's dirty.
FlushArm(x64_state.arm_reg);
break;
}
default:
UNREACHABLE();
break;
}
ASSERT(x64_state.state == X64State::State::Free);
ASSERT(!x64_state.locked);
}
void RegAlloc::LockX64(Gen::X64Reg x64_reg) {
X64State& x64_state = GetState(x64_reg);
ASSERT(!x64_state.locked && x64_state.state == X64State::State::Free);
x64_state.locked = true;
x64_state.state = X64State::State::UserManuallyLocked;
}
void RegAlloc::UnlockX64(Gen::X64Reg x64_reg) {
X64State& x64_state = GetState(x64_reg);
ASSERT(x64_state.locked && x64_state.state == X64State::State::UserManuallyLocked);
x64_state.locked = false;
x64_state.state = X64State::State::Free;
}
void RegAlloc::FlushArm(ArmReg arm_reg) {
ArmState& arm_state = GetState(arm_reg);
ASSERT(!arm_state.locked);
if (!arm_state.IsInX64Register())
return; // Nothing to do
X64State& x64_state = GetState(GetX64For(arm_reg));
ASSERT(!x64_state.locked);
if (x64_state.state == X64State::State::DirtyArmReg) {
code->MOV(32, MJitStateCpuReg(arm_reg), R(x64_state.x64_reg));
}
x64_state.state = X64State::State::Free;
arm_state.location = MJitStateCpuReg(arm_reg);
}
Gen::OpArg RegAlloc::LockArmForRead(ArmReg arm_reg) {
ASSERT(arm_reg != ArmReg::PC); // Not valid for R15 (cannot read from it)
ArmState& arm_state = GetState(arm_reg);
if (arm_state.IsInX64Register()) {
X64State& x64_state = GetState(GetX64For(arm_reg));
ASSERT(!x64_state.locked);
x64_state.locked = true;
}
ASSERT(!arm_state.locked);
arm_state.locked = true;
return arm_state.location;
}
Gen::OpArg RegAlloc::LockArmForWrite(ArmReg arm_reg) {
// Valid for R15 (write-only)
ArmState& arm_state = GetState(arm_reg);
if (arm_state.IsInX64Register()) {
X64State& x64_state = GetState(GetX64For(arm_reg));
ASSERT(!x64_state.locked);
x64_state.locked = true;
x64_state.state = X64State::State::DirtyArmReg;
}
ASSERT(!arm_state.locked);
arm_state.locked = true;
return arm_state.location;
}
Gen::X64Reg RegAlloc::BindArmToX64(ArmReg arm_reg, bool load) {
ArmState& arm_state = GetState(arm_reg);
ASSERT(!arm_state.locked);
if (arm_state.IsInX64Register()) {
X64State& x64_state = GetState(GetX64For(arm_reg));
ASSERT(!x64_state.locked);
arm_state.locked = true;
x64_state.locked = true;
return x64_state.x64_reg;
}
const Gen::X64Reg x64_reg = AllocReg();
X64State& x64_state = GetState(x64_reg);
ASSERT(!x64_state.locked && x64_state.state == X64State::State::Free);
x64_state.locked = true;
x64_state.arm_reg = arm_reg;
x64_state.state = X64State::State::CleanArmReg;
if (load)
code->MOV(32, R(x64_reg), MJitStateCpuReg(arm_reg));
arm_state.locked = true;
arm_state.location = R(x64_reg);
return x64_reg;
}
Gen::X64Reg RegAlloc::BindArmForRead(ArmReg arm_reg) {
ASSERT(arm_reg != ArmReg::PC); // Not valid for R15 (cannot read from it)
return BindArmToX64(arm_reg, true);
}
Gen::X64Reg RegAlloc::BindArmForWrite(ArmReg arm_reg) {
// Valid for R15 (we're not reading from it)
const Gen::X64Reg x64_reg = BindArmToX64(arm_reg, false);
MarkDirty(arm_reg);
return x64_reg;
}
void RegAlloc::UnlockArm(ArmReg arm_reg) {
ArmState& arm_state = GetState(arm_reg);
if (arm_state.IsInX64Register()) {
X64State& x64_state = GetState(GetX64For(arm_reg));
ASSERT(x64_state.locked);
x64_state.locked = false;
}
ASSERT(arm_state.locked);
arm_state.locked = false;
}
void RegAlloc::MarkDirty(ArmReg arm_reg) {
const ArmState& arm_state = GetState(arm_reg);
ASSERT(arm_state.locked && arm_state.IsInX64Register());
X64State& x64_state = GetState(GetX64For(arm_reg));
ASSERT(x64_state.locked);
x64_state.state = X64State::State::DirtyArmReg;
}
void RegAlloc::FlushEverything() {
for (const X64State& x64_state : x64_gpr) {
ASSERT(!x64_state.locked);
FlushX64(x64_state.x64_reg);
ASSERT(x64_state.state == X64State::State::Free);
}
}
void RegAlloc::FlushABICallerSaved() {
for (const X64State& x64_state : x64_gpr) {
if (!ABI_ALL_CALLER_SAVED[x64_state.x64_reg])
continue;
if (x64_state.state != X64State::State::UserManuallyLocked) {
ASSERT(!x64_state.locked);
FlushX64(x64_state.x64_reg);
ASSERT(x64_state.state == X64State::State::Free);
} else {
ASSERT(x64_state.locked);
}
}
ASSERT(!ABI_ALL_CALLER_SAVED[JitStateReg()]);
}
RegAlloc::X64State& RegAlloc::GetState(Gen::X64Reg x64_reg) {
ASSERT(x64_reg_to_index.find(x64_reg) != x64_reg_to_index.end());
const size_t x64_index = x64_reg_to_index.at(x64_reg);
ASSERT(x64_gpr[x64_index].x64_reg == x64_reg);
return x64_gpr[x64_index];
}
const RegAlloc::X64State& RegAlloc::GetState(Gen::X64Reg x64_reg) const {
ASSERT(x64_reg_to_index.find(x64_reg) != x64_reg_to_index.end());
const size_t x64_index = x64_reg_to_index.at(x64_reg);
ASSERT(x64_gpr[x64_index].x64_reg == x64_reg);
return x64_gpr[x64_index];
}
RegAlloc::ArmState& RegAlloc::GetState(ArmReg arm_reg) {
ASSERT(IsValidArmReg(arm_reg));
const size_t arm_index = static_cast<size_t>(arm_reg);
ASSERT(arm_gpr[arm_index].arm_reg == arm_reg);
return arm_gpr[arm_index];
}
const RegAlloc::ArmState& RegAlloc::GetState(ArmReg arm_reg) const {
ASSERT(IsValidArmReg(arm_reg));
const size_t arm_index = static_cast<size_t>(arm_reg);
ASSERT(arm_gpr[arm_index].arm_reg == arm_reg);
return arm_gpr[arm_index];
}
void RegAlloc::AssertStatesConsistent(const X64State& x64_state, const ArmState& arm_state) const {
ASSERT(arm_state.locked == x64_state.locked);
ASSERT(arm_state.IsInX64Register());
ASSERT(arm_state.location.GetSimpleReg() == x64_state.x64_reg);
ASSERT(x64_state.arm_reg == arm_state.arm_reg);
ASSERT(x64_state.state == X64State::State::CleanArmReg || x64_state.state == X64State::State::DirtyArmReg);
}
Gen::X64Reg RegAlloc::GetX64For(ArmReg arm_reg) const {
const ArmState& arm_state = GetState(arm_reg);
ASSERT(arm_state.IsInX64Register());
const Gen::X64Reg x64_reg = arm_state.location.GetSimpleReg();
AssertStatesConsistent(GetState(x64_reg), arm_state);
return x64_reg;
}
Gen::X64Reg RegAlloc::AllocTemp() {
const Gen::X64Reg x64_reg = AllocReg();
X64State& x64_state = GetState(x64_reg);
ASSERT(!x64_state.locked && x64_state.state == X64State::State::Free);
x64_state.locked = true;
x64_state.state = X64State::State::Temp;
return x64_reg;
}
void RegAlloc::UnlockTemp(Gen::X64Reg x64_reg) {
X64State& x64_state = GetState(x64_reg);
ASSERT(x64_state.locked);
ASSERT(x64_state.state == X64State::State::Temp);
x64_state.locked = false;
x64_state.state = X64State::State::Free;
}
void RegAlloc::AssertNoLocked() const {
ASSERT(std::all_of(arm_gpr.begin(), arm_gpr.end(), [&](const ArmState& arm_state) {
if (arm_state.IsInX64Register()) {
const X64State& x64_state = GetState(GetX64For(arm_state.arm_reg));
AssertStatesConsistent(x64_state, arm_state);
}
return !arm_state.locked;
}));
ASSERT(std::all_of(x64_gpr.begin(), x64_gpr.end(), [](const X64State& x64_state) {
ASSERT(x64_state.state != X64State::State::Temp); // Temp is never unlocked.
return !x64_state.locked;
}));
}
Gen::X64Reg RegAlloc::AllocReg() {
// TODO: Improve with an actual register allocator as this is terrible.
// First check to see if there anything free.
auto free_x64 = std::find_if(x64_gpr.begin(), x64_gpr.end(), [](const auto& x64_state) {
return !x64_state.locked && x64_state.state == X64State::State::Free;
});
if (free_x64 != x64_gpr.end()) {
return free_x64->x64_reg;
}
// Otherwise flush something.
auto flushable_x64 = std::find_if(x64_gpr.begin(), x64_gpr.end(), [](const auto& x64_state) {
return !x64_state.locked;
});
if (flushable_x64 != x64_gpr.end()) {
FlushX64(flushable_x64->x64_reg);
return flushable_x64->x64_reg;
}
ASSERT_MSG(false, "Ran out of x64 registers");
UNREACHABLE();
}
}

View file

@ -0,0 +1,210 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <map>
#include "common/x64/emitter.h"
#include "core/arm/decoder/decoder.h"
#include "core/arm/jit_x64/common.h"
namespace JitX64 {
// This is the register allocator
// TODO: Better algorithm required, ideally something that does reigster usage lookahead.
// (Designed so it should be simple to implement later.)
class RegAlloc final {
public:
RegAlloc() { Init(nullptr); }
/// Initialise register allocator (call before compiling a basic block as it resets internal state)
void Init(Gen::XEmitter* emitter);
// Manually load and unlock x64 registers:
// This is required rarely. The most significant case is when shifting,
// because shift instructions must use the CL register.
/// Ensures that the state of that register is State::Free.
void FlushX64(Gen::X64Reg x64_reg);
/// Locks a register: Marks it as in-use so it isn't allocated.
void LockX64(Gen::X64Reg x64_reg);
/// Unlocks a register: Allows it to be used for allocation again.
void UnlockX64(Gen::X64Reg x64_reg);
// Working with ARM registers:
/**
* Locks an ARM register so it doesn't move; ARM reg may either be in a x64 reg or in memory.
* We're going to read from it only.
* Call UnlockArm when done.
*/
Gen::OpArg LockArmForRead(ArmReg arm_reg);
/**
* Locks an ARM register so it doesn't move; ARM reg may either be in a x64 reg or in memory.
* We're going to read and/or write to it.
* Call UnlockArm when done.
*/
Gen::OpArg LockArmForReadWrite(ArmReg arm_reg) {
Gen::OpArg ret = LockArmForRead(arm_reg);
if (GetState(arm_reg).IsInX64Register()) {
MarkDirty(arm_reg);
}
return ret;
}
/**
* Locks an ARM register so it doesn't move; ARM reg may either be in a x64 reg or in memory.
* We're going to write to it only.
* Call UnlockArm when done.
*/
Gen::OpArg LockArmForWrite(ArmReg arm_reg);
/**
* Binds an ARM register to a x64 register.
* We're going to read from it only.
* Call UnlockArm when done.
*/
Gen::X64Reg BindArmForRead(ArmReg arm_reg);
/**
* Binds an ARM register to a x64 register.
* We're going to read and/or write to it.
* Call UnlockArm when done.
*/
Gen::X64Reg BindArmForReadWrite(ArmReg arm_reg) {
Gen::X64Reg ret = BindArmForRead(arm_reg);
MarkDirty(arm_reg);
return ret;
}
/**
* Binds an ARM register to a x64 register.
* We're going to write to it only.
* Call UnlockArm when done.
*/
Gen::X64Reg BindArmForWrite(ArmReg arm_reg);
/// Unlock ARM register.
void UnlockArm(ArmReg arm_reg);
// Temporaries:
/// Allocates a temporary register
Gen::X64Reg AllocTemp();
/// Releases a temporary register
void UnlockTemp(Gen::X64Reg x64_reg);
// JitState pointer:
/// Returns the register in which the JitState pointer is stored.
Gen::X64Reg JitStateReg() const;
// Flush:
/**
* Flush absolutely everything.
* You MUST always flush everything:
* - just before a branch occurs
* - just before calling into the interpreter
* - just before calling a host function
* - just before returning to the dispatcher
* - just before jumping to a new BB
* If unsure, flush. (Only cost is performance.)
*/
void FlushEverything();
/**
* Flush only those registers that are caller-saved in the ABI.
* All registers must be unlocked except those locked by LockX64.
* (We assume you know what you're doing if you've manually locked registers.)
*/
void FlushABICallerSaved();
/// Ensures that the ARM register arm_reg is not in an x64 register.
void FlushArm(ArmReg arm_reg);
// Debug:
void AssertNoLocked() const;
private:
struct ArmState {
ArmState() = default;
ArmState(ArmReg arm_reg, Gen::OpArg location, bool locked) : arm_reg(arm_reg), location(location), locked(locked) {}
/// Which register is this?
ArmReg arm_reg = ArmReg::INVALID_REG;
/**
* Where is the current value of this register? There are two cases:
* - In an x64 register, in which case location.IsSimpleReg() == true.
* - In memory in ARMul_State, in which case location == MJitStateCpuReg(arm_reg).
*/
Gen::OpArg location = { 0, 0, Gen::INVALID_REG, Gen::INVALID_REG };
/// Are we currently in-use?
bool locked = false;
bool IsInX64Register() const { return location.IsSimpleReg(); }
};
/**
* Possible states of X64State:
*
* Free (locked must be false): This x64 reg is free to be allocated for any purpose.
* Temp (locked must be true): This x64 reg is being used as a temporary in a calculation.
* DirtyArmReg (arm_reg is valid): This x64 reg is bound to an ARM reg.
* It is marked as dirty (value has changed).
* This value MUST be flushed back to memory.
* CleanArmReg (arm_reg is valid): This x64 reg is bound to an ARM reg.
* It hasn't been written to (i.e.: value is still the same as the in-memory version).
* This value WILL NOT be flushed back to memory.
* UserManuallyLocked: User has called LockX64 on this register. User must call UnlockX64 to unlock.
*/
struct X64State {
enum class State {
Free,
Temp,
DirtyArmReg,
CleanArmReg,
UserManuallyLocked
};
X64State() = default;
X64State(Gen::X64Reg x64_reg, bool locked, State state, ArmReg arm_reg) : x64_reg(x64_reg), locked(locked), state(state), arm_reg(arm_reg) {}
Gen::X64Reg x64_reg = Gen::INVALID_REG;
bool locked = false;
State state = State::Free;
ArmReg arm_reg = ArmReg::INVALID_REG; ///< Only holds a valid value when state == DirtyArmReg / CleanArmReg
};
std::array<ArmState, 16> arm_gpr;
std::array<X64State, 15> x64_gpr;
Gen::XEmitter* code = nullptr;
private:
static const std::map<Gen::X64Reg, size_t> x64_reg_to_index;
/// INTERNAL: Gets the X64State that corresponds to x64_reg.
X64State& GetState(Gen::X64Reg x64_reg);
const X64State& GetState(Gen::X64Reg x64_reg) const;
/// INTERNAL: Gets the ArmState that corresponds to arm_reg.
ArmState& GetState(ArmReg x64_reg);
const ArmState& GetState(ArmReg x64_reg) const;
/// INTERNAL: Checks consistency of the two states
void AssertStatesConsistent(const X64State& x64_state, const ArmState& arm_state) const;
/// INTERNAL: Gets the x64 register this ArmReg is currently bound to.
Gen::X64Reg GetX64For(ArmReg arm_reg) const;
/// INTERNAL: Marks register as dirty. Ensures that it is written back to memory if it's in a x64 register.
void MarkDirty(ArmReg arm_reg);
/// INTERNAL: Allocates a register that is free. Flushes registers that are not locked if necessary.
Gen::X64Reg AllocReg();
/// INTERNAL: Binds an ARM register to an X64 register. Retrieves binding if already bound.
Gen::X64Reg BindArmToX64(ArmReg arm_reg, bool load);
};
}

View file

@ -240,7 +240,6 @@ public:
// process for our purposes), not per ARMul_State (which tracks CPU core state).
std::unordered_map<u32, int> instruction_cache;
private:
void ResetMPCoreCP15Registers();
// Defines a reservation granule of 2 words, which protects the first 2 words starting at the tag.

View file

@ -104,6 +104,13 @@ void UnmapRegion(VAddr base, u32 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
auto region_iter = std::find_if(current_page_table->special_regions.begin(), current_page_table->special_regions.end(),
[base, size] (const auto& region) { return base == region.base && size == region.size; });
if (region_iter != current_page_table->special_regions.end()) {
current_page_table->special_regions.erase(region_iter);
}
}
/**
@ -111,7 +118,7 @@ void UnmapRegion(VAddr base, u32 size) {
*/
static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
for (const auto& region : current_page_table->special_regions) {
if (vaddr >= region.base && vaddr < (region.base + region.size)) {
if (vaddr >= region.base && (vaddr - region.base) < region.size) {
return region.handler;
}
}

View file

@ -7,6 +7,7 @@
#include "common/common_types.h"
#include "core/memory.h"
#include "core/mmio.h"
namespace Memory {

47
src/tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,47 @@
set(SRCS
tests.cpp
)
set(HEADERS
)
if(ARCHITECTURE_x86_64)
set(SRCS ${SRCS}
core/arm/jit_x64/fuzz_arm_branch.cpp
core/arm/jit_x64/fuzz_arm_common.cpp
core/arm/jit_x64/fuzz_arm_data_processing.cpp
core/arm/jit_x64/fuzz_arm_load_store.cpp
core/arm/jit_x64/fuzz_thumb.cpp
)
set(HEADERS ${HEADERS}
core/arm/jit_x64/fuzz_arm_common.h
core/arm/jit_x64/rand_int.h
)
endif()
create_directory_groups(${SRCS} ${HEADERS})
include_directories(../../externals/catch)
add_executable(tests ${SRCS} ${HEADERS})
target_link_libraries(tests core video_core audio_core common)
target_link_libraries(tests ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
if (MSVC)
target_link_libraries(tests getopt)
endif()
target_link_libraries(tests ${PLATFORM_LIBRARIES})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
install(TARGETS tests RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
if (MSVC)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
windows_copy_files(tests ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll)
unset(DLL_DEST)
endif()

View file

@ -0,0 +1,39 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <catch.hpp>
#include "common/common_types.h"
#include "tests/core/arm/jit_x64/rand_int.h"
#include "tests/core/arm/jit_x64/fuzz_arm_common.h"
TEST_CASE("Fuzz ARM branch instructions", "[JitX64]") {
const std::array<std::pair<u32, u32>, 6> instructions = {{
FromBitString32("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"),
FromBitString32("cccc000100101111111111110011mmmm"),
FromBitString32("cccc1010vvvvvvvvvvvvvvvvvvvvvvvv"),
FromBitString32("cccc1011vvvvvvvvvvvvvvvvvvvvvvvv"),
FromBitString32("cccc000100101111111111110001mmmm"),
FromBitString32("cccc000100101111111111110010mmmm"),
}};
auto instruction_select = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 cond = RandInt<u32>(0, 0xE);
u32 random = RandInt<u32>(0, 0xFFFFFF);
u32 Rm = RandInt<u32>(0, 14);
u32 assemble_randoms = (cond << 28) | (random << 4) | Rm;
return instructions[inst_index].first | (assemble_randoms & (~instructions[inst_index].second));
};
SECTION("R15") {
FuzzJit(1, 1, 10000, instruction_select);
}
}

View file

@ -0,0 +1,206 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <memory>
#include <vector>
#include <catch.hpp>
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/arm/disassembler/arm_disasm.h"
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/arm/jit_x64/interface.h"
#include "core/core.h"
#include "core/memory_setup.h"
#include "core/mmio.h"
#include "tests/core/arm/jit_x64/fuzz_arm_common.h"
#include "tests/core/arm/jit_x64/rand_int.h"
std::pair<u32, u32> FromBitString32(const char* str) {
REQUIRE(strlen(str) == 32);
u32 bits = 0;
u32 mask = 0;
for (int i = 0; i < 32; i++) {
const u32 bit = 1 << (31 - i);
switch (str[i]) {
case '0':
mask |= bit;
break;
case '1':
bits |= bit;
mask |= bit;
break;
default:
// Do nothing
break;
}
}
return{ bits, mask };
}
class TestMemory final : public Memory::MMIORegion {
public:
static constexpr size_t CODE_MEMORY_SIZE = 4096 * 2;
std::array<u32, CODE_MEMORY_SIZE> code_mem{};
u8 Read8(VAddr addr) override { return addr; }
u16 Read16(VAddr addr) override { return addr; }
u32 Read32(VAddr addr) override {
if (addr < CODE_MEMORY_SIZE) {
return code_mem[addr/4];
} else {
return addr;
}
}
u64 Read64(VAddr addr) override { return addr; }
struct WriteRecord {
WriteRecord(size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {}
size_t size;
VAddr addr;
u64 data;
bool operator==(const WriteRecord& o) const {
return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data);
}
};
std::vector<WriteRecord> recording;
void Write8(VAddr addr, u8 data) override { recording.emplace_back(1, addr, data); }
void Write16(VAddr addr, u16 data) override { recording.emplace_back(2, addr, data); }
void Write32(VAddr addr, u32 data) override { recording.emplace_back(4, addr, data); }
void Write64(VAddr addr, u64 data) override { recording.emplace_back(8, addr, data); }
};
static bool DoesBehaviorMatch(const ARM_DynCom& interp, const JitX64::ARM_Jit& jit, std::vector<TestMemory::WriteRecord> interp_mem_recording, std::vector<TestMemory::WriteRecord> jit_mem_recording) {
if (interp.GetCPSR() != jit.GetCPSR())
return false;
for (int i = 0; i <= 15; i++) {
if (interp.GetReg(i) != jit.GetReg(i))
return false;
}
if (interp_mem_recording != jit_mem_recording)
return false;
return true;
}
void FuzzJit(const int instruction_count, const int instructions_to_execute_count, const int run_count, const std::function<u32()> instruction_generator) {
// Init core
Core::Init();
SCOPE_EXIT({ Core::Shutdown(); });
// Prepare memory (we take over the entire address space)
std::shared_ptr<TestMemory> test_mem = std::make_shared<TestMemory>();
Memory::MapIoRegion(0x00000000, 0x80000000, test_mem);
Memory::MapIoRegion(0x80000000, 0x80000000, test_mem);
SCOPE_EXIT({
Memory::UnmapRegion(0x00000000, 0x80000000);
Memory::UnmapRegion(0x80000000, 0x80000000);
});
// Prepare test subjects
JitX64::ARM_Jit jit(PrivilegeMode::USER32MODE);
ARM_DynCom interp(PrivilegeMode::USER32MODE);
SCOPE_EXIT({
jit.FastClearCache();
interp.ClearCache();
});
for (int run_number = 0; run_number < run_count; run_number++) {
jit.FastClearCache();
interp.ClearCache();
u32 initial_regs[15];
for (int i = 0; i < 15; i++) {
u32 val = RandInt<u32>(0, 0xFFFFFFFF);
interp.SetReg(i, val);
jit.SetReg(i, val);
initial_regs[i] = val;
}
interp.SetCPSR(0x000001d0);
jit.SetCPSR(0x000001d0);
interp.SetPC(0);
jit.SetPC(0);
std::generate_n(test_mem->code_mem.begin(), instruction_count, instruction_generator);
test_mem->code_mem[instruction_count] = 0xEAFFFFFE; // b +#0 // busy wait loop
test_mem->recording.clear();
interp.ExecuteInstructions(instructions_to_execute_count);
auto interp_mem_recording = test_mem->recording;
test_mem->recording.clear();
jit.ExecuteInstructions(instructions_to_execute_count);
auto jit_mem_recording = test_mem->recording;
if (!DoesBehaviorMatch(interp, jit, interp_mem_recording, jit_mem_recording)) {
printf("Failed at execution number %i\n", run_number);
printf("\nInstruction Listing: \n");
for (int i = 0; i < instruction_count; i++) {
printf("%s\n", ARM_Disasm::Disassemble(i * 4, Memory::Read32(i * 4)).c_str());
}
printf("\nFinal Register Listing: \n");
printf(" R interp jit\n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x %08x %s\n", i, interp.GetReg(i), jit.GetReg(i), interp.GetReg(i) != jit.GetReg(i) ? "*" : "");
}
printf("CPSR: %08x %08x %s\n", interp.GetCPSR(), jit.GetCPSR(), interp.GetCPSR() != jit.GetCPSR() ? "*" : "");
if (interp_mem_recording != jit_mem_recording) {
printf("memory write recording mismatch *\n");
size_t i = 0;
while (i < interp_mem_recording.size() || i < jit_mem_recording.size()) {
if (i < interp_mem_recording.size())
printf("interp: %zu %08x %08" PRIx64 "\n", interp_mem_recording[i].size, interp_mem_recording[i].addr, interp_mem_recording[i].data);
if (i < jit_mem_recording.size())
printf("jit : %zu %08x %08" PRIx64 "\n", jit_mem_recording[i].size, jit_mem_recording[i].addr, jit_mem_recording[i].data);
i++;
}
}
printf("\nInterpreter walkthrough:\n");
interp.ClearCache();
interp.SetPC(0);
interp.SetCPSR(0x000001d0);
for (int i = 0; i < 15; i++) {
interp.SetReg(i, initial_regs[i]);
printf("%4i: %08x\n", i, interp.GetReg(i));
}
test_mem->recording.clear();
for (int inst = 0; inst < instruction_count; inst++) {
printf("%s\n", ARM_Disasm::Disassemble(inst * 4, Memory::Read32(inst * 4)).c_str());
interp.Step();
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x\n", i, interp.GetReg(i));
}
printf("CPSR: %08x\n", interp.GetCPSR());
}
#ifdef _MSC_VER
DebugBreak();
#endif
FAIL();
}
printf("%i\r", run_number);
if (run_number % 50 == 0) {
fflush(stdout);
}
}
}

View file

@ -0,0 +1,22 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <utility>
#include "common/common_types.h"
std::pair<u32, u32> FromBitString32(const char* str);
/**
* For run_count times:
* 1. Generates instruction_count instructions using instruction_generator
* 2. Requests JitX64 and interpreter to execute them for instructions_to_execute_count instructions
* 3. Verifies that the register state are equivalent
*
* Note: A b #+0 instruction is appended to the list of instructions.
*/
void FuzzJit(const int instruction_count, const int instructions_to_execute_count, const int run_count, const std::function<u32()> instruction_generator);

View file

@ -0,0 +1,115 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <catch.hpp>
#include "common/common_types.h"
#include "tests/core/arm/jit_x64/rand_int.h"
#include "tests/core/arm/jit_x64/fuzz_arm_common.h"
TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
const std::array<std::pair<u32, u32>, 48> instructions = {{
FromBitString32("cccc0010101Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000101Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000101Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0010100Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000100Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000100Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0010000Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000000Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000000Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0011110Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0001110Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0001110Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc00110111nnnn0000rrrrvvvvvvvv"),
FromBitString32("cccc00010111nnnn0000vvvvvrr0mmmm"),
FromBitString32("cccc00010111nnnn0000ssss0rr1mmmm"),
FromBitString32("cccc00110101nnnn0000rrrrvvvvvvvv"),
FromBitString32("cccc00010101nnnn0000vvvvvrr0mmmm"),
FromBitString32("cccc00010101nnnn0000ssss0rr1mmmm"),
FromBitString32("cccc0010001Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000001Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000001Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0011101S0000ddddrrrrvvvvvvvv"),
FromBitString32("cccc0001101S0000ddddvvvvvrr0mmmm"),
FromBitString32("cccc0001101S0000ddddssss0rr1mmmm"),
FromBitString32("cccc0011111S0000ddddrrrrvvvvvvvv"),
FromBitString32("cccc0001111S0000ddddvvvvvrr0mmmm"),
FromBitString32("cccc0001111S0000ddddssss0rr1mmmm"),
FromBitString32("cccc0011100Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0001100Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0001100Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0010011Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000011Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000011Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0010111Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000111Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000111Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0010110Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000110Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000110Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc0010010Snnnnddddrrrrvvvvvvvv"),
FromBitString32("cccc0000010Snnnnddddvvvvvrr0mmmm"),
FromBitString32("cccc0000010Snnnnddddssss0rr1mmmm"),
FromBitString32("cccc00110011nnnn0000rrrrvvvvvvvv"),
FromBitString32("cccc00010011nnnn0000vvvvvrr0mmmm"),
FromBitString32("cccc00010011nnnn0000ssss0rr1mmmm"),
FromBitString32("cccc00110001nnnn0000rrrrvvvvvvvv"),
FromBitString32("cccc00010001nnnn0000vvvvvrr0mmmm"),
FromBitString32("cccc00010001nnnn0000ssss0rr1mmmm"),
}};
auto instruction_select_without_R15 = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 cond = 0xE;
// Have a one-in-twenty-five chance of actually having a cond.
if (RandInt(1, 25) == 1) {
cond = RandInt<u32>(0x0, 0xD);
}
u32 Rn = RandInt<u32>(0, 15);
u32 Rd = RandInt<u32>(0, 14);
u32 S = RandInt<u32>(0, 1);
u32 shifter_operand = RandInt<u32>(0, 0xFFF);
u32 assemble_randoms = (shifter_operand << 0) | (Rd << 12) | (Rn << 16) | (S << 20) | (cond << 28);
return instructions[inst_index].first | (assemble_randoms & (~instructions[inst_index].second));
};
SECTION("short blocks") {
FuzzJit(5, 6, 5000, instruction_select_without_R15);
}
SECTION("long blocks") {
FuzzJit(1024, 1025, 200, instruction_select_without_R15);
}
auto instruction_select_only_R15 = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 cond = 0xE;
// Have a one-in-twenty-five chance of actually having a cond.
if (RandInt(1, 25) == 1) {
cond = RandInt<u32>(0x0, 0xD);
}
u32 Rn = RandInt<u32>(0, 15);
u32 Rd = 15;
u32 S = 0;
u32 shifter_operand = RandInt<u32>(0, 0xFFF);
u32 assemble_randoms = (shifter_operand << 0) | (Rd << 12) | (Rn << 16) | (S << 20) | (cond << 28);
return instructions[inst_index].first | (assemble_randoms & (~instructions[inst_index].second));
};
SECTION("R15") {
FuzzJit(1, 1, 10000, instruction_select_only_R15);
}
}

View file

@ -0,0 +1,156 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <catch.hpp>
#include "common/common_types.h"
#include "tests/core/arm/jit_x64/rand_int.h"
#include "tests/core/arm/jit_x64/fuzz_arm_common.h"
TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64]") {
const std::array<std::pair<u32, u32>, 17> instructions = {{
FromBitString32("cccc010pu0w1nnnnddddvvvvvvvvvvvv"), // LDR_imm
FromBitString32("cccc011pu0w1nnnnddddvvvvvrr0mmmm"), // LDR_reg
FromBitString32("cccc010pu1w1nnnnddddvvvvvvvvvvvv"), // LDRB_imm
FromBitString32("cccc011pu1w1nnnnddddvvvvvrr0mmmm"), // LDRB_reg
FromBitString32("cccc010pu0w0nnnnddddvvvvvvvvvvvv"), // STR_imm
FromBitString32("cccc011pu0w0nnnnddddvvvvvrr0mmmm"), // STR_reg
FromBitString32("cccc010pu1w0nnnnddddvvvvvvvvvvvv"), // STRB_imm
FromBitString32("cccc011pu1w0nnnnddddvvvvvrr0mmmm"), // STRB_reg
FromBitString32("cccc000pu1w1nnnnddddvvvv1011vvvv"), // LDRH_imm
FromBitString32("cccc000pu0w1nnnndddd00001011mmmm"), // LDRH_reg
FromBitString32("cccc000pu1w1nnnnddddvvvv1101vvvv"), // LDRSB_imm
FromBitString32("cccc000pu0w1nnnndddd00001101mmmm"), // LDRSB_reg
FromBitString32("cccc000pu1w1nnnnddddvvvv1111vvvv"), // LDRSH_imm
FromBitString32("cccc000pu0w1nnnndddd00001111mmmm"), // LDRSH_reg
FromBitString32("cccc000pu1w0nnnnddddvvvv1011vvvv"), // STRH_imm
FromBitString32("cccc000pu0w0nnnndddd00001011mmmm"), // STRH_reg
FromBitString32("1111000100000001000000e000000000"), // SETEND
}};
auto instruction_select = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 cond = 0xE;
// Have a one-in-twenty-five chance of actually having a cond.
if (RandInt(1, 25) == 1) {
cond = RandInt<u32>(0x0, 0xD);
}
u32 Rn = RandInt<u32>(0, 14);
u32 Rd = RandInt<u32>(0, 14);
u32 W = 0;
u32 P = RandInt<u32>(0, 1);
if (P) W = RandInt<u32>(0, 1);
u32 U = RandInt<u32>(0, 1);
u32 rand = RandInt<u32>(0, 0xFF);
u32 Rm = RandInt<u32>(0, 14);
if (W) {
while (Rn == Rd) {
Rn = RandInt<u32>(0, 14);
Rd = RandInt<u32>(0, 14);
}
}
u32 assemble_randoms = (Rm << 0) | (rand << 4) | (Rd << 12) | (Rn << 16) | (W << 21) | (U << 23) | (P << 24) | (cond << 28);
return instructions[inst_index].first | (assemble_randoms & (~instructions[inst_index].second));
};
SECTION("short blocks") {
FuzzJit(5, 6, 1000, instruction_select);
}
}
TEST_CASE("Fuzz ARM load/store instructions (double-word)", "[JitX64]") {
const std::array<std::pair<u32, u32>, 4> instructions = {{
FromBitString32("cccc000pu1w0nnnnddddvvvv1101vvvv"), // LDRD_imm
FromBitString32("cccc000pu0w0nnnndddd00001101mmmm"), // LDRD_reg
FromBitString32("cccc000pu1w0nnnnddddvvvv1111vvvv"), // STRD_imm
FromBitString32("cccc000pu0w0nnnndddd00001111mmmm"), // STRD_reg
}};
auto instruction_select = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 cond = 0xE;
// Have a one-in-twenty-five chance of actually having a cond.
if (RandInt(1, 25) == 1) {
cond = RandInt<u32>(0x0, 0xD);
}
u32 Rn = RandInt<u32>(0, 6) * 2;
u32 Rd = RandInt<u32>(0, 6) * 2;
u32 W = 0;
u32 P = RandInt<u32>(0, 1);
if (P) W = RandInt<u32>(0, 1);
u32 U = RandInt<u32>(0, 1);
u32 rand = RandInt<u32>(0, 0xF);
u32 Rm = RandInt<u32>(0, 14);
if (W) {
while (Rn == Rd) {
Rn = RandInt<u32>(0, 6) * 2;
Rd = RandInt<u32>(0, 6) * 2;
}
}
while (Rm == Rd || Rm == Rd + 1) {
Rm = RandInt<u32>(0, 14);
}
u32 assemble_randoms = (Rm << 0) | (rand << 4) | (Rd << 12) | (Rn << 16) | (W << 21) | (U << 23) | (P << 24) | (cond << 28);
return instructions[inst_index].first | (assemble_randoms & (~instructions[inst_index].second));
};
SECTION("short blocks") {
FuzzJit(1, 2, 5000, instruction_select);
}
}
TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64]") {
const std::array<std::pair<u32, u32>, 2> instructions = {{
FromBitString32("cccc100pu0w1nnnnxxxxxxxxxxxxxxxx"), // LDM
FromBitString32("cccc100pu0w0nnnnxxxxxxxxxxxxxxxx"), // STM
}};
auto instruction_select = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 cond = 0xE;
// Have a one-in-twenty-five chance of actually having a cond.
if (RandInt(1, 25) == 1) {
cond = RandInt<u32>(0x0, 0xD);
}
u32 reg_list = RandInt<u32>(1, 0xFFFF);
u32 Rn = RandInt<u32>(0, 14);
u32 flags = RandInt<u32>(0, 0xF);
while (true) {
if (inst_index == 1 && (flags & 2)) {
if (reg_list & (1 << Rn))
reg_list &= ~((1 << Rn) - 1);
} else if (inst_index == 0 && (flags & 2)) {
reg_list &= ~(1 << Rn);
}
if (reg_list)
break;
reg_list = RandInt<u32>(1, 0xFFFF);
}
u32 assemble_randoms = (reg_list << 0) | (Rn << 16) | (flags << 24) | (cond << 28);
return instructions[inst_index].first | (assemble_randoms & (~instructions[inst_index].second));
};
FuzzJit(1, 1, 10000, instruction_select);
}

View file

@ -0,0 +1,335 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <catch.hpp>
#include "common/scope_exit.h"
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/arm/jit_x64/interface.h"
#include "core/core.h"
#include "core/memory_setup.h"
#include "tests/core/arm/jit_x64/rand_int.h"
std::pair<u16, u16> FromBitString16(const char* str) {
REQUIRE(strlen(str) == 16);
u16 bits = 0;
u16 mask = 0;
for (int i = 0; i < 16; i++) {
const u16 bit = 1 << (15 - i);
switch (str[i]) {
case '0':
mask |= bit;
break;
case '1':
bits |= bit;
mask |= bit;
break;
default:
// Do nothing
break;
}
}
return{ bits, mask };
}
class TestMemory final : public Memory::MMIORegion {
public:
static constexpr size_t CODE_MEMORY_SIZE = 4096 * 2;
std::array<u16, CODE_MEMORY_SIZE> code_mem{};
u8 Read8(VAddr addr) override { return addr; }
u16 Read16(VAddr addr) override {
if (addr < CODE_MEMORY_SIZE) {
addr /= 2;
return code_mem[addr];
} else {
return addr;
}
}
u32 Read32(VAddr addr) override {
if (addr < CODE_MEMORY_SIZE) {
addr /= 2;
return code_mem[addr] | (code_mem[addr+1] << 16);
} else {
return addr;
}
}
u64 Read64(VAddr addr) override { return addr; }
struct WriteRecord {
WriteRecord(size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {}
size_t size;
VAddr addr;
u64 data;
bool operator==(const WriteRecord& o) const {
return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data);
}
};
std::vector<WriteRecord> recording;
void Write8(VAddr addr, u8 data) override { recording.emplace_back(1, addr, data); }
void Write16(VAddr addr, u16 data) override { recording.emplace_back(2, addr, data); }
void Write32(VAddr addr, u32 data) override { recording.emplace_back(4, addr, data); }
void Write64(VAddr addr, u64 data) override { recording.emplace_back(8, addr, data); }
};
static bool DoesBehaviorMatch(const ARM_DynCom& interp, const JitX64::ARM_Jit& jit, std::vector<TestMemory::WriteRecord> interp_mem_recording, std::vector<TestMemory::WriteRecord> jit_mem_recording) {
if (interp.GetCPSR() != jit.GetCPSR())
return false;
for (int i = 0; i <= 15; i++) {
if (interp.GetReg(i) != jit.GetReg(i))
return false;
}
if (interp_mem_recording != jit_mem_recording)
return false;
return true;
}
void FuzzJitThumb(const int instruction_count, const int instructions_to_execute_count, const int run_count, const std::function<u16(int)> instruction_generator) {
// Init core
Core::Init();
SCOPE_EXIT({ Core::Shutdown(); });
// Prepare memory
std::shared_ptr<TestMemory> test_mem = std::make_shared<TestMemory>();
Memory::MapIoRegion(0x00000000, 0x80000000, test_mem);
Memory::MapIoRegion(0x80000000, 0x80000000, test_mem);
SCOPE_EXIT({
Memory::UnmapRegion(0x00000000, 0x80000000);
Memory::UnmapRegion(0x80000000, 0x80000000);
});
// Prepare test subjects
JitX64::ARM_Jit jit(PrivilegeMode::USER32MODE);
ARM_DynCom interp(PrivilegeMode::USER32MODE);
SCOPE_EXIT({
jit.FastClearCache();
interp.ClearCache();
});
for (int run_number = 0; run_number < run_count; run_number++) {
jit.FastClearCache();
interp.ClearCache();
u32 initial_regs[15];
for (int i = 0; i < 15; i++) {
u32 val = RandInt<u32>(0, 0xFFFFFFFF);
interp.SetReg(i, val);
jit.SetReg(i, val);
initial_regs[i] = val;
}
interp.SetCPSR(0x000001d0);
jit.SetCPSR(0x000001d0);
interp.SetPC(0);
jit.SetPC(0);
test_mem->code_mem[0] = 0xFFFF;
test_mem->code_mem[1] = 0xFAFF; // blx +#4 // Jump to the following code (switch to thumb)
for (int i = 0; i < instruction_count; i++) {
u16 inst = instruction_generator(i);
test_mem->code_mem[2 + i] = inst;
}
test_mem->code_mem[2 + instruction_count] = 0xE7FE; // b +#0 // busy wait loop
test_mem->recording.clear();
interp.ExecuteInstructions(instructions_to_execute_count + 1);
auto interp_mem_recording = test_mem->recording;
test_mem->recording.clear();
jit.ExecuteInstructions(instructions_to_execute_count + 1);
auto jit_mem_recording = test_mem->recording;
if (!DoesBehaviorMatch(interp, jit, interp_mem_recording, jit_mem_recording)) {
printf("Failed at execution number %i\n", run_number);
printf("\nInstruction Listing: \n");
for (int i = 0; i < instruction_count; i++) {
printf("%04x\n", test_mem->code_mem[2 + i]);
}
printf("\nFinal Register Listing: \n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x %08x %s\n", i, interp.GetReg(i), jit.GetReg(i), interp.GetReg(i) != jit.GetReg(i) ? "*" : "");
}
printf("CPSR: %08x %08x %s\n", interp.GetCPSR(), jit.GetCPSR(), interp.GetCPSR() != jit.GetCPSR() ? "*" : "");
if (interp_mem_recording != jit_mem_recording) {
printf("memory write recording mismatch *\n");
size_t i = 0;
while (i < interp_mem_recording.size() || i < jit_mem_recording.size()) {
if (i < interp_mem_recording.size())
printf("interp: %zu %08x %08" PRIx64 "\n", interp_mem_recording[i].size, interp_mem_recording[i].addr, interp_mem_recording[i].data);
if (i < jit_mem_recording.size())
printf("jit : %zu %08x %08" PRIx64 "\n", jit_mem_recording[i].size, jit_mem_recording[i].addr, jit_mem_recording[i].data);
i++;
}
}
printf("\nInterpreter walkthrough:\n");
interp.ClearCache();
interp.SetPC(0);
interp.SetCPSR(0x000001d0);
for (int i = 0; i < 15; i++) {
interp.SetReg(i, initial_regs[i]);
printf("%4i: %08x\n", i, interp.GetReg(i));
}
for (int inst = 0; inst < instruction_count; inst++) {
interp.Step();
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x\n", i, interp.GetReg(i));
}
printf("CPSR: %08x\n", interp.GetCPSR());
}
#ifdef _MSC_VER
DebugBreak();
#endif
FAIL();
}
printf("%i\r", run_number);
if (run_number % 50 == 0) {
fflush(stdout);
}
}
}
// Things not yet tested:
//
// FromBitString16("10111110xxxxxxxx"), // BKPT
// FromBitString16("11011111xxxxxxxx"), // SWI
// FromBitString16("1011x101xxxxxxxx"), // PUSH/POP (R = 1)
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
const std::array<std::pair<u16, u16>, 24> instructions = {{
FromBitString16("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
FromBitString16("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
FromBitString16("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
FromBitString16("000110oxxxxxxxxx"), // ADD/SUB_reg
FromBitString16("000111oxxxxxxxxx"), // ADD/SUB_imm
FromBitString16("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
FromBitString16("010000ooooxxxxxx"), // Data Processing
FromBitString16("010001000hxxxxxx"), // ADD (high registers)
FromBitString16("010001010hxxxxxx"), // CMP (high registers)
FromBitString16("01000101h0xxxxxx"), // CMP (high registers)
FromBitString16("010001100hxxxxxx"), // MOV (high registers)
FromBitString16("10110000oxxxxxxx"), // Adjust stack pointer
FromBitString16("10110010ooxxxxxx"), // SXT/UXT
FromBitString16("1011101000xxxxxx"), // REV
FromBitString16("1011101001xxxxxx"), // REV16
FromBitString16("1011101011xxxxxx"), // REVSH
FromBitString16("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
FromBitString16("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
FromBitString16("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
FromBitString16("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
FromBitString16("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
FromBitString16("1011x100xxxxxxxx"), // PUSH/POP (R = 0)
FromBitString16("1100xxxxxxxxxxxx"), // STMIA/LDMIA
FromBitString16("101101100101x000"), // SETEND
}};
auto instruction_select = [&](int) -> u16 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
if (inst_index == 22) {
u16 L = RandInt<u16>(0, 1);
u16 Rn = RandInt<u16>(0, 7);
u16 reg_list = RandInt<u16>(1, 0xFF);
if (!L && (reg_list & (1 << Rn))) {
reg_list &= ~((1 << Rn) - 1);
if (reg_list == 0) reg_list = 0x80;
}
u16 random = (L << 11) | (Rn << 8) | reg_list;
return instructions[inst_index].first | (random &~instructions[inst_index].second);
} else if (inst_index == 21) {
u16 L = RandInt<u16>(0, 1);
u16 reg_list = RandInt<u16>(1, 0xFF);
u16 random = (L << 11) | reg_list;
return instructions[inst_index].first | (random &~instructions[inst_index].second);
} else {
u16 random = RandInt<u16>(0, 0xFFFF);
return instructions[inst_index].first | (random &~instructions[inst_index].second);
}
};
SECTION("short blocks") {
FuzzJitThumb(5, 6, 10000, instruction_select);
}
SECTION("long blocks") {
FuzzJitThumb(1024, 1025, 15, instruction_select);
}
}
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
const std::array<std::pair<u16, u16>, 19> instructions = {{
FromBitString16("01000111xxxxx000"), // BLX/BX
FromBitString16("1010oxxxxxxxxxxx"), // add to pc/sp
FromBitString16("11100xxxxxxxxxxx"), // B
FromBitString16("01000100h0xxxxxx"), // ADD (high registers)
FromBitString16("01000110h0xxxxxx"), // MOV (high registers)
FromBitString16("11010000xxxxxxxx"), // B<cond>
FromBitString16("11010001xxxxxxxx"), // B<cond>
FromBitString16("11010010xxxxxxxx"), // B<cond>
FromBitString16("11010011xxxxxxxx"), // B<cond>
FromBitString16("11010100xxxxxxxx"), // B<cond>
FromBitString16("11010101xxxxxxxx"), // B<cond>
FromBitString16("11010110xxxxxxxx"), // B<cond>
FromBitString16("11010111xxxxxxxx"), // B<cond>
FromBitString16("11011000xxxxxxxx"), // B<cond>
FromBitString16("11011001xxxxxxxx"), // B<cond>
FromBitString16("11011010xxxxxxxx"), // B<cond>
FromBitString16("11011011xxxxxxxx"), // B<cond>
FromBitString16("11011100xxxxxxxx"), // B<cond>
FromBitString16("10110110011x0xxx"), // CPS
}};
auto instruction_select = [&](int) -> u16 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
if (inst_index == 0) {
u16 Rm = RandInt<u16>(0, 14) << 3;
return instructions[inst_index].first | (Rm &~instructions[inst_index].second);
} else {
u16 random = RandInt<u16>(0, 0xFFFF);
return instructions[inst_index].first | (random &~instructions[inst_index].second);
}
};
FuzzJitThumb(1, 1, 10000, instruction_select);
}
TEST_CASE("Fuzz Thumb instructions set 3 (32-bit BL/BLX)", "[JitX64][Thumb]") {
auto instruction_select = [&](int i) -> u16 {
std::pair<u16, u16> inst_info;
if (i == 0) {
// BL / BLX prefix
inst_info = FromBitString16("11110xxxxxxxxxxx");
} else {
// BL / BLX suffix
inst_info = RandInt(0, 1) ? FromBitString16("11101xxxxxxxxxx0") : FromBitString16("11111xxxxxxxxxxx");
}
u16 random = RandInt<u16>(0, 0xFFFF);
return inst_info.first | (random &~ inst_info.second);
};
FuzzJitThumb(2, 2, 1000, instruction_select);
}

View file

@ -0,0 +1,20 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <random>
#include <type_traits>
template <typename T>
T RandInt(T min, T max) {
static_assert(std::is_integral<T>::value, "T must be an integral type.");
static_assert(!std::is_same<T, signed char>::value && !std::is_same<T, unsigned char>::value,
"Using char with uniform_int_distribution is undefined behavior.");
static std::random_device rd;
static std::mt19937 mt(rd());
std::uniform_int_distribution<T> rand(min, max);
return rand(mt);
}

9
src/tests/tests.cpp Normal file
View file

@ -0,0 +1,9 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
// Catch provides the main function since we've given it the
// CATCH_CONFIG_MAIN preprocessor directive.