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:
parent
58934192df
commit
7d74f8cf47
61 changed files with 18094 additions and 32 deletions
10445
externals/catch/catch.hpp
vendored
Normal file
10445
externals/catch/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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()
|
||||
|
|
|
@ -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
47
src/common/bit_util.h
Normal 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
|
|
@ -68,18 +68,22 @@ inline u64 _rotr64(u64 x, unsigned int shift){
|
|||
}
|
||||
|
||||
#else // _MSC_VER
|
||||
#if (_MSC_VER < 1900)
|
||||
// Function Cross-Compatibility
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#if (_MSC_VER < 1900)
|
||||
// Function Cross-Compatibility
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Locale Cross-Compatibility
|
||||
#define locale_t _locale_t
|
||||
// Locale Cross-Compatibility
|
||||
#define locale_t _locale_t
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
}
|
||||
#define Crash() {DebugBreak();}
|
||||
|
||||
#define rotr _rotr
|
||||
#define rotl _rotl
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
}
|
||||
#define Crash() {DebugBreak();}
|
||||
#endif // _MSC_VER ndef
|
||||
|
||||
// Generic function to get last error message.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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:
|
||||
|
||||
/**
|
||||
|
|
393
src/core/arm/decoder/arm.cpp
Normal file
393
src/core/arm/decoder/arm.cpp
Normal 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
|
415
src/core/arm/decoder/decoder.h
Normal file
415
src/core/arm/decoder/decoder.h
Normal 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
|
438
src/core/arm/decoder/thumb.cpp
Normal file
438
src/core/arm/decoder/thumb.cpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ARMul_State> state;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
struct ARMul_State;
|
||||
|
||||
unsigned InterpreterMainLoop(ARMul_State* state);
|
||||
void InterpreterClearCache();
|
||||
|
|
56
src/core/arm/jit_x64/common.h
Normal file
56
src/core/arm/jit_x64/common.h
Normal 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);
|
||||
}
|
||||
|
||||
}
|
146
src/core/arm/jit_x64/cond.cpp
Normal file
146
src/core/arm/jit_x64/cond.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
120
src/core/arm/jit_x64/instructions/branch.cpp
Normal file
120
src/core/arm/jit_x64/instructions/branch.cpp
Normal 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
|
17
src/core/arm/jit_x64/instructions/coprocessor.cpp
Normal file
17
src/core/arm/jit_x64/instructions/coprocessor.cpp
Normal 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
|
1435
src/core/arm/jit_x64/instructions/data_processing.cpp
Normal file
1435
src/core/arm/jit_x64/instructions/data_processing.cpp
Normal file
File diff suppressed because it is too large
Load diff
68
src/core/arm/jit_x64/instructions/exception_generating.cpp
Normal file
68
src/core/arm/jit_x64/instructions/exception_generating.cpp
Normal 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
|
22
src/core/arm/jit_x64/instructions/extension.cpp
Normal file
22
src/core/arm/jit_x64/instructions/extension.cpp
Normal 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
|
73
src/core/arm/jit_x64/instructions/helper/load_store.cpp
Normal file
73
src/core/arm/jit_x64/instructions/helper/load_store.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
33
src/core/arm/jit_x64/instructions/helper/load_store.h
Normal file
33
src/core/arm/jit_x64/instructions/helper/load_store.h
Normal 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
|
15
src/core/arm/jit_x64/instructions/hint.cpp
Normal file
15
src/core/arm/jit_x64/instructions/hint.cpp
Normal 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
|
1035
src/core/arm/jit_x64/instructions/load_store.cpp
Normal file
1035
src/core/arm/jit_x64/instructions/load_store.cpp
Normal file
File diff suppressed because it is too large
Load diff
13
src/core/arm/jit_x64/instructions/misc.cpp
Normal file
13
src/core/arm/jit_x64/instructions/misc.cpp
Normal 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
|
42
src/core/arm/jit_x64/instructions/multiply.cpp
Normal file
42
src/core/arm/jit_x64/instructions/multiply.cpp
Normal 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
|
12
src/core/arm/jit_x64/instructions/packing.cpp
Normal file
12
src/core/arm/jit_x64/instructions/packing.cpp
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
14
src/core/arm/jit_x64/instructions/qadd_qsub.cpp
Normal file
14
src/core/arm/jit_x64/instructions/qadd_qsub.cpp
Normal 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
|
13
src/core/arm/jit_x64/instructions/reversal.cpp
Normal file
13
src/core/arm/jit_x64/instructions/reversal.cpp
Normal 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
|
14
src/core/arm/jit_x64/instructions/saturation.cpp
Normal file
14
src/core/arm/jit_x64/instructions/saturation.cpp
Normal 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
|
32
src/core/arm/jit_x64/instructions/status_register.cpp
Normal file
32
src/core/arm/jit_x64/instructions/status_register.cpp
Normal 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
|
297
src/core/arm/jit_x64/instructions/synchronisation.cpp
Normal file
297
src/core/arm/jit_x64/instructions/synchronisation.cpp
Normal 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
|
100
src/core/arm/jit_x64/instructions/thumb.cpp
Normal file
100
src/core/arm/jit_x64/instructions/thumb.cpp
Normal 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
|
12
src/core/arm/jit_x64/instructions/usad.cpp
Normal file
12
src/core/arm/jit_x64/instructions/usad.cpp
Normal 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
|
238
src/core/arm/jit_x64/interface.cpp
Normal file
238
src/core/arm/jit_x64/interface.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
54
src/core/arm/jit_x64/interface.h
Normal file
54
src/core/arm/jit_x64/interface.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
63
src/core/arm/jit_x64/interpret.cpp
Normal file
63
src/core/arm/jit_x64/interpret.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
265
src/core/arm/jit_x64/jit_x64.cpp
Normal file
265
src/core/arm/jit_x64/jit_x64.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
432
src/core/arm/jit_x64/jit_x64.h
Normal file
432
src/core/arm/jit_x64/jit_x64.h
Normal 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
|
383
src/core/arm/jit_x64/reg_alloc.cpp
Normal file
383
src/core/arm/jit_x64/reg_alloc.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
210
src/core/arm/jit_x64/reg_alloc.h
Normal file
210
src/core/arm/jit_x64/reg_alloc.h
Normal 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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
47
src/tests/CMakeLists.txt
Normal 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()
|
39
src/tests/core/arm/jit_x64/fuzz_arm_branch.cpp
Normal file
39
src/tests/core/arm/jit_x64/fuzz_arm_branch.cpp
Normal 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);
|
||||
}
|
||||
}
|
206
src/tests/core/arm/jit_x64/fuzz_arm_common.cpp
Normal file
206
src/tests/core/arm/jit_x64/fuzz_arm_common.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
22
src/tests/core/arm/jit_x64/fuzz_arm_common.h
Normal file
22
src/tests/core/arm/jit_x64/fuzz_arm_common.h
Normal 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);
|
115
src/tests/core/arm/jit_x64/fuzz_arm_data_processing.cpp
Normal file
115
src/tests/core/arm/jit_x64/fuzz_arm_data_processing.cpp
Normal 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);
|
||||
}
|
||||
}
|
156
src/tests/core/arm/jit_x64/fuzz_arm_load_store.cpp
Normal file
156
src/tests/core/arm/jit_x64/fuzz_arm_load_store.cpp
Normal 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);
|
||||
}
|
335
src/tests/core/arm/jit_x64/fuzz_thumb.cpp
Normal file
335
src/tests/core/arm/jit_x64/fuzz_thumb.cpp
Normal 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);
|
||||
}
|
20
src/tests/core/arm/jit_x64/rand_int.h
Normal file
20
src/tests/core/arm/jit_x64/rand_int.h
Normal 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
9
src/tests/tests.cpp
Normal 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.
|
Loading…
Reference in a new issue