From 8dbb4edaddb68dedc5a4bd2d6a4f4c1b57002e32 Mon Sep 17 00:00:00 2001 From: Will Martin Date: Wed, 21 Jan 2026 21:39:54 +0900 Subject: [PATCH] [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 --- src/xenia/cpu/backend/a64/a64_assembler.cc | 2 +- src/xenia/cpu/backend/a64/a64_backend.cc | 78 +-- src/xenia/cpu/backend/a64/a64_code_cache.cc | 117 +---- .../cpu/backend/a64/a64_code_cache_posix.cc | 16 - src/xenia/cpu/backend/a64/a64_emitter.cc | 474 +----------------- src/xenia/cpu/backend/a64/a64_emitter.h | 8 + src/xenia/cpu/backend/a64/a64_seq_control.cc | 26 +- src/xenia/cpu/backend/a64/a64_seq_memory.cc | 184 +++++++ src/xenia/cpu/backend/a64/a64_seq_vector.cc | 50 +- src/xenia/cpu/backend/a64/a64_sequences.cc | 165 ++---- src/xenia/cpu/backend/a64/a64_sequences.h | 19 +- src/xenia/cpu/backend/a64/a64_tracers.cc | 9 +- src/xenia/cpu/backend/a64/premake5.lua | 60 ++- 13 files changed, 406 insertions(+), 802 deletions(-) diff --git a/src/xenia/cpu/backend/a64/a64_assembler.cc b/src/xenia/cpu/backend/a64/a64_assembler.cc index 262de2849..7f24cab29 100644 --- a/src/xenia/cpu/backend/a64/a64_assembler.cc +++ b/src/xenia/cpu/backend/a64/a64_assembler.cc @@ -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"); } diff --git a/src/xenia/cpu/backend/a64/a64_backend.cc b/src/xenia/cpu/backend/a64/a64_backend.cc index 46b882023..b0718867b 100644 --- a/src/xenia/cpu/backend/a64/a64_backend.cc +++ b/src/xenia/cpu/backend/a64/a64_backend.cc @@ -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 A64Backend::CreateGuestFunction( return std::make_unique(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(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(function->machine_code()); - host_offset = static_cast(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(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(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 diff --git a/src/xenia/cpu/backend/a64/a64_code_cache.cc b/src/xenia/cpu/backend/a64/a64_code_cache.cc index 1bc937cf0..b82ed7b71 100644 --- a/src/xenia/cpu/backend/a64/a64_code_cache.cc +++ b/src/xenia/cpu/backend/a64/a64_code_cache.cc @@ -9,7 +9,6 @@ #include "xenia/cpu/backend/a64/a64_code_cache.h" -#include #include #include @@ -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 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(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(kIndirectionTableBase), - kIndirectionTableBase + kIndirectionTableSize); + XELOGE( + "This is likely because the {:X}-{:X} range is in use by some other " + "system DLL", + static_cast(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(kIndirectionTableBase), - static_cast(indirection_table_actual_base_), - static_cast(kIndirectionTableSize), - static_cast(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(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(xe::memory::MapFileView( mapping_, nullptr, kGeneratedCodeSize, @@ -231,9 +195,6 @@ bool A64CodeCache::Initialize() { mapping_, reinterpret_cast(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( xe::memory::MapFileView(mapping_, nullptr, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadOnly, 0)); @@ -243,9 +204,6 @@ bool A64CodeCache::Initialize() { mapping_, reinterpret_cast(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( 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(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(kIndirectionTableSize)); return; } uint64_t* indirection_slot = reinterpret_cast(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(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(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(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(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(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( - reinterpret_cast(code_execute_address))); - } #else uint32_t* indirection_slot = reinterpret_cast( indirection_table_base_ + (guest_address - kIndirectionTableBase)); diff --git a/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc b/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc index c2c8b5df7..0f6329c46 100644 --- a/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc +++ b/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc @@ -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 diff --git a/src/xenia/cpu/backend/a64/a64_emitter.cc b/src/xenia/cpu/backend/a64/a64_emitter.cc index a07de29f1..cae5778e6 100644 --- a/src/xenia/cpu/backend/a64/a64_emitter.cc +++ b/src/xenia/cpu/backend/a64/a64_emitter.cc @@ -9,18 +9,13 @@ #include "xenia/cpu/backend/a64/a64_emitter.h" -#include -#include #include -#include #include #include #include "third_party/fmt/include/fmt/format.h" #include "xenia/base/assert.h" -#include "xenia/base/atomic.h" -#include "xenia/base/byte_order.h" #include "xenia/base/debugging.h" #include "xenia/base/literals.h" #include "xenia/base/logging.h" @@ -36,7 +31,6 @@ #include "xenia/cpu/cpu_flags.h" #include "xenia/cpu/function.h" #include "xenia/cpu/function_debug_info.h" -#include "xenia/cpu/ppc/ppc_opcode_info.h" #include "xenia/cpu/processor.h" #include "xenia/cpu/symbol.h" #include "xenia/cpu/thread_state.h" @@ -49,15 +43,9 @@ DEFINE_bool(debugprint_trap_log, false, "Log debugprint traps to the active debugger", "CPU"); DEFINE_bool(ignore_undefined_externs, true, "Don't exit when an undefined extern is called.", "CPU"); -DEFINE_bool(log_undefined_extern_args, false, - "Log PPC args for undefined externs (once per function).", "CPU"); DEFINE_bool(emit_source_annotations, false, "Add extra movs and nops to make disassembly easier to read.", "CPU"); -DEFINE_bool(a64_resolve_function_log, false, - "Log A64 ResolveFunction failures with module ranges.", "CPU"); -DEFINE_int32(a64_resolve_function_log_limit, 8, - "Maximum ResolveFunction failure logs.", "CPU"); namespace xe { namespace cpu { @@ -71,19 +59,6 @@ using namespace oaknut::util; namespace { -bool ShouldLogResolveFailure() { - if (!cvars::a64_resolve_function_log) { - return false; - } - const int32_t limit = cvars::a64_resolve_function_log_limit; - if (limit <= 0) { - return false; - } - static std::atomic log_count{0}; - const int32_t count = log_count.fetch_add(1, std::memory_order_relaxed); - return count < limit; -} - void AdjustStackPointer(A64Emitter& emitter, size_t stack_size, bool add) { if (!stack_size) { return; @@ -311,7 +286,7 @@ bool A64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) { // Mark block labels. auto label = block->label_head; while (label) { - l(label_lookup_[label->name]); + l(*lookup_label(label)); label = label->next; } @@ -324,8 +299,9 @@ bool A64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) { // No sequence found! // NOTE: If you encounter this after adding a new instruction, do a full // rebuild! + XELOGE("Unable to process HIR opcode {}", + hir::GetOpcodeName(instr->opcode)); assert_always(); - XELOGE("Unable to process HIR opcode {}", instr->opcode->name); break; } instr = new_tail; @@ -420,313 +396,6 @@ uint64_t TrapDebugPrint(void* raw_context, uint64_t address) { return 0; } -uint64_t TrapLogRegs(void* raw_context, uint64_t address) { - static volatile int32_t log_count = 0; - if (xe::atomic_inc(&log_count) > 8) { - return 0; - } - auto guest_context = reinterpret_cast(raw_context); - if (!guest_context) { - return 0; - } - auto thread_state = guest_context->thread_state; - XELOGI( - "TraceOnInstruction 0x{:08X}: r3=0x{:016X} r4=0x{:016X} r11=0x{:016X} " - "r30=0x{:016X} r31=0x{:016X} lr=0x{:016X} ctr=0x{:016X}", - static_cast(cvars::break_on_instruction), guest_context->r[3], - guest_context->r[4], guest_context->r[11], guest_context->r[30], - guest_context->r[31], guest_context->lr, guest_context->ctr); - if (thread_state) { - auto memory = thread_state->memory(); - if (memory) { - auto page_access_to_string = [](xe::memory::PageAccess access) { - switch (access) { - case xe::memory::PageAccess::kNoAccess: - return "no-access"; - case xe::memory::PageAccess::kReadOnly: - return "read-only"; - case xe::memory::PageAccess::kReadWrite: - return "read-write"; - case xe::memory::PageAccess::kExecuteReadOnly: - return "exec-read"; - case xe::memory::PageAccess::kExecuteReadWrite: - return "exec-read-write"; - } - return "unknown"; - }; - auto heap_type_to_string = [](HeapType type) { - switch (type) { - case HeapType::kGuestVirtual: - return "guest-virtual"; - case HeapType::kGuestXex: - return "guest-xex"; - case HeapType::kGuestPhysical: - return "guest-physical"; - case HeapType::kHostPhysical: - return "host-physical"; - } - return "unknown"; - }; - auto can_read_guest = [&](uint32_t addr) -> bool { - if (!addr) { - return false; - } - auto* heap = memory->LookupHeap(addr); - if (!heap) { - return false; - } - return heap->QueryRangeAccess(addr, addr) != - xe::memory::PageAccess::kNoAccess; - }; - auto log_guest_bytes = [&](uint32_t addr, const char* label) { - if (!can_read_guest(addr)) { - auto* heap = memory->LookupHeap(addr); - XELOGI( - "TraceOnInstruction {}: addr=0x{:08X} unreadable heap={} " - "access={}", - label, addr, - heap ? heap_type_to_string(heap->heap_type()) : "none", - heap ? page_access_to_string(heap->QueryRangeAccess(addr, addr)) - : "no-access"); - return; - } - const auto* heap = memory->LookupHeap(addr); - const uint8_t* host_ptr = nullptr; - if (heap && heap->heap_type() == HeapType::kGuestPhysical) { - uint32_t physical_address = memory->GetPhysicalAddress(addr); - host_ptr = memory->TranslatePhysical(physical_address); - } else { - host_ptr = memory->TranslateVirtual(addr); - } - if (!host_ptr) { - XELOGI("TraceOnInstruction {}: addr=0x{:08X} null", label, addr); - return; - } - uint8_t bytes[16] = {}; - std::memcpy(bytes, host_ptr, sizeof(bytes)); - char ascii[sizeof(bytes) + 1] = {}; - for (size_t i = 0; i < sizeof(bytes); ++i) { - uint8_t ch = bytes[i]; - ascii[i] = (ch >= 0x20 && ch <= 0x7E) ? static_cast(ch) : '.'; - } - XELOGI( - "TraceOnInstruction {}: addr=0x{:08X} {:02X} {:02X} {:02X} " - "{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} " - "{:02X} {:02X} {:02X} {:02X} {:02X} ascii={}", - label, addr, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], - bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], - bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], ascii); - }; - auto log_guest_string = [&](uint32_t addr, const char* label) { - if (!can_read_guest(addr)) { - return; - } - const uint8_t* ptr = memory->TranslateVirtual(addr); - if (!ptr) { - return; - } - char buffer[129] = {}; - size_t len = 0; - for (; len < sizeof(buffer) - 1; ++len) { - char ch = static_cast(ptr[len]); - if (!ch) { - break; - } - if (!std::isprint(static_cast(ch))) { - return; - } - buffer[len] = ch; - } - if (len > 0) { - XELOGI("TraceOnInstruction {}: {}", label, buffer); - } - }; - const uint32_t guest_address = static_cast(guest_context->r[4]); - const auto* heap = memory->LookupHeap(guest_address); - if (heap) { - const uint8_t* host_ptr = nullptr; - if (heap->heap_type() == HeapType::kGuestPhysical) { - uint32_t physical_address = memory->GetPhysicalAddress(guest_address); - host_ptr = memory->TranslatePhysical(physical_address); - } else { - host_ptr = memory->TranslateVirtual(guest_address); - } - if (host_ptr) { - uint8_t bytes[16] = {}; - std::memcpy(bytes, host_ptr, sizeof(bytes)); - char ascii[sizeof(bytes) + 1] = {}; - for (size_t i = 0; i < sizeof(bytes); ++i) { - uint8_t ch = bytes[i]; - ascii[i] = (ch >= 0x20 && ch <= 0x7E) ? static_cast(ch) : '.'; - } - XELOGI( - "TraceOnInstruction mem[r4]=0x{:08X}: {:02X} {:02X} {:02X} " - "{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} " - "{:02X} {:02X} {:02X} {:02X} {:02X} ascii={}", - guest_address, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], - bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], - bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], ascii); - } - } - - auto read_u32 = [&](uint32_t addr, uint32_t* out) -> bool { - if (!can_read_guest(addr)) { - return false; - } - const auto* heap = memory->LookupHeap(addr); - if (heap->heap_type() == HeapType::kGuestPhysical) { - uint32_t physical_address = memory->GetPhysicalAddress(addr); - auto ptr = - memory->TranslatePhysical(physical_address); - if (!ptr) { - return false; - } - *out = xe::load_and_swap(ptr); - return true; - } - auto ptr = memory->TranslateVirtual(addr); - if (!ptr) { - return false; - } - *out = xe::load_and_swap(ptr); - return true; - }; - - const uint32_t trace_pc = - static_cast(cvars::break_on_instruction); - auto log_trace_instr = [&](uint32_t pc, const char* label) { - if (!can_read_guest(pc)) { - auto* heap = memory->LookupHeap(pc); - XELOGI( - "TraceOnInstruction {}: pc=0x{:08X} unreadable heap={} " - "access={}", - label, pc, heap ? heap_type_to_string(heap->heap_type()) : "none", - heap ? page_access_to_string(heap->QueryRangeAccess(pc, pc)) - : "no-access"); - return; - } - uint32_t instr = 0; - if (!read_u32(pc, &instr)) { - XELOGI("TraceOnInstruction {}: pc=0x{:08X} unreadable", label, pc); - return; - } - xe::StringBuffer disasm; - if (cpu::ppc::DisasmPPC(pc, instr, &disasm)) { - XELOGI("TraceOnInstruction {}: pc=0x{:08X} instr=0x{:08X} {}", label, - pc, instr, disasm.to_string_view()); - } else { - XELOGI("TraceOnInstruction {}: pc=0x{:08X} instr=0x{:08X}", label, pc, - instr); - } - }; - if (trace_pc) { - log_trace_instr(trace_pc - 4, "target-4"); - log_trace_instr(trace_pc, "target"); - log_trace_instr(trace_pc + 4, "target+4"); - } - if (memory->LookupHeap(trace_pc)) { - for (int offset = -4; offset <= 4; ++offset) { - uint32_t pc = trace_pc + offset * 4; - if (!memory->LookupHeap(pc)) { - continue; - } - uint32_t instr = 0; - if (!read_u32(pc, &instr)) { - XELOGI("TraceOnInstruction window: pc=0x{:08X} unreadable", pc); - continue; - } - xe::StringBuffer disasm_window; - if (cpu::ppc::DisasmPPC(pc, instr, &disasm_window)) { - XELOGI("TraceOnInstruction window: pc=0x{:08X} instr=0x{:08X} {}", - pc, instr, disasm_window.to_string_view()); - } else { - XELOGI("TraceOnInstruction window: pc=0x{:08X} instr=0x{:08X}", pc, - instr); - } - } - } - - const uint32_t obj_address = static_cast(guest_context->r[3]); - if (!obj_address) { - XELOGI("TraceOnInstruction r3 fields: base=0x00000000"); - } else { - const auto* obj_heap = memory->LookupHeap(obj_address); - if (obj_heap) { - uint32_t value_4 = 0; - uint32_t value_8 = 0; - uint32_t value_c = 0; - uint32_t value_20 = 0; - uint32_t value_470 = 0; - bool have_any = false; - have_any |= read_u32(obj_address + 0x4, &value_4); - have_any |= read_u32(obj_address + 0x8, &value_8); - have_any |= read_u32(obj_address + 0xC, &value_c); - have_any |= read_u32(obj_address + 0x20, &value_20); - have_any |= read_u32(obj_address + 0x470, &value_470); - if (have_any) { - XELOGI( - "TraceOnInstruction r3 fields: base=0x{:08X} +0x4=0x{:08X} " - "+0x8=0x{:08X} +0xC=0x{:08X} +0x20=0x{:08X} +0x470=0x{:08X}", - obj_address, value_4, value_8, value_c, value_20, value_470); - if (value_20) { - log_guest_bytes(value_20, "r3+0x20"); - } - if (value_470) { - log_guest_bytes(value_470, "r3+0x470"); - } - } else { - XELOGI("TraceOnInstruction r3 fields: base=0x{:08X} unmapped", - obj_address); - } - } else { - XELOGI("TraceOnInstruction r3 fields: base=0x{:08X} heap=null", - obj_address); - } - log_guest_string(obj_address, "r3 string"); - } - - const uint32_t r31_address = static_cast(guest_context->r[31]); - if (!r31_address) { - XELOGI("TraceOnInstruction r31 fields: base=0x00000000"); - } else { - const auto* r31_heap = memory->LookupHeap(r31_address); - if (r31_heap) { - uint32_t value_4 = 0; - uint32_t value_8 = 0; - uint32_t value_c = 0; - uint32_t value_20 = 0; - uint32_t value_470 = 0; - bool have_any = false; - have_any |= read_u32(r31_address + 0x4, &value_4); - have_any |= read_u32(r31_address + 0x8, &value_8); - have_any |= read_u32(r31_address + 0xC, &value_c); - have_any |= read_u32(r31_address + 0x20, &value_20); - have_any |= read_u32(r31_address + 0x470, &value_470); - if (have_any) { - XELOGI( - "TraceOnInstruction r31 fields: base=0x{:08X} +0x4=0x{:08X} " - "+0x8=0x{:08X} +0xC=0x{:08X} +0x20=0x{:08X} +0x470=0x{:08X}", - r31_address, value_4, value_8, value_c, value_20, value_470); - if (value_20) { - log_guest_bytes(value_20, "r31+0x20"); - } - if (value_470) { - log_guest_bytes(value_470, "r31+0x470"); - } - } else { - XELOGI("TraceOnInstruction r31 fields: base=0x{:08X} unmapped", - r31_address); - } - } else { - XELOGI("TraceOnInstruction r31 fields: base=0x{:08X} heap=null", - r31_address); - } - } - } - } - return 0; -} - uint64_t TrapDebugBreak(void* raw_context, uint64_t address) { [[maybe_unused]] auto thread_state = *reinterpret_cast(raw_context); @@ -744,9 +413,6 @@ void A64Emitter::Trap(uint16_t trap_type) { // 0x0FE00014 is a 'debug print' where r3 = buffer r4 = length CallNative(TrapDebugPrint, 0); break; - case 27: - CallNative(TrapLogRegs, 0); - break; case 0: case 22: // Always trap? @@ -771,16 +437,16 @@ void A64Emitter::UnimplementedInstr(const hir::Instr* i) { // This is used by the A64ThunkEmitter's ResolveFunctionThunk. uint64_t ResolveFunction(void* raw_context, uint64_t target_address) { - auto thread_state = *reinterpret_cast(raw_context); - auto guest_context = thread_state->context(); + auto guest_context = reinterpret_cast(raw_context); + assert_not_null(guest_context); + auto thread_state = guest_context->thread_state; + assert_not_null(thread_state); - uint32_t guest_address; + assert_not_zero(target_address); - // Check if this is a 64-bit host address that needs to be mapped back to - // guest address + uint32_t guest_address = 0; if (target_address > 0xFFFFFFFF) { - // Precise guard: if target_address is within the PPCContext, this is a bug. - auto ctx_ptr = reinterpret_cast(thread_state->context()); + auto ctx_ptr = reinterpret_cast(guest_context); if (target_address >= ctx_ptr && target_address < ctx_ptr + sizeof(ppc::PPCContext)) { XELOGE( @@ -793,7 +459,6 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) { return 0; } - // Try to find a function that contains this host address auto code_cache = static_cast( thread_state->processor()->backend()->code_cache()); auto guest_function = code_cache->LookupFunction(target_address); @@ -801,30 +466,14 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) { guest_address = guest_function->MapMachineCodeToGuestAddress(target_address); } else { - // This might be a guest memory address stored in 64-bit form - // Extract the lower 32 bits as the potential guest address - uint32_t potential_guest = static_cast(target_address); - guest_address = potential_guest; + guest_address = static_cast(target_address); } } else { - // Normal 32-bit guest address guest_address = static_cast(target_address); } - // Xbox 360 guest addresses can be in these ranges: - // 0x00000000-0x3FFFFFFF: v00000000 heap (virtual) - // 0x40000000-0x7EFFFFFF: v40000000 heap (virtual) - // 0x70000000-0x7F000000: Thread stacks - // 0x80000000-0x8FFFFFFF: v80000000 heap (XEX) - // 0x90000000-0x9FFFFFFF: v90000000 heap (XEX) - // 0xA0000000-0xBFFFFFFF: vA0000000 heap (physical) - // 0xC0000000-0xDFFFFFFF: vC0000000 heap (physical) - // 0xE0000000-0xFFCFFFFF: vE0000000 heap (physical) - - // Most executable code should be in XEX ranges (0x80000000-0x9FFFFFFF) - // but allow other ranges as they may contain valid code if (guest_address == 0) { - XELOGE("ResolveFunction: guest_address is 0! This should not happen"); + XELOGE("ResolveFunction: guest_address is 0"); return 0; } @@ -835,74 +484,10 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) { guest_address); XELOGE("ResolveFunction: Original target_address was 0x{:016X}", target_address); - if (ShouldLogResolveFailure()) { - const uint32_t lr_guest = static_cast(guest_context->lr); - XELOGI( - "ResolveFunction: lr=0x{:016X} ctr=0x{:016X} thread_id={} " - "target_is_host={} guest_address=0x{:08X}", - guest_context->lr, guest_context->ctr, guest_context->thread_id, - target_address > 0xFFFFFFFF, guest_address); - auto log_modules_for_address = [&](uint32_t address, const char* label) { - bool found = false; - for (auto* module : thread_state->processor()->GetModules()) { - if (!module) { - continue; - } - if (module->ContainsAddress(address)) { - XELOGI("ResolveFunction: {} module '{}' contains 0x{:08X}", label, - module->name(), address); - found = true; - } - } - if (!found) { - XELOGI("ResolveFunction: {} no module contains 0x{:08X}", label, - address); - } - }; - log_modules_for_address(lr_guest, "lr"); - log_modules_for_address(guest_address, "guest"); - - auto lr_functions = - thread_state->processor()->FindFunctionsWithAddress(lr_guest); - if (lr_functions.empty()) { - XELOGI("ResolveFunction: no resolved function covers LR 0x{:08X}", - lr_guest); - } else { - const auto* fn = lr_functions.front(); - XELOGI("ResolveFunction: LR function {} [0x{:08X},0x{:08X}) name='{}'", - lr_functions.size(), fn->address(), fn->end_address(), - fn->name()); - } - - auto* memory = thread_state->memory(); - if (!memory) { - XELOGI("ResolveFunction: no Memory available for guest dump"); - } else if (!memory->LookupHeap(guest_address)) { - XELOGI( - "ResolveFunction: guest_address 0x{:08X} not in any heap for dump", - guest_address); - } else { - const uint8_t* data = - memory->TranslateVirtual(guest_address); - std::array bytes = {}; - std::memcpy(bytes.data(), data, bytes.size()); - XELOGI( - "ResolveFunction: guest[0x{:08X}] = {:02X} {:02X} {:02X} {:02X} " - "{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} " - "{:02X} {:02X} {:02X} {:02X}", - guest_address, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], - bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], - bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); - } - } - - // This can happen if the target address doesn't point to a valid function - // Return 0 to indicate failure - the calling code should handle this return 0; } auto a64_fn = static_cast(fn); - uint64_t addr = reinterpret_cast(a64_fn->machine_code()); if (!a64_fn->machine_code()) { XELOGE( "ResolveFunction: Function at guest address 0x{:08X} has no machine " @@ -911,7 +496,7 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) { return 0; } - return addr; + return reinterpret_cast(a64_fn->machine_code()); } void A64Emitter::Call(const hir::Instr* instr, GuestFunction* function) { @@ -952,9 +537,11 @@ void A64Emitter::Call(const hir::Instr* instr, GuestFunction* function) { } #endif } else { - XELOGE("A64 indirection table missing; ResolveFunction fallback removed"); - BRK(0xF000); - B(epilog_label()); + // Old-style resolve. + // Not too important because indirection table is almost always available. + // TODO: Overwrite the call-site with a straight call. + CallNative(&ResolveFunction, function->address()); + MOV(X16, X0); } // Actually jump/call to X16. @@ -1015,9 +602,14 @@ void A64Emitter::CallIndirect(const hir::Instr* instr, } #endif } else { - XELOGE("A64 indirection table missing; ResolveFunction fallback removed"); - BRK(0xF000); - B(epilog_label()); + // Old-style resolve. + // Not too important because indirection table is almost always available. + MOV(X0, GetContextReg()); + MOV(W1, reg.toW()); + + MOV(X16, reinterpret_cast(ResolveFunction)); + BLR(X16); + MOV(X16, X0); } // Actually jump/call to X16. @@ -1044,19 +636,6 @@ void A64Emitter::CallIndirect(const hir::Instr* instr, uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) { auto function = reinterpret_cast(function_ptr); - if (cvars::log_undefined_extern_args && - function->name() == "XeKeysConsolePrivateKeySign") { - static std::atomic logged{false}; - if (!logged.exchange(true)) { - auto* context = reinterpret_cast(raw_context); - XELOGI( - "Undefined extern {} args: r3={:016X} r4={:016X} r5={:016X} " - "r6={:016X} r7={:016X} r8={:016X} r9={:016X} r10={:016X}", - function->name(), context->r[3], context->r[4], context->r[5], - context->r[6], context->r[7], context->r[8], context->r[9], - context->r[10]); - } - } if (!cvars::ignore_undefined_externs) { xe::FatalError(fmt::format("undefined extern call to {:08X} {}", function->address(), function->name().c_str())); @@ -1308,6 +887,7 @@ static const vec128_t v_consts[] = { /* VQNaN */ vec128i(0x7FC00000u), /* VInt127 */ vec128i(0x7Fu), /* V2To32 */ vec128f(0x1.0p32f), + /* VSingleDenormalMask */ vec128i(0x7F800000u), }; // First location to try and place constants. diff --git a/src/xenia/cpu/backend/a64/a64_emitter.h b/src/xenia/cpu/backend/a64/a64_emitter.h index 9330b41dc..c2352c85e 100644 --- a/src/xenia/cpu/backend/a64/a64_emitter.h +++ b/src/xenia/cpu/backend/a64/a64_emitter.h @@ -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_; } diff --git a/src/xenia/cpu/backend/a64/a64_seq_control.cc b/src/xenia/cpu/backend/a64/a64_seq_control.cc index e68d2955b..8e2d8bb11 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_control.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_control.cc @@ -419,7 +419,7 @@ EMITTER_OPCODE_TABLE(OPCODE_SET_RETURN_ADDRESS, SET_RETURN_ADDRESS); // ============================================================================ struct BRANCH : Sequence> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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); diff --git a/src/xenia/cpu/backend/a64/a64_seq_memory.cc b/src/xenia/cpu/backend/a64/a64_seq_memory.cc index 3fb205a6a..d9dbf4934 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_memory.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_memory.cc @@ -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(0x83)); + template 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> { + 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> { + 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> { + 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(&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> { + 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(&kStvlShuffle)); + e.LDR(Q0, X2); + e.DUP(Q1.B16(), W0); + e.SUB(Q0.B16(), Q0.B16(), Q1.B16()); + + e.MOV(X2, reinterpret_cast(&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> { + 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> { + 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> { + 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(i.src2.constant())); + } + e.STLXR(W0, value, address); + e.CMP(W0, 0); + e.CSET(i.dest, Cond::EQ); + } +}; + +struct RESERVED_STORE_INT64 + : Sequence> { + 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 // ============================================================================ diff --git a/src/xenia/cpu/backend/a64/a64_seq_vector.cc b/src/xenia/cpu/backend/a64/a64_seq_vector.cc index f9a70f1bb..95517a35b 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_vector.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_vector.cc @@ -8,6 +8,7 @@ */ #include "xenia/cpu/backend/a64/a64_sequences.h" +#include #include #include @@ -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> { + 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> { } 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(h); } } @@ -2105,7 +2126,7 @@ struct UNPACK : Sequence> { vld1q_u8(reinterpret_cast(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(a[VEC128_W(4 + i)]); } // Store the float array into a uint8x16_t NEON register @@ -2414,6 +2435,31 @@ struct UNPACK : Sequence> { }; 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> { + static void Emit(A64Emitter& e, const EmitArgType& i) { + if (i.src1.is_constant) { + e.CallNative(SetNJMForwarder, static_cast(i.src1.constant())); + return; + } + e.UXTB(W1, i.src1); + e.CallNativeSafe(reinterpret_cast(SetNJMForwarder)); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_SET_NJM, SET_NJM_I8); + } // namespace a64 } // namespace backend } // namespace cpu diff --git a/src/xenia/cpu/backend/a64/a64_sequences.cc b/src/xenia/cpu/backend/a64/a64_sequences.cc index fd9d3bbda..ae5720280 100644 --- a/src/xenia/cpu/backend/a64/a64_sequences.cc +++ b/src/xenia/cpu/backend/a64/a64_sequences.cc @@ -25,8 +25,6 @@ #include "xenia/cpu/backend/a64/a64_sequences.h" #include -#include - #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 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> { + 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { + I> { 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> { + I> { 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> { + static void Emit(A64Emitter& e, const EmitArgType& i) { + e.CallNativeSafe(reinterpret_cast(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; } diff --git a/src/xenia/cpu/backend/a64/a64_sequences.h b/src/xenia/cpu/backend/a64/a64_sequences.h index 65d4adad3..eaf4fb0f1 100644 --- a/src/xenia/cpu/backend/a64/a64_sequences.h +++ b/src/xenia/cpu/backend/a64/a64_sequences.h @@ -10,11 +10,12 @@ #ifndef XENIA_CPU_BACKEND_A64_A64_SEQUENCES_H_ #define XENIA_CPU_BACKEND_A64_A64_SEQUENCES_H_ -#include -#include // For logging -#include #include "xenia/cpu/hir/instr.h" +#include + +#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& GetSequenceTable() { static std::unordered_map sequence_table; return sequence_table; } -// Registration Functions template 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 bool RegisterAll() { - return (RegisterSingle() && ...); // Fold expression (C++17) + bool ok = true; + ((ok &= RegisterSingle()), ...); + 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); diff --git a/src/xenia/cpu/backend/a64/a64_tracers.cc b/src/xenia/cpu/backend/a64/a64_tracers.cc index 146f50982..49b9ed1cb 100644 --- a/src/xenia/cpu/backend/a64/a64_tracers.cc +++ b/src/xenia/cpu/backend/a64/a64_tracers.cc @@ -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; diff --git a/src/xenia/cpu/backend/a64/premake5.lua b/src/xenia/cpu/backend/a64/premake5.lua index 32b2d51a0..2ff243630 100644 --- a/src/xenia/cpu/backend/a64/premake5.lua +++ b/src/xenia/cpu/backend/a64/premake5.lua @@ -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()