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") {