[ARM64] Initial commit for arm64 backend
Based entirely off existing xbyak x86 implementation and available tests. Still needs a lot of optimization and testing on non-Windows platforms. So far passes all tests and boots at least some games on Windows.
This commit is contained in:
143
src/xenia/cpu/backend/a64/a64_assembler.cc
Normal file
143
src/xenia/cpu/backend/a64/a64_assembler.cc
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2026 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/backend/a64/a64_assembler.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "third_party/capstone/include/capstone/aarch64.h"
|
||||
#include "third_party/capstone/include/capstone/capstone.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/base/reset_scope.h"
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/cpu/backend/a64/a64_backend.h"
|
||||
#include "xenia/cpu/backend/a64/a64_code_cache.h"
|
||||
#include "xenia/cpu/backend/a64/a64_emitter.h"
|
||||
#include "xenia/cpu/backend/a64/a64_function.h"
|
||||
#include "xenia/cpu/cpu_flags.h"
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
#include "xenia/cpu/hir/label.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
namespace a64 {
|
||||
|
||||
using xe::cpu::hir::HIRBuilder;
|
||||
|
||||
A64Assembler::A64Assembler(A64Backend* backend)
|
||||
: Assembler(backend), a64_backend_(backend), capstone_handle_(0) {
|
||||
if (cs_open(CS_ARCH_AARCH64, CS_MODE_ARM, &capstone_handle_) != CS_ERR_OK) {
|
||||
assert_always("Failed to initialize capstone for ARM64");
|
||||
}
|
||||
cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_OFF);
|
||||
}
|
||||
|
||||
A64Assembler::~A64Assembler() {
|
||||
emitter_.reset();
|
||||
|
||||
if (capstone_handle_) {
|
||||
cs_close(&capstone_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool A64Assembler::Initialize() {
|
||||
if (!Assembler::Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
emitter_.reset(new A64Emitter(a64_backend_, &allocator_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void A64Assembler::Reset() {
|
||||
string_buffer_.Reset();
|
||||
Assembler::Reset();
|
||||
}
|
||||
|
||||
bool A64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
|
||||
uint32_t debug_info_flags,
|
||||
std::unique_ptr<FunctionDebugInfo> debug_info) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
|
||||
// Reset when we leave.
|
||||
xe::make_reset_scope(this);
|
||||
|
||||
// Lower HIR -> ARM64.
|
||||
void* machine_code = nullptr;
|
||||
size_t code_size = 0;
|
||||
if (!emitter_->Emit(function, builder, debug_info_flags, debug_info.get(),
|
||||
&machine_code, &code_size, &function->source_map())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stash generated machine code.
|
||||
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
|
||||
DumpMachineCode(machine_code, code_size, function->source_map(),
|
||||
&string_buffer_);
|
||||
debug_info->set_machine_code_disasm(xe_strdup(string_buffer_.buffer()));
|
||||
string_buffer_.Reset();
|
||||
}
|
||||
|
||||
function->set_debug_info(std::move(debug_info));
|
||||
static_cast<A64Function*>(function)->Setup(
|
||||
reinterpret_cast<uint8_t*>(machine_code), code_size);
|
||||
|
||||
// Install into indirection table.
|
||||
uint64_t host_address = reinterpret_cast<uint64_t>(machine_code);
|
||||
assert_true((host_address >> 32) == 0);
|
||||
reinterpret_cast<A64CodeCache*>(backend_->code_cache())
|
||||
->AddIndirection(function->address(),
|
||||
static_cast<uint32_t>(host_address));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void A64Assembler::DumpMachineCode(
|
||||
void* machine_code, size_t code_size,
|
||||
const std::vector<SourceMapEntry>& source_map, StringBuffer* str) {
|
||||
if (source_map.empty()) {
|
||||
return;
|
||||
}
|
||||
auto source_map_index = 0;
|
||||
uint32_t next_code_offset = source_map[0].code_offset;
|
||||
|
||||
const uint8_t* code_ptr = reinterpret_cast<uint8_t*>(machine_code);
|
||||
size_t remaining_code_size = code_size;
|
||||
uint64_t address = uint64_t(machine_code);
|
||||
cs_insn insn = {0};
|
||||
while (remaining_code_size &&
|
||||
cs_disasm_iter(capstone_handle_, &code_ptr, &remaining_code_size,
|
||||
&address, &insn)) {
|
||||
// Look up source offset.
|
||||
auto code_offset =
|
||||
uint32_t(code_ptr - reinterpret_cast<uint8_t*>(machine_code));
|
||||
if (code_offset >= next_code_offset &&
|
||||
source_map_index < source_map.size()) {
|
||||
auto& source_map_entry = source_map[source_map_index];
|
||||
str->AppendFormat("{:08X} ", source_map_entry.guest_address);
|
||||
++source_map_index;
|
||||
next_code_offset = source_map_index < source_map.size()
|
||||
? source_map[source_map_index].code_offset
|
||||
: UINT_MAX;
|
||||
} else {
|
||||
str->Append(" ");
|
||||
}
|
||||
|
||||
str->AppendFormat("{:08X} {:<6} {}\n", uint32_t(insn.address),
|
||||
insn.mnemonic, insn.op_str);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace a64
|
||||
} // namespace backend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
Reference in New Issue
Block a user