First Commit
This commit is contained in:
commit
65df15633d
55 changed files with 18405 additions and 0 deletions
58
CMakeLists.txt
Normal file
58
CMakeLists.txt
Normal file
|
@ -0,0 +1,58 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
project(dynarmic)
|
||||
|
||||
# Dynarmic project options
|
||||
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
|
||||
|
||||
# Compiler flags
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14 -Wall -Werror -Wextra -pedantic -Wfatal-errors -Wno-unused-parameter -static-libgcc -static-libstdc++")
|
||||
|
||||
# Arch detection
|
||||
include(CheckSymbolExists)
|
||||
function(detect_architecture symbol arch)
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
set(CMAKE_REQUIRED_QUIET 1)
|
||||
check_symbol_exists("${symbol}" "" ARCHITECTURE_${arch})
|
||||
unset(CMAKE_REQUIRED_QUIET)
|
||||
|
||||
# The output variable needs to be unique across invocations otherwise
|
||||
# CMake's crazy scope rules will keep it defined
|
||||
if (ARCHITECTURE_${arch})
|
||||
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
|
||||
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
|
||||
add_definitions(-DARCHITECTURE_${arch}=1)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
if (MSVC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
endif()
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
set(ARCHITECTURE "GENERIC")
|
||||
set(ARCHITECTURE_GENERIC 1)
|
||||
add_definitions(-DARCHITECTURE_GENERIC=1)
|
||||
endif()
|
||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
|
||||
# Include Boost
|
||||
if(DYNARMIC_USE_SYSTEM_BOOST)
|
||||
find_package(Boost 1.57.0 REQUIRED)
|
||||
else()
|
||||
if(NOT Boost_INCLUDE_DIRS)
|
||||
message(FATAL_ERROR "Please provide a path to a boost installation using Boost_INCLUDE_DIRS")
|
||||
endif()
|
||||
endif()
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
# Include Catch
|
||||
include_directories(externals/catch)
|
||||
enable_testing(true) # Enables unit-testing.
|
||||
|
||||
# Dynarmic project files
|
||||
add_subdirectory(src)
|
27
README.md
Normal file
27
README.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
Dynarmic
|
||||
========
|
||||
|
||||
An (eventual) dynamic recompiler for ARM6K. The code is a mess.
|
||||
|
||||
A lot of optimization work can be done (it currently produces bad code, worse than that non-IR JIT).
|
||||
|
||||
Plans
|
||||
-----
|
||||
|
||||
### Near-term
|
||||
|
||||
* Actually finish the translators off
|
||||
* Get everything working
|
||||
* Redundant Get/Set elimination
|
||||
* Handle immediates properly
|
||||
* Allow ARM flags to be stored in host flags
|
||||
|
||||
### Medium-term
|
||||
|
||||
* Undo strength-reduction (invert some common ARM idioms)
|
||||
* Other optimizations
|
||||
|
||||
### Long-term
|
||||
|
||||
* ARMv7A support
|
||||
* ARMv5 support
|
10509
externals/catch/catch.hpp
vendored
Normal file
10509
externals/catch/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
7
src/CMakeLists.txt
Normal file
7
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
include_directories(.)
|
||||
|
||||
add_subdirectory(backend_x64)
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(daggen)
|
||||
add_subdirectory(frontend_arm)
|
||||
add_subdirectory(tests)
|
18
src/backend_x64/CMakeLists.txt
Normal file
18
src/backend_x64/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
set(SRCS
|
||||
emit_x64.cpp
|
||||
reg_alloc.cpp
|
||||
routines.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
../interface/interface.h
|
||||
emit_x64.h
|
||||
jitstate.h
|
||||
reg_alloc.h
|
||||
routines.h
|
||||
)
|
||||
|
||||
source_group(frontend_x64 FILES ${SRCS} ${HEADERS})
|
||||
add_library(dynarmic_backend_x64 STATIC ${SRCS} ${HEADERS})
|
||||
target_link_libraries(dynarmic_backend_x64 dynarmic_common)
|
||||
set_target_properties(dynarmic_backend_x64 PROPERTIES LINKER_LANGUAGE CXX)
|
311
src/backend_x64/emit_x64.cpp
Normal file
311
src/backend_x64/emit_x64.cpp
Normal file
|
@ -0,0 +1,311 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "backend_x64/emit_x64.h"
|
||||
#include "common/x64/emitter.h"
|
||||
|
||||
// TODO: More optimal use of immediates.
|
||||
// TODO: Have ARM flags in host flags and not have them use up GPR registers unless necessary.
|
||||
// TODO: Actually implement that proper instruction selector you've always wanted to sweetheart.
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
// Mapping from opcode to Emit* member function.
|
||||
const static std::map<IR::Opcode, void (EmitX64::*)(IR::Value*)> emit_fns {
|
||||
#define OPCODE(name, type, ...) { IR::Opcode::name, &EmitX64::Emit##name },
|
||||
#include "frontend_arm/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
|
||||
static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) {
|
||||
// Gets first found use.
|
||||
auto uses = inst->GetUses();
|
||||
auto iter = std::find_if(uses.begin(), uses.end(), [opcode](const auto& use){ return use->GetOpcode() == opcode; });
|
||||
return iter == uses.end() ? nullptr : reinterpret_cast<IR::Inst*>(iter->get());
|
||||
}
|
||||
|
||||
CodePtr EmitX64::Emit(Dynarmic::IR::Block block) {
|
||||
code->INT3();
|
||||
CodePtr code_ptr = code->GetCodePtr();
|
||||
|
||||
// Call Emit* member function for each instruction.
|
||||
for (const auto& value : block.instructions) {
|
||||
if (inhibit_emission.count(value.get()) != 0)
|
||||
continue;
|
||||
|
||||
(this->*emit_fns.at(value->GetOpcode()))(value.get());
|
||||
reg_alloc.EndOfAllocScope();
|
||||
}
|
||||
|
||||
EmitReturnToDispatch();
|
||||
|
||||
return code_ptr;
|
||||
}
|
||||
|
||||
void EmitX64::EmitImmU1(IR::Value*) {
|
||||
ASSERT_MSG(0, "Unimplemented");
|
||||
}
|
||||
|
||||
void EmitX64::EmitImmU8(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::ImmU8*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), Imm32(value->value));
|
||||
}
|
||||
|
||||
void EmitX64::EmitImmU32(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::ImmU32*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), Imm32(value->value));
|
||||
}
|
||||
|
||||
void EmitX64::EmitImmRegRef(IR::Value*) {
|
||||
return; // No need to do anything.
|
||||
}
|
||||
|
||||
void EmitX64::EmitGetRegister(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
auto regref = reinterpret_cast<IR::ImmRegRef*>(value->GetArg(0).get());
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Reg) + static_cast<size_t>(regref->value) * sizeof(u32)));
|
||||
}
|
||||
|
||||
void EmitX64::EmitSetRegister(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
auto regref = reinterpret_cast<IR::ImmRegRef*>(value->GetArg(0).get());
|
||||
|
||||
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(1).get());
|
||||
|
||||
code->MOV(32, MDisp(R15, offsetof(JitState, Reg) + static_cast<size_t>(regref->value) * sizeof(u32)), R(to_store));
|
||||
}
|
||||
|
||||
void EmitX64::EmitGetNFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
||||
code->SHR(32, R(result), Imm8(31));
|
||||
}
|
||||
|
||||
void EmitX64::EmitSetNFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
|
||||
|
||||
code->SHL(32, R(to_store), Imm8(31));
|
||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 31)));
|
||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
||||
}
|
||||
|
||||
void EmitX64::EmitGetZFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
||||
code->SHR(32, R(result), Imm8(30));
|
||||
code->AND(32, R(result), Imm32(1));
|
||||
}
|
||||
|
||||
void EmitX64::EmitSetZFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
|
||||
|
||||
code->SHL(32, R(to_store), Imm8(30));
|
||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 30)));
|
||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
||||
}
|
||||
|
||||
void EmitX64::EmitGetCFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
||||
code->SHR(32, R(result), Imm8(29));
|
||||
code->AND(32, R(result), Imm32(1));
|
||||
}
|
||||
|
||||
void EmitX64::EmitSetCFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
|
||||
|
||||
code->SHL(32, R(to_store), Imm8(29));
|
||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 29)));
|
||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
||||
}
|
||||
|
||||
void EmitX64::EmitGetVFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.DefRegister(value);
|
||||
|
||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
||||
code->SHR(32, R(result), Imm8(28));
|
||||
code->AND(32, R(result), Imm32(1));
|
||||
}
|
||||
|
||||
void EmitX64::EmitSetVFlag(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
|
||||
|
||||
code->SHL(32, R(to_store), Imm8(28));
|
||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 28)));
|
||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
||||
}
|
||||
|
||||
void EmitX64::EmitGetCarryFromOp(IR::Value*) {
|
||||
ASSERT_MSG(0, "should never happen");
|
||||
}
|
||||
|
||||
void EmitX64::EmitLeastSignificantByte(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
}
|
||||
|
||||
void EmitX64::EmitMostSignificantBit(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
|
||||
code->SHL(32, R(result), Imm8(31));
|
||||
}
|
||||
|
||||
void EmitX64::EmitIsZero(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
|
||||
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
|
||||
code->TEST(32, R(result), R(result));
|
||||
code->SETcc(CCFlags::CC_E, R(result));
|
||||
code->MOVZX(32, 8, result, R(result));
|
||||
}
|
||||
|
||||
void EmitX64::EmitLogicalShiftLeft(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp);
|
||||
|
||||
// TODO: Consider using BMI2 instructions like SHLX when arm-in-host flags is implemented.
|
||||
|
||||
if (!carry_inst) {
|
||||
X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX});
|
||||
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
X64Reg zero = reg_alloc.ScratchRegister();
|
||||
|
||||
// The 32-bit x64 SHL instruction masks the shift count by 0x1F before performing the shift.
|
||||
// ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros.
|
||||
|
||||
code->SHL(32, R(result), R(shift));
|
||||
code->XOR(32, R(zero), R(zero));
|
||||
code->CMP(8, R(shift), Imm8(32));
|
||||
code->CMOVcc(32, result, R(zero), CC_NB);
|
||||
} else {
|
||||
inhibit_emission.insert(carry_inst);
|
||||
|
||||
X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX});
|
||||
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
|
||||
|
||||
// TODO: Optimize this.
|
||||
|
||||
code->CMP(8, R(shift), Imm8(32));
|
||||
auto Rs_gt32 = code->J_CC(CC_A);
|
||||
auto Rs_eq32 = code->J_CC(CC_E);
|
||||
// if (Rs & 0xFF < 32) {
|
||||
code->BT(32, R(carry), Imm8(0)); // Set the carry flag for correct behaviour in the case when Rs & 0xFF == 0
|
||||
code->SHL(32, R(result), R(shift));
|
||||
code->SETcc(CC_C, R(carry));
|
||||
auto jmp_to_end_1 = code->J();
|
||||
// } else if (Rs & 0xFF > 32) {
|
||||
code->SetJumpTarget(Rs_gt32);
|
||||
code->XOR(32, R(result), R(result));
|
||||
code->XOR(32, R(carry), R(carry));
|
||||
auto jmp_to_end_2 = code->J();
|
||||
// } else if (Rs & 0xFF == 32) {
|
||||
code->SetJumpTarget(Rs_eq32);
|
||||
code->MOV(32, R(carry), R(result));
|
||||
code->AND(32, R(carry), Imm8(1));
|
||||
code->XOR(32, R(result), R(result));
|
||||
// }
|
||||
code->SetJumpTarget(jmp_to_end_1);
|
||||
code->SetJumpTarget(jmp_to_end_2);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitLogicalShiftRight(IR::Value* value_) {
|
||||
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||
auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp);
|
||||
|
||||
if (!carry_inst) {
|
||||
X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX});
|
||||
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
X64Reg zero = reg_alloc.ScratchRegister();
|
||||
|
||||
// The 32-bit x64 SHR instruction masks the shift count by 0x1F before performing the shift.
|
||||
// ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros.
|
||||
|
||||
code->SHR(32, R(result), R(shift));
|
||||
code->XOR(32, R(zero), R(zero));
|
||||
code->CMP(8, R(shift), Imm8(32));
|
||||
code->CMOVcc(32, result, R(zero), CC_NB);
|
||||
} else {
|
||||
inhibit_emission.insert(carry_inst);
|
||||
|
||||
X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX});
|
||||
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
|
||||
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
|
||||
|
||||
// TODO: Optimize this.
|
||||
|
||||
code->CMP(32, R(shift), Imm8(32));
|
||||
auto Rs_gt32 = code->J_CC(CC_A);
|
||||
auto Rs_eq32 = code->J_CC(CC_E);
|
||||
// if (Rs & 0xFF == 0) goto end;
|
||||
code->TEST(32, R(shift), R(shift));
|
||||
auto Rs_zero = code->J_CC(CC_Z);
|
||||
// if (Rs & 0xFF < 32) {
|
||||
code->SHR(32, R(result), R(shift));
|
||||
code->SETcc(CC_C, R(carry));
|
||||
auto jmp_to_end_1 = code->J();
|
||||
// } else if (Rs & 0xFF > 32) {
|
||||
code->SetJumpTarget(Rs_gt32);
|
||||
code->MOV(32, R(result), Imm32(0));
|
||||
code->MOV(8, R(carry), Imm8(0));
|
||||
auto jmp_to_end_2 = code->J();
|
||||
// } else if (Rs & 0xFF == 32) {
|
||||
code->SetJumpTarget(Rs_eq32);
|
||||
code->BT(32, R(result), Imm8(31));
|
||||
code->SETcc(CC_C, R(carry));
|
||||
code->MOV(32, R(result), Imm32(0));
|
||||
// }
|
||||
code->SetJumpTarget(jmp_to_end_1);
|
||||
code->SetJumpTarget(jmp_to_end_2);
|
||||
code->SetJumpTarget(Rs_zero);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitReturnToDispatch() {
|
||||
code->JMP(routines->RunCodeReturnAddress(), true);
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
58
src/backend_x64/emit_x64.h
Normal file
58
src/backend_x64/emit_x64.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "backend_x64/reg_alloc.h"
|
||||
#include "backend_x64/routines.h"
|
||||
#include "common/x64/emitter.h"
|
||||
#include "frontend_arm/ir/ir.h"
|
||||
#include "interface/interface.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
class EmitX64 final {
|
||||
public:
|
||||
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb) : code(code), reg_alloc(code), routines(routines), cb(cb) {}
|
||||
|
||||
CodePtr Emit(IR::Block ir);
|
||||
|
||||
void EmitImmU1(IR::Value* value);
|
||||
void EmitImmU8(IR::Value* value);
|
||||
void EmitImmU32(IR::Value* value);
|
||||
void EmitImmRegRef(IR::Value* value);
|
||||
void EmitGetRegister(IR::Value* value);
|
||||
void EmitSetRegister(IR::Value* value);
|
||||
void EmitGetNFlag(IR::Value* value);
|
||||
void EmitSetNFlag(IR::Value* value);
|
||||
void EmitGetZFlag(IR::Value* value);
|
||||
void EmitSetZFlag(IR::Value* value);
|
||||
void EmitGetCFlag(IR::Value* value);
|
||||
void EmitSetCFlag(IR::Value* value);
|
||||
void EmitGetVFlag(IR::Value* value);
|
||||
void EmitSetVFlag(IR::Value* value);
|
||||
void EmitGetCarryFromOp(IR::Value* value);
|
||||
void EmitLeastSignificantByte(IR::Value* value);
|
||||
void EmitMostSignificantBit(IR::Value* value);
|
||||
void EmitIsZero(IR::Value* value);
|
||||
void EmitLogicalShiftLeft(IR::Value* value);
|
||||
void EmitLogicalShiftRight(IR::Value* value);
|
||||
|
||||
void EmitReturnToDispatch();
|
||||
|
||||
private:
|
||||
std::set<IR::Value*> inhibit_emission;
|
||||
Gen::XEmitter* code;
|
||||
RegAlloc reg_alloc;
|
||||
Routines* routines;
|
||||
UserCallbacks cb;
|
||||
};
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
32
src/backend_x64/jitstate.h
Normal file
32
src/backend_x64/jitstate.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
constexpr size_t SpillCount = 32;
|
||||
|
||||
struct JitState {
|
||||
u32 Cpsr;
|
||||
std::array<u32, 16> Reg{}; // Current register file.
|
||||
// TODO: Mode-specific register sets unimplemented.
|
||||
|
||||
std::array<u32, SpillCount> Spill{}; // Spill.
|
||||
|
||||
// For internal use (See: Routines::RunCode)
|
||||
u64 save_host_RSP;
|
||||
s64 cycles_remaining;
|
||||
};
|
||||
|
||||
using CodePtr = const u8*;
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
204
src/backend_x64/reg_alloc.cpp
Normal file
204
src/backend_x64/reg_alloc.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include "backend_x64/reg_alloc.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/x64/emitter.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
const static std::map<HostLoc, Gen::X64Reg> hostloc_to_x64 = {
|
||||
{ HostLoc::RAX, Gen::RAX },
|
||||
{ HostLoc::RBX, Gen::RBX },
|
||||
{ HostLoc::RCX, Gen::RCX },
|
||||
{ HostLoc::RDX, Gen::RDX },
|
||||
{ HostLoc::RSI, Gen::RSI },
|
||||
{ HostLoc::RDI, Gen::RDI },
|
||||
{ HostLoc::RBP, Gen::RBP },
|
||||
{ HostLoc::RSP, Gen::RSP },
|
||||
{ HostLoc::R8, Gen::R8 },
|
||||
{ HostLoc::R9, Gen::R9 },
|
||||
{ HostLoc::R10, Gen::R10 },
|
||||
{ HostLoc::R11, Gen::R11 },
|
||||
{ HostLoc::R12, Gen::R12 },
|
||||
{ HostLoc::R13, Gen::R13 },
|
||||
{ HostLoc::R14, Gen::R14 },
|
||||
};
|
||||
|
||||
static Gen::OpArg SpillToOpArg(HostLoc loc) {
|
||||
ASSERT(HostLocIsSpill(loc));
|
||||
|
||||
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
|
||||
return Gen::MDisp(Gen::R15, offsetof(JitState, Spill) + i * sizeof(u32));
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::DefRegister(IR::Value* def_value, std::initializer_list<HostLoc> desired_locations) {
|
||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
ASSERT_MSG(remaining_uses.find(def_value) == remaining_uses.end(), "def_value has already been defined");
|
||||
ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined");
|
||||
|
||||
HostLoc location = SelectARegister(desired_locations);
|
||||
|
||||
if (IsRegisterOccupied(location)) {
|
||||
SpillRegister(location);
|
||||
}
|
||||
|
||||
// Update state
|
||||
hostloc_state[location] = HostLocState::Def;
|
||||
hostloc_to_value[location] = def_value;
|
||||
remaining_uses[def_value] = def_value->NumUses();
|
||||
|
||||
return hostloc_to_x64.at(location);
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations) {
|
||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
ASSERT_MSG(remaining_uses.find(def_value) == remaining_uses.end(), "def_value has already been defined");
|
||||
ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined");
|
||||
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
||||
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
||||
|
||||
// TODO: Optimize the case when this is the last use_value use.
|
||||
Gen::X64Reg use_reg = UseRegister(use_value);
|
||||
Gen::X64Reg def_reg = DefRegister(def_value, desired_locations);
|
||||
code->MOV(32, Gen::R(def_reg), Gen::R(use_reg));
|
||||
return def_reg;
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations) {
|
||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
||||
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
||||
ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)");
|
||||
|
||||
HostLoc current_location = ValueLocations(use_value).front();
|
||||
auto iter = std::find(desired_locations.begin(), desired_locations.end(), current_location);
|
||||
if (iter != desired_locations.end()) {
|
||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle || hostloc_state[current_location] == HostLocState::Use);
|
||||
|
||||
// Update state
|
||||
hostloc_state[current_location] = HostLocState::Use;
|
||||
remaining_uses[use_value]--;
|
||||
|
||||
return hostloc_to_x64.at(current_location);
|
||||
}
|
||||
|
||||
HostLoc new_location = SelectARegister(desired_locations);
|
||||
|
||||
if (HostLocIsSpill(current_location)) {
|
||||
if (IsRegisterOccupied(new_location)) {
|
||||
SpillRegister(new_location);
|
||||
}
|
||||
|
||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), SpillToOpArg(current_location));
|
||||
|
||||
hostloc_state[new_location] = HostLocState::Use;
|
||||
hostloc_to_value[new_location] = use_value;
|
||||
remaining_uses[use_value]--;
|
||||
} else if (HostLocIsRegister(current_location)) {
|
||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
||||
|
||||
code->XCHG(32, Gen::R(hostloc_to_x64.at(current_location)), Gen::R(hostloc_to_x64.at(new_location)));
|
||||
|
||||
hostloc_state[new_location] = HostLocState::Use;
|
||||
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
|
||||
remaining_uses[use_value]--;
|
||||
} else {
|
||||
ASSERT_MSG(0, "Invalid current_location");
|
||||
}
|
||||
|
||||
return hostloc_to_x64.at(new_location);
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
|
||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
|
||||
HostLoc location = SelectARegister(desired_locations);
|
||||
|
||||
if (IsRegisterOccupied(location)) {
|
||||
SpillRegister(location);
|
||||
}
|
||||
|
||||
// Update state
|
||||
hostloc_state[location] = HostLocState::Scratch;
|
||||
|
||||
return hostloc_to_x64.at(location);
|
||||
}
|
||||
|
||||
HostLoc RegAlloc::SelectARegister(std::initializer_list<HostLoc> desired_locations) const {
|
||||
std::vector<HostLoc> candidates = desired_locations;
|
||||
|
||||
// Find all locations that have not been allocated..
|
||||
auto allocated_locs = std::partition(candidates.begin(), candidates.end(), [this](auto loc){
|
||||
return !this->IsRegisterAllocated(loc);
|
||||
});
|
||||
candidates.erase(allocated_locs, candidates.end());
|
||||
ASSERT_MSG(!candidates.empty(), "All candidate registers have already been allocated");
|
||||
|
||||
// Selects the best location out of the available locations.
|
||||
// TODO: Actually do LRU or something. Currently we just try to pick something without a value if possible.
|
||||
|
||||
std::partition(candidates.begin(), candidates.end(), [this](auto loc){
|
||||
return !this->IsRegisterOccupied(loc);
|
||||
});
|
||||
|
||||
return candidates.front();
|
||||
}
|
||||
|
||||
std::vector<HostLoc> RegAlloc::ValueLocations(IR::Value* value) const {
|
||||
std::vector<HostLoc> locations;
|
||||
|
||||
for (const auto& iter : hostloc_to_value)
|
||||
if (iter.second == value)
|
||||
locations.emplace_back(iter.first);
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
bool RegAlloc::IsRegisterOccupied(HostLoc loc) const {
|
||||
return hostloc_to_value.find(loc) != hostloc_to_value.end() && hostloc_to_value.at(loc) != nullptr;
|
||||
}
|
||||
|
||||
bool RegAlloc::IsRegisterAllocated(HostLoc loc) const {
|
||||
return hostloc_state.find(loc) != hostloc_state.end() && hostloc_state.at(loc) != HostLocState::Idle;
|
||||
}
|
||||
|
||||
void RegAlloc::SpillRegister(HostLoc loc) {
|
||||
ASSERT_MSG(HostLocIsRegister(loc), "Only registers can be spilled");
|
||||
ASSERT_MSG(hostloc_state[loc] == HostLocState::Idle, "Allocated registers cannot be spilled");
|
||||
ASSERT_MSG(IsRegisterOccupied(loc), "There is no need to spill unoccupied registers");
|
||||
ASSERT_MSG(!IsRegisterAllocated(loc), "Registers that have been allocated must not be spilt");
|
||||
|
||||
HostLoc new_loc = FindFreeSpill();
|
||||
|
||||
code->MOV(32, SpillToOpArg(new_loc), Gen::R(hostloc_to_x64.at(loc)));
|
||||
|
||||
hostloc_to_value[new_loc] = hostloc_to_value[loc];
|
||||
hostloc_to_value[loc] = nullptr;
|
||||
}
|
||||
|
||||
HostLoc RegAlloc::FindFreeSpill() const {
|
||||
for (size_t i = 0; i < SpillCount; i++)
|
||||
if (!IsRegisterOccupied(HostLocSpill(i)))
|
||||
return HostLocSpill(i);
|
||||
|
||||
ASSERT_MSG(0, "All spill locations are full");
|
||||
}
|
||||
|
||||
void RegAlloc::EndOfAllocScope() {
|
||||
hostloc_state.clear();
|
||||
|
||||
for (auto& iter : hostloc_to_value)
|
||||
if (iter.second && remaining_uses[iter.second] == 0)
|
||||
iter.second = nullptr;
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
99
src/backend_x64/reg_alloc.h
Normal file
99
src/backend_x64/reg_alloc.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/x64/emitter.h"
|
||||
#include "frontend_arm/ir/ir.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
enum class HostLoc {
|
||||
RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14,
|
||||
CF, PF, AF, ZF, SF, OF,
|
||||
FirstSpill,
|
||||
};
|
||||
|
||||
enum class HostLocState {
|
||||
Idle, Def, Use, Scratch
|
||||
};
|
||||
|
||||
inline bool HostLocIsRegister(HostLoc reg) {
|
||||
return reg >= HostLoc::RAX && reg <= HostLoc::R14;
|
||||
}
|
||||
|
||||
inline bool HostLocIsFlag(HostLoc reg) {
|
||||
return reg >= HostLoc::CF && reg <= HostLoc::OF;
|
||||
}
|
||||
|
||||
inline HostLoc HostLocSpill(size_t i) {
|
||||
ASSERT_MSG(i < SpillCount, "Invalid spill");
|
||||
return static_cast<HostLoc>(static_cast<int>(HostLoc::FirstSpill) + i);
|
||||
}
|
||||
|
||||
inline bool HostLocIsSpill(HostLoc reg) {
|
||||
return reg >= HostLoc::FirstSpill && reg <= HostLocSpill(SpillCount - 1);
|
||||
}
|
||||
|
||||
constexpr std::initializer_list<HostLoc> hostloc_any_register = {
|
||||
HostLoc::RAX,
|
||||
HostLoc::RBX,
|
||||
HostLoc::RCX,
|
||||
HostLoc::RDX,
|
||||
HostLoc::RSI,
|
||||
HostLoc::RDI,
|
||||
HostLoc::RBP,
|
||||
HostLoc::RSP,
|
||||
HostLoc::R8,
|
||||
HostLoc::R9,
|
||||
HostLoc::R10,
|
||||
HostLoc::R11,
|
||||
HostLoc::R12,
|
||||
HostLoc::R13,
|
||||
HostLoc::R14
|
||||
};
|
||||
|
||||
class RegAlloc final {
|
||||
public:
|
||||
RegAlloc(Gen::XEmitter* code) : code(code) {}
|
||||
|
||||
/// Late-def
|
||||
Gen::X64Reg DefRegister(IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
/// Early-use, Late-def
|
||||
Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
/// Early-use
|
||||
Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
/// Early-def, Late-use, single-use
|
||||
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||
|
||||
// TODO: Values in host flags
|
||||
|
||||
void EndOfAllocScope();
|
||||
|
||||
private:
|
||||
HostLoc SelectARegister(std::initializer_list<HostLoc> desired_locations) const;
|
||||
std::vector<HostLoc> ValueLocations(IR::Value* value) const;
|
||||
bool IsRegisterOccupied(HostLoc loc) const;
|
||||
bool IsRegisterAllocated(HostLoc loc) const;
|
||||
|
||||
void SpillRegister(HostLoc loc);
|
||||
HostLoc FindFreeSpill() const;
|
||||
|
||||
Gen::XEmitter* code = nullptr;
|
||||
|
||||
using mapping_map_t = std::map<HostLoc, IR::Value*>;
|
||||
mapping_map_t hostloc_to_value;
|
||||
std::map<HostLoc, HostLocState> hostloc_state;
|
||||
std::map<IR::Value*, size_t> remaining_uses;
|
||||
};
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
61
src/backend_x64/routines.cpp
Normal file
61
src/backend_x64/routines.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "backend_x64/routines.h"
|
||||
#include "common/x64/abi.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
Routines::Routines() {
|
||||
AllocCodeSpace(1024);
|
||||
|
||||
GenRunCode();
|
||||
GenReturnFromRunCode();
|
||||
}
|
||||
|
||||
size_t Routines::RunCode(JitState* jit_state, CodePtr basic_block, size_t cycles_to_run) const {
|
||||
ASSERT(cycles_to_run <= std::numeric_limits<decltype(jit_state->cycles_remaining)>::max());
|
||||
|
||||
jit_state->cycles_remaining = cycles_to_run;
|
||||
run_code(jit_state, basic_block);
|
||||
return cycles_to_run - jit_state->cycles_remaining; // Return number of cycles actually run.
|
||||
}
|
||||
|
||||
CodePtr Routines::RunCodeReturnAddress() const {
|
||||
return return_from_run_code;
|
||||
}
|
||||
|
||||
void Routines::GenRunCode() {
|
||||
run_code = reinterpret_cast<RunCodeFuncType>(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, R(R15), R(ABI_PARAM1));
|
||||
MOV(64, MDisp(R15, offsetof(JitState, save_host_RSP)), R(RSP));
|
||||
|
||||
JMPptr(R(ABI_PARAM2));
|
||||
}
|
||||
|
||||
void Routines::GenReturnFromRunCode() {
|
||||
return_from_run_code = this->GetCodePtr();
|
||||
|
||||
MOV(64, R(RSP), MDisp(R15, offsetof(JitState, save_host_RSP)));
|
||||
ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
|
||||
RET();
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
33
src/backend_x64/routines.h
Normal file
33
src/backend_x64/routines.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/x64/emitter.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
class Routines final : private Gen::XCodeBlock {
|
||||
public:
|
||||
Routines();
|
||||
|
||||
size_t RunCode(JitState* jit_state, CodePtr basic_block, size_t cycles_to_run) const;
|
||||
CodePtr RunCodeReturnAddress() const;
|
||||
|
||||
private:
|
||||
using RunCodeFuncType = void(*)(JitState*, CodePtr);
|
||||
RunCodeFuncType run_code;
|
||||
void GenRunCode();
|
||||
|
||||
CodePtr return_from_run_code;
|
||||
void GenReturnFromRunCode();
|
||||
};
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
20
src/common/CMakeLists.txt
Normal file
20
src/common/CMakeLists.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
set(SRCS
|
||||
string_util.cpp
|
||||
x64/abi.cpp
|
||||
x64/cpu_detect.cpp
|
||||
x64/emitter.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
bit_util.h
|
||||
common_types.h
|
||||
mp.h
|
||||
string_util.h
|
||||
x64/abi.h
|
||||
x64/cpu_detect.h
|
||||
x64/emitter.h
|
||||
assert.h logging/log.h logging/log.cpp bit_set.h memory_util.h memory_util.cpp code_block.h)
|
||||
|
||||
source_group(common FILES ${SRCS} ${HEADERS})
|
||||
add_library(dynarmic_common STATIC ${SRCS} ${HEADERS})
|
||||
set_target_properties(dynarmic_common PROPERTIES LINKER_LANGUAGE CXX)
|
53
src/common/assert.h
Normal file
53
src/common/assert.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
|
||||
// For asserts we'd like to keep all the junk executed when an assert happens away from the
|
||||
// important code in the function. One way of doing this is to put all the relevant code inside a
|
||||
// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
|
||||
// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
|
||||
// template that calls the lambda. This seems to generate an extra instruction at the call-site
|
||||
// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
|
||||
// enough for our purposes.
|
||||
template <typename Fn>
|
||||
#if defined(_MSC_VER)
|
||||
__declspec(noinline, noreturn)
|
||||
#elif defined(__GNUC__)
|
||||
__attribute__((noinline, noreturn, cold))
|
||||
#endif
|
||||
static void assert_noinline_call(const Fn& fn) {
|
||||
fn();
|
||||
exit(1); // Keeps GCC's mouth shut about this actually returning
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do if (!(_a_)) { assert_noinline_call([] { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
throw ""; \
|
||||
}); } while (0)
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do if (!(_a_)) { assert_noinline_call([&] { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
|
||||
throw ""; \
|
||||
}); } while (0)
|
||||
|
||||
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
|
||||
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
|
||||
#else // not debug
|
||||
#define DEBUG_ASSERT(_a_)
|
||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__)
|
190
src/common/bit_set.h
Normal file
190
src/common/bit_set.h
Normal file
|
@ -0,0 +1,190 @@
|
|||
// This file is under the public domain.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#include <initializer_list>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
|
||||
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
|
||||
namespace Common {
|
||||
|
||||
// Helper functions:
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename T>
|
||||
static inline int CountSetBits(T v)
|
||||
{
|
||||
// from https://graphics.stanford.edu/~seander/bithacks.html
|
||||
// GCC has this built in, but MSVC's intrinsic will only emit the actual
|
||||
// POPCNT instruction, which we're not depending on
|
||||
v = v - ((v >> 1) & (T)~(T)0/3);
|
||||
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);
|
||||
v = (v + (v >> 4)) & (T)~(T)0/255*15;
|
||||
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(uint8_t val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(uint16_t val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(uint32_t val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(uint64_t val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
#else
|
||||
static inline int CountSetBits(uint8_t val) { return __builtin_popcount(val); }
|
||||
static inline int CountSetBits(uint16_t val) { return __builtin_popcount(val); }
|
||||
static inline int CountSetBits(uint32_t val) { return __builtin_popcount(val); }
|
||||
static inline int CountSetBits(uint64_t val) { return __builtin_popcountll(val); }
|
||||
static inline int LeastSignificantSetBit(uint8_t val) { return __builtin_ctz(val); }
|
||||
static inline int LeastSignificantSetBit(uint16_t val) { return __builtin_ctz(val); }
|
||||
static inline int LeastSignificantSetBit(uint32_t val) { return __builtin_ctz(val); }
|
||||
static inline int LeastSignificantSetBit(uint64_t val) { return __builtin_ctzll(val); }
|
||||
#endif
|
||||
|
||||
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
|
||||
// using the set bits of an integer to represent a set of integers. Like that
|
||||
// class, it acts like an array of bools:
|
||||
// BitSet32 bs;
|
||||
// bs[1] = true;
|
||||
// but also like the underlying integer ([0] = least significant bit):
|
||||
// BitSet32 bs2 = ...;
|
||||
// bs = (bs ^ bs2) & BitSet32(0xffff);
|
||||
// The following additional functionality is provided:
|
||||
// - Construction using an initializer list.
|
||||
// BitSet bs { 1, 2, 4, 8 };
|
||||
// - Efficiently iterating through the set bits:
|
||||
// for (int i : bs)
|
||||
// [i is the *index* of a set bit]
|
||||
// (This uses the appropriate CPU instruction to find the next set bit in one
|
||||
// operation.)
|
||||
// - Counting set bits using .Count() - see comment on that method.
|
||||
|
||||
// TODO: use constexpr when MSVC gets out of the Dark Ages
|
||||
|
||||
template <typename IntTy>
|
||||
class BitSet
|
||||
{
|
||||
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
|
||||
public:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
class Ref
|
||||
{
|
||||
public:
|
||||
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
|
||||
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
|
||||
operator bool() const { return (m_bs->m_val & m_mask) != 0; }
|
||||
bool operator=(bool set)
|
||||
{
|
||||
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
|
||||
return set;
|
||||
}
|
||||
private:
|
||||
BitSet* m_bs;
|
||||
IntTy m_mask;
|
||||
};
|
||||
|
||||
// A STL-like iterator is required to be able to use range-based for loops.
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
|
||||
Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
|
||||
Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; }
|
||||
int operator*() { return m_bit; }
|
||||
Iterator& operator++()
|
||||
{
|
||||
if (m_val == 0)
|
||||
{
|
||||
m_bit = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int bit = LeastSignificantSetBit(m_val);
|
||||
m_val &= ~(1 << bit);
|
||||
m_bit = bit;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Iterator operator++(int _)
|
||||
{
|
||||
Iterator other(*this);
|
||||
++*this;
|
||||
return other;
|
||||
}
|
||||
bool operator==(Iterator other) const { return m_bit == other.m_bit; }
|
||||
bool operator!=(Iterator other) const { return m_bit != other.m_bit; }
|
||||
private:
|
||||
IntTy m_val;
|
||||
int m_bit;
|
||||
};
|
||||
|
||||
BitSet() : m_val(0) {}
|
||||
explicit BitSet(IntTy val) : m_val(val) {}
|
||||
BitSet(std::initializer_list<int> init)
|
||||
{
|
||||
m_val = 0;
|
||||
for (int bit : init)
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
|
||||
static BitSet AllTrue(size_t count)
|
||||
{
|
||||
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
|
||||
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
|
||||
bool operator==(BitSet other) const { return m_val == other.m_val; }
|
||||
bool operator!=(BitSet other) const { return m_val != other.m_val; }
|
||||
bool operator<(BitSet other) const { return m_val < other.m_val; }
|
||||
bool operator>(BitSet other) const { return m_val > other.m_val; }
|
||||
BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); }
|
||||
BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); }
|
||||
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); }
|
||||
BitSet operator~() const { return BitSet(~m_val); }
|
||||
BitSet& operator|=(BitSet other) { return *this = *this | other; }
|
||||
BitSet& operator&=(BitSet other) { return *this = *this & other; }
|
||||
BitSet& operator^=(BitSet other) { return *this = *this ^ other; }
|
||||
operator uint32_t() = delete;
|
||||
operator bool() { return m_val != 0; }
|
||||
|
||||
// Warning: Even though on modern CPUs this is a single fast instruction,
|
||||
// Dolphin's official builds do not currently assume POPCNT support on x86,
|
||||
// so slower explicit bit twiddling is generated. Still should generally
|
||||
// be faster than a loop.
|
||||
unsigned int Count() const { return CountSetBits(m_val); }
|
||||
|
||||
Iterator begin() const { Iterator it(m_val, 0); return ++it; }
|
||||
Iterator end() const { return Iterator(m_val, -1); }
|
||||
|
||||
IntTy m_val;
|
||||
};
|
||||
|
||||
} // Common
|
||||
|
||||
typedef Common::BitSet<uint8_t> BitSet8;
|
||||
typedef Common::BitSet<uint16_t> BitSet16;
|
||||
typedef Common::BitSet<uint32_t> BitSet32;
|
||||
typedef Common::BitSet<uint64_t> BitSet64;
|
55
src/common/bit_util.h
Normal file
55
src/common/bit_util.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Common {
|
||||
|
||||
/// 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 bit_count, typename T>
|
||||
inline T SignExtend(const T value) {
|
||||
static_assert(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T");
|
||||
|
||||
constexpr T mask = static_cast<T>(1ULL << bit_count) - 1;
|
||||
const T signbit = Bit<bit_count - 1>(value);
|
||||
if (signbit != 0) {
|
||||
return value | ~mask;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Dynarmic
|
92
src/common/code_block.h
Normal file
92
src/common/code_block.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_util.h"
|
||||
|
||||
// Everything that needs to generate code should inherit from this.
|
||||
// You get memory management for free, plus, you can use all emitter functions without
|
||||
// having to prefix them with gen-> or something similar.
|
||||
// Example implementation:
|
||||
// class JIT : public CodeBlock<ARMXEmitter> {}
|
||||
template<class T> class CodeBlock : public T
|
||||
{
|
||||
private:
|
||||
// A privately used function to set the executable RAM space to something invalid.
|
||||
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction
|
||||
virtual void PoisonMemory() = 0;
|
||||
|
||||
protected:
|
||||
u8 *region;
|
||||
size_t region_size;
|
||||
|
||||
public:
|
||||
CodeBlock() : region(nullptr), region_size(0) {}
|
||||
virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
|
||||
|
||||
CodeBlock(const CodeBlock&) = delete;
|
||||
CodeBlock& operator=(const CodeBlock&) = delete;
|
||||
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int size)
|
||||
{
|
||||
region_size = size;
|
||||
region = (u8*)AllocateExecutableMemory(region_size);
|
||||
T::SetCodePtr(region);
|
||||
}
|
||||
|
||||
// Always clear code space with breakpoints, so that if someone accidentally executes
|
||||
// uninitialized, it just breaks into the debugger.
|
||||
void ClearCodeSpace()
|
||||
{
|
||||
PoisonMemory();
|
||||
ResetCodePtr();
|
||||
}
|
||||
|
||||
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
||||
void FreeCodeSpace()
|
||||
{
|
||||
#ifdef __SYMBIAN32__
|
||||
ResetExecutableMemory(region);
|
||||
#else
|
||||
FreeMemoryPages(region, region_size);
|
||||
#endif
|
||||
region = nullptr;
|
||||
region_size = 0;
|
||||
}
|
||||
|
||||
bool IsInSpace(const u8 *ptr)
|
||||
{
|
||||
return (ptr >= region) && (ptr < (region + region_size));
|
||||
}
|
||||
|
||||
// Cannot currently be undone. Will write protect the entire code region.
|
||||
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
|
||||
void WriteProtect()
|
||||
{
|
||||
WriteProtectMemory(region, region_size, true);
|
||||
}
|
||||
|
||||
void ResetCodePtr()
|
||||
{
|
||||
T::SetCodePtr(region);
|
||||
}
|
||||
|
||||
size_t GetSpaceLeft() const
|
||||
{
|
||||
return region_size - (T::GetCodePtr() - region);
|
||||
}
|
||||
|
||||
u8 *GetBasePtr() {
|
||||
return region;
|
||||
}
|
||||
|
||||
size_t GetOffset(const u8 *ptr) const {
|
||||
return ptr - region;
|
||||
}
|
||||
};
|
29
src/common/common_types.h
Normal file
29
src/common/common_types.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using uptr = std::uintptr_t;
|
||||
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
using sptr = std::intptr_t;
|
||||
|
||||
using size_t = std::size_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide");
|
||||
static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide");
|
21
src/common/logging/log.cpp
Normal file
21
src/common/logging/log.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Log {
|
||||
|
||||
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
} // namespace Log
|
68
src/common/logging/log.h
Normal file
68
src/common/logging/log.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2014 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 Log {
|
||||
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
/// pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
/// completed.
|
||||
Critical, ///< Major problems during execution that threathen the stability of the entire
|
||||
/// application.
|
||||
|
||||
Count ///< Total number of logging levels
|
||||
};
|
||||
|
||||
typedef u8 ClassType;
|
||||
|
||||
/**
|
||||
* Specifies the sub-system that generated the log message.
|
||||
*
|
||||
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in backend.cpp.
|
||||
*/
|
||||
enum class Class : ClassType {
|
||||
Log,
|
||||
Common,
|
||||
Common_Memory,
|
||||
Debug,
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
/// Logs a message to the global logger.
|
||||
void LogMessage(Class log_class, Level log_level,
|
||||
const char* filename, unsigned int line_nr, const char* function,
|
||||
#ifdef _MSC_VER
|
||||
_Printf_format_string_
|
||||
#endif
|
||||
const char* format, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(gnu_printf, 6, 7)))
|
||||
#endif
|
||||
;
|
||||
|
||||
} // namespace Log
|
||||
|
||||
#define LOG_GENERIC(log_class, log_level, ...) \
|
||||
::Log::LogMessage(log_class, log_level, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define LOG_TRACE( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Trace, __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_TRACE( log_class, ...) (void(0))
|
||||
#endif
|
||||
|
||||
#define LOG_DEBUG( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Debug, __VA_ARGS__)
|
||||
#define LOG_INFO( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Info, __VA_ARGS__)
|
||||
#define LOG_WARNING( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Warning, __VA_ARGS__)
|
||||
#define LOG_ERROR( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
|
||||
#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
|
192
src/common/memory_util.cpp
Normal file
192
src/common/memory_util.cpp
Normal file
|
@ -0,0 +1,192 @@
|
|||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
||||
#endif
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
const char* GetLastErrorMsg()
|
||||
{
|
||||
static const size_t buff_size = 255;
|
||||
|
||||
#ifdef _WIN32
|
||||
static thread_local char err_str[buff_size] = {};
|
||||
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
err_str, buff_size, nullptr);
|
||||
#else
|
||||
static __thread char err_str[buff_size] = {};
|
||||
|
||||
// Thread safe (XSI-compliant)
|
||||
strerror_r(errno, err_str, buff_size);
|
||||
#endif
|
||||
|
||||
return err_str;
|
||||
}
|
||||
|
||||
|
||||
// This is purposely not a full wrapper for virtualalloc/mmap, but it
|
||||
// provides exactly the primitive operations that Dolphin needs.
|
||||
|
||||
void* AllocateExecutableMemory(size_t size, bool low)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#else
|
||||
static char* map_hint = nullptr;
|
||||
#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
|
||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
||||
// but if we hint that we want a low address it is very likely we will
|
||||
// get one.
|
||||
// An older version of this code used MAP_FIXED, but that has the side
|
||||
// effect of discarding already mapped pages that happen to be in the
|
||||
// requested virtual memory range (such as the emulated RAM, sometimes).
|
||||
if (low && (!map_hint))
|
||||
map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
, -1, 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
#else
|
||||
if (ptr == MAP_FAILED)
|
||||
{
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
|
||||
else
|
||||
{
|
||||
if (low)
|
||||
{
|
||||
map_hint += size;
|
||||
map_hint = (char*)round_page(map_hint); /* round up to the next page */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if EMU_ARCH_BITS == 64
|
||||
if ((u64)ptr >= 0x80000000 && low == true)
|
||||
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
|
||||
MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateAlignedMemory(size_t size,size_t alignment)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* ptr = _aligned_malloc(size,alignment);
|
||||
#else
|
||||
void* ptr = nullptr;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* ptr, size_t size)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n%s", GetLastErrorMsg());
|
||||
#else
|
||||
munmap(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAlignedMemory(void* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n%s", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
|
||||
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n%s", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MemUsage()
|
||||
{
|
||||
return "";
|
||||
}
|
19
src/common/memory_util.h
Normal file
19
src/common/memory_util.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
void* AllocateExecutableMemory(size_t size, bool low = true);
|
||||
void* AllocateMemoryPages(size_t size);
|
||||
void FreeMemoryPages(void* ptr, size_t size);
|
||||
void* AllocateAlignedMemory(size_t size,size_t alignment);
|
||||
void FreeAlignedMemory(void* ptr);
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
|
||||
std::string MemUsage();
|
||||
|
||||
inline int GetPageSize() { return 4096; }
|
26
src/common/mp.h
Normal file
26
src/common/mp.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace mp {
|
||||
|
||||
template<typename MemFnT, MemFnT fn>
|
||||
struct MemFnInfo;
|
||||
|
||||
/// This struct provides information about a member function pointer.
|
||||
template<typename T, typename ReturnT, typename ...Args, ReturnT (T::*fn)(Args...)>
|
||||
struct MemFnInfo<ReturnT (T::*)(Args...), fn> {
|
||||
using class_type = T;
|
||||
using return_type = ReturnT;
|
||||
static constexpr size_t args_count = sizeof...(Args);
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
} // namespace Dynarmic
|
36
src/common/string_util.cpp
Normal file
36
src/common/string_util.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Common {
|
||||
|
||||
std::string StringFromFormat(const char* format, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
int size = std::vsnprintf(nullptr, 0, format, args);
|
||||
va_end(args);
|
||||
|
||||
std::string str;
|
||||
str.resize(size+1);
|
||||
|
||||
va_start(args, format);
|
||||
std::vsnprintf(&str[0], str.size(), format, args);
|
||||
va_end(args);
|
||||
|
||||
str.resize(size);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Dynarmic
|
17
src/common/string_util.h
Normal file
17
src/common/string_util.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Common {
|
||||
|
||||
std::string StringFromFormat(const char* format, ...) __attribute__((format(gnu_printf, 1, 2)));
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Dynarmic
|
363
src/common/x64/abi.cpp
Normal file
363
src/common/x64/abi.cpp
Normal file
|
@ -0,0 +1,363 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "abi.h"
|
||||
#include "emitter.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
// Shared code between Win64 and Unix64
|
||||
|
||||
void XEmitter::ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size, size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp) {
|
||||
size_t shadow = 0;
|
||||
#if defined(_WIN32)
|
||||
shadow = 0x20;
|
||||
#endif
|
||||
|
||||
int count = (mask & ABI_ALL_GPRS).Count();
|
||||
rsp_alignment -= count * 8;
|
||||
size_t subtraction = 0;
|
||||
int fpr_count = (mask & ABI_ALL_FPRS).Count();
|
||||
if (fpr_count) {
|
||||
// If we have any XMMs to save, we must align the stack here.
|
||||
subtraction = rsp_alignment & 0xf;
|
||||
}
|
||||
subtraction += 16 * fpr_count;
|
||||
size_t xmm_base_subtraction = subtraction;
|
||||
subtraction += needed_frame_size;
|
||||
subtraction += shadow;
|
||||
// Final alignment.
|
||||
rsp_alignment -= subtraction;
|
||||
subtraction += rsp_alignment & 0xf;
|
||||
|
||||
*shadowp = shadow;
|
||||
*subtractionp = subtraction;
|
||||
*xmm_offsetp = subtraction - xmm_base_subtraction;
|
||||
}
|
||||
|
||||
size_t XEmitter::ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) {
|
||||
size_t shadow, subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset);
|
||||
|
||||
for (int r : mask & ABI_ALL_GPRS)
|
||||
PUSH((X64Reg)r);
|
||||
|
||||
if (subtraction)
|
||||
SUB(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction));
|
||||
|
||||
for (int x : mask & ABI_ALL_FPRS) {
|
||||
MOVAPD(MDisp(RSP, (int)xmm_offset), (X64Reg)(x - 16));
|
||||
xmm_offset += 16;
|
||||
}
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
void XEmitter::ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) {
|
||||
size_t shadow, subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset);
|
||||
|
||||
for (int x : mask & ABI_ALL_FPRS) {
|
||||
MOVAPD((X64Reg) (x - 16), MDisp(RSP, (int)xmm_offset));
|
||||
xmm_offset += 16;
|
||||
}
|
||||
|
||||
if (subtraction)
|
||||
ADD(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction));
|
||||
|
||||
for (int r = 15; r >= 0; r--) {
|
||||
if (mask[r])
|
||||
POP((X64Reg)r);
|
||||
}
|
||||
}
|
||||
|
||||
// Common functions
|
||||
void XEmitter::ABI_CallFunction(const void *func) {
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionC16(const void *func, u16 param1) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32((u32)param1));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCC16(const void *func, u32 param1, u16 param2) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32((u32)param2));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionC(const void *func, u32 param1) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCC(const void *func, u32 param1, u32 param2) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32 param3) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
MOV(32, R(ABI_PARAM3), Imm32(param3));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
MOV(64, R(ABI_PARAM3), ImmPtr(param3));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2, u32 param3, void *param4) {
|
||||
MOV(32, R(ABI_PARAM1), Imm32(param1));
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
MOV(32, R(ABI_PARAM3), Imm32(param3));
|
||||
MOV(64, R(ABI_PARAM4), ImmPtr(param4));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionP(const void *func, void *param1) {
|
||||
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2) {
|
||||
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
|
||||
if (!arg2.IsSimpleReg(ABI_PARAM2))
|
||||
MOV(32, R(ABI_PARAM2), arg2);
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3) {
|
||||
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
|
||||
if (!arg2.IsSimpleReg(ABI_PARAM2))
|
||||
MOV(32, R(ABI_PARAM2), arg2);
|
||||
if (!arg3.IsSimpleReg(ABI_PARAM3))
|
||||
MOV(32, R(ABI_PARAM3), arg3);
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) {
|
||||
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
|
||||
MOV(64, R(ABI_PARAM2), ImmPtr(param2));
|
||||
MOV(32, R(ABI_PARAM3), Imm32(param3));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass a register as a parameter.
|
||||
void XEmitter::ABI_CallFunctionR(const void *func, X64Reg reg1) {
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(32, R(ABI_PARAM1), R(reg1));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass two registers as parameters.
|
||||
void XEmitter::ABI_CallFunctionRR(const void *func, X64Reg reg1, X64Reg reg2) {
|
||||
if (reg2 != ABI_PARAM1) {
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(64, R(ABI_PARAM1), R(reg1));
|
||||
if (reg2 != ABI_PARAM2)
|
||||
MOV(64, R(ABI_PARAM2), R(reg2));
|
||||
} else {
|
||||
if (reg2 != ABI_PARAM2)
|
||||
MOV(64, R(ABI_PARAM2), R(reg2));
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(64, R(ABI_PARAM1), R(reg1));
|
||||
}
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionAC(const void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
{
|
||||
if (!arg1.IsSimpleReg(ABI_PARAM1))
|
||||
MOV(32, R(ABI_PARAM1), arg1);
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3)
|
||||
{
|
||||
if (!arg1.IsSimpleReg(ABI_PARAM1))
|
||||
MOV(32, R(ABI_PARAM1), arg1);
|
||||
MOV(32, R(ABI_PARAM2), Imm32(param2));
|
||||
MOV(64, R(ABI_PARAM3), Imm64(param3));
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1)
|
||||
{
|
||||
if (!arg1.IsSimpleReg(ABI_PARAM1))
|
||||
MOV(32, R(ABI_PARAM1), arg1);
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2)
|
||||
{
|
||||
if (!arg1.IsSimpleReg(ABI_PARAM1))
|
||||
MOV(32, R(ABI_PARAM1), arg1);
|
||||
if (!arg2.IsSimpleReg(ABI_PARAM2))
|
||||
MOV(32, R(ABI_PARAM2), arg2);
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL
|
||||
&& distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
MOV(64, R(RAX), ImmPtr(func));
|
||||
CALLptr(R(RAX));
|
||||
} else {
|
||||
CALL(func);
|
||||
}
|
||||
}
|
59
src/common/x64/abi.h
Normal file
59
src/common/x64/abi.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_set.h"
|
||||
#include "emitter.h"
|
||||
|
||||
// x64 ABI:s, and helpers to help follow them when JIT-ing code.
|
||||
// All convensions return values in EAX (+ possibly EDX).
|
||||
|
||||
// Windows 64-bit
|
||||
// * 4-reg "fastcall" variant, very new-skool stack handling
|
||||
// * Callee moves stack pointer, to make room for shadow regs for the biggest function _it itself calls_
|
||||
// * Parameters passed in RCX, RDX, ... further parameters are MOVed into the allocated stack space.
|
||||
// Scratch: RAX RCX RDX R8 R9 R10 R11
|
||||
// Callee-save: RBX RSI RDI RBP R12 R13 R14 R15
|
||||
// Parameters: RCX RDX R8 R9, further MOV-ed
|
||||
|
||||
// Linux 64-bit
|
||||
// * 6-reg "fastcall" variant, old skool stack handling (parameters are pushed)
|
||||
// Scratch: RAX RCX RDX RSI RDI R8 R9 R10 R11
|
||||
// Callee-save: RBX RBP R12 R13 R14 R15
|
||||
// Parameters: RDI RSI RDX RCX R8 R9
|
||||
|
||||
#define ABI_ALL_FPRS BitSet32(0xffff0000)
|
||||
#define ABI_ALL_GPRS BitSet32(0x0000ffff)
|
||||
|
||||
#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
|
||||
|
||||
// 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 })
|
||||
#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
|
||||
|
||||
// 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 } | \
|
||||
ABI_ALL_FPRS)
|
||||
#endif // WIN32
|
||||
|
||||
#define ABI_ALL_CALLEE_SAVED (~ABI_ALL_CALLER_SAVED)
|
||||
|
||||
#define ABI_RETURN RAX
|
187
src/common/x64/cpu_detect.cpp
Normal file
187
src/common/x64/cpu_detect.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "cpu_detect.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
#include <machine/cpufunc.h>
|
||||
#endif
|
||||
|
||||
static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
|
||||
#ifdef __FreeBSD__
|
||||
// Despite the name, this is just do_cpuid() with ECX as second input.
|
||||
cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
|
||||
#else
|
||||
info[0] = function_id; // eax
|
||||
info[2] = subfunction_id; // ecx
|
||||
__asm__(
|
||||
"cpuid"
|
||||
: "=a" (info[0]),
|
||||
"=b" (info[1]),
|
||||
"=c" (info[2]),
|
||||
"=d" (info[3])
|
||||
: "a" (function_id),
|
||||
"c" (subfunction_id)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __cpuid(int info[4], int function_id) {
|
||||
return __cpuidex(info, function_id, 0);
|
||||
}
|
||||
|
||||
#define _XCR_XFEATURE_ENABLED_MASK 0
|
||||
static u64 _xgetbv(u32 index) {
|
||||
u32 eax, edx;
|
||||
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
|
||||
return ((u64)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#endif // ifndef _MSC_VER
|
||||
|
||||
// Detects the various CPU features
|
||||
static CPUCaps Detect() {
|
||||
CPUCaps caps = {};
|
||||
|
||||
caps.num_cores = std::thread::hardware_concurrency();
|
||||
|
||||
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
|
||||
// Citra at all anyway
|
||||
|
||||
int cpu_id[4];
|
||||
memset(caps.brand_string, 0, sizeof(caps.brand_string));
|
||||
|
||||
// Detect CPU's CPUID capabilities and grab CPU string
|
||||
__cpuid(cpu_id, 0x00000000);
|
||||
u32 max_std_fn = cpu_id[0]; // EAX
|
||||
|
||||
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
|
||||
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
|
||||
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
|
||||
|
||||
__cpuid(cpu_id, 0x80000000);
|
||||
|
||||
u32 max_ex_fn = cpu_id[0];
|
||||
if (!strcmp(caps.brand_string, "GenuineIntel"))
|
||||
caps.vendor = CPUVendor::INTEL;
|
||||
else if (!strcmp(caps.brand_string, "AuthenticAMD"))
|
||||
caps.vendor = CPUVendor::AMD;
|
||||
else
|
||||
caps.vendor = CPUVendor::OTHER;
|
||||
|
||||
// Set reasonable default brand string even if brand string not available
|
||||
strcpy(caps.cpu_string, caps.brand_string);
|
||||
|
||||
// Detect family and other miscellaneous features
|
||||
if (max_std_fn >= 1) {
|
||||
__cpuid(cpu_id, 0x00000001);
|
||||
|
||||
if ((cpu_id[3] >> 25) & 1) caps.sse = true;
|
||||
if ((cpu_id[3] >> 26) & 1) caps.sse2 = true;
|
||||
if ((cpu_id[2]) & 1) caps.sse3 = true;
|
||||
if ((cpu_id[2] >> 9) & 1) caps.ssse3 = true;
|
||||
if ((cpu_id[2] >> 19) & 1) caps.sse4_1 = true;
|
||||
if ((cpu_id[2] >> 20) & 1) caps.sse4_2 = true;
|
||||
if ((cpu_id[2] >> 22) & 1) caps.movbe = true;
|
||||
if ((cpu_id[2] >> 25) & 1) caps.aes = true;
|
||||
|
||||
if ((cpu_id[3] >> 24) & 1) {
|
||||
caps.fxsave_fxrstor = true;
|
||||
}
|
||||
|
||||
// AVX support requires 3 separate checks:
|
||||
// - Is the AVX bit set in CPUID?
|
||||
// - Is the XSAVE bit set in CPUID?
|
||||
// - XGETBV result has the XCR bit set.
|
||||
if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) {
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
caps.avx = true;
|
||||
if ((cpu_id[2] >> 12) & 1)
|
||||
caps.fma = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_std_fn >= 7) {
|
||||
__cpuidex(cpu_id, 0x00000007, 0x00000000);
|
||||
// Can't enable AVX2 unless the XSAVE/XGETBV checks above passed
|
||||
if ((cpu_id[1] >> 5) & 1)
|
||||
caps.avx2 = caps.avx;
|
||||
if ((cpu_id[1] >> 3) & 1)
|
||||
caps.bmi1 = true;
|
||||
if ((cpu_id[1] >> 8) & 1)
|
||||
caps.bmi2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
caps.flush_to_zero = caps.sse;
|
||||
|
||||
if (max_ex_fn >= 0x80000004) {
|
||||
// Extract CPU model string
|
||||
__cpuid(cpu_id, 0x80000002);
|
||||
std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
|
||||
__cpuid(cpu_id, 0x80000003);
|
||||
std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
|
||||
__cpuid(cpu_id, 0x80000004);
|
||||
std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
|
||||
}
|
||||
|
||||
if (max_ex_fn >= 0x80000001) {
|
||||
// Check for more features
|
||||
__cpuid(cpu_id, 0x80000001);
|
||||
if (cpu_id[2] & 1) caps.lahf_sahf_64 = true;
|
||||
if ((cpu_id[2] >> 5) & 1) caps.lzcnt = true;
|
||||
if ((cpu_id[2] >> 16) & 1) caps.fma4 = true;
|
||||
if ((cpu_id[3] >> 29) & 1) caps.long_mode = true;
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
const CPUCaps& GetCPUCaps() {
|
||||
static CPUCaps caps = Detect();
|
||||
return caps;
|
||||
}
|
||||
|
||||
std::string GetCPUCapsString() {
|
||||
auto caps = GetCPUCaps();
|
||||
|
||||
std::string sum(caps.cpu_string);
|
||||
sum += " (";
|
||||
sum += caps.brand_string;
|
||||
sum += ")";
|
||||
|
||||
if (caps.sse) sum += ", SSE";
|
||||
if (caps.sse2) {
|
||||
sum += ", SSE2";
|
||||
if (!caps.flush_to_zero) sum += " (without DAZ)";
|
||||
}
|
||||
|
||||
if (caps.sse3) sum += ", SSE3";
|
||||
if (caps.ssse3) sum += ", SSSE3";
|
||||
if (caps.sse4_1) sum += ", SSE4.1";
|
||||
if (caps.sse4_2) sum += ", SSE4.2";
|
||||
if (caps.avx) sum += ", AVX";
|
||||
if (caps.avx2) sum += ", AVX2";
|
||||
if (caps.bmi1) sum += ", BMI1";
|
||||
if (caps.bmi2) sum += ", BMI2";
|
||||
if (caps.fma) sum += ", FMA";
|
||||
if (caps.aes) sum += ", AES";
|
||||
if (caps.movbe) sum += ", MOVBE";
|
||||
if (caps.long_mode) sum += ", 64-bit support";
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
} // namespace Common
|
66
src/common/x64/cpu_detect.h
Normal file
66
src/common/x64/cpu_detect.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// x86/x64 CPU vendors that may be detected by this module
|
||||
enum class CPUVendor {
|
||||
INTEL,
|
||||
AMD,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
/// x86/x64 CPU capabilities that may be detected by this module
|
||||
struct CPUCaps {
|
||||
CPUVendor vendor;
|
||||
char cpu_string[0x21];
|
||||
char brand_string[0x41];
|
||||
int num_cores;
|
||||
bool sse;
|
||||
bool sse2;
|
||||
bool sse3;
|
||||
bool ssse3;
|
||||
bool sse4_1;
|
||||
bool sse4_2;
|
||||
bool lzcnt;
|
||||
bool avx;
|
||||
bool avx2;
|
||||
bool bmi1;
|
||||
bool bmi2;
|
||||
bool fma;
|
||||
bool fma4;
|
||||
bool aes;
|
||||
|
||||
// Support for the FXSAVE and FXRSTOR instructions
|
||||
bool fxsave_fxrstor;
|
||||
|
||||
bool movbe;
|
||||
|
||||
// This flag indicates that the hardware supports some mode in which denormal inputs and outputs
|
||||
// are automatically set to (signed) zero.
|
||||
bool flush_to_zero;
|
||||
|
||||
// Support for LAHF and SAHF instructions in 64-bit mode
|
||||
bool lahf_sahf_64;
|
||||
|
||||
bool long_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the supported capabilities of the host CPU
|
||||
* @return Reference to a CPUCaps struct with the detected host CPU capabilities
|
||||
*/
|
||||
const CPUCaps& GetCPUCaps();
|
||||
|
||||
/**
|
||||
* Gets a string summary of the name and supported capabilities of the host CPU
|
||||
* @return String summary
|
||||
*/
|
||||
std::string GetCPUCapsString();
|
||||
|
||||
} // namespace Common
|
2013
src/common/x64/emitter.cpp
Normal file
2013
src/common/x64/emitter.cpp
Normal file
File diff suppressed because it is too large
Load diff
1048
src/common/x64/emitter.h
Normal file
1048
src/common/x64/emitter.h
Normal file
File diff suppressed because it is too large
Load diff
11
src/daggen/CMakeLists.txt
Normal file
11
src/daggen/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
set(SRCS
|
||||
main.cpp
|
||||
print_tuples.h)
|
||||
|
||||
set(HEADERS
|
||||
)
|
||||
|
||||
source_group(daggen FILES ${SRCS} ${HEADERS})
|
||||
add_executable(dynarmic_daggen ${SRCS})
|
||||
target_link_libraries(dynarmic_daggen dynarmic_common)
|
||||
set_target_properties(dynarmic_daggen PROPERTIES LINKER_LANGUAGE CXX)
|
137
src/daggen/main.cpp
Normal file
137
src/daggen/main.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#define BOOST_RESULT_OF_USE_DECLTYPE
|
||||
#define BOOST_SPIRIT_USE_PHOENIX_V3
|
||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||
#include <boost/fusion/include/std_tuple.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend_arm/ir/opcodes.h"
|
||||
|
||||
#include "print_tuples.h"
|
||||
|
||||
using Opcode = Dynarmic::IR::Opcode;
|
||||
|
||||
struct Inst;
|
||||
struct Ref;
|
||||
using Needle = boost::variant<Inst, Ref>;
|
||||
|
||||
struct Inst {
|
||||
Opcode opcode;
|
||||
std::vector<Needle> children;
|
||||
std::vector<Needle> parents;
|
||||
};
|
||||
|
||||
struct Ref {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::string input{R"(
|
||||
tile TestTile
|
||||
in [ a (REGISTER), b (REGISTER) ]
|
||||
out [ o?(REGISTER) ]
|
||||
match
|
||||
o: ()
|
||||
endmatch
|
||||
code
|
||||
code
|
||||
endcode
|
||||
endtile
|
||||
)"};
|
||||
|
||||
std::string out_input = "out(REGISTER)";
|
||||
std::string ident_input = "out";
|
||||
|
||||
using Iterator = decltype(input.begin());
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::ascii;
|
||||
using namespace qi::labels;
|
||||
|
||||
using TileIn = std::tuple<std::string, std::string>;
|
||||
using TileOut = std::tuple<std::string, bool, std::string>;
|
||||
using TileEntry = std::tuple<std::string, std::vector<TileIn>, std::vector<TileOut>, std::string, std::string>;
|
||||
|
||||
qi::rule<Iterator, std::string()> identifier;
|
||||
identifier %=
|
||||
qi::lexeme[ascii::alpha >> *ascii::alnum];
|
||||
|
||||
qi::rule<Iterator, std::string()> needle_name;
|
||||
needle_name %=
|
||||
qi::lexeme['$' >> *ascii::alnum];
|
||||
|
||||
qi::rule<Iterator, std::string()> until_closeparen;
|
||||
until_closeparen %=
|
||||
qi::lexeme[*(qi::char_ - ')')];
|
||||
|
||||
qi::rule<Iterator, TileIn(), ascii::space_type> in;
|
||||
in %=
|
||||
(identifier >> '(' >> until_closeparen >> ')');
|
||||
|
||||
qi::rule<Iterator, bool()> question_mark;
|
||||
question_mark =
|
||||
('?' >> qi::attr(true)) | qi::attr(false);
|
||||
|
||||
qi::rule<Iterator, TileOut(), ascii::space_type> out;
|
||||
out %=
|
||||
(identifier >> question_mark >> '(' >> until_closeparen >> ')');
|
||||
|
||||
qi::rule<Iterator, TileEntry(), ascii::space_type> entry;
|
||||
entry %= (
|
||||
qi::lit("tile") >> identifier >>
|
||||
qi::lit("in") >> '[' >> (in % ',') >> ']' >>
|
||||
qi::lit("out") >> '[' >> (out % ',') >> ']' >>
|
||||
qi::lit("match") >>
|
||||
qi::lexeme[*(qi::char_ - qi::lit("endmatch"))] >>
|
||||
qi::lit("endmatch") >>
|
||||
qi::lit("code") >>
|
||||
qi::lexeme[*(qi::char_ - qi::lit("endcode"))] >>
|
||||
qi::lit("endcode") >>
|
||||
qi::lit("endtile")
|
||||
);
|
||||
|
||||
auto first = input.begin(), last = input.end();
|
||||
TileEntry te;
|
||||
bool r = qi::phrase_parse(first, last, entry, ascii::space, te);
|
||||
|
||||
if (first != last || !r) {
|
||||
std::cout << "Fail!" << std::endl;
|
||||
std::cout << te << std::endl;
|
||||
} else {
|
||||
std::cout << te << std::endl;
|
||||
}
|
||||
|
||||
first = out_input.begin(), last = out_input.end();
|
||||
TileOut to;
|
||||
r = qi::phrase_parse(first, last, out, ascii::space, to);
|
||||
|
||||
if (first != last || !r) {
|
||||
std::cout << "Fail!" << std::endl;
|
||||
std::cout << to << std::endl;
|
||||
} else {
|
||||
std::cout << to << std::endl;
|
||||
}
|
||||
|
||||
|
||||
first = ident_input.begin(), last = ident_input.end();
|
||||
std::string test_ident;
|
||||
r = qi::phrase_parse(first, last, identifier, ascii::space, test_ident);
|
||||
|
||||
if (first != last || !r) {
|
||||
std::cout << "Fail!" << std::endl;
|
||||
std::cout << test_ident << std::endl;
|
||||
} else {
|
||||
std::cout << test_ident << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
48
src/daggen/print_tuples.h
Normal file
48
src/daggen/print_tuples.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
template<typename Type, unsigned N, unsigned Last>
|
||||
struct tuple_printer {
|
||||
|
||||
static void print(std::ostream& out, const Type& value) {
|
||||
out << std::get<N>(value) << ", ";
|
||||
tuple_printer<Type, N + 1, Last>::print(out, value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type, unsigned N>
|
||||
struct tuple_printer<Type, N, N> {
|
||||
|
||||
static void print(std::ostream& out, const Type& value) {
|
||||
out << std::get<N>(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename... Types>
|
||||
std::ostream& operator<<(std::ostream& out, const std::tuple<Types...>& value) {
|
||||
out << "(";
|
||||
tuple_printer<std::tuple<Types...>, 0, sizeof...(Types) - 1>::print(out, value);
|
||||
out << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<T>& value) {
|
||||
out << "{";
|
||||
bool first = true;
|
||||
for (const auto& v : value) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
out << v;
|
||||
}
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
22
src/frontend_arm/CMakeLists.txt
Normal file
22
src/frontend_arm/CMakeLists.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
set(SRCS
|
||||
arm_disassembler.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
arm_disassembler.h
|
||||
arm_types.h
|
||||
decoder/arm.h
|
||||
decoder/decoder_detail.h
|
||||
decoder/thumb1.h
|
||||
frontend_arm.h
|
||||
ir/ir.cpp
|
||||
ir/ir.h
|
||||
ir_emitter.cpp
|
||||
ir_emitter.h
|
||||
translate_thumb.h
|
||||
ir/opcodes.h)
|
||||
|
||||
source_group(frontend_arm FILES ${SRCS} ${HEADERS})
|
||||
add_library(dynarmic_frontend_arm STATIC ${SRCS} ${HEADERS})
|
||||
target_link_libraries(dynarmic_frontend_arm dynarmic_common)
|
||||
set_target_properties(dynarmic_frontend_arm PROPERTIES LINKER_LANGUAGE CXX)
|
526
src/frontend_arm/arm_disassembler.cpp
Normal file
526
src/frontend_arm/arm_disassembler.cpp
Normal file
|
@ -0,0 +1,526 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "frontend_arm/arm_types.h"
|
||||
#include "frontend_arm/decoder/arm.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
class DisassemblerVisitor {
|
||||
public:
|
||||
const char* CondStr(Cond cond) {
|
||||
switch (cond) {
|
||||
case Cond::EQ:
|
||||
return "eq";
|
||||
case Cond::NE:
|
||||
return "ne";
|
||||
case Cond::CS:
|
||||
return "cs";
|
||||
case Cond::CC:
|
||||
return "cc";
|
||||
case Cond::MI:
|
||||
return "mi";
|
||||
case Cond::PL:
|
||||
return "pl";
|
||||
case Cond::VS:
|
||||
return "vs";
|
||||
case Cond::VC:
|
||||
return "vc";
|
||||
case Cond::HI:
|
||||
return "hi";
|
||||
case Cond::LS:
|
||||
return "ls";
|
||||
case Cond::GE:
|
||||
return "ge";
|
||||
case Cond::LT:
|
||||
return "lt";
|
||||
case Cond::GT:
|
||||
return "gt";
|
||||
case Cond::LE:
|
||||
return "le";
|
||||
case Cond::AL:
|
||||
return "";
|
||||
case Cond::NV:
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
return "<internal error>";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const char* SignStr(T value) {
|
||||
return value >= 0 ? "+" : "-";
|
||||
}
|
||||
|
||||
const char* RegStr(Reg reg) {
|
||||
switch (reg) {
|
||||
case Reg::R0:
|
||||
return "r0";
|
||||
case Reg::R1:
|
||||
return "r1";
|
||||
case Reg::R2:
|
||||
return "r2";
|
||||
case Reg::R3:
|
||||
return "r3";
|
||||
case Reg::R4:
|
||||
return "r4";
|
||||
case Reg::R5:
|
||||
return "r5";
|
||||
case Reg::R6:
|
||||
return "r6";
|
||||
case Reg::R7:
|
||||
return "r7";
|
||||
case Reg::R8:
|
||||
return "r8";
|
||||
case Reg::R9:
|
||||
return "r9";
|
||||
case Reg::R10:
|
||||
return "r10";
|
||||
case Reg::R11:
|
||||
return "r11";
|
||||
case Reg::R12:
|
||||
return "r12";
|
||||
case Reg::R13:
|
||||
return "sp";
|
||||
case Reg::R14:
|
||||
return "lr";
|
||||
case Reg::R15:
|
||||
return "pc";
|
||||
case Reg::INVALID_REG:
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
return "<internal error>";
|
||||
}
|
||||
|
||||
u32 rotr(u32 x, int shift) {
|
||||
shift &= 31;
|
||||
if (!shift) return x;
|
||||
return (x >> shift) | (x << (32 - shift));
|
||||
}
|
||||
|
||||
u32 ArmExpandImm(int rotate, Imm8 imm8) {
|
||||
return rotr(static_cast<u32>(imm8), rotate*2);
|
||||
}
|
||||
|
||||
std::string ShiftStr(ShiftType shift, Imm5 imm5) {
|
||||
switch (shift) {
|
||||
case ShiftType::LSL:
|
||||
if (imm5 == 0) return "";
|
||||
return Common::StringFromFormat(", lsl #%hhu", imm5);
|
||||
case ShiftType::LSR:
|
||||
if (imm5 == 0) return ", lsr #32";
|
||||
return Common::StringFromFormat(", lsr #%hhu", imm5);
|
||||
case ShiftType::ASR:
|
||||
if (imm5 == 0) return ", asr #32";
|
||||
return Common::StringFromFormat(", asr #%hhu", imm5);
|
||||
case ShiftType::ROR:
|
||||
if (imm5 == 0) return ", rrx";
|
||||
return Common::StringFromFormat(", ror #%hhu", imm5);
|
||||
}
|
||||
assert(false);
|
||||
return "<internal error>";
|
||||
}
|
||||
|
||||
std::string RsrStr(Reg s, ShiftType shift, Reg m) {
|
||||
switch (shift){
|
||||
case ShiftType::LSL:
|
||||
return Common::StringFromFormat("%s, LSL %s", RegStr(m), RegStr(s));
|
||||
case ShiftType::LSR:
|
||||
return Common::StringFromFormat("%s, LSR %s", RegStr(m), RegStr(s));
|
||||
case ShiftType::ASR:
|
||||
return Common::StringFromFormat("%s, ASR %s", RegStr(m), RegStr(s));
|
||||
case ShiftType::ROR:
|
||||
return Common::StringFromFormat("%s, ROR %s", RegStr(m), RegStr(s));
|
||||
}
|
||||
assert(false);
|
||||
return "<internal error>";
|
||||
}
|
||||
|
||||
// Branch instructions
|
||||
std::string arm_B(Cond cond, Imm24 imm24) {
|
||||
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
|
||||
return Common::StringFromFormat("b%s %s#%i", CondStr(cond), SignStr(offset), abs(offset));
|
||||
}
|
||||
std::string arm_BL(Cond cond, Imm24 imm24) {
|
||||
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
|
||||
return Common::StringFromFormat("bl%s %s#%i", CondStr(cond), SignStr(offset), abs(offset));
|
||||
}
|
||||
std::string arm_BLX_imm(bool H, Imm24 imm24) {
|
||||
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8 + (H ? 2 : 0);
|
||||
return Common::StringFromFormat("blx %s#%i", SignStr(offset), abs(offset));
|
||||
}
|
||||
std::string arm_BLX_reg(Cond cond, Reg m) {
|
||||
return Common::StringFromFormat("blx%s %s", CondStr(cond), RegStr(m));
|
||||
}
|
||||
std::string arm_BX(Cond cond, Reg m) {
|
||||
return Common::StringFromFormat("bx%s %s", CondStr(cond), RegStr(m));
|
||||
}
|
||||
std::string arm_BXJ(Cond cond, Reg m) {
|
||||
return Common::StringFromFormat("bxj%s %s", CondStr(cond), RegStr(m));
|
||||
}
|
||||
|
||||
// Coprocessor instructions
|
||||
std::string arm_CDP() { return "<unimplemented>"; }
|
||||
std::string arm_LDC() { return "<unimplemented>"; }
|
||||
std::string arm_MCR() { return "<unimplemented>"; }
|
||||
std::string arm_MCRR() { return "<unimplemented>"; }
|
||||
std::string arm_MRC() { return "<unimplemented>"; }
|
||||
std::string arm_MRRC() { return "<unimplemented>"; }
|
||||
std::string arm_STC() { return "<unimplemented>"; }
|
||||
|
||||
// Data processing instructions
|
||||
std::string arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("adc%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("adc%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_ADC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("adc%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_ADD_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("add%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_ADD_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("add%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_ADD_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("add%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_AND_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("and%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_AND_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("and%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_AND_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("and%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_BIC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("bic%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("bic%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_BIC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("bic%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_CMN_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("cmn%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_CMN_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("cmn%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_CMN_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("cmn%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_CMP_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("cmp%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_CMP_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("cmp%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_CMP_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("cmp%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_EOR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("eor%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_EOR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("eor%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_EOR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("eor%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_MOV_imm(Cond cond, bool S, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("mov%s%s %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_MOV_reg(Cond cond, bool S, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("mov%s%s %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_MOV_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("mov%s%s %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_MVN_imm(Cond cond, bool S, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("mvn%s%s %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_MVN_reg(Cond cond, bool S, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("mvn%s%s %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_MVN_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("mvn%s%s %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_ORR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("orr%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_ORR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("orr%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_ORR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("orr%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_RSB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("rsb%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("rsb%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_RSB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("rsb%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_RSC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("rsc%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_RSC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("rsc%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_RSC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("rsc%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_SBC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("sbc%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_SBC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("sbc%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_SBC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("sbc%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_SUB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("sub%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_SUB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("sub%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_SUB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("sub%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_TEQ_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("teq%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_TEQ_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("teq%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_TEQ_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("teq%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
std::string arm_TST_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
|
||||
return Common::StringFromFormat("tst%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
|
||||
}
|
||||
std::string arm_TST_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("tst%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
|
||||
}
|
||||
std::string arm_TST_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
|
||||
return Common::StringFromFormat("tst%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
|
||||
}
|
||||
|
||||
// Exception generation instructions
|
||||
std::string arm_BKPT(Cond cond, Imm12 imm12, Imm4 imm4) { return "ice"; }
|
||||
std::string arm_SVC(Cond cond, Imm24 imm24) { return "ice"; }
|
||||
std::string arm_UDF() { return "ice"; }
|
||||
|
||||
// Extension functions
|
||||
std::string arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_SXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_SXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_SXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_SXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_SXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_UXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_UXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_UXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_UXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_UXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
std::string arm_UXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||
|
||||
// Hint instructions
|
||||
std::string arm_PLD() { return "<unimplemented>"; }
|
||||
std::string arm_SEV() { return "<unimplemented>"; }
|
||||
std::string arm_WFE() { return "<unimplemented>"; }
|
||||
std::string arm_WFI() { return "<unimplemented>"; }
|
||||
std::string arm_YIELD() { return "<unimplemented>"; }
|
||||
|
||||
// Load/Store instructions
|
||||
std::string arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
|
||||
std::string arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
|
||||
std::string arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
|
||||
std::string arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
|
||||
std::string arm_LDRBT() { return "ice"; }
|
||||
std::string arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
|
||||
std::string arm_LDRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
|
||||
std::string arm_LDRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_LDRHT() { return "ice"; }
|
||||
std::string arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
|
||||
std::string arm_LDRSB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_LDRSBT() { return "ice"; }
|
||||
std::string arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
|
||||
std::string arm_LDRSH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_LDRSHT() { return "ice"; }
|
||||
std::string arm_LDRT() { return "ice"; }
|
||||
std::string arm_STR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
|
||||
std::string arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
|
||||
std::string arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
|
||||
std::string arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
|
||||
std::string arm_STRBT() { return "ice"; }
|
||||
std::string arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
|
||||
std::string arm_STRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_STRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
|
||||
std::string arm_STRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_STRHT() { return "ice"; }
|
||||
std::string arm_STRT() { return "ice"; }
|
||||
|
||||
// Load/Store multiple instructions
|
||||
std::string arm_LDM(Cond cond, bool P, bool U, bool W, Reg n, RegList list) { return "ice"; }
|
||||
std::string arm_LDM_usr() { return "ice"; }
|
||||
std::string arm_LDM_eret() { return "ice"; }
|
||||
std::string arm_STM(Cond cond, bool P, bool U, bool W, Reg n, RegList list) { return "ice"; }
|
||||
std::string arm_STM_usr() { return "ice"; }
|
||||
|
||||
// Miscellaneous instructions
|
||||
std::string arm_CLZ(Cond cond, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_NOP() { return "ice"; }
|
||||
std::string arm_SEL(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Unsigned sum of absolute difference functions
|
||||
std::string arm_USAD8(Cond cond, Reg d, Reg m, Reg n) { return "ice"; }
|
||||
std::string arm_USADA8(Cond cond, Reg d, Reg a, Reg m, Reg n) { return "ice"; }
|
||||
|
||||
// Packing instructions
|
||||
std::string arm_PKHBT(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { return "ice"; }
|
||||
std::string arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { return "ice"; }
|
||||
|
||||
// Reversal instructions
|
||||
std::string arm_REV(Cond cond, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_REV16(Cond cond, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_REVSH(Cond cond, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Saturation instructions
|
||||
std::string arm_SSAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n) { return "ice"; }
|
||||
std::string arm_SSAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; }
|
||||
std::string arm_USAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n) { return "ice"; }
|
||||
std::string arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; }
|
||||
|
||||
// Multiply (Normal) instructions
|
||||
std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { return "ice"; }
|
||||
std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { return "ice"; }
|
||||
|
||||
// Multiply (Long) instructions
|
||||
std::string arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
||||
std::string arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
||||
std::string arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
||||
std::string arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
||||
std::string arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
|
||||
|
||||
// Multiply (Halfword) instructions
|
||||
std::string arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) { return "ice"; }
|
||||
std::string arm_SMLAxy(Cond cond, Reg d, Reg a, Reg m, bool M, bool N, Reg n) { return "ice"; }
|
||||
std::string arm_SMULxy(Cond cond, Reg d, Reg m, bool M, bool N, Reg n) { return "ice"; }
|
||||
|
||||
// Multiply (word by halfword) instructions
|
||||
std::string arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { return "ice"; }
|
||||
std::string arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n) { return "ice"; }
|
||||
|
||||
// Multiply (Most significant word) instructions
|
||||
std::string arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) { return "ice"; }
|
||||
std::string arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) { return "ice"; }
|
||||
std::string arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n) { return "ice"; }
|
||||
|
||||
// Multiply (Dual) instructions
|
||||
std::string arm_SMLAD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { return "ice"; }
|
||||
std::string arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) { return "ice"; }
|
||||
std::string arm_SMLSD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { return "ice"; }
|
||||
std::string arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) { return "ice"; }
|
||||
std::string arm_SMUAD(Cond cond, Reg d, Reg m, bool M, Reg n) { return "ice"; }
|
||||
std::string arm_SMUSD(Cond cond, Reg d, Reg m, bool M, Reg n) { return "ice"; }
|
||||
|
||||
// Parallel Add/Subtract (Modulo arithmetic) instructions
|
||||
std::string arm_SADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_USAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_USUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_USUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Parallel Add/Subtract (Saturating) instructions
|
||||
std::string arm_QADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UQADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UQADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UQASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UQSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UQSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UQSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Parallel Add/Subtract (Halving) instructions
|
||||
std::string arm_SHADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SHADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SHASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SHSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SHSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SHSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UHADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UHADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UHASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UHSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UHSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_UHSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Saturated Add/Subtract instructions
|
||||
std::string arm_QADD(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QSUB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QDADD(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_QDSUB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Synchronization Primitive instructions
|
||||
std::string arm_CLREX() { return "ice"; }
|
||||
std::string arm_LDREX(Cond cond, Reg n, Reg d) { return "ice"; }
|
||||
std::string arm_LDREXB(Cond cond, Reg n, Reg d) { return "ice"; }
|
||||
std::string arm_LDREXD(Cond cond, Reg n, Reg d) { return "ice"; }
|
||||
std::string arm_LDREXH(Cond cond, Reg n, Reg d) { return "ice"; }
|
||||
std::string arm_STREX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_STREXB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_STREXD(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_STREXH(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SWP(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
std::string arm_SWPB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
|
||||
|
||||
// Status register access instructions
|
||||
std::string arm_CPS() { return "ice"; }
|
||||
std::string arm_MRS() { return "ice"; }
|
||||
std::string arm_MSR() { return "ice"; }
|
||||
std::string arm_RFE() { return "ice"; }
|
||||
std::string arm_SETEND(bool E) { return "ice"; }
|
||||
std::string arm_SRS() { return "ice"; }
|
||||
|
||||
|
||||
};
|
||||
|
||||
std::string DisassembleArm(u32 instruction) {
|
||||
DisassemblerVisitor visitor;
|
||||
auto decoder = DecodeArm<DisassemblerVisitor>(instruction);
|
||||
return !decoder ? "UNKNOWN" : decoder->call(visitor, instruction);
|
||||
}
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
19
src/frontend_arm/arm_disassembler.h
Normal file
19
src/frontend_arm/arm_disassembler.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
std::string DisassembleArm(u32 instruction);
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
76
src/frontend_arm/arm_types.h
Normal file
76
src/frontend_arm/arm_types.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
enum class Cond {
|
||||
EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV
|
||||
};
|
||||
|
||||
enum class Reg {
|
||||
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 Reg operator+(Reg reg, int number) {
|
||||
assert(reg != Reg::INVALID_REG);
|
||||
|
||||
int new_reg = static_cast<int>(reg) + number;
|
||||
assert(new_reg >= 0 && new_reg <= 15);
|
||||
|
||||
return static_cast<Reg>(new_reg);
|
||||
}
|
||||
|
||||
using Imm4 = u32;
|
||||
using Imm5 = u32;
|
||||
using Imm8 = u32;
|
||||
using Imm11 = u32;
|
||||
using Imm12 = u32;
|
||||
using Imm24 = u32;
|
||||
using RegList = u16;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct LocationDescriptor {
|
||||
LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag, Cond cond = Cond::AL)
|
||||
: arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag), cond(cond) {}
|
||||
|
||||
u32 arm_pc;
|
||||
bool TFlag; ///< Thumb / ARM
|
||||
bool EFlag; ///< Big / Little Endian
|
||||
Cond cond;
|
||||
|
||||
bool operator == (const LocationDescriptor& o) const {
|
||||
return std::tie(arm_pc, TFlag, EFlag, cond) == std::tie(o.arm_pc, o.TFlag, o.EFlag, o.cond);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
351
src/frontend_arm/decoder/arm.h
Normal file
351
src/frontend_arm/decoder/arm.h
Normal file
|
@ -0,0 +1,351 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*
|
||||
* Original version of table by Lioncash.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend_arm/decoder/decoder_detail.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
template <typename Visitor>
|
||||
struct ArmMatcher {
|
||||
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::arm_UDF), &Visitor::arm_UDF>::return_type;
|
||||
|
||||
ArmMatcher(const char* const name, u32 mask, u32 expect, std::function<CallRetT(Visitor&, u32)> fn)
|
||||
: name(name), mask(mask), expect(expect), fn(fn) {}
|
||||
|
||||
/// Gets the name of this type of instruction.
|
||||
const char* GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if the instruction is this type of instruction.
|
||||
* @param instruction The instruction to test
|
||||
* @returns true if the instruction is
|
||||
*/
|
||||
bool Matches(u32 instruction) const {
|
||||
return (instruction & mask) == expect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the corresponding instruction handler on visitor for this type of instruction.
|
||||
* @param v The visitor to use
|
||||
* @param instruction The instruction to decode.
|
||||
*/
|
||||
CallRetT call(Visitor& v, u32 instruction) const {
|
||||
assert(Matches(instruction));
|
||||
return fn(v, instruction);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name;
|
||||
u32 mask, expect;
|
||||
std::function<CallRetT(Visitor&, u32)> fn;
|
||||
};
|
||||
|
||||
template <typename V>
|
||||
static const std::array<ArmMatcher<V>, 221> g_arm_instruction_table = {{
|
||||
|
||||
#define INST(fn, name, bitstring) detail::detail<ArmMatcher, u32, 32>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
||||
|
||||
// Branch instructions
|
||||
{ INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") }, // v5
|
||||
{ INST(&V::arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") }, // v5
|
||||
{ INST(&V::arm_B, "B", "cccc1010vvvvvvvvvvvvvvvvvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_BX, "BX", "cccc000100101111111111110001mmmm") }, // v4T
|
||||
{ INST(&V::arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm") }, // v5J
|
||||
|
||||
// Coprocessor instructions
|
||||
{ INST(&V::arm_CDP, "CDP2", "11111110-------------------1----") }, // v5
|
||||
{ INST(&V::arm_CDP, "CDP", "----1110-------------------0----") }, // v2
|
||||
{ INST(&V::arm_LDC, "LDC2", "1111110----1--------------------") }, // v5
|
||||
{ INST(&V::arm_LDC, "LDC", "----110----1--------------------") }, // v2
|
||||
{ INST(&V::arm_MCR, "MCR2", "----1110---0---------------1----") }, // v5
|
||||
{ INST(&V::arm_MCR, "MCR", "----1110---0---------------1----") }, // v2
|
||||
{ INST(&V::arm_MCRR, "MCRR2", "111111000100--------------------") }, // v6
|
||||
{ INST(&V::arm_MCRR, "MCRR", "----11000100--------------------") }, // v5E
|
||||
{ INST(&V::arm_MRC, "MRC2", "11111110---1---------------1----") }, // v5
|
||||
{ INST(&V::arm_MRC, "MRC", "----1110---1---------------1----") }, // v2
|
||||
{ INST(&V::arm_MRRC, "MRRC2", "111111000101--------------------") }, // v6
|
||||
{ INST(&V::arm_MRRC, "MRRC", "----11000101--------------------") }, // v5E
|
||||
{ INST(&V::arm_STC, "STC2", "1111110----0--------------------") }, // v5
|
||||
{ INST(&V::arm_STC, "STC", "----110----0--------------------") }, // v2
|
||||
|
||||
// Data Processing instructions
|
||||
{ INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_ADC_reg, "ADC (reg)", "cccc0000101Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_ADC_rsr, "ADC (rsr)", "cccc0000101Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_ADD_reg, "ADD (reg)", "cccc0000100Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_ADD_rsr, "ADD (rsr)", "cccc0000100Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_AND_imm, "AND (imm)", "cccc0010000Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_AND_reg, "AND (reg)", "cccc0000000Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_AND_rsr, "AND (rsr)", "cccc0000000Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_BIC_imm, "BIC (imm)", "cccc0011110Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_BIC_reg, "BIC (reg)", "cccc0001110Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_BIC_rsr, "BIC (rsr)", "cccc0001110Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_CMN_imm, "CMN (imm)", "cccc00110111nnnn0000rrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_CMN_reg, "CMN (reg)", "cccc00010111nnnn0000vvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_CMN_rsr, "CMN (rsr)", "cccc00010111nnnn0000ssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_CMP_imm, "CMP (imm)", "cccc00110101nnnn0000rrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_CMP_reg, "CMP (reg)", "cccc00010101nnnn0000vvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_CMP_rsr, "CMP (rsr)", "cccc00010101nnnn0000ssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_EOR_imm, "EOR (imm)", "cccc0010001Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_EOR_reg, "EOR (reg)", "cccc0000001Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_EOR_rsr, "EOR (rsr)", "cccc0000001Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_MOV_imm, "MOV (imm)", "cccc0011101S0000ddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_MOV_reg, "MOV (reg)", "cccc0001101S0000ddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_MOV_rsr, "MOV (rsr)", "cccc0001101S0000ddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_MVN_imm, "MVN (imm)", "cccc0011111S0000ddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_MVN_reg, "MVN (reg)", "cccc0001111S0000ddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_MVN_rsr, "MVN (rsr)", "cccc0001111S0000ddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_ORR_imm, "ORR (imm)", "cccc0011100Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_ORR_reg, "ORR (reg)", "cccc0001100Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_ORR_rsr, "ORR (rsr)", "cccc0001100Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_RSB_imm, "RSB (imm)", "cccc0010011Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_RSB_reg, "RSB (reg)", "cccc0000011Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_RSB_rsr, "RSB (rsr)", "cccc0000011Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_RSC_imm, "RSC (imm)", "cccc0010111Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_RSC_reg, "RSC (reg)", "cccc0000111Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_RSC_rsr, "RSC (rsr)", "cccc0000111Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_TEQ_reg, "TEQ (reg)", "cccc00010011nnnn0000vvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_TEQ_rsr, "TEQ (rsr)", "cccc00010011nnnn0000ssss0rr1mmmm") }, // all
|
||||
{ INST(&V::arm_TST_imm, "TST (imm)", "cccc00110001nnnn0000rrrrvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_TST_reg, "TST (reg)", "cccc00010001nnnn0000vvvvvrr0mmmm") }, // all
|
||||
{ INST(&V::arm_TST_rsr, "TST (rsr)", "cccc00010001nnnn0000ssss0rr1mmmm") }, // all
|
||||
|
||||
// Exception Generating instructions
|
||||
{ INST(&V::arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv") }, // v5
|
||||
{ INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv") }, // all
|
||||
{ INST(&V::arm_UDF, "UDF", "111001111111------------1111----") }, // all
|
||||
|
||||
// Extension instructions
|
||||
{ INST(&V::arm_SXTB, "SXTB", "cccc011010101111ddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_SXTB16, "SXTB16", "cccc011010001111ddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_SXTH, "SXTH", "cccc011010111111ddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_SXTAB, "SXTAB", "cccc01101010nnnnddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_SXTAB16, "SXTAB16", "cccc01101000nnnnddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_SXTAH, "SXTAH", "cccc01101011nnnnddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_UXTB, "UXTB", "cccc011011101111ddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_UXTB16, "UXTB16", "cccc011011001111ddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_UXTH, "UXTH", "cccc011011111111ddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_UXTAB, "UXTAB", "cccc01101110nnnnddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_UXTAB16, "UXTAB16", "cccc01101100nnnnddddrr000111mmmm") }, // v6
|
||||
{ INST(&V::arm_UXTAH, "UXTAH", "cccc01101111nnnnddddrr000111mmmm") }, // v6
|
||||
|
||||
// Hint instructions
|
||||
{ INST(&V::arm_PLD, "PLD", "111101---101----1111------------") }, // v5E
|
||||
{ INST(&V::arm_SEV, "SEV", "----0011001000001111000000000100") }, // v6K
|
||||
{ INST(&V::arm_WFE, "WFE", "----0011001000001111000000000010") }, // v6K
|
||||
{ INST(&V::arm_WFI, "WFI", "----0011001000001111000000000011") }, // v6K
|
||||
{ INST(&V::arm_YIELD, "YIELD", "----0011001000001111000000000001") }, // v6K
|
||||
|
||||
// Synchronization Primitive instructions
|
||||
{ INST(&V::arm_CLREX, "CLREX", "11110101011111111111000000011111") }, // v6K
|
||||
{ INST(&V::arm_LDREX, "LDREX", "cccc00011001nnnndddd111110011111") }, // v6
|
||||
{ INST(&V::arm_LDREXB, "LDREXB", "cccc00011101nnnndddd111110011111") }, // v6K
|
||||
{ INST(&V::arm_LDREXD, "LDREXD", "cccc00011011nnnndddd111110011111") }, // v6K
|
||||
{ INST(&V::arm_LDREXH, "LDREXH", "cccc00011111nnnndddd111110011111") }, // v6K
|
||||
{ INST(&V::arm_STREX, "STREX", "cccc00011000nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_STREXB, "STREXB", "cccc00011100nnnndddd11111001mmmm") }, // v6K
|
||||
{ INST(&V::arm_STREXD, "STREXD", "cccc00011010nnnndddd11111001mmmm") }, // v6K
|
||||
{ INST(&V::arm_STREXH, "STREXH", "cccc00011110nnnndddd11111001mmmm") }, // v6K
|
||||
{ INST(&V::arm_SWP, "SWP", "cccc00010000nnnndddd00001001mmmm") }, // v2S (v6: Deprecated)
|
||||
{ INST(&V::arm_SWPB, "SWPB", "cccc00010100nnnndddd00001001mmmm") }, // v2S (v6: Deprecated)
|
||||
|
||||
// Load/Store instructions
|
||||
{ INST(&V::arm_LDR_imm, "LDR (imm)", "cccc010pu0w1nnnnddddvvvvvvvvvvvv") },
|
||||
{ INST(&V::arm_LDR_reg, "LDR (reg)", "cccc011pu0w1nnnnddddvvvvvrr0mmmm") },
|
||||
{ INST(&V::arm_LDRB_imm, "LDRB (imm)", "cccc010pu1w1nnnnddddvvvvvvvvvvvv") },
|
||||
{ INST(&V::arm_LDRB_reg, "LDRB (reg)", "cccc011pu1w1nnnnddddvvvvvrr0mmmm") },
|
||||
{ INST(&V::arm_LDRBT, "LDRBT (A1)", "----0100-111--------------------") },
|
||||
{ INST(&V::arm_LDRBT, "LDRBT (A2)", "----0110-111---------------0----") },
|
||||
{ INST(&V::arm_LDRD_imm, "LDRD (imm)", "cccc000pu1w0nnnnddddvvvv1101vvvv") }, // v5E
|
||||
{ INST(&V::arm_LDRD_reg, "LDRD (reg)", "cccc000pu0w0nnnndddd00001101mmmm") }, // v5E
|
||||
{ INST(&V::arm_LDRH_imm, "LDRH (imm)", "cccc000pu1w1nnnnddddvvvv1011vvvv") },
|
||||
{ INST(&V::arm_LDRH_reg, "LDRH (reg)", "cccc000pu0w1nnnndddd00001011mmmm") },
|
||||
{ INST(&V::arm_LDRHT, "LDRHT (A1)", "----0000-111------------1011----") },
|
||||
{ INST(&V::arm_LDRHT, "LDRHT (A2)", "----0000-011--------00001011----") },
|
||||
{ INST(&V::arm_LDRSB_imm, "LDRSB (imm)", "cccc000pu1w1nnnnddddvvvv1101vvvv") },
|
||||
{ INST(&V::arm_LDRSB_reg, "LDRSB (reg)", "cccc000pu0w1nnnndddd00001101mmmm") },
|
||||
{ INST(&V::arm_LDRSBT, "LDRSBT (A1)", "----0000-111------------1101----") },
|
||||
{ INST(&V::arm_LDRSBT, "LDRSBT (A2)", "----0000-011--------00001101----") },
|
||||
{ INST(&V::arm_LDRSH_imm, "LDRSH (imm)", "cccc000pu1w1nnnnddddvvvv1111vvvv") },
|
||||
{ INST(&V::arm_LDRSH_reg, "LDRSH (reg)", "cccc000pu0w1nnnndddd00001111mmmm") },
|
||||
{ INST(&V::arm_LDRSHT, "LDRSHT (A1)", "----0000-111------------1111----") },
|
||||
{ INST(&V::arm_LDRSHT, "LDRSHT (A2)", "----0000-011--------00001111----") },
|
||||
{ INST(&V::arm_LDRT, "LDRT (A1)", "----0100-011--------------------") },
|
||||
{ INST(&V::arm_LDRT, "LDRT (A2)", "----0110-011---------------0----") },
|
||||
{ INST(&V::arm_STR_imm, "STR (imm)", "cccc010pu0w0nnnnddddvvvvvvvvvvvv") },
|
||||
{ INST(&V::arm_STR_reg, "STR (reg)", "cccc011pu0w0nnnnddddvvvvvrr0mmmm") },
|
||||
{ INST(&V::arm_STRB_imm, "STRB (imm)", "cccc010pu1w0nnnnddddvvvvvvvvvvvv") },
|
||||
{ INST(&V::arm_STRB_reg, "STRB (reg)", "cccc011pu1w0nnnnddddvvvvvrr0mmmm") },
|
||||
{ INST(&V::arm_STRBT, "STRBT (A1)", "----0100-110--------------------") },
|
||||
{ INST(&V::arm_STRBT, "STRBT (A2)", "----0110-110---------------0----") },
|
||||
{ INST(&V::arm_STRD_imm, "STRD (imm)", "cccc000pu1w0nnnnddddvvvv1111vvvv") }, // v5E
|
||||
{ INST(&V::arm_STRD_reg, "STRD (reg)", "cccc000pu0w0nnnndddd00001111mmmm") }, // v5E
|
||||
{ INST(&V::arm_STRH_imm, "STRH (imm)", "cccc000pu1w0nnnnddddvvvv1011vvvv") },
|
||||
{ INST(&V::arm_STRH_reg, "STRH (reg)", "cccc000pu0w0nnnndddd00001011mmmm") },
|
||||
{ INST(&V::arm_STRHT, "STRHT (A1)", "----0000-110------------1011----") },
|
||||
{ INST(&V::arm_STRHT, "STRHT (A2)", "----0000-010--------00001011----") },
|
||||
{ INST(&V::arm_STRT, "STRT (A1)", "----0100-010--------------------") },
|
||||
{ INST(&V::arm_STRT, "STRT (A2)", "----0110-010---------------0----") },
|
||||
|
||||
// Load/Store Multiple instructions
|
||||
{ INST(&V::arm_LDM, "LDM", "cccc100pu0w1nnnnxxxxxxxxxxxxxxxx") }, // all
|
||||
{ INST(&V::arm_LDM_usr, "LDM (usr reg)", "----100--101--------------------") }, // all
|
||||
{ INST(&V::arm_LDM_eret, "LDM (exce ret)", "----100--1-1----1---------------") }, // all
|
||||
{ INST(&V::arm_STM, "STM", "cccc100pu0w0nnnnxxxxxxxxxxxxxxxx") }, // all
|
||||
{ INST(&V::arm_STM_usr, "STM (usr reg)", "----100--100--------------------") }, // all
|
||||
|
||||
// Miscellaneous instructions
|
||||
{ INST(&V::arm_CLZ, "CLZ", "cccc000101101111dddd11110001mmmm") }, // v5
|
||||
{ INST(&V::arm_NOP, "NOP", "----001100100000111100000000----") }, // v6K
|
||||
{ INST(&V::arm_SEL, "SEL", "cccc01101000nnnndddd11111011mmmm") }, // v6
|
||||
|
||||
// Unsigned Sum of Absolute Differences instructions
|
||||
{ INST(&V::arm_USAD8, "USAD8", "cccc01111000dddd1111mmmm0001nnnn") }, // v6
|
||||
{ INST(&V::arm_USADA8, "USADA8", "cccc01111000ddddaaaammmm0001nnnn") }, // v6
|
||||
|
||||
// Packing instructions
|
||||
{ INST(&V::arm_PKHBT, "PKHBT", "cccc01101000nnnnddddvvvvv001mmmm") }, // v6K
|
||||
{ INST(&V::arm_PKHTB, "PKHTB", "cccc01101000nnnnddddvvvvv101mmmm") }, // v6K
|
||||
|
||||
// Reversal instructions
|
||||
{ INST(&V::arm_REV, "REV", "cccc011010111111dddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_REV16, "REV16", "cccc011010111111dddd11111011mmmm") }, // v6
|
||||
{ INST(&V::arm_REVSH, "REVSH", "cccc011011111111dddd11111011mmmm") }, // v6
|
||||
|
||||
// Saturation instructions
|
||||
{ INST(&V::arm_SSAT, "SSAT", "cccc0110101vvvvvddddvvvvvr01nnnn") }, // v6
|
||||
{ INST(&V::arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn") }, // v6
|
||||
{ INST(&V::arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn") }, // v6
|
||||
{ INST(&V::arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn") }, // v6
|
||||
|
||||
// Multiply (Normal) instructions
|
||||
{ INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") }, // v2
|
||||
{ INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") }, // v2
|
||||
|
||||
// Multiply (Long) instructions
|
||||
{ INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn") }, // v3M
|
||||
{ INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn") }, // v3M
|
||||
{ INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn") }, // v6
|
||||
{ INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn") }, // v3M
|
||||
{ INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn") }, // v3M
|
||||
|
||||
// Multiply (Halfword) instructions
|
||||
{ INST(&V::arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn") }, // v5xP
|
||||
{ INST(&V::arm_SMLAxy, "SMLAXY", "cccc00010000ddddaaaammmm1xy0nnnn") }, // v5xP
|
||||
{ INST(&V::arm_SMULxy, "SMULXY", "cccc00010110dddd0000mmmm1xy0nnnn") }, // v5xP
|
||||
|
||||
// Multiply (Word by Halfword) instructions
|
||||
{ INST(&V::arm_SMLAWy, "SMLAWY", "cccc00010010ddddaaaammmm1y00nnnn") }, // v5xP
|
||||
{ INST(&V::arm_SMULWy, "SMULWY", "cccc00010010dddd0000mmmm1y10nnnn") }, // v5xP
|
||||
|
||||
// Multiply (Most Significant Word) instructions
|
||||
{ INST(&V::arm_SMMUL, "SMMUL", "cccc01110101dddd1111mmmm00R1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMMLA, "SMMLA", "cccc01110101ddddaaaammmm00R1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMMLS, "SMMLS", "cccc01110101ddddaaaammmm11R1nnnn") }, // v6
|
||||
|
||||
// Multiply (Dual) instructions
|
||||
{ INST(&V::arm_SMLAD, "SMLAD", "cccc01110000ddddaaaammmm00M1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMLALD, "SMLALD", "cccc01110100ddddaaaammmm00M1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMLSD, "SMLSD", "cccc01110000ddddaaaammmm01M1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMLSLD, "SMLSLD", "cccc01110100ddddaaaammmm01M1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMUAD, "SMUAD", "cccc01110000dddd1111mmmm00M1nnnn") }, // v6
|
||||
{ INST(&V::arm_SMUSD, "SMUSD", "cccc01110000dddd1111mmmm01M1nnnn") }, // v6
|
||||
|
||||
// Parallel Add/Subtract (Modulo) instructions
|
||||
{ INST(&V::arm_SADD8, "SADD8", "cccc01100001nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_SADD16, "SADD16", "cccc01100001nnnndddd11110001mmmm") }, // v6
|
||||
{ INST(&V::arm_SASX, "SASX", "cccc01100001nnnndddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_SSAX, "SSAX", "cccc01100001nnnndddd11110101mmmm") }, // v6
|
||||
{ INST(&V::arm_SSUB8, "SSUB8", "cccc01100001nnnndddd11111111mmmm") }, // v6
|
||||
{ INST(&V::arm_SSUB16, "SSUB16", "cccc01100001nnnndddd11110111mmmm") }, // v6
|
||||
{ INST(&V::arm_UADD8, "UADD8", "cccc01100101nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_UADD16, "UADD16", "cccc01100101nnnndddd11110001mmmm") }, // v6
|
||||
{ INST(&V::arm_UASX, "UASX", "cccc01100101nnnndddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_USAX, "USAX", "cccc01100101nnnndddd11110101mmmm") }, // v6
|
||||
{ INST(&V::arm_USUB8, "USUB8", "cccc01100101nnnndddd11111111mmmm") }, // v6
|
||||
{ INST(&V::arm_USUB16, "USUB16", "cccc01100101nnnndddd11110111mmmm") }, // v6
|
||||
|
||||
// Parallel Add/Subtract (Saturating) instructions
|
||||
{ INST(&V::arm_QADD8, "QADD8", "cccc01100010nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_QADD16, "QADD16", "cccc01100010nnnndddd11110001mmmm") }, // v6
|
||||
{ INST(&V::arm_QASX, "QASX", "cccc01100010nnnndddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_QSAX, "QSAX", "cccc01100010nnnndddd11110101mmmm") }, // v6
|
||||
{ INST(&V::arm_QSUB8, "QSUB8", "cccc01100010nnnndddd11111111mmmm") }, // v6
|
||||
{ INST(&V::arm_QSUB16, "QSUB16", "cccc01100010nnnndddd11110111mmmm") }, // v6
|
||||
{ INST(&V::arm_UQADD8, "UQADD8", "cccc01100110nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_UQADD16, "UQADD16", "cccc01100110nnnndddd11110001mmmm") }, // v6
|
||||
{ INST(&V::arm_UQASX, "UQASX", "cccc01100110nnnndddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_UQSAX, "UQSAX", "cccc01100110nnnndddd11110101mmmm") }, // v6
|
||||
{ INST(&V::arm_UQSUB8, "UQSUB8", "cccc01100110nnnndddd11111111mmmm") }, // v6
|
||||
{ INST(&V::arm_UQSUB16, "UQSUB16", "cccc01100110nnnndddd11110111mmmm") }, // v6
|
||||
|
||||
// Parallel Add/Subtract (Halving) instructions
|
||||
{ INST(&V::arm_SHADD8, "SHADD8", "cccc01100011nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_SHADD16, "SHADD16", "cccc01100011nnnndddd11110001mmmm") }, // v6
|
||||
{ INST(&V::arm_SHASX, "SHASX", "cccc01100011nnnndddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_SHSAX, "SHSAX", "cccc01100011nnnndddd11110101mmmm") }, // v6
|
||||
{ INST(&V::arm_SHSUB8, "SHSUB8", "cccc01100011nnnndddd11111111mmmm") }, // v6
|
||||
{ INST(&V::arm_SHSUB16, "SHSUB16", "cccc01100011nnnndddd11110111mmmm") }, // v6
|
||||
{ INST(&V::arm_UHADD8, "UHADD8", "cccc01100111nnnndddd11111001mmmm") }, // v6
|
||||
{ INST(&V::arm_UHADD16, "UHADD16", "cccc01100111nnnndddd11110001mmmm") }, // v6
|
||||
{ INST(&V::arm_UHASX, "UHASX", "cccc01100111nnnndddd11110011mmmm") }, // v6
|
||||
{ INST(&V::arm_UHSAX, "UHSAX", "cccc01100111nnnndddd11110101mmmm") }, // v6
|
||||
{ INST(&V::arm_UHSUB8, "UHSUB8", "cccc01100111nnnndddd11111111mmmm") }, // v6
|
||||
{ INST(&V::arm_UHSUB16, "UHSUB16", "cccc01100111nnnndddd11110111mmmm") }, // v6
|
||||
|
||||
// Saturated Add/Subtract instructions
|
||||
{ INST(&V::arm_QADD, "QADD", "cccc00010000nnnndddd00000101mmmm") }, // v5xP
|
||||
{ INST(&V::arm_QSUB, "QSUB", "cccc00010010nnnndddd00000101mmmm") }, // v5xP
|
||||
{ INST(&V::arm_QDADD, "QDADD", "cccc00010100nnnndddd00000101mmmm") }, // v5xP
|
||||
{ INST(&V::arm_QDSUB, "QDSUB", "cccc00010110nnnndddd00000101mmmm") }, // v5xP
|
||||
|
||||
// Status Register Access instructions
|
||||
{ INST(&V::arm_CPS, "CPS", "111100010000---00000000---0-----") }, // v6
|
||||
{ INST(&V::arm_SETEND, "SETEND", "1111000100000001000000e000000000") }, // v6
|
||||
{ INST(&V::arm_MRS, "MRS", "----00010-00--------00--00000000") }, // v3
|
||||
{ INST(&V::arm_MSR, "MSR", "----00-10-10----1111------------") }, // v3
|
||||
{ INST(&V::arm_RFE, "RFE", "----0001101-0000---------110----") }, // v6
|
||||
{ INST(&V::arm_SRS, "SRS", "0000011--0-00000000000000001----") }, // v6
|
||||
|
||||
#undef INST
|
||||
|
||||
}};
|
||||
|
||||
template<typename Visitor>
|
||||
boost::optional<const ArmMatcher<Visitor>&> DecodeArm(u32 instruction) {
|
||||
const auto& table = g_arm_instruction_table<Visitor>;
|
||||
const auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
|
||||
|
||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
||||
return iter != table.end() ? boost::make_optional<const ArmMatcher<Visitor>&>(*iter) : boost::none;
|
||||
}
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
132
src/frontend_arm/decoder/decoder_detail.h
Normal file
132
src/frontend_arm/decoder/decoder_detail.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/mp.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Helper functions for the decoders.
|
||||
* @tparam MatcherT The type of the matcher. (ARM: ArmMatcher, Thumb1: Thumb1Matcher)
|
||||
* @tparam InstructionT The type that represents an instruction. (ARM: u32, Thumb1: u16)
|
||||
* @tparam instruction_bit_size Bit-size for an instruction. (ARM: 32, Thumb1: 16)
|
||||
*/
|
||||
template<template<typename> class MatcherT, typename InstructionT, size_t instruction_bit_size>
|
||||
struct detail {
|
||||
private:
|
||||
/**
|
||||
* Generates the mask and the expected value after masking from a given bitstring.
|
||||
* A '0' in a bitstring indicates that a zero must be present at that bit position.
|
||||
* A '1' in a bitstring indicates that a one must be present at that bit position.
|
||||
*/
|
||||
static auto GetMaskAndExpect(const char* const bitstring) {
|
||||
InstructionT mask = 0, expect = 0;
|
||||
for (size_t i = 0; i < instruction_bit_size; i++) {
|
||||
const size_t bit_position = instruction_bit_size - i - 1;
|
||||
switch (bitstring[i]) {
|
||||
case '0':
|
||||
mask |= 1 << bit_position;
|
||||
break;
|
||||
case '1':
|
||||
expect |= 1 << bit_position;
|
||||
mask |= 1 << bit_position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(mask, expect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the masks and shifts for each argument.
|
||||
* A '-' in a bitstring indicates that we don't care about that value.
|
||||
* An argument is specified by a continuous string of the same character.
|
||||
*/
|
||||
template<size_t N>
|
||||
static auto GetArgInfo(const char* const bitstring) {
|
||||
std::array<InstructionT, N> masks = {};
|
||||
std::array<size_t, N> shifts = {};
|
||||
size_t arg_index = 0;
|
||||
char ch = 0;
|
||||
|
||||
for (size_t i = 0; i < instruction_bit_size; i++) {
|
||||
const size_t bit_position = instruction_bit_size - i - 1;
|
||||
|
||||
if (bitstring[i] == '0' || bitstring[i] == '1' || bitstring[i] == '-') {
|
||||
if (ch != 0) {
|
||||
ch = 0;
|
||||
arg_index++;
|
||||
}
|
||||
} else {
|
||||
if (ch == 0) {
|
||||
ch = bitstring[i];
|
||||
} else if (ch != bitstring[i]) {
|
||||
ch = bitstring[i];
|
||||
arg_index++;
|
||||
}
|
||||
|
||||
assert(arg_index < N);
|
||||
masks[arg_index] |= 1 << bit_position;
|
||||
shifts[arg_index] = bit_position;
|
||||
}
|
||||
}
|
||||
|
||||
assert(std::all_of(masks.begin(), masks.end(), [](auto m){ return m != 0; }));
|
||||
|
||||
return std::make_tuple(masks, shifts);
|
||||
}
|
||||
|
||||
/**
|
||||
* This struct's Make member function generates a lambda which decodes an instruction based on
|
||||
* the provided arg_masks and arg_shifts. The Visitor member function to call is provided as a
|
||||
* template argument.
|
||||
*/
|
||||
template<typename FnT, FnT fn>
|
||||
struct VisitorCaller;
|
||||
|
||||
template<typename Visitor, typename ...Args, typename CallRetT, CallRetT (Visitor::*fn)(Args...)>
|
||||
struct VisitorCaller<CallRetT(Visitor::*)(Args...), fn> {
|
||||
template<size_t ...iota>
|
||||
static auto Make(std::integer_sequence<size_t, iota...>,
|
||||
const std::array<InstructionT, sizeof...(iota)> arg_masks,
|
||||
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
||||
return [arg_masks, arg_shifts](Visitor& v, InstructionT instruction) {
|
||||
(void)instruction;
|
||||
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a matcher that can match and parse instructions based on bitstring.
|
||||
* See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
|
||||
*/
|
||||
template<typename FnT, FnT fn>
|
||||
static auto GetMatcher(const char* const name, const char* const bitstring) {
|
||||
using Visitor = typename mp::MemFnInfo<FnT, fn>::class_type;
|
||||
constexpr size_t args_count = mp::MemFnInfo<FnT, fn>::args_count;
|
||||
using Iota = std::make_index_sequence<args_count>;
|
||||
|
||||
const auto mask_and_expect = GetMaskAndExpect(bitstring);
|
||||
const auto arg_info = GetArgInfo<args_count>(bitstring);
|
||||
const auto proxy_fn = VisitorCaller<FnT, fn>::Make(Iota(), std::get<0>(arg_info), std::get<1>(arg_info));
|
||||
|
||||
return MatcherT<Visitor>(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
166
src/frontend_arm/decoder/thumb1.h
Normal file
166
src/frontend_arm/decoder/thumb1.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend_arm/decoder/decoder_detail.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
template <typename Visitor>
|
||||
struct Thumb1Matcher {
|
||||
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::thumb1_UDF), &Visitor::thumb1_UDF>::return_type;
|
||||
|
||||
Thumb1Matcher(const char* const name, u16 mask, u16 expect, std::function<CallRetT(Visitor&, u16)> fn)
|
||||
: name(name), mask(mask), expect(expect), fn(fn) {}
|
||||
|
||||
/// Gets the name of this type of instruction.
|
||||
const char* GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if the instruction is this type of instruction.
|
||||
* @param instruction The instruction to test
|
||||
* @returns true if the instruction is
|
||||
*/
|
||||
bool Matches(u16 instruction) const {
|
||||
return (instruction & mask) == expect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the corresponding instruction handler on visitor for this type of instruction.
|
||||
* @param v The visitor to use
|
||||
* @param instruction The instruction to decode.
|
||||
*/
|
||||
CallRetT call(Visitor& v, u16 instruction) const {
|
||||
assert(Matches(instruction));
|
||||
return fn(v, instruction);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name;
|
||||
u16 mask, expect;
|
||||
std::function<CallRetT(Visitor&, u16)> fn;
|
||||
};
|
||||
|
||||
template <typename V>
|
||||
static const std::array<Thumb1Matcher<V>, 2> g_thumb1_instruction_table {{
|
||||
|
||||
#define INST(fn, name, bitstring) detail::detail<Thumb1Matcher, u16, 16>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
||||
|
||||
// Shift (immediate), add, subtract, move and compare instructions
|
||||
{ INST(&V::thumb1_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd") },
|
||||
{ INST(&V::thumb1_LSR_imm, "LSR (imm)", "00001vvvvvmmmddd") },
|
||||
/*
|
||||
{ INST(&V::thumb1_ASR_rri, "ASR (rri)", "00010vvvvvmmmddd") },
|
||||
{ INST(&V::thumb1_ADD_rrr, "ADD (rrr)", "0001100mmmnnnddd") },
|
||||
{ INST(&V::thumb1_SUB_rrr, "SUB (rrr)", "0001101mmmnnnddd") },
|
||||
{ INST(&V::thumb1_ADD_rri, "ADD (rri)", "0001110mmmnnnddd") },
|
||||
{ INST(&V::thumb1_SUB_rri, "SUB (rri)", "0001111mmmnnnddd") },
|
||||
{ INST(&V::thumb1_MOV_ri, "MOV (ri)", "00100dddvvvvvvvv") },
|
||||
{ INST(&V::thumb1_CMP_ri, "CMP (ri)", "00101dddvvvvvvvv") },
|
||||
{ INST(&V::thumb1_ADD_ri, "ADD (ri)", "00110dddvvvvvvvv") },
|
||||
{ INST(&V::thumb1_SUB_ri, "SUB (ri)", "00111dddvvvvvvvv") },
|
||||
|
||||
// Data-processing instructions
|
||||
{ INST(&V::thumb1_ANDS_rr, "ANDS (rr)", "0100000000mmmddd") },
|
||||
{ INST(&V::thumb1_EORS_rr, "EORS (rr)", "0100000001mmmddd") },
|
||||
{ INST(&V::thumb1_LSLS_reg, "LSLS (reg)", "0100000010mmmddd") },
|
||||
{ INST(&V::thumb1_LSRS_rr, "LSRS (rr)", "0100000011sssddd") },
|
||||
{ INST(&V::thumb1_ASRS_rr, "ASRS (rr)", "0100000100sssddd") },
|
||||
{ INST(&V::thumb1_ADCS_rr, "ADCS (rr)", "0100000101mmmddd") },
|
||||
{ INST(&V::thumb1_SBCS_rr, "SBCS (rr)", "0100000110mmmddd") },
|
||||
{ INST(&V::thumb1_RORS_rr, "RORS (rr)", "0100000111sssddd") },
|
||||
{ INST(&V::thumb1_TST_rr, "TST (rr)", "0100001000mmmnnn") },
|
||||
{ INST(&V::thumb1_NEGS_rr, "NEGS (rr)", "0100001001mmmddd") },
|
||||
{ INST(&V::thumb1_CMP_rr, "CMP (rr)", "0100001010mmmnnn") },
|
||||
{ INST(&V::thumb1_CMN_rr, "CMN (rr)", "0100001011mmmnnn") },
|
||||
{ INST(&V::thumb1_ORRS_rr, "ORRS (rr)", "0100001100mmmddd") },
|
||||
{ INST(&V::thumb1_MULS_rr, "MULS (rr)", "0100001101mmmddd") },
|
||||
{ INST(&V::thumb1_BICS_rr, "BICS (rr)", "0100001110mmmddd") },
|
||||
{ INST(&V::thumb1_MVNS_rr, "MVNS (rr)", "0100001111mmmddd") },
|
||||
|
||||
// Special data instructions
|
||||
{ INST(&V::thumb1_ADD_high, "ADD (high)", "01000100dmmmmddd") }, // v4T, Low regs: v6T2
|
||||
{ INST(&V::thumb1_CMP_high, "CMP (high)", "01000101dmmmmddd") }, // v4T
|
||||
{ INST(&V::thumb1_MOV_high, "MOV (high)", "01000110dmmmmddd") }, // v4T, Low regs: v6
|
||||
|
||||
// Store/Load single data item instructions
|
||||
{ INST(&V::thumb1_LDR_lit, "LDR (literal)", "01001dddvvvvvvvv") },
|
||||
{ INST(&V::thumb1_STR_rrr, "STR (rrr)", "0101000mmmnnnddd") },
|
||||
{ INST(&V::thumb1_STRH_rrr, "STRH (rrr)", "0101001mmmnnnddd") },
|
||||
{ INST(&V::thumb1_STRB_rrr, "STRB (rrr)", "0101010mmmnnnddd") },
|
||||
{ INST(&V::thumb1_LDRSB_rrr, "LDRSB (rrr)", "0101011mmmnnnddd") },
|
||||
{ INST(&V::thumb1_LDR_rrr, "LDR (rrr)", "0101100mmmnnnddd") },
|
||||
{ INST(&V::thumb1_LDRH_rrr, "LDRH (rrr)", "0101101mmmnnnddd") },
|
||||
{ INST(&V::thumb1_LDRB_rrr, "LDRB (rrr)", "0101110mmmnnnddd") },
|
||||
{ INST(&V::thumb1_LDRSH_rrr, "LDRSH (rrr)", "0101111mmmnnnddd") },
|
||||
{ INST(&V::thumb1_STRH_rri, "STRH (rri)", "10000vvvvvnnnddd") },
|
||||
{ INST(&V::thumb1_LDRH_rri, "LDRH (rri)", "10001vvvvvnnnddd") },
|
||||
{ INST(&V::thumb1_STR_sp, "STR (SP)", "10010dddvvvvvvvv") },
|
||||
{ INST(&V::thumb1_LDR_sp, "LDR (SP)", "10011dddvvvvvvvv") },
|
||||
|
||||
// Generate relative address instruction
|
||||
{ INST(&V::thumb1_ADR, "ADR", "10100dddvvvvvvvv") },
|
||||
{ INST(&V::thumb1_ADD_sp, "ADD (relative to SP)", "10101dddvvvvvvvv") },
|
||||
|
||||
// Miscellaneous 16-bit instructions
|
||||
{ INST(&V::thumb1_ADD_spsp, "ADD (imm to SP)", "101100000vvvvvvv") }, // v4T
|
||||
{ INST(&V::thumb1_SUB_spsp, "SUB (imm from SP)", "101100001vvvvvvv") }, // v4T
|
||||
{ INST(&V::thumb1_SXTH, "SXTH", "1011001000mmmddd") }, // v6
|
||||
{ INST(&V::thumb1_SXTB, "SXTB", "1011001001mmmddd") }, // v6
|
||||
{ INST(&V::thumb1_UXTH, "UXTH", "1011001010mmmddd") }, // v6
|
||||
{ INST(&V::thumb1_UXTB, "UXTB", "1011001011mmmddd") }, // v6
|
||||
{ INST(&V::thumb1_PUSH, "PUSH", "1011010rxxxxxxxx") }, // v4T
|
||||
{ INST(&V::thumb1_POP, "POP", "1011110rxxxxxxxx") }, // v4T
|
||||
{ INST(&V::thumb1_SETEND, "SETEND", "101101100101x000") }, // v6
|
||||
{ INST(&V::thumb1_CPS, "CPS", "10110110011m0aif") }, // v6
|
||||
{ INST(&V::thumb1_REV, "REV", "1011101000nnnddd") }, // v6
|
||||
{ INST(&V::thumb1_REV16, "REV16", "1011101001nnnddd") }, // v6
|
||||
{ INST(&V::thumb1_REVSH, "REVSH", "1011101011nnnddd") }, // v6
|
||||
{ INST(&V::thumb1_BKPT, "BKPT", "10111110xxxxxxxx") }, // v5
|
||||
|
||||
// Store/Load multiple registers
|
||||
{ INST(&V::thumb1_STMIA, "STMIA", "11000nnnxxxxxxxx") },
|
||||
{ INST(&V::thumb1_LDMIA, "LDMIA", "11001nnnxxxxxxxx") },
|
||||
|
||||
// Branch instructions
|
||||
{ INST(&V::thumb1_BX, "BX (reg)", "010001110mmmm000") }, // v4T
|
||||
{ INST(&V::thumb1_BLX, "BLX (reg)", "010001111mmmm000") }, // v5T
|
||||
{ INST(&V::thumb1_UDF, "UDF", "11011110--------") },
|
||||
{ INST(&V::thumb1_SWI, "SWI", "11011111xxxxxxxx") },
|
||||
{ INST(&V::thumb1_B_cond, "B (cond)", "1101ccccxxxxxxxx") },
|
||||
{ INST(&V::thumb1_B_imm, "B (imm)", "11100xxxxxxxxxxx") },
|
||||
{ INST(&V::thumb1_BLX_suffix, "BLX (imm, suffix)", "11101xxxxxxxxxx0") },
|
||||
{ INST(&V::thumb1_BLX_prefix, "BL/BLX (imm, prefix)", "11110xxxxxxxxxxx") },
|
||||
{ INST(&V::thumb1_BL_suffix, "BL (imm, suffix)", "11111xxxxxxxxxxx") },*/
|
||||
|
||||
#undef INST
|
||||
|
||||
}};
|
||||
|
||||
template<typename Visitor>
|
||||
boost::optional<const Thumb1Matcher<Visitor>&> DecodeThumb1(u16 instruction) {
|
||||
const auto& table = g_thumb1_instruction_table<Visitor>;
|
||||
auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
|
||||
|
||||
assert(std::count_if(table.begin(), table.end(), matches_instruction) <= 1);
|
||||
|
||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
||||
return iter != table.end() ? boost::make_optional<const Thumb1Matcher<Visitor>&>(*iter) : boost::none;
|
||||
}
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
15
src/frontend_arm/frontend_arm.h
Normal file
15
src/frontend_arm/frontend_arm.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
130
src/frontend_arm/ir/ir.cpp
Normal file
130
src/frontend_arm/ir/ir.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "frontend_arm/ir/ir.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace IR {
|
||||
|
||||
// Opcode information
|
||||
|
||||
namespace OpcodeInfo {
|
||||
|
||||
using T = Dynarmic::IR::Type;
|
||||
|
||||
struct Meta {
|
||||
const char* name;
|
||||
Type type;
|
||||
std::vector<Type> arg_types;
|
||||
};
|
||||
|
||||
static const std::map<Opcode, Meta> opcode_info {{
|
||||
#define OPCODE(name, type, ...) { Opcode::name, { #name, type, { __VA_ARGS__ } } },
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
}};
|
||||
|
||||
} // namespace OpcodeInfo
|
||||
|
||||
Type GetTypeOf(Opcode op) {
|
||||
return OpcodeInfo::opcode_info.at(op).type;
|
||||
}
|
||||
|
||||
size_t GetNumArgsOf(Opcode op) {
|
||||
return OpcodeInfo::opcode_info.at(op).arg_types.size();
|
||||
}
|
||||
|
||||
Type GetArgTypeOf(Opcode op, size_t arg_index) {
|
||||
return OpcodeInfo::opcode_info.at(op).arg_types.at(arg_index);
|
||||
}
|
||||
|
||||
const char* GetNameOf(Opcode op) {
|
||||
return OpcodeInfo::opcode_info.at(op).name;
|
||||
}
|
||||
|
||||
// Value class member definitions
|
||||
|
||||
void Value::ReplaceUsesWith(ValuePtr replacement) {
|
||||
for (const auto& use : uses) {
|
||||
use.use_owner.lock()->ReplaceUseOfXWithY(use.value.lock(), replacement);
|
||||
}
|
||||
assert(uses.empty());
|
||||
}
|
||||
|
||||
std::vector<ValuePtr> Value::GetUses() const {
|
||||
std::vector<ValuePtr> result(uses.size());
|
||||
std::transform(uses.begin(), uses.end(), result.begin(), [](const auto& use){ return use.use_owner.lock(); });
|
||||
return result;
|
||||
}
|
||||
|
||||
void Value::AddUse(ValuePtr owner) {
|
||||
// There can be multiple uses from the same owner.
|
||||
uses.push_back({ shared_from_this(), owner });
|
||||
}
|
||||
|
||||
void Value::RemoveUse(ValuePtr owner) {
|
||||
// Remove only one use.
|
||||
auto iter = std::find_if(uses.begin(), uses.end(), [&owner](auto use) { return use.use_owner.lock() == owner; });
|
||||
ASSERT_MSG(iter != uses.end(), "RemoveUse without associated AddUse. Bug in use management code.");
|
||||
uses.erase(iter);
|
||||
}
|
||||
|
||||
void Value::ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) {
|
||||
// This should never be called. Use management is incorrect if this is ever called.
|
||||
ASSERT_MSG(false, "This Value type doesn't use any values. Bug in use management code.");
|
||||
}
|
||||
|
||||
// Inst class member definitions
|
||||
|
||||
Inst::Inst(Opcode op_) : Value(op_) {
|
||||
args.resize(GetNumArgsOf(op));
|
||||
}
|
||||
|
||||
void Inst::SetArg(size_t index, ValuePtr value) {
|
||||
auto this_ = shared_from_this();
|
||||
|
||||
if (auto prev_value = args.at(index).lock()) {
|
||||
prev_value->RemoveUse(this_);
|
||||
}
|
||||
|
||||
ASSERT(value->GetType() == GetArgTypeOf(op, index));
|
||||
args.at(index) = value;
|
||||
|
||||
value->AddUse(this_);
|
||||
}
|
||||
|
||||
ValuePtr Inst::GetArg(size_t index) const {
|
||||
ASSERT_MSG(!args.at(index).expired(), "This should never happen. All Values should be owned by a MicroBlock.");
|
||||
return args.at(index).lock();
|
||||
}
|
||||
|
||||
void Inst::AssertValid() {
|
||||
ASSERT(std::all_of(args.begin(), args.end(), [](const auto& arg) { return !arg.expired(); }));
|
||||
}
|
||||
|
||||
void Inst::ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) {
|
||||
bool has_use = false;
|
||||
auto this_ = shared_from_this();
|
||||
|
||||
// Note that there may be multiple uses of x.
|
||||
for (auto& arg : args) {
|
||||
if (arg.lock() == x) {
|
||||
arg = y;
|
||||
has_use = true;
|
||||
x->RemoveUse(this_);
|
||||
y->AddUse(this_);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_MSG(has_use, "This Inst doesn't have x. Bug in use management code.");
|
||||
}
|
||||
|
||||
} // namespace IR
|
||||
} // namespace Dynarmic
|
168
src/frontend_arm/ir/ir.h
Normal file
168
src/frontend_arm/ir/ir.h
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "frontend_arm/arm_types.h"
|
||||
#include "frontend_arm/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace IR {
|
||||
|
||||
// ARM JIT Microinstruction Intermediate Representation
|
||||
//
|
||||
// This intermediate representation is an SSA IR. It is designed primarily for analysis,
|
||||
// though it can be lowered into a reduced form for interpretation. Each IR node (Value)
|
||||
// is a microinstruction of an idealised ARM CPU. The choice of microinstructions is made
|
||||
// not based on any existing microarchitecture but on ease of implementation.
|
||||
//
|
||||
// A basic block is represented as an IR::Block.
|
||||
|
||||
enum class Type {
|
||||
Void = 1 << 0,
|
||||
RegRef = 1 << 1,
|
||||
Opaque = 1 << 2,
|
||||
U1 = 1 << 3,
|
||||
U8 = 1 << 4,
|
||||
U16 = 1 << 5,
|
||||
U32 = 1 << 6,
|
||||
U64 = 1 << 7,
|
||||
};
|
||||
|
||||
Type GetTypeOf(Opcode op);
|
||||
size_t GetNumArgsOf(Opcode op);
|
||||
Type GetArgTypeOf(Opcode op, size_t arg_index);
|
||||
const char* GetNameOf(Opcode op);
|
||||
|
||||
// Type declarations
|
||||
|
||||
/// Base class for microinstructions to derive from.
|
||||
|
||||
class Value;
|
||||
using ValuePtr = std::shared_ptr<Value>;
|
||||
using ValueWeakPtr = std::weak_ptr<Value>;
|
||||
|
||||
class Value : public std::enable_shared_from_this<Value> {
|
||||
public:
|
||||
virtual ~Value() = default;
|
||||
|
||||
bool HasUses() const { return !uses.empty(); }
|
||||
bool HasOneUse() const { return uses.size() == 1; }
|
||||
bool HasManyUses() const { return uses.size() > 1; }
|
||||
|
||||
/// Replace all uses of this Value with `replacement`.
|
||||
void ReplaceUsesWith(ValuePtr replacement);
|
||||
|
||||
/// Get the microop this microinstruction represents.
|
||||
Opcode GetOpcode() const { return op; }
|
||||
/// Get the type this instruction returns.
|
||||
Type GetType() const { return GetTypeOf(op); }
|
||||
/// Get the number of arguments this instruction has.
|
||||
size_t NumArgs() const { return GetNumArgsOf(op); }
|
||||
/// Get the number of uses this instruction has.
|
||||
size_t NumUses() const { return uses.size(); }
|
||||
|
||||
std::vector<ValuePtr> GetUses() const;
|
||||
|
||||
intptr_t GetTag() const { return tag; }
|
||||
void SetTag(intptr_t tag_) { tag = tag_; }
|
||||
|
||||
protected:
|
||||
friend class Inst;
|
||||
|
||||
Value(Opcode op_) : op(op_) {}
|
||||
|
||||
void AddUse(ValuePtr owner);
|
||||
void RemoveUse(ValuePtr owner);
|
||||
virtual void ReplaceUseOfXWithY(ValuePtr x, ValuePtr y);
|
||||
|
||||
private:
|
||||
Opcode op;
|
||||
|
||||
struct Use {
|
||||
/// The instruction which is being used.
|
||||
ValueWeakPtr value;
|
||||
/// The instruction which is using `value`.
|
||||
ValueWeakPtr use_owner;
|
||||
};
|
||||
std::list<Use> uses;
|
||||
|
||||
intptr_t tag;
|
||||
};
|
||||
|
||||
/// Representation of a u8 immediate.
|
||||
class ImmU8 final : public Value {
|
||||
public:
|
||||
explicit ImmU8(u8 value_) : Value(Opcode::ImmU8), value(value_) {}
|
||||
~ImmU8() override = default;
|
||||
|
||||
const u8 value; ///< Literal value to load
|
||||
};
|
||||
|
||||
/// Representation of a u32 immediate.
|
||||
class ImmU32 final : public Value {
|
||||
public:
|
||||
explicit ImmU32(u32 value_) : Value(Opcode::ImmU32), value(value_) {}
|
||||
~ImmU32() override = default;
|
||||
|
||||
const u32 value; ///< Literal value to load
|
||||
};
|
||||
|
||||
/// Representation of a GPR reference.
|
||||
class ImmRegRef final : public Value {
|
||||
public:
|
||||
explicit ImmRegRef(Arm::Reg value_) : Value(Opcode::ImmRegRef), value(value_) {}
|
||||
~ImmRegRef() override = default;
|
||||
|
||||
const Arm::Reg value; ///< Literal value to load
|
||||
};
|
||||
|
||||
/**
|
||||
* A representation of a microinstruction. A single ARM/Thumb instruction may be
|
||||
* converted into zero or more microinstructions.
|
||||
*/
|
||||
class Inst final : public Value {
|
||||
public:
|
||||
explicit Inst(Opcode op);
|
||||
~Inst() override = default;
|
||||
|
||||
/// Set argument number `index` to `value`.
|
||||
void SetArg(size_t index, ValuePtr value);
|
||||
/// Get argument number `index`.
|
||||
ValuePtr GetArg(size_t index) const;
|
||||
|
||||
void AssertValid();
|
||||
protected:
|
||||
void ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) override;
|
||||
|
||||
private:
|
||||
std::vector<ValueWeakPtr> args;
|
||||
};
|
||||
|
||||
/**
|
||||
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
|
||||
* Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to
|
||||
* the microinstructions. This only matters before chaining is done in order to correctly
|
||||
* order memory accesses.
|
||||
*/
|
||||
class Block final {
|
||||
public:
|
||||
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
|
||||
|
||||
Arm::LocationDescriptor location;
|
||||
std::list<ValuePtr> instructions;
|
||||
};
|
||||
|
||||
|
||||
} // namespace IR
|
||||
} // namespace Dynarmic
|
24
src/frontend_arm/ir/opcodes.h
Normal file
24
src/frontend_arm/ir/opcodes.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace IR {
|
||||
|
||||
enum class Opcode {
|
||||
#define OPCODE(name, type, ...) name,
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
NUM_OPCODE
|
||||
};
|
||||
|
||||
constexpr size_t OpcodeCount = static_cast<size_t>(Opcode::NUM_OPCODE);
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
29
src/frontend_arm/ir/opcodes.inc
Normal file
29
src/frontend_arm/ir/opcodes.inc
Normal file
|
@ -0,0 +1,29 @@
|
|||
// opcode name, return type, arg1 type, arg2 type, arg3 type, ...
|
||||
|
||||
// Immediate values
|
||||
OPCODE(ImmU1, T::U1, )
|
||||
OPCODE(ImmU8, T::U8, )
|
||||
OPCODE(ImmU32, T::U32, )
|
||||
OPCODE(ImmRegRef, T::RegRef, )
|
||||
|
||||
// ARM Context getters/setters
|
||||
OPCODE(GetRegister, T::U32, T::RegRef )
|
||||
OPCODE(SetRegister, T::Void, T::RegRef, T::U32 )
|
||||
OPCODE(GetNFlag, T::U1, )
|
||||
OPCODE(SetNFlag, T::Void, T::U1 )
|
||||
OPCODE(GetZFlag, T::U1, )
|
||||
OPCODE(SetZFlag, T::Void, T::U1 )
|
||||
OPCODE(GetCFlag, T::U1, )
|
||||
OPCODE(SetCFlag, T::Void, T::U1 )
|
||||
OPCODE(GetVFlag, T::U1, )
|
||||
OPCODE(SetVFlag, T::Void, T::U1 )
|
||||
|
||||
// Pseudo-operation, handled specially at final emit
|
||||
OPCODE(GetCarryFromOp, T::U1, T::U32 )
|
||||
|
||||
// Calculations
|
||||
OPCODE(LeastSignificantByte, T::U8, T::U32 )
|
||||
OPCODE(MostSignificantBit, T::U1, T::U32 )
|
||||
OPCODE(IsZero, T::U1, T::U32 )
|
||||
OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 )
|
||||
OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 )
|
97
src/frontend_arm/ir_emitter.cpp
Normal file
97
src/frontend_arm/ir_emitter.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include "ir_emitter.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
void IREmitter::Unimplemented() {
|
||||
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::Imm8(u8 i) {
|
||||
auto imm8 = std::make_shared<IR::ImmU8>(i);
|
||||
AddToBlock(imm8);
|
||||
return imm8;
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::GetRegister(Dynarmic::Arm::Reg reg) {
|
||||
return Inst(IR::Opcode::GetRegister, { RegRef(reg) });
|
||||
}
|
||||
|
||||
void IREmitter::SetRegister(const Reg reg, IR::ValuePtr value) {
|
||||
Inst(IR::Opcode::SetRegister, { RegRef(reg), value });
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::GetCFlag() {
|
||||
return Inst(IR::Opcode::GetCFlag, {});
|
||||
}
|
||||
|
||||
void IREmitter::SetNFlag(IR::ValuePtr value) {
|
||||
Inst(IR::Opcode::SetNFlag, {value});
|
||||
}
|
||||
|
||||
void IREmitter::SetZFlag(IR::ValuePtr value) {
|
||||
Inst(IR::Opcode::SetZFlag, {value});
|
||||
}
|
||||
|
||||
void IREmitter::SetCFlag(IR::ValuePtr value) {
|
||||
Inst(IR::Opcode::SetCFlag, {value});
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::LeastSignificantByte(IR::ValuePtr value) {
|
||||
return Inst(IR::Opcode::LeastSignificantByte, {value});
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::MostSignificantBit(IR::ValuePtr value) {
|
||||
return Inst(IR::Opcode::MostSignificantBit, {value});
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::IsZero(IR::ValuePtr value) {
|
||||
return Inst(IR::Opcode::IsZero, {value});
|
||||
}
|
||||
|
||||
IREmitter::ResultAndCarry IREmitter::LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
|
||||
auto result = Inst(IR::Opcode::LogicalShiftLeft, {value_in, shift_amount, carry_in});
|
||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||
return {result, carry_out};
|
||||
}
|
||||
|
||||
IREmitter::ResultAndCarry IREmitter::LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
|
||||
auto result = Inst(IR::Opcode::LogicalShiftRight, {value_in, shift_amount, carry_in});
|
||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||
return {result, carry_out};
|
||||
}
|
||||
|
||||
|
||||
IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args) {
|
||||
auto inst = std::make_shared<IR::Inst>(op);
|
||||
assert(args.size() == inst->NumArgs());
|
||||
|
||||
std::for_each(args.begin(), args.end(), [&inst, op, index = size_t(0)](const auto& v) mutable {
|
||||
assert(IR::GetArgTypeOf(op, index) == v->GetType());
|
||||
inst->SetArg(index, v);
|
||||
index++;
|
||||
});
|
||||
|
||||
AddToBlock(inst);
|
||||
return inst;
|
||||
}
|
||||
|
||||
IR::ValuePtr IREmitter::RegRef(Reg reg) {
|
||||
auto regref = std::make_shared<IR::ImmRegRef>(reg);
|
||||
AddToBlock(regref);
|
||||
return regref;
|
||||
}
|
||||
|
||||
void IREmitter::AddToBlock(IR::ValuePtr value) {
|
||||
block.instructions.emplace_back(value);
|
||||
}
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
||||
|
51
src/frontend_arm/ir_emitter.h
Normal file
51
src/frontend_arm/ir_emitter.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "frontend_arm/arm_types.h"
|
||||
#include "frontend_arm/ir/ir.h"
|
||||
#include "frontend_arm/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
class IREmitter {
|
||||
public:
|
||||
IR::Block block = Dynarmic::IR::Block({0, false, false});
|
||||
|
||||
struct ResultAndCarry {
|
||||
IR::ValuePtr result;
|
||||
IR::ValuePtr carry;
|
||||
};
|
||||
|
||||
void Unimplemented();
|
||||
|
||||
IR::ValuePtr Imm8(u8 value);
|
||||
|
||||
IR::ValuePtr GetRegister(Reg source_reg);
|
||||
void SetRegister(const Reg dest_reg, IR::ValuePtr value);
|
||||
|
||||
IR::ValuePtr GetCFlag();
|
||||
void SetNFlag(IR::ValuePtr value);
|
||||
void SetZFlag(IR::ValuePtr value);
|
||||
void SetCFlag(IR::ValuePtr value);
|
||||
|
||||
IR::ValuePtr LeastSignificantByte(IR::ValuePtr value);
|
||||
IR::ValuePtr MostSignificantBit(IR::ValuePtr value);
|
||||
IR::ValuePtr IsZero(IR::ValuePtr value);
|
||||
|
||||
ResultAndCarry LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
||||
ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
||||
|
||||
private:
|
||||
IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args);
|
||||
IR::ValuePtr RegRef(Reg reg);
|
||||
void AddToBlock(IR::ValuePtr value);
|
||||
};
|
||||
|
||||
} // namespace Arm
|
||||
} // namespace Dynarmic
|
293
src/frontend_arm/translate_thumb.h
Normal file
293
src/frontend_arm/translate_thumb.h
Normal file
|
@ -0,0 +1,293 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "frontend_arm/arm_types.h"
|
||||
#include "frontend_arm/ir_emitter.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace Arm {
|
||||
|
||||
class TranslatorVisitor {
|
||||
public:
|
||||
IREmitter ir;
|
||||
|
||||
void thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
||||
u8 shift_n = imm5;
|
||||
// LSLS <Rd>, <Rm>, #<imm5>
|
||||
auto cpsr_c = ir.GetCFlag();
|
||||
auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
|
||||
ir.SetRegister(d, result.result);
|
||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||
ir.SetZFlag(ir.IsZero(result.result));
|
||||
ir.SetCFlag(result.carry);
|
||||
}
|
||||
void thumb1_LSR_imm(Imm5 imm5, Reg m, Reg d) {
|
||||
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
||||
// LSRS <Rd>, <Rm>, #<imm5>
|
||||
auto cpsr_c = ir.GetCFlag();
|
||||
auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
|
||||
ir.SetRegister(d, result.result);
|
||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||
ir.SetZFlag(ir.IsZero(result.result));
|
||||
ir.SetCFlag(result.carry);
|
||||
}
|
||||
void thumb1_ASR_rri(Imm5 imm5, Reg m, Reg d) {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADD_rrr(Reg m, Reg n, Reg d) {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SUB_rrr(Reg m, Reg n, Reg d) {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADD_rri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SUB_rri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_MOV_ri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_CMP_ri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADD_ri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SUB_ri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ANDS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_EORS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LSLS_reg(Reg m, Reg d_n) {
|
||||
const Reg d = d_n, n = d_n;
|
||||
// LSLS <Rdn>, <Rm>
|
||||
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
|
||||
auto apsr_c = ir.GetCFlag();
|
||||
auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(d), shift_n, apsr_c);
|
||||
ir.SetRegister(d, result_carry.result);
|
||||
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
|
||||
ir.SetZFlag(ir.IsZero(result_carry.result));
|
||||
ir.SetCFlag(result_carry.carry);
|
||||
}
|
||||
void thumb1_LSRS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ASRS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADCS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SBCS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_RORS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_TST_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_NEGS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_CMP_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_CMN_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ORRS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_MULS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BICS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_MVNS_rr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADD_high() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_CMP_high() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_MOV_high() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDR_lit() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_STR_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_STRH_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_STRB_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDRSB_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDR_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDRH_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDRB_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDRSH_rrr() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_STRH_rri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDRH_rri() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_STR_sp() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDR_sp() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADR() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADD_sp() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_ADD_spsp() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SUB_spsp() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SXTH() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SXTB() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_UXTH() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_UXTB() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_PUSH() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_POP() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SETEND() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_CPS() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_REV() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_REV16() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_REVSH() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BKPT() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_STMIA() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_LDMIA() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BX() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BLX() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BEQ() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BNE() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BCS() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BCC() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BMI() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BPL() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BVS() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BVC() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BHI() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BLS() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BGE() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BLT() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BGT() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BLE() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_UDF() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_SWI() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_B() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BLX_suffix() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BLX_prefix() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
void thumb1_BL_suffix() {
|
||||
ir.Unimplemented();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Arm
|
||||
} // namepsace Dynarmic
|
29
src/interface/interface.h
Normal file
29
src/interface/interface.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
struct UserCallbacks {
|
||||
u8 (*MemoryRead8)(u32 vaddr);
|
||||
u16 (*MemoryRead16)(u32 vaddr);
|
||||
u32 (*MemoryRead32)(u32 vaddr);
|
||||
u64 (*MemoryRead64)(u32 vaddr);
|
||||
|
||||
void (*MemoryWrite8)(u32 vaddr, u8 value);
|
||||
void (*MemoryWrite16)(u32 vaddr, u16 value);
|
||||
void (*MemoryWrite32)(u32 vaddr, u32 value);
|
||||
void (*MemoryWrite64)(u32 vaddr, u64 value);
|
||||
|
||||
void (*InterpreterFallback)(u32 pc, void* jit_state);
|
||||
|
||||
bool (*SoftwareInterrupt)(u32 swi);
|
||||
};
|
||||
|
||||
} // namespace Dynarmic
|
13
src/tests/CMakeLists.txt
Normal file
13
src/tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
set(SRCS
|
||||
arm/test_arm_disassembler.cpp
|
||||
main.cpp
|
||||
arm/test_thumb_instructions.cpp
|
||||
arm/fuzz_thumb.cpp)
|
||||
|
||||
set(HEADERS
|
||||
)
|
||||
|
||||
source_group(tests FILES ${SRCS} ${HEADERS})
|
||||
add_executable(dynarmic_tests ${SRCS})
|
||||
target_link_libraries(dynarmic_tests dynarmic_common dynarmic_frontend_arm dynarmic_backend_x64)
|
||||
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
6
src/tests/arm/fuzz_thumb.cpp
Normal file
6
src/tests/arm/fuzz_thumb.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
24
src/tests/arm/test_arm_disassembler.cpp
Normal file
24
src/tests/arm/test_arm_disassembler.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "frontend_arm/arm_disassembler.h"
|
||||
|
||||
TEST_CASE( "Disassemble branch instructions", "[arm][disassembler]" ) {
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xEAFFFFFE) == "b +#0");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xEB000008) == "bl +#40");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xFBFFFFFE) == "blx +#2");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xFAFFFFFF) == "blx +#4");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xFBE1E7FE) == "blx -#7888894");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xE12FFF3D) == "blx sp");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0x312FFF13) == "bxcc r3");
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0x012FFF29) == "bxjeq r9");
|
||||
}
|
||||
|
||||
TEST_CASE( "Disassemble data processing instructions", "[arm][disassembler]" ) {
|
||||
REQUIRE(Dynarmic::Arm::DisassembleArm(0xE2853004) == "add r3, r5, #4");
|
||||
}
|
59
src/tests/arm/test_thumb_instructions.cpp
Normal file
59
src/tests/arm/test_thumb_instructions.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "backend_x64/emit_x64.h"
|
||||
#include "common/common_types.h"
|
||||
#include "frontend_arm/decoder/thumb1.h"
|
||||
#include "frontend_arm/translate_thumb.h"
|
||||
|
||||
struct TinyBlockOfCode : Gen::XCodeBlock {
|
||||
TinyBlockOfCode() {
|
||||
AllocCodeSpace(256);
|
||||
}
|
||||
};
|
||||
|
||||
void RunSingleThumbInstruction(u16 thumb_instruction, Dynarmic::BackendX64::JitState* jit_state_ptr) {
|
||||
Dynarmic::Arm::TranslatorVisitor visitor;
|
||||
auto decoder = Dynarmic::Arm::DecodeThumb1<Dynarmic::Arm::TranslatorVisitor>(thumb_instruction);
|
||||
REQUIRE(!!decoder);
|
||||
decoder->call(visitor, thumb_instruction);
|
||||
|
||||
TinyBlockOfCode block_of_code;
|
||||
Dynarmic::BackendX64::Routines routines;
|
||||
Dynarmic::UserCallbacks callbacks;
|
||||
Dynarmic::BackendX64::EmitX64 emitter(&block_of_code, &routines, callbacks);
|
||||
|
||||
Dynarmic::BackendX64::CodePtr code = emitter.Emit(visitor.ir.block);
|
||||
routines.RunCode(jit_state_ptr, code, 1);
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
||||
Dynarmic::BackendX64::JitState jit_state;
|
||||
jit_state.Reg[0] = 1;
|
||||
jit_state.Reg[1] = 2;
|
||||
jit_state.Cpsr = 0;
|
||||
|
||||
RunSingleThumbInstruction(0x0088, &jit_state);
|
||||
|
||||
REQUIRE( jit_state.Reg[0] == 8 );
|
||||
REQUIRE( jit_state.Reg[1] == 2 );
|
||||
REQUIRE( jit_state.Cpsr == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
||||
Dynarmic::BackendX64::JitState jit_state;
|
||||
jit_state.Reg[0] = 1;
|
||||
jit_state.Reg[1] = 0xFFFFFFFF;
|
||||
jit_state.Cpsr = 0;
|
||||
|
||||
RunSingleThumbInstruction(0x07C8, &jit_state);
|
||||
|
||||
REQUIRE( jit_state.Reg[0] == 0x80000000 );
|
||||
REQUIRE( jit_state.Reg[1] == 0xffffffff );
|
||||
REQUIRE( jit_state.Cpsr == 0x20000000 );
|
||||
}
|
8
src/tests/main.cpp
Normal file
8
src/tests/main.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
|
||||
#include <catch.hpp>
|
Loading…
Reference in a new issue