Backend/A64: Add Dolphin's ARM emitter
This commit is contained in:
parent
f7664d9161
commit
7905eeb94b
@ -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()
|
||||
|
3726
src/backend/A64/emitter/a64_emitter.cpp
Normal file
3726
src/backend/A64/emitter/a64_emitter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1128
src/backend/A64/emitter/a64_emitter.h
Normal file
1128
src/backend/A64/emitter/a64_emitter.h
Normal file
File diff suppressed because it is too large
Load Diff
28
src/backend/A64/emitter/arm_common.h
Normal file
28
src/backend/A64/emitter/arm_common.h
Normal 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
|
126
src/backend/A64/emitter/code_block.h
Normal file
126
src/backend/A64/emitter/code_block.h
Normal 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
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user