Backend/A64: Add Dolphin's ARM emitter

This commit is contained in:
BreadFish64 2018-08-16 16:16:23 -05:00 committed by SachinVin
parent f7664d9161
commit 7905eeb94b
7 changed files with 5021 additions and 1 deletions

View File

@ -322,6 +322,13 @@ if (ARCHITECTURE_x86_64)
else()
target_sources(dynarmic PRIVATE backend/x64/exception_handler_generic.cpp)
endif()
elseif(ARCHITECTURE_Aarch64)
target_sources(dynarmic PRIVATE
backend/A64/emitter/a64_emitter.cpp
backend/A64/emitter/a64_emitter.h
backend/A64/emitter/arm_common.h
backend/A64/emitter/code_block.h
)
else()
message(FATAL_ERROR "Unsupported architecture")
endif()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
// Copyright 2014 Dolphin Emulator Project / 2018 dynarmic project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "common/common_types.h"
namespace Dynarmic::BackendA64 {
enum CCFlags {
CC_EQ = 0, // Equal
CC_NEQ, // Not equal
CC_CS, // Carry Set
CC_CC, // Carry Clear
CC_MI, // Minus (Negative)
CC_PL, // Plus
CC_VS, // Overflow
CC_VC, // No Overflow
CC_HI, // Unsigned higher
CC_LS, // Unsigned lower or same
CC_GE, // Signed greater than or equal
CC_LT, // Signed less than
CC_GT, // Signed greater than
CC_LE, // Signed less than or equal
CC_AL, // Always (unconditional) 14
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
};
const u32 NO_COND = 0xE0000000;
} // namespace Dynarmic::BackendA64

View File

@ -0,0 +1,126 @@
// Copyright 2014 Dolphin Emulator Project / 2018 dynarmic project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <vector>
#include <sys/mman.h>
#include "common/assert.h"
#include "common/common_types.h"
namespace Dynarmic::BackendA64 {
// 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 = nullptr;
// Size of region we can use.
size_t region_size = 0;
// Original size of the region we allocated.
size_t total_region_size = 0;
bool m_is_child = false;
std::vector<CodeBlock*> m_children;
public:
CodeBlock() = default;
virtual ~CodeBlock() {
if (region)
FreeCodeSpace();
}
CodeBlock(const CodeBlock&) = delete;
CodeBlock& operator=(const CodeBlock&) = delete;
CodeBlock(CodeBlock&&) = delete;
CodeBlock& operator=(CodeBlock&&) = delete;
// Call this before you generate any code.
void AllocCodeSpace(size_t size) {
region_size = size;
total_region_size = size;
void* ptr =
mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED)
ptr = nullptr;
region = static_cast<u8*>(ptr);
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() {
ASSERT(!m_is_child);
ASSERT(munmap(region, total_region_size) != 0);
region = nullptr;
region_size = 0;
total_region_size = 0;
for (CodeBlock* child : m_children) {
child->region = nullptr;
child->region_size = 0;
child->total_region_size = 0;
}
}
bool IsInSpace(const u8* ptr) const {
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() {
ASSERT(mprotect(region, region_size, PROT_READ | PROT_EXEC) != 0);
}
void ResetCodePtr() {
T::SetCodePtr(region);
}
size_t GetSpaceLeft() const {
ASSERT(static_cast<size_t>(T::GetCodePtr() - region) < region_size);
return region_size - (T::GetCodePtr() - region);
}
bool IsAlmostFull() const {
// This should be bigger than the biggest block ever.
return GetSpaceLeft() < 0x10000;
}
bool HasChildren() const {
return region_size != total_region_size;
}
u8* AllocChildCodeSpace(size_t child_size) {
ASSERT_MSG(child_size < GetSpaceLeft(), "Insufficient space for child allocation.");
u8* child_region = region + region_size - child_size;
region_size -= child_size;
return child_region;
}
void AddChildCodeSpace(CodeBlock* child, size_t child_size) {
u8* child_region = AllocChildCodeSpace(child_size);
child->m_is_child = true;
child->region = child_region;
child->region_size = child_size;
child->total_region_size = child_size;
child->ResetCodePtr();
m_children.emplace_back(child);
}
};
} // namespace Dynarmic::BackendA64

View File

@ -44,4 +44,9 @@ u8 RecipEstimate(u64 a);
*/
u8 RecipSqrtEstimate(u64 a);
template <typename T>
constexpr bool IsPow2(T imm){
return imm > 0 && (imm & (imm - 1)) == 0;
}
} // namespace Dynarmic::Common

View File

@ -3,9 +3,9 @@
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
#include "common/bit_util.h"
#include "frontend/ir/terminal.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {