Skip to content

Commit

Permalink
JitArm64: Never check downcount on block entry
Browse files Browse the repository at this point in the history
Jumping between linked blocks currently works as follows: First, at the
end of the first block, we check if the downcount is greater than zero.
If it is, we jump to the `normalEntry` of the block. So far so good. But
if the downcount wasn't greater than zero, we jump to the `checkedEntry`
of the block, which checks the downcount *again* and then jumps to
`do_timing` if it's less than zero (which seems like an off by one error
- Jit64 doesn't do anything like this). This second check is rather
redundant. Let's jump to `do_timing` where we previously jumped to
`checkedEntry`.

Jit64 doesn't check the downcount on block entry. See 5236dc3.
  • Loading branch information
JosJuice committed Oct 22, 2022
1 parent b7310a1 commit 2c5fe5a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 42 deletions.
16 changes: 3 additions & 13 deletions Source/Core/Core/PowerPC/JitArm64/Jit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return

if (LK)
{
// Push {ARM_PC+20; PPC_PC} on the stack
// Push {ARM_PC; PPC_PC} on the stack
MOVI2R(ARM64Reg::X1, exit_address_after_return);
ADR(ARM64Reg::X0, 20);
ADR(ARM64Reg::X0, JitArm64BlockCache::BLOCK_LINK_FAST_BL_OFFSET + 12);
STP(IndexType::Pre, ARM64Reg::X0, ARM64Reg::X1, ARM64Reg::SP, -16);
}

Expand Down Expand Up @@ -806,17 +806,7 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)

u8* const start = GetWritableCodePtr();
b->checkedEntry = start;

// Downcount flag check, Only valid for linked blocks
{
FixupBranch bail = B(CC_PL);
MOVI2R(DISPATCHER_PC, js.blockStart);
B(do_timing);
SetJumpTarget(bail);
}

// Normal entry doesn't need to check for downcount.
b->normalEntry = GetWritableCodePtr();
b->normalEntry = start;

// Conditionally add profiling code.
if (jo.profile_blocks)
Expand Down
75 changes: 46 additions & 29 deletions Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,52 +22,69 @@ void JitArm64BlockCache::Init()
void JitArm64BlockCache::WriteLinkBlock(Arm64Gen::ARM64XEmitter& emit,
const JitBlock::LinkData& source, const JitBlock* dest)
{
const u8* start = emit.GetCodePtr();

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, ShiftAmount::Shift0);
emit.MOVK(DISPATCHER_PC, source.exitAddress >> 16, ShiftAmount::Shift16);

emit.MOVI2R(DISPATCHER_PC, source.exitAddress);
if (source.call)
{
while (emit.GetCodePtr() < start + BLOCK_LINK_FAST_BL_OFFSET)
emit.NOP();
emit.BL(m_jit.GetAsmRoutines()->dispatcher);
}
else
{
emit.B(m_jit.GetAsmRoutines()->dispatcher);
return;
}
}

if (source.call)
else
{
// 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_GT);
emit.BL(dest->checkedEntry);
emit.SetJumpTarget(fast_link);
emit.BL(dest->normalEntry);
return;
if (source.call)
{
// The "fast" BL should be the last instruction, so that the return address matches the
// address that was pushed onto the stack by the function that called WriteLinkBlock
FixupBranch fast = emit.B(CC_GT);
emit.MOVI2R(DISPATCHER_PC, source.exitAddress);
emit.BL(m_jit.GetAsmRoutines()->do_timing);
while (emit.GetCodePtr() < start + BLOCK_LINK_FAST_BL_OFFSET)
emit.BRK(101);
emit.SetJumpTarget(fast);
emit.BL(dest->normalEntry);
}
else
{
// Are we able to jump directly to the block?
s64 block_distance = ((s64)dest->normalEntry - (s64)emit.GetCodePtr()) >> 2;
if (block_distance >= -0x40000 && block_distance <= 0x3FFFF)
{
emit.B(CC_GT, dest->normalEntry);
emit.MOVI2R(DISPATCHER_PC, source.exitAddress);
emit.B(m_jit.GetAsmRoutines()->do_timing);
}
else
{
FixupBranch slow = emit.B(CC_LE);
emit.B(dest->normalEntry);
emit.SetJumpTarget(slow);
emit.MOVI2R(DISPATCHER_PC, source.exitAddress);
emit.B(m_jit.GetAsmRoutines()->do_timing);
}
}
}

// 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_GT, dest->normalEntry);
emit.B(dest->checkedEntry);
// Use a fixed number of instructions so we have enough room for any patching needed later.
const u8* end = start + BLOCK_LINK_SIZE;
while (emit.GetCodePtr() < end)
emit.BRK(101);
return;
}

FixupBranch fast_link = emit.B(CC_GT);
emit.B(dest->checkedEntry);
emit.SetJumpTarget(fast_link);
emit.B(dest->normalEntry);
ASSERT(emit.GetCodePtr() == end);
}

void JitArm64BlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
{
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
u8* location = source.exitPtrs;
ARM64XEmitter emit(location, location + 12);
ARM64XEmitter emit(location, location + BLOCK_LINK_SIZE);

WriteLinkBlock(emit, source, dest);
emit.FlushIcache();
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class JitArm64BlockCache : public JitBaseBlockCache
void WriteLinkBlock(Arm64Gen::ARM64XEmitter& emit, const JitBlock::LinkData& source,
const JitBlock* dest = nullptr);

static constexpr size_t BLOCK_LINK_SIZE = 5 * sizeof(u32);
static constexpr size_t BLOCK_LINK_FAST_BL_OFFSET = 4 * sizeof(u32);

private:
void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override;
void WriteDestroyBlock(const JitBlock& block) override;
Expand Down

0 comments on commit 2c5fe5a

Please sign in to comment.