From 7bb0ce810ecacba48950237183c779469ff3115d Mon Sep 17 00:00:00 2001 From: Lioncache Date: Thu, 13 Nov 2025 09:55:16 -0500 Subject: [PATCH] JIT: Expand LongAddressGen() to handle movz+movk sequence This is only ever used on the path where we'd want to handle something like this (in EmitEntryPoint()), so we can just extend the long handler type instead of introducing a new type to handle this. --- CodeEmitter/CodeEmitter/ALUOps.inl | 27 ++++++++++++------ CodeEmitter/CodeEmitter/Emitter.h | 38 ++++++++++++++----------- FEXCore/unittests/Emitter/ALU_Tests.cpp | 38 ++++++++++++++----------- 3 files changed, 63 insertions(+), 40 deletions(-) diff --git a/CodeEmitter/CodeEmitter/ALUOps.inl b/CodeEmitter/CodeEmitter/ALUOps.inl index 39f970df4..7bb8e0b85 100644 --- a/CodeEmitter/CodeEmitter/ALUOps.inl +++ b/CodeEmitter/CodeEmitter/ALUOps.inl @@ -100,16 +100,22 @@ public: } [[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, const BackwardLabel* Label) { - int64_t Imm = reinterpret_cast(Label->Location) - (GetCursorAddress()); + const auto SLocation = reinterpret_cast(Label->Location); + const auto ULocation = std::bit_cast(SLocation); + + const int64_t Imm = SLocation - (GetCursorAddress()); + const auto UImm = std::bit_cast(Imm); + if (IsADRRange(Imm)) { // If the range is in ADR range then we can just use ADR. return adr(rd, Label); - } else if (IsADRPRange(Imm)) { - int64_t ADRPImm = (reinterpret_cast(Label->Location) & ~0xFFFLL) - (GetCursorAddress() & ~0xFFFLL); + } + if (IsADRPRange(Imm)) { + const int64_t ADRPImm = (SLocation & ~0xFFFLL) - (GetCursorAddress() & ~0xFFFLL); // If the range is in the ADRP range then we can use ADRP. - bool NeedsOffset = !IsADRPAligned(reinterpret_cast(Label->Location)); - uint64_t AlignedOffset = reinterpret_cast(Label->Location) & 0xFFFULL; + const bool NeedsOffset = !IsADRPAligned(ULocation); + const uint64_t AlignedOffset = ULocation & 0xFFFULL; // First emit ADRP adrp(rd, ADRPImm >> 12); @@ -122,14 +128,19 @@ public: return BranchEncodeSucceeded::Success; } - // Can't encode. - return BranchEncodeSucceeded::Failure; + // Stinky path, we need to load the address as a sequence of movz+movk+movk + movz(ARMEmitter::Size::i64Bit, rd, (UImm >> 32) & 0xFFFF, 32); + movk(ARMEmitter::Size::i64Bit, rd, (UImm >> 16) & 0xFFFF, 16); + movk(ARMEmitter::Size::i64Bit, rd, UImm & 0xFFFF); + + return BranchEncodeSucceeded::Success; } [[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, ForwardLabel* Label) { AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress(), .Type = ForwardLabel::InstType::LONG_ADDRESS_GEN}); - // Emit a register index and a nop. These will be backpatched. + // Emit a register index and two nops. These will be backpatched. dc32(rd.Idx()); nop(); + nop(); // Forward label doesn't know if it can encode until Bind. return BranchEncodeSucceeded::Success; diff --git a/CodeEmitter/CodeEmitter/Emitter.h b/CodeEmitter/CodeEmitter/Emitter.h index f416bad8d..e8918af5e 100644 --- a/CodeEmitter/CodeEmitter/Emitter.h +++ b/CodeEmitter/CodeEmitter/Emitter.h @@ -741,38 +741,44 @@ public: break; } case ForwardLabel::InstType::LONG_ADDRESS_GEN: { - uint32_t* Instructions = reinterpret_cast(Label->Location); - int64_t ImmInstOne = reinterpret_cast(CurrentAddress) - reinterpret_cast(&Instructions[0]); - int64_t ImmInstTwo = reinterpret_cast(CurrentAddress) - reinterpret_cast(&Instructions[1]); - auto OriginalOffset = GetCursorOffset(); + const auto* Instructions = reinterpret_cast(Label->Location); + const auto ImmInstOne = reinterpret_cast(CurrentAddress) - reinterpret_cast(&Instructions[0]); + const auto ImmInstTwo = reinterpret_cast(CurrentAddress) - reinterpret_cast(&Instructions[1]); + const auto ImmInstThree = reinterpret_cast(CurrentAddress) - reinterpret_cast(&Instructions[2]); + const auto OriginalOffset = GetCursorOffset(); - auto InstOffset = GetCursorOffsetFromAddress(Instructions); + const auto InstOffset = GetCursorOffsetFromAddress(Instructions); SetCursorOffset(InstOffset); // We encoded the destination register in to the first instruction space. // Read it back. ARMEmitter::Register DestReg(Instructions[0]); - if (IsADRRange(ImmInstTwo)) { - // If within ADR range from the second instruction, then we can emit NOP+ADR + if (IsADRRange(ImmInstThree)) { + // If within ADR range from the third instruction, then we can emit NOP+NOP+ADR nop(); - adr(DestReg, static_cast(ImmInstTwo) & 0x7FFF); - } else if (IsADRPRange(ImmInstOne)) { + nop(); + adr(DestReg, static_cast(ImmInstThree) & 0x7FFF); + } else if (IsADRPRange(ImmInstTwo)) { // If within ADRP range from the first instruction, then we are /definitely/ in range for the second instruction. // First check if we are in non-offset range for second instruction. if (IsADRPAligned(reinterpret_cast(CurrentAddress))) { - // We can emit nop + adrp + // We can emit nop + nop + adrp + nop(); + nop(); + adrp(DestReg, static_cast(ImmInstThree >> 12) & 0x7FFF); + } else { + // Not aligned, need nop + adrp + add nop(); adrp(DestReg, static_cast(ImmInstTwo >> 12) & 0x7FFF); - } else { - // Not aligned, need adrp + add - adrp(DestReg, static_cast(ImmInstOne >> 12) & 0x7FFF); - add(ARMEmitter::Size::i64Bit, DestReg, DestReg, ImmInstOne & 0xFFF); + add(ARMEmitter::Size::i64Bit, DestReg, DestReg, ImmInstTwo & 0xFFF); } } else { - LOGMAN_MSG_A_FMT("Unscaled offset is too large"); - FEX_UNREACHABLE; + // Stinky path, we need to emit a movz+movk+movk sequence. + movz(ARMEmitter::Size::i64Bit, DestReg, uint32_t(ImmInstOne >> 32) & 0x7FFF, 32); + movk(ARMEmitter::Size::i64Bit, DestReg, uint32_t(ImmInstOne >> 16) & 0xFFFF, 16); + movk(ARMEmitter::Size::i64Bit, DestReg, uint32_t(ImmInstOne) & 0xFFFF); } SetCursorOffset(OriginalOffset); diff --git a/FEXCore/unittests/Emitter/ALU_Tests.cpp b/FEXCore/unittests/Emitter/ALU_Tests.cpp index a3b5d6b59..7aaa0448f 100644 --- a/FEXCore/unittests/Emitter/ALU_Tests.cpp +++ b/FEXCore/unittests/Emitter/ALU_Tests.cpp @@ -95,14 +95,15 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { CHECK(DisassembleEncoding(1) == 0x10fffffe); } { - // Will generate nop + adr. + // Will generate nop + nop + adr. ForwardLabel Label; (void)LongAddressGen(Reg::r30, &Label); (void)Bind(&Label); dc32(0); CHECK(DisassembleEncoding(0) == 0xd503201f); - CHECK(DisassembleEncoding(1) == 0x1000003e); + CHECK(DisassembleEncoding(0) == 0xd503201f); + CHECK(DisassembleEncoding(2) == 0x1000003e); } { // Will generate adr. @@ -115,14 +116,15 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { } { - // Will generate nop + adr. + // Will generate nop + nop + adr. BiDirectionalLabel Label; (void)LongAddressGen(Reg::r30, &Label); (void)Bind(&Label); dc32(0); CHECK(DisassembleEncoding(0) == 0xd503201f); - CHECK(DisassembleEncoding(1) == 0x1000003e); + CHECK(DisassembleEncoding(1) == 0xd503201f); + CHECK(DisassembleEncoding(2) == 0x1000003e); } { @@ -143,12 +145,12 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { } { - // Will generate nop + adrp. + // Will generate nop + nop + adrp. ForwardLabel Label; (void)LongAddressGen(Reg::r30, &Label); // Move label 1MB away, plus a page, and then aligned to a page. - for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) { + for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 3); ++i) { nop(); } @@ -156,11 +158,12 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { dc32(0); CHECK(DisassembleEncoding(0) == 0xd503201f); - CHECK(DisassembleEncoding(1) == 0x9000081e); + CHECK(DisassembleEncoding(1) == 0xd503201f); + CHECK(DisassembleEncoding(2) == 0x9000081e); } { - // Will generate adrp + add. + // Will generate nop + adrp + add. ForwardLabel Label; (void)LongAddressGen(Reg::r30, &Label); @@ -172,8 +175,9 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { (void)Bind(&Label); dc32(0); - CHECK(DisassembleEncoding(0) == 0xb000081e); - CHECK(DisassembleEncoding(1) == 0x910013de); + CHECK(DisassembleEncoding(0) == 0xd503201f); + CHECK(DisassembleEncoding(1) == 0xb000081e); + CHECK(DisassembleEncoding(2) == 0x910013de); } @@ -195,12 +199,12 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { } { - // Will generate nop + adrp. + // Will generate nop + nop + adrp. BiDirectionalLabel Label; (void)LongAddressGen(Reg::r30, &Label); // Move label 1MB away, plus a page, and then aligned to a page. - for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) { + for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 3); ++i) { nop(); } @@ -208,11 +212,12 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { dc32(0); CHECK(DisassembleEncoding(0) == 0xd503201f); - CHECK(DisassembleEncoding(1) == 0x9000081e); + CHECK(DisassembleEncoding(1) == 0xd503201f); + CHECK(DisassembleEncoding(2) == 0x9000081e); } { - // Will generate adrp + add. + // Will generate nop + adrp + add. BiDirectionalLabel Label; (void)LongAddressGen(Reg::r30, &Label); @@ -224,8 +229,9 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") { (void)Bind(&Label); dc32(0); - CHECK(DisassembleEncoding(0) == 0xb000081e); - CHECK(DisassembleEncoding(1) == 0x910013de); + CHECK(DisassembleEncoding(0) == 0xd503201f); + CHECK(DisassembleEncoding(1) == 0xb000081e); + CHECK(DisassembleEncoding(2) == 0x910013de); } } TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Add/subtract immediate") {