diff --git a/src/xenia/cpu/backend/a64/a64_emitter.cc b/src/xenia/cpu/backend/a64/a64_emitter.cc index 0968c6653..deb3240ae 100644 --- a/src/xenia/cpu/backend/a64/a64_emitter.cc +++ b/src/xenia/cpu/backend/a64/a64_emitter.cc @@ -71,7 +71,7 @@ const uint32_t A64Emitter::vec_reg_map_[VEC_COUNT] = { }; A64Emitter::A64Emitter(A64Backend* backend, XbyakA64Allocator* allocator) - : CodeGenerator(kMaxCodeSize, Xbyak_aarch64::DontSetProtectRWE, allocator), + : CodeGenerator(kMaxCodeSize, Xbyak_aarch64::AutoGrow, allocator), processor_(backend->processor()), backend_(backend), code_cache_(backend->code_cache()), @@ -324,6 +324,78 @@ void A64Emitter::DebugBreak() { brk(0xF000); } void A64Emitter::Trap(uint16_t trap_type) { brk(trap_type); } +void A64Emitter::b(const Xbyak_aarch64::Cond cond, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::b(static_cast(cond ^ 1), skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::cbz(const Xbyak_aarch64::WReg& rt, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::cbnz(rt, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::cbz(const Xbyak_aarch64::XReg& rt, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::cbnz(rt, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::cbnz(const Xbyak_aarch64::WReg& rt, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::cbz(rt, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::cbnz(const Xbyak_aarch64::XReg& rt, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::cbz(rt, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::tbz(const Xbyak_aarch64::WReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::tbnz(rt, imm, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::tbz(const Xbyak_aarch64::XReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::tbnz(rt, imm, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::tbnz(const Xbyak_aarch64::WReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::tbz(rt, imm, skip); + CodeGenerator::b(label); + L(skip); +} + +void A64Emitter::tbnz(const Xbyak_aarch64::XReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label) { + Xbyak_aarch64::Label skip; + CodeGenerator::tbz(rt, imm, skip); + CodeGenerator::b(label); + L(skip); +} + void A64Emitter::UnimplementedInstr(const hir::Instr* i) { XELOGE("A64: Unimplemented HIR instruction: {}", hir::GetOpcodeName(i->GetOpcodeInfo())); @@ -674,16 +746,18 @@ void A64Emitter::EnsureSynchronizedGuestAndHostStack() { StackLayout::GUEST_SAVED_STACKPOINT_DEPTH))); cmp(w17, w16); - auto& sync_label = AddToTail([&return_from_sync](A64Emitter& e, Label& lbl) { - // Set up arguments for the sync helper: + auto& sync_label = AddToTail([](A64Emitter& e, Label& lbl) { + // x8 was set up in the body to point at return_from_sync; do that there + // instead of here because adr's ±1 MiB range can't span body+tail in + // large functions. // x8 = return address (where to resume after fixup) // x9 = this function's stack size - e.adr(e.x8, return_from_sync); e.mov(e.x9, static_cast(e.stack_size())); e.mov(e.x10, reinterpret_cast( e.backend()->synchronize_guest_and_host_stack_helper())); e.br(e.x10); }); + adr(x8, return_from_sync); b(NE, sync_label); L(return_from_sync); diff --git a/src/xenia/cpu/backend/a64/a64_emitter.h b/src/xenia/cpu/backend/a64/a64_emitter.h index 9007aace9..094a9f92f 100644 --- a/src/xenia/cpu/backend/a64/a64_emitter.h +++ b/src/xenia/cpu/backend/a64/a64_emitter.h @@ -153,6 +153,31 @@ class A64Emitter : public Xbyak_aarch64::CodeGenerator { uint32_t alignment = 0); Xbyak_aarch64::Label& NewCachedLabel(); + // ARM64 conditional branches (cbz/cbnz: ±1 MiB, tbz/tbnz: ±32 KiB, + // b.cond: ±1 MiB) can fall short of their target in large guest functions. + // These shadows emit the safe pattern ` skip; b target; skip:`, + // routing the long branch through unconditional b (±128 MiB). The + // int64_t-immediate overloads remain available via the using-declarations + // for hand-tuned thunks that pass literal byte offsets. + using Xbyak_aarch64::CodeGenerator::b; + using Xbyak_aarch64::CodeGenerator::cbnz; + using Xbyak_aarch64::CodeGenerator::cbz; + using Xbyak_aarch64::CodeGenerator::tbnz; + using Xbyak_aarch64::CodeGenerator::tbz; + void b(const Xbyak_aarch64::Cond cond, const Xbyak_aarch64::Label& label); + void cbz(const Xbyak_aarch64::WReg& rt, const Xbyak_aarch64::Label& label); + void cbz(const Xbyak_aarch64::XReg& rt, const Xbyak_aarch64::Label& label); + void cbnz(const Xbyak_aarch64::WReg& rt, const Xbyak_aarch64::Label& label); + void cbnz(const Xbyak_aarch64::XReg& rt, const Xbyak_aarch64::Label& label); + void tbz(const Xbyak_aarch64::WReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label); + void tbz(const Xbyak_aarch64::XReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label); + void tbnz(const Xbyak_aarch64::WReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label); + void tbnz(const Xbyak_aarch64::XReg& rt, uint32_t imm, + const Xbyak_aarch64::Label& label); + // Get or create a xbyak_aarch64 label for a HIR label ID. Xbyak_aarch64::Label& GetLabel(uint32_t label_id);