diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index e0b4181120..379e08aff3 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -325,20 +325,10 @@ void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return linkData.call = LK; b->linkData.push_back(linkData); - MOVI2R(DISPATCHER_PC, destination); + blocks.WriteLinkBlock(*this, linkData); - if (!LK) + if (LK) { - B(dispatcher); - } - else - { - BL(dispatcher); - - // MOVI2R might only require one instruction. So the const offset of 20 bytes - // might be wrong. Be sure and just add a NOP here. - HINT(HINT_NOP); - // Write the regular exit node after the return. linkData.exitAddress = exit_address_after_return; linkData.exitPtrs = GetWritableCodePtr(); @@ -346,8 +336,7 @@ void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return linkData.call = false; b->linkData.push_back(linkData); - MOVI2R(DISPATCHER_PC, exit_address_after_return); - B(dispatcher); + blocks.WriteLinkBlock(*this, linkData); } } @@ -387,8 +376,7 @@ void JitArm64::WriteExit(Arm64Gen::ARM64Reg dest, bool LK, u32 exit_address_afte linkData.call = false; b->linkData.push_back(linkData); - MOVI2R(DISPATCHER_PC, exit_address_after_return); - B(dispatcher); + blocks.WriteLinkBlock(*this, linkData); } } @@ -417,8 +405,7 @@ void JitArm64::FakeLKExit(u32 exit_address_after_return) linkData.call = false; b->linkData.push_back(linkData); - MOVI2R(DISPATCHER_PC, exit_address_after_return); - B(dispatcher); + blocks.WriteLinkBlock(*this, linkData); SetJumpTarget(skip_exit); } diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp index 7cf4f4aa41..254c5961d5 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp @@ -2,9 +2,9 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/PowerPC/JitArm64/Jit.h" -#include "Common/CommonTypes.h" #include "Core/PowerPC/JitArm64/JitArm64Cache.h" +#include "Common/CommonTypes.h" +#include "Core/PowerPC/JitArm64/Jit.h" #include "Core/PowerPC/JitCommon/JitBase.h" #include "Core/PowerPC/JitInterface.h" @@ -12,39 +12,57 @@ JitArm64BlockCache::JitArm64BlockCache(JitBase& jit) : JitBaseBlockCache{jit} { } +void JitArm64BlockCache::WriteLinkBlock(Arm64Gen::ARM64XEmitter& emit, + const JitBlock::LinkData& source, const JitBlock* dest) +{ + if (!dest) + { + // Use a fixed amount of instructions, so we can assume to use 3 instructions on patching. + emit.MOVZ(DISPATCHER_PC, source.exitAddress & 0xFFFF, SHIFT_0); + emit.MOVK(DISPATCHER_PC, source.exitAddress >> 16, SHIFT_16); + + if (source.call) + emit.BL(m_jit.GetAsmRoutines()->dispatcher); + else + emit.B(m_jit.GetAsmRoutines()->dispatcher); + return; + } + + if (source.call) + { + // The "fast" BL must be the third instruction. So just use the former two to inline the + // downcount check here. It's better to do this near jump before the long jump to the other + // block. + FixupBranch fast_link = emit.B(CC_PL); + emit.BL(dest->checkedEntry); + emit.SetJumpTarget(fast_link); + emit.BL(dest->normalEntry); + return; + } + + // Are we able to jump directly to the normal entry? + s64 distance = ((s64)dest->normalEntry - (s64)emit.GetCodePtr()) >> 2; + if (distance >= -0x40000 && distance <= 0x3FFFF) + { + emit.B(CC_PL, dest->normalEntry); + emit.B(dest->checkedEntry); + emit.BRK(101); + return; + } + + FixupBranch fast_link = emit.B(CC_PL); + emit.B(dest->checkedEntry); + emit.SetJumpTarget(fast_link); + emit.B(dest->normalEntry); +} + void JitArm64BlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) { u8* location = source.exitPtrs; ARM64XEmitter emit(location); - if (dest) - { - if (source.call) - { - emit.BL(dest->checkedEntry); - } - else - { - // Are we able to jump directly to the normal entry? - s64 distance = ((s64)dest->normalEntry - (s64)location) >> 2; - if (distance >= -0x40000 && distance <= 0x3FFFF) - { - emit.B(CC_PL, dest->normalEntry); - } + WriteLinkBlock(emit, source, dest); - // Use the checked entry if either downcount is smaller zero, - // or if we're not able to inline the downcount check here. - emit.B(dest->checkedEntry); - } - } - else - { - emit.MOVI2R(DISPATCHER_PC, source.exitAddress); - if (source.call) - emit.BL(m_jit.GetAsmRoutines()->dispatcher); - else - emit.B(m_jit.GetAsmRoutines()->dispatcher); - } emit.FlushIcache(); } diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h index f4b1b3af7f..01d6db5cea 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h @@ -4,6 +4,7 @@ #pragma once +#include "Common/Arm64Emitter.h" #include "Core/PowerPC/JitCommon/JitCache.h" class JitBase; @@ -15,6 +16,9 @@ class JitArm64BlockCache : public JitBaseBlockCache public: explicit JitArm64BlockCache(JitBase& jit); + void WriteLinkBlock(Arm64Gen::ARM64XEmitter& emit, const JitBlock::LinkData& source, + const JitBlock* dest = nullptr); + private: void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override; void WriteDestroyBlock(const JitBlock& block) override;