mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-26 15:49:50 -06:00

Much of Jit64Util consists of essentials, not utilities. Breaking these out into their own files also prevents unrelated includes from being present near other classes. This also makes it easier to find and change certain components of the x86-64 JIT, should it be necessary.
224 lines
5.4 KiB
C++
224 lines
5.4 KiB
C++
// Copyright 2014 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#include "Core/PowerPC/CachedInterpreter.h"
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/Logging/Log.h"
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/CoreTiming.h"
|
|
#include "Core/HLE/HLE.h"
|
|
#include "Core/HW/CPU.h"
|
|
#include "Core/PowerPC/Gekko.h"
|
|
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
|
|
#include "Core/PowerPC/PPCAnalyst.h"
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
|
|
void CachedInterpreter::Init()
|
|
{
|
|
m_code.reserve(CODE_SIZE / sizeof(Instruction));
|
|
|
|
jo.enableBlocklink = false;
|
|
|
|
JitBaseBlockCache::Init();
|
|
UpdateMemoryOptions();
|
|
|
|
code_block.m_stats = &js.st;
|
|
code_block.m_gpa = &js.gpa;
|
|
code_block.m_fpa = &js.fpa;
|
|
}
|
|
|
|
void CachedInterpreter::Shutdown()
|
|
{
|
|
JitBaseBlockCache::Shutdown();
|
|
}
|
|
|
|
void CachedInterpreter::ExecuteOneBlock()
|
|
{
|
|
const u8* normal_entry = JitBaseBlockCache::Dispatch();
|
|
const Instruction* code = reinterpret_cast<const Instruction*>(normal_entry);
|
|
|
|
for (; code->type != Instruction::INSTRUCTION_ABORT; ++code)
|
|
{
|
|
switch (code->type)
|
|
{
|
|
case Instruction::INSTRUCTION_TYPE_COMMON:
|
|
code->common_callback(UGeckoInstruction(code->data));
|
|
break;
|
|
|
|
case Instruction::INSTRUCTION_TYPE_CONDITIONAL:
|
|
if (code->conditional_callback(code->data))
|
|
return;
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG(POWERPC, "Unknown CachedInterpreter Instruction: %d", code->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CachedInterpreter::Run()
|
|
{
|
|
while (CPU::GetState() == CPU::CPU_RUNNING)
|
|
{
|
|
// Start new timing slice
|
|
// NOTE: Exceptions may change PC
|
|
CoreTiming::Advance();
|
|
|
|
do
|
|
{
|
|
ExecuteOneBlock();
|
|
} while (PowerPC::ppcState.downcount > 0);
|
|
}
|
|
}
|
|
|
|
void CachedInterpreter::SingleStep()
|
|
{
|
|
// Enter new timing slice
|
|
CoreTiming::Advance();
|
|
ExecuteOneBlock();
|
|
}
|
|
|
|
static void EndBlock(UGeckoInstruction data)
|
|
{
|
|
PC = NPC;
|
|
PowerPC::ppcState.downcount -= data.hex;
|
|
}
|
|
|
|
static void WritePC(UGeckoInstruction data)
|
|
{
|
|
PC = data.hex;
|
|
NPC = data.hex + 4;
|
|
}
|
|
|
|
static void WriteBrokenBlockNPC(UGeckoInstruction data)
|
|
{
|
|
NPC = data.hex;
|
|
}
|
|
|
|
static bool CheckFPU(u32 data)
|
|
{
|
|
UReg_MSR& msr = (UReg_MSR&)MSR;
|
|
if (!msr.FP)
|
|
{
|
|
PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE;
|
|
PowerPC::CheckExceptions();
|
|
PowerPC::ppcState.downcount -= data;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool CheckDSI(u32 data)
|
|
{
|
|
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
|
|
{
|
|
PowerPC::CheckExceptions();
|
|
PowerPC::ppcState.downcount -= data;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CachedInterpreter::Jit(u32 address)
|
|
{
|
|
if (m_code.size() >= CODE_SIZE / sizeof(Instruction) - 0x1000 || IsFull() ||
|
|
SConfig::GetInstance().bJITNoBlockCache)
|
|
{
|
|
ClearCache();
|
|
}
|
|
|
|
u32 nextPC = analyzer.Analyze(PC, &code_block, &code_buffer, code_buffer.GetSize());
|
|
if (code_block.m_memory_exception)
|
|
{
|
|
// Address of instruction could not be translated
|
|
NPC = nextPC;
|
|
PowerPC::ppcState.Exceptions |= EXCEPTION_ISI;
|
|
PowerPC::CheckExceptions();
|
|
WARN_LOG(POWERPC, "ISI exception at 0x%08x", nextPC);
|
|
return;
|
|
}
|
|
|
|
int block_num = AllocateBlock(PC);
|
|
JitBlock* b = GetBlock(block_num);
|
|
|
|
js.blockStart = PC;
|
|
js.firstFPInstructionFound = false;
|
|
js.fifoBytesSinceCheck = 0;
|
|
js.downcountAmount = 0;
|
|
js.curBlock = b;
|
|
|
|
PPCAnalyst::CodeOp* ops = code_buffer.codebuffer;
|
|
|
|
b->checkedEntry = GetCodePtr();
|
|
b->normalEntry = GetCodePtr();
|
|
b->runCount = 0;
|
|
|
|
for (u32 i = 0; i < code_block.m_num_instructions; i++)
|
|
{
|
|
js.downcountAmount += ops[i].opinfo->numCycles;
|
|
|
|
u32 function = HLE::GetFunctionIndex(ops[i].address);
|
|
if (function != 0)
|
|
{
|
|
int type = HLE::GetFunctionTypeByIndex(function);
|
|
if (type == HLE::HLE_HOOK_START || type == HLE::HLE_HOOK_REPLACE)
|
|
{
|
|
int flags = HLE::GetFunctionFlagsByIndex(function);
|
|
if (HLE::IsEnabled(flags))
|
|
{
|
|
m_code.emplace_back(WritePC, ops[i].address);
|
|
m_code.emplace_back(Interpreter::HLEFunction, ops[i].inst);
|
|
if (type == HLE::HLE_HOOK_REPLACE)
|
|
{
|
|
m_code.emplace_back(EndBlock, js.downcountAmount);
|
|
m_code.emplace_back();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ops[i].skip)
|
|
{
|
|
bool check_fpu = (ops[i].opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound;
|
|
bool endblock = (ops[i].opinfo->flags & FL_ENDBLOCK) != 0;
|
|
bool memcheck = (ops[i].opinfo->flags & FL_LOADSTORE) && jo.memcheck;
|
|
|
|
if (check_fpu)
|
|
{
|
|
m_code.emplace_back(WritePC, ops[i].address);
|
|
m_code.emplace_back(CheckFPU, js.downcountAmount);
|
|
js.firstFPInstructionFound = true;
|
|
}
|
|
|
|
if (endblock || memcheck)
|
|
m_code.emplace_back(WritePC, ops[i].address);
|
|
m_code.emplace_back(GetInterpreterOp(ops[i].inst), ops[i].inst);
|
|
if (memcheck)
|
|
m_code.emplace_back(CheckDSI, js.downcountAmount);
|
|
if (endblock)
|
|
m_code.emplace_back(EndBlock, js.downcountAmount);
|
|
}
|
|
}
|
|
if (code_block.m_broken)
|
|
{
|
|
m_code.emplace_back(WriteBrokenBlockNPC, nextPC);
|
|
m_code.emplace_back(EndBlock, js.downcountAmount);
|
|
}
|
|
m_code.emplace_back();
|
|
|
|
b->codeSize = (u32)(GetCodePtr() - b->checkedEntry);
|
|
b->originalSize = code_block.m_num_instructions;
|
|
|
|
FinalizeBlock(block_num, jo.enableBlocklink, b->checkedEntry);
|
|
}
|
|
|
|
void CachedInterpreter::ClearCache()
|
|
{
|
|
m_code.clear();
|
|
JitBaseBlockCache::Clear();
|
|
UpdateMemoryOptions();
|
|
}
|