mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-10-27 10:29:07 +00:00
Jit64: Dynamic length of regular jump instruction (for known addresses)
Conditional jumps already do that, so let's be consistent.
This commit is contained in:
parent
70bd0943a7
commit
f6e5448b43
9 changed files with 55 additions and 40 deletions
|
|
@ -412,26 +412,30 @@ void XEmitter::Rex(int w, int r, int x, int b)
|
||||||
Write8(rx);
|
Write8(rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XEmitter::JMP(const u8* addr, const Jump jump)
|
void XEmitter::JMP(const u8* addr, bool force_near_padding)
|
||||||
{
|
{
|
||||||
u64 fn = (u64)addr;
|
u64 fn = (u64)addr;
|
||||||
if (jump == Jump::Short)
|
s64 distance = (s64)(fn - ((u64)code + SHORT_JMP_LEN));
|
||||||
|
if (distance < -0x80 || distance >= 0x80)
|
||||||
{
|
{
|
||||||
s64 distance = (s64)(fn - ((u64)code + 2));
|
distance = (s64)(fn - ((u64)code + NEAR_JMP_LEN));
|
||||||
ASSERT_MSG(DYNA_REC, distance >= -0x80 && distance < 0x80,
|
ASSERT_MSG(DYNA_REC, distance >= -0x80000000LL && distance < 0x80000000LL,
|
||||||
"Jump::Short target too far away ({}), needs Jump::Near", distance);
|
"Jump target too far away ({}), needs indirect register", distance);
|
||||||
// 8 bits will do
|
Write8(0xE9);
|
||||||
Write8(0xEB);
|
Write32((u32)(s32)distance);
|
||||||
Write8((u8)(s8)distance);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s64 distance = (s64)(fn - ((u64)code + 5));
|
Write8(0xEB);
|
||||||
|
Write8((u8)(s8)distance);
|
||||||
ASSERT_MSG(DYNA_REC, distance >= -0x80000000LL && distance < 0x80000000LL,
|
if (force_near_padding)
|
||||||
"Jump::Near target too far away ({}), needs indirect register", distance);
|
{
|
||||||
Write8(0xE9);
|
for (int i = 0; i < NEAR_JMP_LEN - SHORT_JMP_LEN; i++)
|
||||||
Write32((u32)(s32)distance);
|
{
|
||||||
|
// INT3 is more efficient than NOP if never executed, as it stops CPU speculation.
|
||||||
|
INT3();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -444,6 +444,8 @@ public:
|
||||||
Short,
|
Short,
|
||||||
Near,
|
Near,
|
||||||
};
|
};
|
||||||
|
static const int SHORT_JMP_LEN = 2;
|
||||||
|
static const int NEAR_JMP_LEN = 5;
|
||||||
|
|
||||||
// Flow control
|
// Flow control
|
||||||
void RET();
|
void RET();
|
||||||
|
|
@ -451,7 +453,7 @@ public:
|
||||||
void UD2();
|
void UD2();
|
||||||
[[nodiscard]] FixupBranch J(Jump jump = Jump::Short);
|
[[nodiscard]] FixupBranch J(Jump jump = Jump::Short);
|
||||||
|
|
||||||
void JMP(const u8* addr, Jump jump = Jump::Short);
|
void JMP(const u8* addr, bool force_near_padding = false);
|
||||||
void JMPptr(const OpArg& arg);
|
void JMPptr(const OpArg& arg);
|
||||||
void JMPself(); // infinite loop!
|
void JMPself(); // infinite loop!
|
||||||
#ifdef CALL
|
#ifdef CALL
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ void DSPEmitter::checkExceptions(u32 retval)
|
||||||
TEST(32, R(ABI_RETURN), R(ABI_RETURN));
|
TEST(32, R(ABI_RETURN), R(ABI_RETURN));
|
||||||
FixupBranch skip_return = J_CC(CC_Z, Jump::Short);
|
FixupBranch skip_return = J_CC(CC_Z, Jump::Short);
|
||||||
MOV(32, R(EAX), Imm32(retval));
|
MOV(32, R(EAX), Imm32(retval));
|
||||||
JMP(m_return_dispatcher, Jump::Near);
|
JMP(m_return_dispatcher);
|
||||||
SetJumpTarget(skip_return);
|
SetJumpTarget(skip_return);
|
||||||
m_gpr.LoadRegs(false);
|
m_gpr.LoadRegs(false);
|
||||||
m_gpr.FlushRegs(c, false);
|
m_gpr.FlushRegs(c, false);
|
||||||
|
|
@ -293,7 +293,7 @@ void DSPEmitter::Compile(u16 start_addr)
|
||||||
{
|
{
|
||||||
MOV(16, R(EAX), Imm16(m_block_size[start_addr]));
|
MOV(16, R(EAX), Imm16(m_block_size[start_addr]));
|
||||||
}
|
}
|
||||||
JMP(m_return_dispatcher, Jump::Near);
|
JMP(m_return_dispatcher);
|
||||||
m_gpr.LoadRegs(false);
|
m_gpr.LoadRegs(false);
|
||||||
m_gpr.FlushRegs(c, false);
|
m_gpr.FlushRegs(c, false);
|
||||||
|
|
||||||
|
|
@ -329,7 +329,7 @@ void DSPEmitter::Compile(u16 start_addr)
|
||||||
{
|
{
|
||||||
MOV(16, R(EAX), Imm16(m_block_size[start_addr]));
|
MOV(16, R(EAX), Imm16(m_block_size[start_addr]));
|
||||||
}
|
}
|
||||||
JMP(m_return_dispatcher, Jump::Near);
|
JMP(m_return_dispatcher);
|
||||||
m_gpr.LoadRegs(false);
|
m_gpr.LoadRegs(false);
|
||||||
m_gpr.FlushRegs(c, false);
|
m_gpr.FlushRegs(c, false);
|
||||||
|
|
||||||
|
|
@ -392,7 +392,7 @@ void DSPEmitter::Compile(u16 start_addr)
|
||||||
{
|
{
|
||||||
MOV(16, R(EAX), Imm16(m_block_size[start_addr]));
|
MOV(16, R(EAX), Imm16(m_block_size[start_addr]));
|
||||||
}
|
}
|
||||||
JMP(m_return_dispatcher, Jump::Near);
|
JMP(m_return_dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSPEmitter::CompileCurrent(DSPEmitter& emitter)
|
void DSPEmitter::CompileCurrent(DSPEmitter& emitter)
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ void DSPEmitter::WriteBranchExit()
|
||||||
{
|
{
|
||||||
MOV(16, R(EAX), Imm16(m_block_size[m_start_address]));
|
MOV(16, R(EAX), Imm16(m_block_size[m_start_address]));
|
||||||
}
|
}
|
||||||
JMP(m_return_dispatcher, Jump::Near);
|
JMP(m_return_dispatcher);
|
||||||
m_gpr.LoadRegs(false);
|
m_gpr.LoadRegs(false);
|
||||||
m_gpr.FlushRegs(c, false);
|
m_gpr.FlushRegs(c, false);
|
||||||
}
|
}
|
||||||
|
|
@ -130,7 +130,7 @@ void DSPEmitter::WriteBlockLink(u16 dest)
|
||||||
|
|
||||||
SUB(16, R(ECX), Imm16(m_block_size[m_start_address]));
|
SUB(16, R(ECX), Imm16(m_block_size[m_start_address]));
|
||||||
MOV(16, MatR(RAX), R(ECX));
|
MOV(16, MatR(RAX), R(ECX));
|
||||||
JMP(m_block_links[dest], Jump::Near);
|
JMP(m_block_links[dest]);
|
||||||
SetJumpTarget(notEnoughCycles);
|
SetJumpTarget(notEnoughCycles);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ bool Jit64::BackPatch(SContext* ctx)
|
||||||
|
|
||||||
// Patch the original memory operation.
|
// Patch the original memory operation.
|
||||||
XEmitter emitter(start, start + info.len);
|
XEmitter emitter(start, start + info.len);
|
||||||
emitter.JMP(trampoline, Jump::Near);
|
emitter.JMP(trampoline);
|
||||||
// NOPs become dead code
|
// NOPs become dead code
|
||||||
const u8* end = info.start + info.len;
|
const u8* end = info.start + info.len;
|
||||||
for (const u8* i = emitter.GetCodePtr(); i < end; ++i)
|
for (const u8* i = emitter.GetCodePtr(); i < end; ++i)
|
||||||
|
|
@ -594,7 +594,10 @@ void Jit64::JustWriteExit(u32 destination, bool bl, u32 after)
|
||||||
J_CC(CC_LE, asm_routines.do_timing);
|
J_CC(CC_LE, asm_routines.do_timing);
|
||||||
|
|
||||||
linkData.exitPtrs = GetWritableCodePtr();
|
linkData.exitPtrs = GetWritableCodePtr();
|
||||||
JMP(asm_routines.dispatcher_no_timing_check, Jump::Near);
|
// Padding required for correctness, as the JMP length might differ between dispatcher and
|
||||||
|
// linked block: if this wrote a Short JMP but then JitBlockCache::WriteLinkBlock wrote a Near
|
||||||
|
// JMP, the latter would overwrite other instructions.
|
||||||
|
JMP(asm_routines.dispatcher_no_timing_check, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
b->linkData.push_back(linkData);
|
b->linkData.push_back(linkData);
|
||||||
|
|
@ -622,7 +625,7 @@ void Jit64::WriteExitDestInRSCRATCH(bool bl, u32 after)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JMP(asm_routines.dispatcher, Jump::Near);
|
JMP(asm_routines.dispatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -660,7 +663,7 @@ void Jit64::WriteRfiExitDestInRSCRATCH()
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
EmitUpdateMembase();
|
EmitUpdateMembase();
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||||
JMP(asm_routines.dispatcher, Jump::Near);
|
JMP(asm_routines.dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Jit64::WriteIdleExit(u32 destination)
|
void Jit64::WriteIdleExit(u32 destination)
|
||||||
|
|
@ -682,7 +685,7 @@ void Jit64::WriteExceptionExit()
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
EmitUpdateMembase();
|
EmitUpdateMembase();
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||||
JMP(asm_routines.dispatcher, Jump::Near);
|
JMP(asm_routines.dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Jit64::WriteExternalExceptionExit()
|
void Jit64::WriteExternalExceptionExit()
|
||||||
|
|
@ -695,7 +698,7 @@ void Jit64::WriteExternalExceptionExit()
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
EmitUpdateMembase();
|
EmitUpdateMembase();
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||||
JMP(asm_routines.dispatcher, Jump::Near);
|
JMP(asm_routines.dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Jit64::Run()
|
void Jit64::Run()
|
||||||
|
|
@ -936,7 +939,7 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||||
ABI_CallFunctionPC(JitInterface::CompileExceptionCheckFromJIT, &m_system.GetJitInterface(),
|
ABI_CallFunctionPC(JitInterface::CompileExceptionCheckFromJIT, &m_system.GetJitInterface(),
|
||||||
static_cast<u32>(JitInterface::ExceptionType::PairedQuantize));
|
static_cast<u32>(JitInterface::ExceptionType::PairedQuantize));
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
JMP(asm_routines.dispatcher_no_check, Jump::Near);
|
JMP(asm_routines.dispatcher_no_check);
|
||||||
SwitchToNearCode();
|
SwitchToNearCode();
|
||||||
|
|
||||||
// Insert a check that the GQRs are still the value we expect at
|
// Insert a check that the GQRs are still the value we expect at
|
||||||
|
|
@ -1064,7 +1067,7 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||||
Cleanup();
|
Cleanup();
|
||||||
MOV(32, PPCSTATE(npc), Imm32(op.address));
|
MOV(32, PPCSTATE(npc), Imm32(op.address));
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||||
JMP(asm_routines.dispatcher_exit, Jump::Near);
|
JMP(asm_routines.dispatcher_exit);
|
||||||
|
|
||||||
SetJumpTarget(noBreakpoint);
|
SetJumpTarget(noBreakpoint);
|
||||||
}
|
}
|
||||||
|
|
@ -1284,7 +1287,7 @@ void Jit64::IntializeSpeculativeConstants()
|
||||||
ABI_CallFunctionPC(JitInterface::CompileExceptionCheckFromJIT, &m_system.GetJitInterface(),
|
ABI_CallFunctionPC(JitInterface::CompileExceptionCheckFromJIT, &m_system.GetJitInterface(),
|
||||||
static_cast<u32>(JitInterface::ExceptionType::SpeculativeConstants));
|
static_cast<u32>(JitInterface::ExceptionType::SpeculativeConstants));
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
JMP(asm_routines.dispatcher_no_check, Jump::Near);
|
JMP(asm_routines.dispatcher_no_check);
|
||||||
SwitchToNearCode();
|
SwitchToNearCode();
|
||||||
}
|
}
|
||||||
CMP(32, PPCSTATE_GPR(i), Imm32(compileTimeValue));
|
CMP(32, PPCSTATE_GPR(i), Imm32(compileTimeValue));
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ void Jit64AsmRoutineManager::Generate()
|
||||||
// If jitting triggered an ISI exception, MSR.DR may have changed
|
// If jitting triggered an ISI exception, MSR.DR may have changed
|
||||||
MOV(64, R(RMEM), PPCSTATE(mem_ptr));
|
MOV(64, R(RMEM), PPCSTATE(mem_ptr));
|
||||||
|
|
||||||
JMP(dispatcher_no_check, Jump::Near);
|
JMP(dispatcher_no_check);
|
||||||
|
|
||||||
SetJumpTarget(bail);
|
SetJumpTarget(bail);
|
||||||
do_timing = GetCodePtr();
|
do_timing = GetCodePtr();
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,19 @@ void JitBlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBl
|
||||||
// to emit JMP. So just NOP out the gap to the next block.
|
// to emit JMP. So just NOP out the gap to the next block.
|
||||||
// Support up to 3 additional bytes because of alignment.
|
// Support up to 3 additional bytes because of alignment.
|
||||||
s64 offset = address - location;
|
s64 offset = address - location;
|
||||||
if (offset > 0 && offset <= 5 + 3)
|
if (offset > 0 && offset <= Gen::XEmitter::NEAR_JMP_LEN + 3)
|
||||||
{
|
{
|
||||||
Gen::XEmitter emit(location, location + offset);
|
Gen::XEmitter emit(location, location + offset);
|
||||||
emit.NOP(offset);
|
emit.NOP(offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Gen::XEmitter emit(location, location + 5);
|
// Length forced to Near because JMP length might differ between dispatcher and linked block.
|
||||||
emit.JMP(address, Gen::XEmitter::Jump::Near);
|
// Technically this isn't necessary, as this is executed after Jit64::JustWriteExit (which
|
||||||
|
// also pads), and a Short JMP written on top of a Near JMP isn't incorrect since the garbage
|
||||||
|
// bytes are skipped. But they confuse the disassembler, and probably the CPU speculation too.
|
||||||
|
Gen::XEmitter emit(location, location + Gen::XEmitter::NEAR_JMP_LEN);
|
||||||
|
emit.JMP(address, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ const u8* TrampolineCache::GenerateReadTrampoline(const TrampolineInfo& info)
|
||||||
SafeLoadToReg(info.op_reg, info.op_arg, info.accessSize << 3, info.offset, info.registersInUse,
|
SafeLoadToReg(info.op_reg, info.op_arg, info.accessSize << 3, info.offset, info.registersInUse,
|
||||||
info.signExtend, info.flags | SAFE_LOADSTORE_FORCE_SLOW_ACCESS);
|
info.signExtend, info.flags | SAFE_LOADSTORE_FORCE_SLOW_ACCESS);
|
||||||
|
|
||||||
JMP(info.start + info.len, Jump::Near);
|
JMP(info.start + info.len);
|
||||||
|
|
||||||
Common::JitRegister::Register(trampoline, GetCodePtr(), "JIT_ReadTrampoline_{:x}", info.pc);
|
Common::JitRegister::Register(trampoline, GetCodePtr(), "JIT_ReadTrampoline_{:x}", info.pc);
|
||||||
return trampoline;
|
return trampoline;
|
||||||
|
|
@ -65,7 +65,7 @@ const u8* TrampolineCache::GenerateWriteTrampoline(const TrampolineInfo& info)
|
||||||
SafeWriteRegToReg(info.op_arg, info.op_reg, info.accessSize << 3, info.offset,
|
SafeWriteRegToReg(info.op_arg, info.op_reg, info.accessSize << 3, info.offset,
|
||||||
info.registersInUse, info.flags | SAFE_LOADSTORE_FORCE_SLOW_ACCESS);
|
info.registersInUse, info.flags | SAFE_LOADSTORE_FORCE_SLOW_ACCESS);
|
||||||
|
|
||||||
JMP(info.start + info.len, Jump::Near);
|
JMP(info.start + info.len);
|
||||||
|
|
||||||
Common::JitRegister::Register(trampoline, GetCodePtr(), "JIT_WriteTrampoline_{:x}", info.pc);
|
Common::JitRegister::Register(trampoline, GetCodePtr(), "JIT_WriteTrampoline_{:x}", info.pc);
|
||||||
return trampoline;
|
return trampoline;
|
||||||
|
|
|
||||||
|
|
@ -298,12 +298,14 @@ TEST_F(x64EmitterTest, POP_Register)
|
||||||
TEST_F(x64EmitterTest, JMP)
|
TEST_F(x64EmitterTest, JMP)
|
||||||
{
|
{
|
||||||
emitter->NOP(1);
|
emitter->NOP(1);
|
||||||
emitter->JMP(code_buffer, XEmitter::Jump::Short);
|
emitter->JMP(code_buffer);
|
||||||
ExpectBytes({/* nop */ 0x90, /* short jmp */ 0xeb, /* offset -3 */ 0xfd});
|
ExpectBytes({/* nop */ 0x90, /* short jmp */ 0xeb, /* offset -3 */ 0xfd});
|
||||||
|
|
||||||
emitter->NOP(1);
|
emitter->NOP(0x90);
|
||||||
emitter->JMP(code_buffer, XEmitter::Jump::Near);
|
const u8* after_nops = emitter->GetCodePtr();
|
||||||
ExpectBytes({/* nop */ 0x90, /* near jmp */ 0xe9, /* offset -6 */ 0xfa, 0xff, 0xff, 0xff});
|
ResetCodeBuffer();
|
||||||
|
emitter->JMP(after_nops);
|
||||||
|
ExpectBytes({/* near jmp */ 0xe9, /* offset */ 0x8B, 0, 0, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(x64EmitterTest, JMPptr_Register)
|
TEST_F(x64EmitterTest, JMPptr_Register)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue