[A64] Update ARM64 backend from xenia-mac delta

- sync sequences/emitter/code cache/tracers from macOS integration
- add NEON helpers, denorm flush, SET_NJM, and half-float fixes
This commit is contained in:
Will Martin
2026-01-26 23:35:52 +09:00
parent 745581d4bd
commit 8dbb4edadd
13 changed files with 406 additions and 802 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ using xe::cpu::hir::HIRBuilder;
A64Assembler::A64Assembler(A64Backend* backend)
: Assembler(backend), a64_backend_(backend), capstone_handle_(0) {
if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) !=
if (cs_open(CS_ARCH_AARCH64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) !=
CS_ERR_OK) {
assert_always("Failed to initialize capstone");
}
+21 -57
View File
@@ -70,15 +70,12 @@ class A64ThunkEmitter : public A64Emitter {
};
A64Backend::A64Backend() : Backend(), code_cache_(nullptr) {
cs_err err = cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_);
cs_err err =
cs_open(CS_ARCH_AARCH64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_);
if (err) {
printf("Failed on cs_open() with error returned: %u\n", err);
assert_always("Failed to initialize capstone");
}
// if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) !=
// CS_ERR_OK) {
// assert_always("Failed to initialize capstone");
// }
cs_option(capstone_handle_, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL);
cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_ON);
cs_option(capstone_handle_, CS_OPT_SKIPDATA, CS_OPT_OFF);
@@ -160,7 +157,7 @@ std::unique_ptr<GuestFunction> A64Backend::CreateGuestFunction(
return std::make_unique<A64Function>(module, address);
}
uint64_t ReadCapstoneReg(HostThreadContext* context, arm64_reg reg) {
uint64_t ReadCapstoneReg(HostThreadContext* context, aarch64_reg reg) {
switch (reg) {
case ARM64_REG_X0:
return context->x[0];
@@ -300,37 +297,37 @@ bool TestCapstonePstate(arm64_cc cond, uint32_t pstate) {
const bool C = !!(pstate & 0x20000000);
const bool V = !!(pstate & 0x10000000);
switch (cond) {
case ARM64_CC_EQ:
case ARM64CC_EQ:
return (Z == true);
case ARM64_CC_NE:
case ARM64CC_NE:
return (Z == false);
case ARM64_CC_HS:
case ARM64CC_HS:
return (C == true);
case ARM64_CC_LO:
case ARM64CC_LO:
return (C == false);
case ARM64_CC_MI:
case ARM64CC_MI:
return (N == true);
case ARM64_CC_PL:
case ARM64CC_PL:
return (N == false);
case ARM64_CC_VS:
case ARM64CC_VS:
return (V == true);
case ARM64_CC_VC:
case ARM64CC_VC:
return (V == false);
case ARM64_CC_HI:
case ARM64CC_HI:
return ((C == true) && (Z == false));
case ARM64_CC_LS:
case ARM64CC_LS:
return ((C == false) || (Z == true));
case ARM64_CC_GE:
case ARM64CC_GE:
return (N == V);
case ARM64_CC_LT:
case ARM64CC_LT:
return (N != V);
case ARM64_CC_GT:
case ARM64CC_GT:
return ((Z == false) && (N == V));
case ARM64_CC_LE:
case ARM64CC_LE:
return ((Z == true) || (N != V));
case ARM64_CC_AL:
case ARM64CC_AL:
return true;
case ARM64_CC_NV:
case ARM64CC_NV:
return false;
default:
assert_unhandled_case(cond);
@@ -348,14 +345,14 @@ uint64_t A64Backend::CalculateNextHostInstruction(ThreadDebugInfo* thread_info,
insn.detail = &all_detail;
cs_disasm_iter(capstone_handle_, &machine_code_ptr,
&remaining_machine_code_size, &host_address, &insn);
const auto& detail = all_detail.arm64;
const auto& detail = all_detail.aarch64;
switch (insn.id) {
case ARM64_INS_B:
case ARM64_INS_BL: {
assert_true(detail.operands[0].type == ARM64_OP_IMM);
const int64_t pc_offset = static_cast<int64_t>(detail.operands[0].imm);
const bool test_passed =
TestCapstonePstate(detail.cc, thread_info->host_context.cpsr);
TestCapstonePstate(detail.cc, thread_info->host_context.pstate);
if (test_passed) {
return current_pc + pc_offset;
} else {
@@ -451,39 +448,6 @@ bool A64Backend::ExceptionCallbackThunk(Exception* ex, void* data) {
}
bool A64Backend::ExceptionCallback(Exception* ex) {
if (ex->code() == Exception::Code::kAccessViolation) {
const uint64_t host_pc = ex->pc();
const uint64_t fault_address = ex->fault_address();
uint64_t guest_pc = 0;
uint32_t host_offset = 0;
auto function = code_cache_->LookupFunction(host_pc);
if (function && function->machine_code()) {
const uint64_t function_pc =
reinterpret_cast<uint64_t>(function->machine_code());
host_offset = static_cast<uint32_t>(host_pc - function_pc);
if (const auto* entry = function->LookupMachineCodeOffset(host_offset)) {
guest_pc = entry->guest_address;
}
}
#if XE_ARCH_ARM64
auto* thread_context = ex->thread_context();
XELOGE(
"A64 AV: host_pc=0x{:016X} guest_pc=0x{:08X} host_off=0x{:X} "
"fault=0x{:016X} op={} x21=0x{:016X} x27=0x{:016X} x28=0x{:016X}",
host_pc, guest_pc, host_offset, fault_address,
static_cast<int>(ex->access_violation_operation()),
thread_context ? thread_context->x[21] : 0,
thread_context ? thread_context->x[27] : 0,
thread_context ? thread_context->x[28] : 0);
#else
XELOGE(
"A64 AV: host_pc=0x{:016X} guest_pc=0x{:08X} host_off=0x{:X} "
"fault=0x{:016X} op={}",
host_pc, guest_pc, host_offset, fault_address,
static_cast<int>(ex->access_violation_operation()));
#endif
return false;
}
if (ex->code() != Exception::Code::kIllegalInstruction) {
// We only care about illegal instructions. Other things will be handled by
// other handlers (probably). If nothing else picks it up we'll be called
+6 -111
View File
@@ -9,7 +9,6 @@
#include "xenia/cpu/backend/a64/a64_code_cache.h"
#include <atomic>
#include <cstdlib>
#include <cstring>
@@ -20,7 +19,6 @@
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/clock.h"
#include "xenia/base/cvar.h"
#include "xenia/base/literals.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
@@ -28,11 +26,6 @@
#include "xenia/cpu/function.h"
#include "xenia/cpu/module.h"
DEFINE_bool(a64_indirection_table_log, false,
"Log A64 indirection table mapping and updates.", "CPU");
DEFINE_int32(a64_indirection_table_log_limit, 32,
"Maximum number of A64 indirection table log entries.", "CPU");
namespace xe {
namespace cpu {
namespace backend {
@@ -40,23 +33,6 @@ namespace a64 {
using namespace xe::literals;
namespace {
bool ShouldLogIndirectionTable() {
if (!cvars::a64_indirection_table_log) {
return false;
}
const int32_t limit = cvars::a64_indirection_table_log_limit;
if (limit <= 0) {
return false;
}
static std::atomic<int32_t> log_count{0};
const int32_t count = log_count.fetch_add(1, std::memory_order_relaxed);
return count < limit;
}
} // namespace
// Define static constants for linking
const size_t A64CodeCache::kIndirectionTableSize;
#if XE_A64_INDIRECTION_64BIT
@@ -132,16 +108,17 @@ bool A64CodeCache::Initialize() {
xe::memory::AllocationType::kReserve,
xe::memory::PageAccess::kReadWrite));
if (!indirection_table_base_) {
XELOGW("Preferred indirection table base unavailable; falling back");
indirection_table_base_ = reinterpret_cast<uint8_t*>(xe::memory::AllocFixed(
nullptr, kIndirectionTableSize, xe::memory::AllocationType::kReserve,
xe::memory::PageAccess::kReadWrite));
}
if (!indirection_table_base_) {
XELOGE("Unable to allocate code cache indirection table");
XELOGE("Tried preferred range {:X}-{:X} with fallback to OS-chosen",
static_cast<uint64_t>(kIndirectionTableBase),
kIndirectionTableBase + kIndirectionTableSize);
XELOGE(
"This is likely because the {:X}-{:X} range is in use by some other "
"system DLL",
static_cast<uint64_t>(kIndirectionTableBase),
kIndirectionTableBase + kIndirectionTableSize);
return false;
}
indirection_table_actual_base_ =
@@ -153,18 +130,8 @@ bool A64CodeCache::Initialize() {
#endif
#endif
if (ShouldLogIndirectionTable()) {
XELOGI(
"A64 indirection table: guest_base=0x{:08X} table_base=0x{:016X} "
"size=0x{:X} entry_bytes={}",
static_cast<uint32_t>(kIndirectionTableBase),
static_cast<uint64_t>(indirection_table_actual_base_),
static_cast<uint32_t>(kIndirectionTableSize),
static_cast<uint32_t>(kIndirectionEntrySize));
}
// Create mmap file. This allows us to share the code cache with the debugger.
file_name_ = fmt::format("xenia_code_cache");
file_name_ = fmt::format("xenia_code_cache_{}", Clock::QueryHostTickCount());
mapping_ = xe::memory::CreateFileMappingHandle(
file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite,
false);
@@ -193,9 +160,6 @@ bool A64CodeCache::Initialize() {
mapping_, reinterpret_cast<void*>(kGeneratedCodeExecuteBase),
kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite, 0));
if (!generated_code_execute_base_) {
XELOGW(
"Fixed address mapping for generated code failed, trying OS-chosen "
"address");
generated_code_execute_base_ =
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
mapping_, nullptr, kGeneratedCodeSize,
@@ -231,9 +195,6 @@ bool A64CodeCache::Initialize() {
mapping_, reinterpret_cast<void*>(kGeneratedCodeExecuteBase),
kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadOnly, 0));
if (!generated_code_execute_base_) {
XELOGW(
"Fixed address mapping for execute code failed, trying OS-chosen "
"address");
generated_code_execute_base_ = reinterpret_cast<uint8_t*>(
xe::memory::MapFileView(mapping_, nullptr, kGeneratedCodeSize,
xe::memory::PageAccess::kExecuteReadOnly, 0));
@@ -243,9 +204,6 @@ bool A64CodeCache::Initialize() {
mapping_, reinterpret_cast<void*>(kGeneratedCodeWriteBase),
kGeneratedCodeSize, xe::memory::PageAccess::kReadWrite, 0));
if (!generated_code_write_base_) {
XELOGW(
"Fixed address mapping for write code failed, trying OS-chosen "
"address");
generated_code_write_base_ = reinterpret_cast<uint8_t*>(
xe::memory::MapFileView(mapping_, nullptr, kGeneratedCodeSize,
xe::memory::PageAccess::kReadWrite, 0));
@@ -309,51 +267,27 @@ void A64CodeCache::AddIndirection64(uint32_t guest_address,
}
if (guest_address < kIndirectionTableBase) {
XELOGE(
"A64CodeCache::AddIndirection64: guest_address 0x{:08X} below base "
"0x{:08X}",
guest_address, static_cast<uint32_t>(kIndirectionTableBase));
return;
}
const uint64_t guest_delta = guest_address - kIndirectionTableBase;
if (guest_delta & 0x3) {
XELOGW(
"A64CodeCache::AddIndirection64: guest_address 0x{:08X} not 4-byte "
"aligned (delta=0x{:X})",
guest_address, guest_delta);
}
// Calculate offset from the logical base (0x80000000), not from actual table
// address.
const uint64_t guest_offset = (guest_delta >> 2) * kIndirectionEntrySize;
if (guest_offset + kIndirectionEntrySize > kIndirectionTableSize) {
XELOGE(
"A64CodeCache::AddIndirection64: guest_address 0x{:08X} offset 0x{:X} "
"exceeds table size 0x{:X}",
guest_address, guest_offset,
static_cast<uint32_t>(kIndirectionTableSize));
return;
}
uint64_t* indirection_slot =
reinterpret_cast<uint64_t*>(indirection_table_base_ + guest_offset);
*indirection_slot = host_address;
if (ShouldLogIndirectionTable()) {
XELOGI(
"A64 indirection add: guest=0x{:08X} delta=0x{:X} offset=0x{:X} "
"slot=0x{:016X} host=0x{:016X}",
guest_address, guest_delta, guest_offset,
reinterpret_cast<uint64_t>(indirection_slot), host_address);
}
}
#endif
void A64CodeCache::CommitExecutableRange(uint32_t guest_low,
uint32_t guest_high) {
if (!indirection_table_base_) {
XELOGE("CommitExecutableRange: indirection_table_base_ is null!");
return;
}
@@ -364,10 +298,6 @@ void A64CodeCache::CommitExecutableRange(uint32_t guest_low,
// Calculate offsets from the guest address base, not the table base
if (guest_low < kGuestAddressBase) {
XELOGE(
"CommitExecutableRange: guest_low 0x{:08X} is below guest base "
"0x{:08X}",
guest_low, kGuestAddressBase);
return;
}
@@ -377,10 +307,6 @@ void A64CodeCache::CommitExecutableRange(uint32_t guest_low,
// Sanity check bounds; the table should fully cover the XEX guest range now.
if (start_offset + size > kIndirectionTableSize) {
XELOGE(
"CommitExecutableRange: range [0x{:08X}, 0x{:08X}) exceeds table (size "
"0x{:X})",
guest_low, guest_high, (unsigned)kIndirectionTableSize);
return;
}
@@ -391,14 +317,6 @@ void A64CodeCache::CommitExecutableRange(uint32_t guest_low,
for (uint32_t i = 0; i < entry_count; i++) {
p[i] = indirection_default_value_;
}
if (ShouldLogIndirectionTable()) {
XELOGI(
"A64 indirection commit: guest=[0x{:08X},0x{:08X}) "
"offset=0x{:X} size=0x{:X} entries={} base=0x{:016X}",
guest_low, guest_high, start_offset, size, entry_count,
static_cast<uint64_t>(indirection_table_actual_base_));
}
#else
// Other platforms: use 32-bit entries
uint32_t start_offset = (guest_low - kIndirectionTableBase);
@@ -536,20 +454,10 @@ void A64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
// Calculate offset from the logical guest base (0x80000000)
if (guest_address < kIndirectionTableBase) {
XELOGE(
"A64CodeCache::PlaceGuestCode: ERROR - guest_address 0x{:08X} is "
"below logical base 0x{:08X}!",
guest_address, static_cast<uint32_t>(kIndirectionTableBase));
return;
}
uintptr_t guest_diff = guest_address - kIndirectionTableBase;
if (guest_diff & 0x3) {
XELOGW(
"A64CodeCache::PlaceGuestCode: guest_address 0x{:08X} not 4-byte "
"aligned (delta=0x{:X})",
guest_address, guest_diff);
}
uintptr_t guest_offset =
(guest_diff >> 2) * kIndirectionEntrySize; // 8-byte entries
uintptr_t slot_address =
@@ -560,23 +468,10 @@ void A64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
uintptr_t table_end = reinterpret_cast<uintptr_t>(indirection_table_base_) +
kIndirectionTableSize;
if (slot_address >= table_end) {
XELOGE(
"A64CodeCache::PlaceGuestCode: slot 0x{:016X} beyond table end "
"0x{:016X}",
slot_address, table_end);
return;
}
*indirection_slot = reinterpret_cast<uint64_t>(code_execute_address);
if (ShouldLogIndirectionTable()) {
XELOGI(
"A64 indirection place: guest=0x{:08X} diff=0x{:X} offset=0x{:X} "
"slot=0x{:016X} host=0x{:016X}",
guest_address, guest_diff, guest_offset, slot_address,
static_cast<uint64_t>(
reinterpret_cast<uintptr_t>(code_execute_address)));
}
#else
uint32_t* indirection_slot = reinterpret_cast<uint32_t*>(
indirection_table_base_ + (guest_address - kIndirectionTableBase));
@@ -21,7 +21,6 @@
#include "xenia/base/assert.h"
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/cpu/function.h"
@@ -138,21 +137,6 @@ void PosixA64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
// Store in the reserved slot
unwind_table_[unwind_reservation.table_slot] = unwind_info;
// Validate address alignment before cache flushing
if ((uintptr_t)code_execute_address % 4 != 0) {
XELOGW(
"PosixA64CodeCache::PlaceCode: WARNING - code address 0x{:016X} is not "
"4-byte aligned",
(uintptr_t)code_execute_address);
}
if (func_info.code_size.total % 4 != 0) {
XELOGW(
"PosixA64CodeCache::PlaceCode: WARNING - code size {} is not 4-byte "
"aligned",
func_info.code_size.total);
}
// Flush instruction cache
#ifdef XE_PLATFORM_MAC
// On macOS, use sys_icache_invalidate
File diff suppressed because it is too large Load Diff
+8
View File
@@ -115,6 +115,7 @@ enum VConst {
VQNaN,
VInt127,
V2To32,
VSingleDenormalMask,
};
enum A64EmitterFeatureFlags {
@@ -174,6 +175,13 @@ class A64Emitter : public oaknut::VectorCodeGenerator {
oaknut::Label* lookup_label(const char* label_name) {
return &label_lookup_[label_name];
}
oaknut::Label* lookup_label(hir::Label* label) {
assert_not_null(label);
if (label->name) {
return &label_lookup_[label->name];
}
return &label_lookup_[label->GetIdString()];
}
oaknut::Label& epilog_label() { return *epilog_label_; }
+13 -13
View File
@@ -419,7 +419,7 @@ EMITTER_OPCODE_TABLE(OPCODE_SET_RETURN_ADDRESS, SET_RETURN_ADDRESS);
// ============================================================================
struct BRANCH : Sequence<BRANCH, I<OPCODE_BRANCH, VoidOp, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src1.value->name);
oaknut::Label* label = e.lookup_label(i.src1.value);
assert_not_null(label);
e.B(*label);
}
@@ -432,7 +432,7 @@ EMITTER_OPCODE_TABLE(OPCODE_BRANCH, BRANCH);
struct BRANCH_TRUE_I8
: Sequence<BRANCH_TRUE_I8, I<OPCODE_BRANCH_TRUE, VoidOp, I8Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBNZ(i.src1, *label);
}
@@ -440,7 +440,7 @@ struct BRANCH_TRUE_I8
struct BRANCH_TRUE_I16
: Sequence<BRANCH_TRUE_I16, I<OPCODE_BRANCH_TRUE, VoidOp, I16Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBNZ(i.src1, *label);
}
@@ -448,7 +448,7 @@ struct BRANCH_TRUE_I16
struct BRANCH_TRUE_I32
: Sequence<BRANCH_TRUE_I32, I<OPCODE_BRANCH_TRUE, VoidOp, I32Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBNZ(i.src1, *label);
}
@@ -456,7 +456,7 @@ struct BRANCH_TRUE_I32
struct BRANCH_TRUE_I64
: Sequence<BRANCH_TRUE_I64, I<OPCODE_BRANCH_TRUE, VoidOp, I64Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBNZ(i.src1, *label);
}
@@ -464,7 +464,7 @@ struct BRANCH_TRUE_I64
struct BRANCH_TRUE_F32
: Sequence<BRANCH_TRUE_F32, I<OPCODE_BRANCH_TRUE, VoidOp, F32Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.FCMP(i.src1, 0);
e.B(Cond::NE, *label);
@@ -473,7 +473,7 @@ struct BRANCH_TRUE_F32
struct BRANCH_TRUE_F64
: Sequence<BRANCH_TRUE_F64, I<OPCODE_BRANCH_TRUE, VoidOp, F64Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.FCMP(i.src1, 0);
e.B(Cond::NE, *label);
@@ -489,7 +489,7 @@ EMITTER_OPCODE_TABLE(OPCODE_BRANCH_TRUE, BRANCH_TRUE_I8, BRANCH_TRUE_I16,
struct BRANCH_FALSE_I8
: Sequence<BRANCH_FALSE_I8, I<OPCODE_BRANCH_FALSE, VoidOp, I8Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBZ(i.src1, *label);
}
@@ -498,7 +498,7 @@ struct BRANCH_FALSE_I16
: Sequence<BRANCH_FALSE_I16,
I<OPCODE_BRANCH_FALSE, VoidOp, I16Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBZ(i.src1, *label);
}
@@ -507,7 +507,7 @@ struct BRANCH_FALSE_I32
: Sequence<BRANCH_FALSE_I32,
I<OPCODE_BRANCH_FALSE, VoidOp, I32Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBZ(i.src1, *label);
}
@@ -516,7 +516,7 @@ struct BRANCH_FALSE_I64
: Sequence<BRANCH_FALSE_I64,
I<OPCODE_BRANCH_FALSE, VoidOp, I64Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.CBZ(i.src1, *label);
}
@@ -525,7 +525,7 @@ struct BRANCH_FALSE_F32
: Sequence<BRANCH_FALSE_F32,
I<OPCODE_BRANCH_FALSE, VoidOp, F32Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.FCMP(i.src1, 0);
e.B(Cond::EQ, *label);
@@ -535,7 +535,7 @@ struct BRANCH_FALSE_F64
: Sequence<BRANCH_FALSE_F64,
I<OPCODE_BRANCH_FALSE, VoidOp, F64Op, LabelOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
oaknut::Label* label = e.lookup_label(i.src2.value->name);
oaknut::Label* label = e.lookup_label(i.src2.value);
assert_not_null(label);
e.FCMP(i.src1, 0);
e.B(Cond::EQ, *label);
+184
View File
@@ -23,6 +23,11 @@ namespace a64 {
volatile int anchor_memory = 0;
// vec128b stores bytes in reversed 32-bit chunks; use reversed args for 0..15.
static const vec128_t kStvlShuffle =
vec128b(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12);
static const vec128_t kStvrSwapMask = vec128b(static_cast<uint8_t>(0x83));
template <typename T>
XReg ComputeMemoryAddressOffset(A64Emitter& e, const T& guest, const T& offset,
WReg address_register = W3) {
@@ -176,6 +181,132 @@ EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_EXCHANGE, ATOMIC_EXCHANGE_I8,
ATOMIC_EXCHANGE_I16, ATOMIC_EXCHANGE_I32,
ATOMIC_EXCHANGE_I64);
// ============================================================================
// OPCODE_LVL/LVR/STVL/STVR
// ============================================================================
struct LVL_V128 : Sequence<LVL_V128, I<OPCODE_LVL, V128Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W4);
e.AND(W0, address.toW(), 0xF);
e.SUB(X1, address, X0);
e.LDR(Q2, X1);
e.MOV(X2, e.GetVConstPtr());
e.LDR(Q0, X2, e.GetVConstOffset(VByteSwapMask));
e.DUP(Q1.B16(), W0);
e.ADD(Q0.B16(), Q0.B16(), Q1.B16());
e.TBL(i.dest.reg().B16(), List{Q2.B16()}, Q0.B16());
}
};
EMITTER_OPCODE_TABLE(OPCODE_LVL, LVL_V128);
struct LVR_V128 : Sequence<LVR_V128, I<OPCODE_LVR, V128Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W4);
e.AND(W0, address.toW(), 0xF);
e.EOR(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16());
oaknut::Label done;
e.CBZ(W0, done);
e.SUB(X1, address, X0);
e.LDR(Q2, X1);
e.MOV(X2, e.GetVConstPtr());
e.LDR(Q0, X2, e.GetVConstOffset(VByteSwapMask));
e.DUP(Q1.B16(), W0);
e.ADD(Q0.B16(), Q0.B16(), Q1.B16());
e.MOVI(Q1.B16(), 0x10);
e.CMHS(Q3.B16(), Q0.B16(), Q1.B16());
e.SUB(Q0.B16(), Q0.B16(), Q1.B16());
e.MOVI(Q1.B16(), 0x80);
e.BSL(Q3.B16(), Q0.B16(), Q1.B16());
e.TBL(i.dest.reg().B16(), List{Q2.B16()}, Q3.B16());
e.l(done);
}
};
EMITTER_OPCODE_TABLE(OPCODE_LVR, LVR_V128);
struct STVL_V128 : Sequence<STVL_V128, I<OPCODE_STVL, VoidOp, I64Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W4);
e.AND(W0, address.toW(), 0xF);
e.SUB(X1, address, X0);
e.LDR(Q2, X1);
e.MOV(X2, reinterpret_cast<uintptr_t>(&kStvlShuffle));
e.LDR(Q0, X2);
e.DUP(Q1.B16(), W0);
e.SUB(Q0.B16(), Q0.B16(), Q1.B16());
e.MOV(X2, e.GetVConstPtr());
e.LDR(Q1, X2, e.GetVConstOffset(VSwapWordMask));
e.EOR(Q0.B16(), Q0.B16(), Q1.B16());
const QReg shuffled = Q3;
if (i.src2.is_constant) {
e.LoadConstantV(shuffled, i.src2.constant());
} else {
e.MOV(shuffled.B16(), i.src2.reg().B16());
}
e.TBL(shuffled.B16(), List{shuffled.B16()}, Q0.B16());
e.MOVI(Q1.B16(), 0x80);
e.CMHS(Q1.B16(), Q0.B16(), Q1.B16());
e.BSL(Q1.B16(), Q2.B16(), shuffled.B16());
e.STR(Q1, X1);
}
};
EMITTER_OPCODE_TABLE(OPCODE_STVL, STVL_V128);
struct STVR_V128 : Sequence<STVR_V128, I<OPCODE_STVR, VoidOp, I64Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W4);
e.AND(W0, address.toW(), 0xF);
oaknut::Label done;
e.CBZ(W0, done);
e.SUB(X1, address, X0);
e.LDR(Q2, X1);
e.MOV(X2, reinterpret_cast<uintptr_t>(&kStvlShuffle));
e.LDR(Q0, X2);
e.DUP(Q1.B16(), W0);
e.SUB(Q0.B16(), Q0.B16(), Q1.B16());
e.MOV(X2, reinterpret_cast<uintptr_t>(&kStvrSwapMask));
e.LDR(Q1, X2);
e.EOR(Q0.B16(), Q0.B16(), Q1.B16());
e.MOVI(Q1.B16(), 0x0F);
e.AND(Q1.B16(), Q0.B16(), Q1.B16());
e.MOVI(Q3.B16(), 0x80);
e.AND(Q3.B16(), Q0.B16(), Q3.B16());
e.ORR(Q1.B16(), Q1.B16(), Q3.B16());
const QReg shuffled = Q3;
if (i.src2.is_constant) {
e.LoadConstantV(shuffled, i.src2.constant());
} else {
e.MOV(shuffled.B16(), i.src2.reg().B16());
}
e.TBL(shuffled.B16(), List{shuffled.B16()}, Q1.B16());
e.MOVI(Q1.B16(), 0x80);
e.CMHS(Q1.B16(), Q0.B16(), Q1.B16());
e.BSL(Q1.B16(), Q2.B16(), shuffled.B16());
e.STR(Q1, X1);
e.l(done);
}
};
EMITTER_OPCODE_TABLE(OPCODE_STVR, STVR_V128);
// ============================================================================
// OPCODE_ATOMIC_COMPARE_EXCHANGE
// ============================================================================
@@ -282,6 +413,59 @@ struct ATOMIC_COMPARE_EXCHANGE_I64
EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_COMPARE_EXCHANGE,
ATOMIC_COMPARE_EXCHANGE_I32, ATOMIC_COMPARE_EXCHANGE_I64);
// ============================================================================
// OPCODE_RESERVED_LOAD / OPCODE_RESERVED_STORE
// ============================================================================
struct RESERVED_LOAD_INT32
: Sequence<RESERVED_LOAD_INT32, I<OPCODE_RESERVED_LOAD, I32Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W3);
e.LDAXR(i.dest, address);
}
};
struct RESERVED_LOAD_INT64
: Sequence<RESERVED_LOAD_INT64, I<OPCODE_RESERVED_LOAD, I64Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W3);
e.LDAXR(i.dest, address);
}
};
EMITTER_OPCODE_TABLE(OPCODE_RESERVED_LOAD, RESERVED_LOAD_INT32,
RESERVED_LOAD_INT64);
struct RESERVED_STORE_INT32
: Sequence<RESERVED_STORE_INT32,
I<OPCODE_RESERVED_STORE, I8Op, I64Op, I32Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W3);
const WReg value = i.src2.is_constant ? W4 : i.src2;
if (i.src2.is_constant) {
e.MOV(value, static_cast<uint32_t>(i.src2.constant()));
}
e.STLXR(W0, value, address);
e.CMP(W0, 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct RESERVED_STORE_INT64
: Sequence<RESERVED_STORE_INT64,
I<OPCODE_RESERVED_STORE, I8Op, I64Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const XReg address = ComputeMemoryAddress(e, i.src1, W3);
const XReg value = i.src2.is_constant ? X4 : i.src2;
if (i.src2.is_constant) {
e.MOV(value, i.src2.constant());
}
e.STLXR(W0, value, address);
e.CMP(W0, 0);
e.CSET(i.dest, Cond::EQ);
}
};
EMITTER_OPCODE_TABLE(OPCODE_RESERVED_STORE, RESERVED_STORE_INT32,
RESERVED_STORE_INT64);
// ============================================================================
// OPCODE_LOAD_LOCAL
// ============================================================================
+48 -2
View File
@@ -8,6 +8,7 @@
*/
#include "xenia/cpu/backend/a64/a64_sequences.h"
#include <arm_neon.h>
#include <algorithm>
#include <cstring>
@@ -63,6 +64,26 @@ struct VECTOR_CONVERT_F2I
};
EMITTER_OPCODE_TABLE(OPCODE_VECTOR_CONVERT_F2I, VECTOR_CONVERT_F2I);
// ============================================================================
// OPCODE_VECTOR_DENORMFLUSH
// ============================================================================
struct VECTOR_DENORMFLUSH
: Sequence<VECTOR_DENORMFLUSH,
I<OPCODE_VECTOR_DENORMFLUSH, V128Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
// Clear denormals to signed zero, preserving sign bits.
e.MOV(X2, e.GetVConstPtr());
e.LDR(Q0, X2, e.GetVConstOffset(VSingleDenormalMask));
e.AND(Q0.B16(), i.src1.reg().B16(), Q0.B16());
e.CMEQ(Q0.S4(), Q0.S4(), 0);
e.BIC(Q1.B16(), i.src1.reg().B16(), Q0.B16());
e.LDR(Q2, X2, e.GetVConstOffset(VSignMaskF32));
e.AND(Q2.B16(), i.src1.reg().B16(), Q2.B16());
e.ORR(i.dest.reg().B16(), Q1.B16(), Q2.B16());
}
};
EMITTER_OPCODE_TABLE(OPCODE_VECTOR_DENORMFLUSH, VECTOR_DENORMFLUSH);
// ============================================================================
// OPCODE_LOAD_VECTOR_SHL
// ============================================================================
@@ -1985,7 +2006,7 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
} else if (h == 0xFFFF) {
b[i] = -131008.0f; // Special negative sentinel (0xC7FFE000)
} else {
b[i] = half_float::detail::half2float(h);
b[i] = half_float::detail::half2float<float>(h);
}
}
@@ -2105,7 +2126,7 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
vld1q_u8(reinterpret_cast<const uint8_t*>(src1)));
for (int i = 0; i < 4; i++) {
b[i] = half_float::detail::half2float(a[VEC128_W(4 + i)]);
b[i] = half_float::detail::half2float<float>(a[VEC128_W(4 + i)]);
}
// Store the float array into a uint8x16_t NEON register
@@ -2414,6 +2435,31 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
};
EMITTER_OPCODE_TABLE(OPCODE_UNPACK, UNPACK);
namespace {
thread_local bool a64_njm_enabled = true;
uint64_t SetNJMForwarder(void* raw_context, uint64_t value) {
(void)raw_context;
a64_njm_enabled = value != 0;
return 0;
}
} // namespace
// ============================================================================
// OPCODE_SET_NJM
// ============================================================================
struct SET_NJM_I8 : Sequence<SET_NJM_I8, I<OPCODE_SET_NJM, VoidOp, I8Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
if (i.src1.is_constant) {
e.CallNative(SetNJMForwarder, static_cast<uint64_t>(i.src1.constant()));
return;
}
e.UXTB(W1, i.src1);
e.CallNativeSafe(reinterpret_cast<void*>(SetNJMForwarder));
}
};
EMITTER_OPCODE_TABLE(OPCODE_SET_NJM, SET_NJM_I8);
} // namespace a64
} // namespace backend
} // namespace cpu
+46 -119
View File
@@ -25,8 +25,6 @@
#include "xenia/cpu/backend/a64/a64_sequences.h"
#include <algorithm>
#include <unordered_map>
#include "xenia/base/assert.h"
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
@@ -53,7 +51,6 @@ using namespace xe::cpu::hir;
using xe::cpu::hir::Instr;
typedef bool (*SequenceSelectFn)(A64Emitter&, const Instr*);
// std::unordered_map<uint32_t, SequenceSelectFn> sequence_table; Removed
// ============================================================================
// OPCODE_COMMENT
@@ -355,6 +352,22 @@ EMITTER_OPCODE_TABLE(OPCODE_CONVERT, CONVERT_I32_F32, CONVERT_I32_F64,
CONVERT_I64_F64, CONVERT_F32_I32, CONVERT_F32_F64,
CONVERT_F64_I64, CONVERT_F64_F32);
// ============================================================================
// OPCODE_TO_SINGLE
// ============================================================================
struct TOSINGLE_F64_F64
: Sequence<TOSINGLE_F64_F64, I<OPCODE_TO_SINGLE, F64Op, F64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
const DReg src = i.src1.is_constant ? D1 : i.src1;
if (i.src1.is_constant) {
e.LoadConstantV(src.toQ(), i.src1.constant());
}
e.FCVT(S0, src);
e.FCVT(i.dest, S0);
}
};
EMITTER_OPCODE_TABLE(OPCODE_TO_SINGLE, TOSINGLE_F64_F64);
// ============================================================================
// OPCODE_ROUND
// ============================================================================
@@ -727,107 +740,6 @@ EMITTER_OPCODE_TABLE(OPCODE_SELECT, SELECT_I8, SELECT_I16, SELECT_I32,
SELECT_I64, SELECT_F32, SELECT_F64, SELECT_V128_I8,
SELECT_V128_V128);
// ============================================================================
// OPCODE_IS_TRUE
// ============================================================================
struct IS_TRUE_I8 : Sequence<IS_TRUE_I8, I<OPCODE_IS_TRUE, I8Op, I8Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::NE);
}
};
struct IS_TRUE_I16 : Sequence<IS_TRUE_I16, I<OPCODE_IS_TRUE, I8Op, I16Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::NE);
}
};
struct IS_TRUE_I32 : Sequence<IS_TRUE_I32, I<OPCODE_IS_TRUE, I8Op, I32Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::NE);
}
};
struct IS_TRUE_I64 : Sequence<IS_TRUE_I64, I<OPCODE_IS_TRUE, I8Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::NE);
}
};
struct IS_TRUE_F32 : Sequence<IS_TRUE_F32, I<OPCODE_IS_TRUE, I8Op, F32Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.FCMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::NE);
}
};
struct IS_TRUE_F64 : Sequence<IS_TRUE_F64, I<OPCODE_IS_TRUE, I8Op, F64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.FCMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::NE);
}
};
struct IS_TRUE_V128 : Sequence<IS_TRUE_V128, I<OPCODE_IS_TRUE, I8Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.UMAXV(Q0.toS(), i.src1.reg().S4());
e.MOV(W0, Q0.Selem()[0]);
e.CMP(W0, 0);
e.CSET(i.dest, Cond::NE);
}
};
EMITTER_OPCODE_TABLE(OPCODE_IS_TRUE, IS_TRUE_I8, IS_TRUE_I16, IS_TRUE_I32,
IS_TRUE_I64, IS_TRUE_F32, IS_TRUE_F64, IS_TRUE_V128);
// ============================================================================
// OPCODE_IS_FALSE
// ============================================================================
struct IS_FALSE_I8 : Sequence<IS_FALSE_I8, I<OPCODE_IS_FALSE, I8Op, I8Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct IS_FALSE_I16 : Sequence<IS_FALSE_I16, I<OPCODE_IS_FALSE, I8Op, I16Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct IS_FALSE_I32 : Sequence<IS_FALSE_I32, I<OPCODE_IS_FALSE, I8Op, I32Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct IS_FALSE_I64 : Sequence<IS_FALSE_I64, I<OPCODE_IS_FALSE, I8Op, I64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct IS_FALSE_F32 : Sequence<IS_FALSE_F32, I<OPCODE_IS_FALSE, I8Op, F32Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.FCMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct IS_FALSE_F64 : Sequence<IS_FALSE_F64, I<OPCODE_IS_FALSE, I8Op, F64Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.FCMP(i.src1.reg(), 0);
e.CSET(i.dest, Cond::EQ);
}
};
struct IS_FALSE_V128
: Sequence<IS_FALSE_V128, I<OPCODE_IS_FALSE, I8Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.UMAXV(Q0.toS(), i.src1.reg().S4());
e.MOV(W0, Q0.Selem()[0]);
e.CMP(W0, 0);
e.CSET(i.dest, Cond::EQ);
}
};
EMITTER_OPCODE_TABLE(OPCODE_IS_FALSE, IS_FALSE_I8, IS_FALSE_I16, IS_FALSE_I32,
IS_FALSE_I64, IS_FALSE_F32, IS_FALSE_F64, IS_FALSE_V128);
// ============================================================================
// OPCODE_IS_NAN
// ============================================================================
@@ -2116,15 +2028,17 @@ EMITTER_OPCODE_TABLE(OPCODE_LOG2, LOG2_F32, LOG2_F64, LOG2_V128);
// ============================================================================
struct DOT_PRODUCT_3_V128
: Sequence<DOT_PRODUCT_3_V128,
I<OPCODE_DOT_PRODUCT_3, F32Op, V128Op, V128Op>> {
I<OPCODE_DOT_PRODUCT_3, V128Op, V128Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
// https://msdn.microsoft.com/en-us/library/bb514054(v=vs.90).aspx
EmitCommutativeBinaryVOp(
e, i, [](A64Emitter& e, SReg dest, QReg src1, QReg src2) {
e.FMUL(dest.toQ().S4(), src1.S4(), src2.S4());
e.MOV(dest.toQ().Selem()[3], WZR);
e.FADDP(dest.toQ().S4(), dest.toQ().S4(), dest.toQ().S4());
e.FADDP(dest.toS(), dest.toD().S2());
e, i, [](A64Emitter& e, QReg dest, QReg src1, QReg src2) {
e.FMUL(dest.S4(), src1.S4(), src2.S4());
e.MOV(dest.Selem()[3], WZR);
e.FADDP(dest.S4(), dest.S4(), dest.S4());
e.FADDP(S0, dest.toD().S2());
e.FMOV(W0, S0);
e.DUP(dest.S4(), W0);
});
}
};
@@ -2135,14 +2049,16 @@ EMITTER_OPCODE_TABLE(OPCODE_DOT_PRODUCT_3, DOT_PRODUCT_3_V128);
// ============================================================================
struct DOT_PRODUCT_4_V128
: Sequence<DOT_PRODUCT_4_V128,
I<OPCODE_DOT_PRODUCT_4, F32Op, V128Op, V128Op>> {
I<OPCODE_DOT_PRODUCT_4, V128Op, V128Op, V128Op>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
// https://msdn.microsoft.com/en-us/library/bb514054(v=vs.90).aspx
EmitCommutativeBinaryVOp(
e, i, [](A64Emitter& e, SReg dest, QReg src1, QReg src2) {
e.FMUL(dest.toQ().S4(), src1.S4(), src2.S4());
e.FADDP(dest.toQ().S4(), dest.toQ().S4(), dest.toQ().S4());
e.FADDP(dest.toS(), dest.toD().S2());
e, i, [](A64Emitter& e, QReg dest, QReg src1, QReg src2) {
e.FMUL(dest.S4(), src1.S4(), src2.S4());
e.FADDP(dest.S4(), dest.S4(), dest.S4());
e.FADDP(S0, dest.toD().S2());
e.FMOV(W0, S0);
e.DUP(dest.S4(), W0);
});
}
};
@@ -2782,6 +2698,18 @@ struct SET_ROUNDING_MODE_I32
};
EMITTER_OPCODE_TABLE(OPCODE_SET_ROUNDING_MODE, SET_ROUNDING_MODE_I32);
static void MaybeYieldForwarder(void* ctx) { xe::threading::MaybeYield(); }
// ============================================================================
// OPCODE_DELAY_EXECUTION
// ============================================================================
struct DELAY_EXECUTION
: Sequence<DELAY_EXECUTION, I<OPCODE_DELAY_EXECUTION, VoidOp>> {
static void Emit(A64Emitter& e, const EmitArgType& i) {
e.CallNativeSafe(reinterpret_cast<void*>(MaybeYieldForwarder));
}
};
EMITTER_OPCODE_TABLE(OPCODE_DELAY_EXECUTION, DELAY_EXECUTION);
// Include anchors to other sequence sources so they get included in the build.
extern volatile int anchor_control;
static int anchor_control_dest = anchor_control;
@@ -2795,15 +2723,14 @@ static int anchor_vector_dest = anchor_vector;
bool SelectSequence(A64Emitter* e, const hir::Instr* i,
const hir::Instr** new_tail) {
const InstrKey key(i);
auto& table = GetSequenceTable(); // Use the singleton accessor
auto it = table.find(key);
if (it != table.end()) {
auto it = GetSequenceTable().find(key);
if (it != GetSequenceTable().end()) {
if (it->second(*e, i)) {
*new_tail = i->next;
return true;
}
}
XELOGE("No sequence match for variant {}", i->opcode->name);
XELOGE("No sequence match for variant {}", hir::GetOpcodeName(i->opcode));
return false;
}
+9 -10
View File
@@ -10,11 +10,12 @@
#ifndef XENIA_CPU_BACKEND_A64_A64_SEQUENCES_H_
#define XENIA_CPU_BACKEND_A64_A64_SEQUENCES_H_
#include <functional>
#include <iostream> // For logging
#include <unordered_map>
#include "xenia/cpu/hir/instr.h"
#include <unordered_map>
#include "xenia/base/logging.h"
namespace xe {
namespace cpu {
namespace backend {
@@ -24,33 +25,31 @@ class A64Emitter;
typedef bool (*SequenceSelectFn)(A64Emitter&, const hir::Instr*);
// Singleton accessor for sequence_table
// Singleton accessor for sequence table.
inline std::unordered_map<uint32_t, SequenceSelectFn>& GetSequenceTable() {
static std::unordered_map<uint32_t, SequenceSelectFn> sequence_table;
return sequence_table;
}
// Registration Functions
template <typename T>
bool RegisterSingle() {
bool inserted = GetSequenceTable().emplace(T::head_key(), T::Select).second;
if (!inserted) {
std::cerr << "Warning: Duplicate head_key detected for key "
<< T::head_key() << std::endl;
XELOGW("A64 sequence registration duplicate key 0x{:08X}", T::head_key());
}
return inserted;
}
template <typename... Ts>
bool RegisterAll() {
return (RegisterSingle<Ts>() && ...); // Fold expression (C++17)
bool ok = true;
((ok &= RegisterSingle<Ts>()), ...);
return ok;
}
// Macro for Registration
#define EMITTER_OPCODE_TABLE(name, ...) \
static const bool A64_INSTR_##name = RegisterAll<__VA_ARGS__>();
// Function to Select Sequence
bool SelectSequence(A64Emitter* e, const hir::Instr* i,
const hir::Instr** new_tail);
+5 -4
View File
@@ -34,11 +34,12 @@ bool trace_enabled = true;
#define IFLUSH()
#define IPRINT(s) \
if (trace_enabled && THREAD_MATCH) \
xe::logging::AppendLogLine(xe::LogLevel::Debug, 't', s)
xe::logging::AppendLogLine(xe::LogLevel::Debug, 't', s, xe::LogSrc::Cpu)
#define DFLUSH()
#define DPRINT(...) \
if (trace_enabled && THREAD_MATCH) \
xe::logging::AppendLogLineFormat(xe::LogLevel::Debug, 't', __VA_ARGS__)
#define DPRINT(...) \
if (trace_enabled && THREAD_MATCH) \
xe::logging::AppendLogLineFormat(xe::LogSrc::Cpu, xe::LogLevel::Debug, 't', \
__VA_ARGS__)
uint32_t GetTracingMode() {
uint32_t mode = 0;
+38 -22
View File
@@ -4,28 +4,44 @@ include(project_root.."/tools/build")
group("src")
project("xenia-cpu-backend-a64")
uuid("495f3f3e-f5e8-489a-bd0f-289d0495bc08")
-- Apply settings only for ARM64
filter("architecture:ARM64")
kind("StaticLib")
filter("architecture:not ARM64")
kind("None")
language("C++")
cppdialect("C++20")
links({
"fmt",
"xenia-base",
"xenia-cpu",
})
sysincludedirs({
project_root.."/third_party/oaknut/include",
})
defines({
})
-- Add oaknut as external include to suppress warnings
filter("toolset:clang or toolset:gcc")
externalincludedirs({
project_root.."/third_party/oaknut/include",
})
-- Also explicitly disable the warning for third-party code
buildoptions({
"-Wno-shorten-64-to-32",
})
filter("toolset:msc")
includedirs({
project_root.."/third_party/oaknut/include",
})
filter("architecture:ARM64")
-- Include only ARM64-specific files
local_platform_files()
-- For non-ARM64 architectures, create an empty static lib
filter("architecture:x86_64")
kind("None")
-- Reset filter
filter({})
language("C++")
cppdialect("C++20")
links({
"fmt",
"xenia-base",
"xenia-cpu",
})
defines({
})
disablewarnings({
-- Silence errors in oaknut
"4146", -- unary minus operator applied to unsigned type, result still unsigned
"4267" -- 'initializing': conversion from 'size_t' to 'uint32_t', possible loss of data
})
includedirs({
project_root.."/third_party/oaknut/include",
})
local_platform_files()