Play-/Source/ee/VuBasicBlock.cpp

257 lines
7.4 KiB
C++
Raw Normal View History

#include "VuBasicBlock.h"
#include "MA_VU.h"
#include "offsetof_def.h"
2017-07-06 23:35:22 -04:00
#include "MemoryUtils.h"
#include "Vpu.h"
CVuBasicBlock::CVuBasicBlock(CMIPS& context, uint32 begin, uint32 end)
2018-04-30 21:01:23 +01:00
: CBasicBlock(context, begin, end)
{
}
void CVuBasicBlock::CompileRange(CMipsJitter* jitter)
{
2018-07-21 20:49:58 -04:00
CompileProlog(jitter);
assert((m_begin & 0x07) == 0);
assert(((m_end + 4) & 0x07) == 0);
2016-08-06 22:42:47 -04:00
auto arch = static_cast<CMA_VU*>(m_context.m_pArch);
auto integerBranchDelayInfo = GetIntegerBranchDelayInfo();
2017-08-06 13:34:05 -04:00
bool hasPendingXgKick = false;
const auto clearPendingXgKick =
2018-04-30 21:01:23 +01:00
[&]() {
assert(hasPendingXgKick);
EmitXgKick(jitter);
hasPendingXgKick = false;
};
2017-07-06 23:35:22 -04:00
for(uint32 address = m_begin; address <= m_end; address += 8)
{
uint32 relativePipeTime = (address - m_begin) / 8;
uint32 addressLo = address + 0;
uint32 addressHi = address + 4;
uint32 opcodeLo = m_context.m_pMemoryMap->GetInstruction(addressLo);
uint32 opcodeHi = m_context.m_pMemoryMap->GetInstruction(addressHi);
2017-01-06 23:23:27 -05:00
auto loOps = arch->GetAffectedOperands(&m_context, addressLo, opcodeLo);
auto hiOps = arch->GetAffectedOperands(&m_context, addressHi, opcodeHi);
//No upper instruction writes to Q
assert(hiOps.syncQ == false);
2018-04-30 21:01:23 +01:00
//No lower instruction reads Q
assert(loOps.readQ == false);
2017-08-06 13:34:05 -04:00
bool loIsXgKick = (opcodeLo & ~(0x1F << 11)) == 0x800006FC;
if(loOps.syncQ)
{
VUShared::FlushPipeline(VUShared::g_pipeInfoQ, jitter);
}
if(hiOps.readQ)
{
VUShared::CheckPipeline(VUShared::g_pipeInfoQ, jitter, relativePipeTime);
}
uint8 savedReg = 0;
if(hiOps.writeF != 0)
{
assert(hiOps.writeF != loOps.writeF);
if(
2018-04-30 21:01:23 +01:00
(hiOps.writeF == loOps.readF0) ||
(hiOps.writeF == loOps.readF1))
{
savedReg = hiOps.writeF;
jitter->MD_PushRel(offsetof(CMIPS, m_State.nCOP2[savedReg]));
jitter->MD_PullRel(offsetof(CMIPS, m_State.nCOP2VF_PreUp));
}
}
if(address == integerBranchDelayInfo.saveRegAddress)
{
// grab the value of the delayed reg to use in the conditional branch later
jitter->PushRel(offsetof(CMIPS, m_State.nCOP2VI[integerBranchDelayInfo.regIndex]));
jitter->PullRel(offsetof(CMIPS, m_State.savedIntReg));
}
arch->SetRelativePipeTime(relativePipeTime);
arch->CompileInstruction(addressHi, jitter, &m_context);
if(savedReg != 0)
{
jitter->MD_PushRel(offsetof(CMIPS, m_State.nCOP2[savedReg]));
jitter->MD_PullRel(offsetof(CMIPS, m_State.nCOP2VF_UpRes));
jitter->MD_PushRel(offsetof(CMIPS, m_State.nCOP2VF_PreUp));
jitter->MD_PullRel(offsetof(CMIPS, m_State.nCOP2[savedReg]));
}
if(address == integerBranchDelayInfo.useRegAddress)
{
// set the target from the saved value
jitter->PushRel(offsetof(CMIPS, m_State.nCOP2VI[integerBranchDelayInfo.regIndex]));
jitter->PullRel(offsetof(CMIPS, m_State.savedIntRegTemp));
jitter->PushRel(offsetof(CMIPS, m_State.savedIntReg));
jitter->PullRel(offsetof(CMIPS, m_State.nCOP2VI[integerBranchDelayInfo.regIndex]));
}
//If there's a pending XGKICK and the current lower instruction is
//an XGKICK, make sure we flush the pending one first
if(loIsXgKick && hasPendingXgKick)
{
clearPendingXgKick();
}
arch->CompileInstruction(addressLo, jitter, &m_context);
if(address == integerBranchDelayInfo.useRegAddress)
{
// put the target value back
jitter->PushRel(offsetof(CMIPS, m_State.savedIntRegTemp));
jitter->PullRel(offsetof(CMIPS, m_State.nCOP2VI[integerBranchDelayInfo.regIndex]));
}
if(savedReg != 0)
{
jitter->MD_PushRel(offsetof(CMIPS, m_State.nCOP2VF_UpRes));
jitter->MD_PullRel(offsetof(CMIPS, m_State.nCOP2[savedReg]));
}
2017-08-06 13:34:05 -04:00
if(hasPendingXgKick)
2017-07-06 23:35:22 -04:00
{
2017-08-06 13:34:05 -04:00
clearPendingXgKick();
2017-07-06 23:35:22 -04:00
}
2017-08-06 13:34:05 -04:00
if(loIsXgKick)
2017-07-06 23:35:22 -04:00
{
2017-08-06 13:34:05 -04:00
assert(!hasPendingXgKick);
hasPendingXgKick = true;
2017-07-06 23:35:22 -04:00
}
//Sanity check
assert(jitter->IsStackEmpty());
}
2017-08-06 13:34:05 -04:00
if(hasPendingXgKick)
2017-07-06 23:35:22 -04:00
{
2017-08-06 13:34:05 -04:00
clearPendingXgKick();
2017-07-06 23:35:22 -04:00
}
2017-08-06 13:34:05 -04:00
assert(!hasPendingXgKick);
2017-07-06 23:35:22 -04:00
//Increment pipeTime
{
uint32 timeInc = ((m_end - m_begin) / 8) + 1;
jitter->PushRel(offsetof(CMIPS, m_State.pipeTime));
jitter->PushCst(timeInc);
jitter->Add();
jitter->PullRel(offsetof(CMIPS, m_State.pipeTime));
}
2018-07-17 18:51:31 -04:00
CompileEpilog(jitter);
}
bool CVuBasicBlock::IsConditionalBranch(uint32 opcodeLo)
{
//Conditional branches are in the contiguous opcode range 0x28 -> 0x2F inclusive
uint32 id = (opcodeLo >> 25) & 0x7F;
return (id >= 0x28) && (id < 0x30);
}
CVuBasicBlock::INTEGER_BRANCH_DELAY_INFO CVuBasicBlock::GetIntegerBranchDelayInfo() const
{
// Test if the block ends with a conditional branch instruction where the condition variable has been
// set in the prior instruction.
// In this case, the pipeline shortcut fails and we need to use the value from 4 instructions previous.
// If the relevant set instruction is not part of this block, use initial value of the integer register.
INTEGER_BRANCH_DELAY_INFO result;
auto arch = static_cast<CMA_VU*>(m_context.m_pArch);
uint32 adjustedEnd = m_end - 4;
// Check if we have a conditional branch instruction.
uint32 branchOpcodeAddr = adjustedEnd - 8;
uint32 branchOpcodeLo = m_context.m_pMemoryMap->GetInstruction(branchOpcodeAddr);
if(IsConditionalBranch(branchOpcodeLo))
{
// We have a conditional branch instruction. Now we need to check that the condition register is not written
// by the previous instruction.
uint32 priorOpcodeAddr = adjustedEnd - 16;
uint32 priorOpcodeLo = m_context.m_pMemoryMap->GetInstruction(priorOpcodeAddr);
auto priorLoOps = arch->GetAffectedOperands(&m_context, priorOpcodeAddr, priorOpcodeLo);
if((priorLoOps.writeI != 0) && !priorLoOps.branchValue)
{
auto branchLoOps = arch->GetAffectedOperands(&m_context, branchOpcodeAddr, branchOpcodeLo);
if(
2018-04-30 21:01:23 +01:00
(branchLoOps.readI0 == priorLoOps.writeI) ||
(branchLoOps.readI1 == priorLoOps.writeI))
{
//Check if our block is a "special" loop. Disable delayed integer processing if it's the case
//TODO: Handle that case better
bool isSpecialLoop = CheckIsSpecialIntegerLoop(priorLoOps.writeI);
if(!isSpecialLoop)
{
// we need to use the value of intReg 4 steps prior or use initial value.
2018-04-30 21:01:23 +01:00
result.regIndex = priorLoOps.writeI;
result.saveRegAddress = std::max(adjustedEnd - 5 * 8, m_begin);
2018-04-30 21:01:23 +01:00
result.useRegAddress = adjustedEnd - 8;
}
}
}
}
return result;
}
bool CVuBasicBlock::CheckIsSpecialIntegerLoop(unsigned int regI) const
{
//This checks for a pattern where all instructions within a block
//modifies an integer register except for one branch instruction that
//tests that integer register
//Required by BGDA that has that kind of loop inside its VU microcode
auto arch = static_cast<CMA_VU*>(m_context.m_pArch);
uint32 length = (m_end - m_begin) / 8;
if(length != 4) return false;
for(uint32 index = 0; index <= length; index++)
{
uint32 address = m_begin + (index * 8);
uint32 opcodeLo = m_context.m_pMemoryMap->GetInstruction(address);
if(index == (length - 1))
{
assert(IsConditionalBranch(opcodeLo));
uint32 branchTarget = arch->GetInstructionEffectiveAddress(&m_context, address, opcodeLo);
if(branchTarget != m_begin) return false;
}
else
{
auto loOps = arch->GetAffectedOperands(&m_context, address, opcodeLo);
if(loOps.writeI != regI) return false;
}
}
return true;
}
2017-07-06 23:35:22 -04:00
2017-08-06 13:34:05 -04:00
void CVuBasicBlock::EmitXgKick(CMipsJitter* jitter)
2017-07-06 23:35:22 -04:00
{
//Push context
jitter->PushCtx();
//Push value
jitter->PushRel(offsetof(CMIPS, m_State.xgkickAddress));
2017-07-06 23:35:22 -04:00
//Compute Address
jitter->PushCst(CVpu::VU_XGKICK);
jitter->Call(reinterpret_cast<void*>(&MemoryUtils_SetWordProxy), 3, false);
}