Commit 193e7263 authored by Jean-Philip Desjardins's avatar Jean-Philip Desjardins
Browse files

Block lookup table now only stores "entry-point" functions to guest code.

Used to be a 1 to 1 map to covering function for a specific address.
parent 7f6c35ef
......@@ -177,22 +177,7 @@ void CBasicBlock::CompileRange(CMipsJitter* jitter)
return;
}
uint32 fixedEnd = m_end;
bool needsPcAdjust = false;
//Update block end because MipsAnalysis might not include an instruction
//in a delay slot when cutting a function into basic blocks
{
uint32 endOpcode = m_context.m_pMemoryMap->GetWord(m_end);
auto branchType = m_context.m_pArch->IsInstructionBranch(&m_context, m_end, endOpcode);
if(branchType == MIPS_BRANCH_NORMAL)
{
fixedEnd += 4;
needsPcAdjust = true;
}
}
for(uint32 address = m_begin; address <= fixedEnd; address += 4)
for(uint32 address = m_begin; address <= m_end; address += 4)
{
m_context.m_pArch->CompileInstruction(
address,
......@@ -202,20 +187,6 @@ void CBasicBlock::CompileRange(CMipsJitter* jitter)
assert(jitter->IsStackEmpty());
}
//Adjust PC to make sure we don't execute the delay slot at the next block
if(needsPcAdjust)
{
jitter->PushCst(MIPS_INVALID_PC);
jitter->PushRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
jitter->BeginIf(Jitter::CONDITION_EQ);
{
jitter->PushCst(fixedEnd + 4);
jitter->PullRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
}
jitter->EndIf();
}
CompileProlog(jitter);
}
......
......@@ -34,39 +34,18 @@ public:
}
}
std::set<BlockType> ClearInRange(uint32 start, uint32 end, BlockType protectedBlock)
{
std::set<BlockType> clearedBlocks;
for(uint32 address = start; address <= end; address += INSTRUCTION_SIZE)
{
auto block = m_blockTable[address / INSTRUCTION_SIZE];
if(block == nullptr) continue;
if(block == protectedBlock) continue;
if(!IsInsideRange(block->GetBeginAddress(), start, end) && !IsInsideRange(block->GetEndAddress(), start, end)) continue;
DeleteBlock(block);
clearedBlocks.insert(block);
}
return clearedBlocks;
}
void AddBlock(BlockType block)
{
for(uint32 address = block->GetBeginAddress(); address <= block->GetEndAddress(); address += INSTRUCTION_SIZE)
{
assert(m_blockTable[address / INSTRUCTION_SIZE] == m_emptyBlock);
m_blockTable[address / INSTRUCTION_SIZE] = block;
}
uint32 address = block->GetBeginAddress();
assert(m_blockTable[address / INSTRUCTION_SIZE] == m_emptyBlock);
m_blockTable[address / INSTRUCTION_SIZE] = block;
}
void DeleteBlock(BlockType block)
{
for(uint32 address = block->GetBeginAddress(); address <= block->GetEndAddress(); address += INSTRUCTION_SIZE)
{
assert(m_blockTable[address / INSTRUCTION_SIZE] != m_emptyBlock);
m_blockTable[address / INSTRUCTION_SIZE] = m_emptyBlock;
}
uint32 address = block->GetBeginAddress();
assert(m_blockTable[address / INSTRUCTION_SIZE] != m_emptyBlock);
m_blockTable[address / INSTRUCTION_SIZE] = m_emptyBlock;
}
BlockType FindBlockAt(uint32 address) const
......@@ -126,73 +105,36 @@ public:
}
}
std::set<BlockType> ClearInRange(uint32 start, uint32 end, BlockType protectedBlock)
{
uint32 hiStart = start >> SUBTABLE_BITS;
uint32 hiEnd = end >> SUBTABLE_BITS;
//TODO: improve this
//We need to increase the range to make sure we catch any
//block that are straddling table boundaries
if(hiStart != 0) hiStart--;
if(hiEnd != (m_subTableCount - 1)) hiEnd++;
std::set<BlockType> clearedBlocks;
for(uint32 hi = hiStart; hi <= hiEnd; hi++)
{
auto table = m_blockTable[hi];
if(!table) continue;
for(uint32 lo = 0; lo < SUBTABLE_SIZE; lo += INSTRUCTION_SIZE)
{
uint32 tableAddress = (hi << SUBTABLE_BITS) | lo;
auto block = table[lo / INSTRUCTION_SIZE];
if(block == m_emptyBlock) continue;
if(block == protectedBlock) continue;
if(!IsInsideRange(block->GetBeginAddress(), start, end) && !IsInsideRange(block->GetEndAddress(), start, end)) continue;
table[lo / INSTRUCTION_SIZE] = m_emptyBlock;
clearedBlocks.insert(block);
}
}
return clearedBlocks;
}
void AddBlock(BlockType block)
{
for(uint32 address = block->GetBeginAddress(); address <= block->GetEndAddress(); address += INSTRUCTION_SIZE)
uint32 address = block->GetBeginAddress();
uint32 hiAddress = address >> SUBTABLE_BITS;
uint32 loAddress = address & SUBTABLE_MASK;
assert(hiAddress < m_subTableCount);
auto& subTable = m_blockTable[hiAddress];
if(!subTable)
{
uint32 hiAddress = address >> SUBTABLE_BITS;
uint32 loAddress = address & SUBTABLE_MASK;
assert(hiAddress < m_subTableCount);
auto& subTable = m_blockTable[hiAddress];
if(!subTable)
const uint32 subTableSize = SUBTABLE_SIZE / INSTRUCTION_SIZE;
subTable = new BlockType[subTableSize];
for(uint32 i = 0; i < subTableSize; i++)
{
const uint32 subTableSize = SUBTABLE_SIZE / INSTRUCTION_SIZE;
subTable = new BlockType[subTableSize];
for(uint32 i = 0; i < subTableSize; i++)
{
subTable[i] = m_emptyBlock;
}
subTable[i] = m_emptyBlock;
}
assert(subTable[loAddress / INSTRUCTION_SIZE] == m_emptyBlock);
subTable[loAddress / INSTRUCTION_SIZE] = block;
}
assert(subTable[loAddress / INSTRUCTION_SIZE] == m_emptyBlock);
subTable[loAddress / INSTRUCTION_SIZE] = block;
}
void DeleteBlock(BlockType block)
{
for(uint32 address = block->GetBeginAddress(); address <= block->GetEndAddress(); address += INSTRUCTION_SIZE)
{
uint32 hiAddress = address >> SUBTABLE_BITS;
uint32 loAddress = address & SUBTABLE_MASK;
assert(hiAddress < m_subTableCount);
auto& subTable = m_blockTable[hiAddress];
assert(subTable);
assert(subTable[loAddress / INSTRUCTION_SIZE] != m_emptyBlock);
subTable[loAddress / INSTRUCTION_SIZE] = m_emptyBlock;
}
uint32 address = block->GetBeginAddress();
uint32 hiAddress = address >> SUBTABLE_BITS;
uint32 loAddress = address & SUBTABLE_MASK;
assert(hiAddress < m_subTableCount);
auto& subTable = m_blockTable[hiAddress];
assert(subTable);
assert(subTable[loAddress / INSTRUCTION_SIZE] != m_emptyBlock);
subTable[loAddress / INSTRUCTION_SIZE] = m_emptyBlock;
}
BlockType FindBlockAt(uint32 address) const
......@@ -227,6 +169,11 @@ template <typename BlockLookupType, TranslateFunctionType TranslateFunction = &C
class CMipsExecutor
{
public:
enum
{
MAX_BLOCK_SIZE = 0x1000,
};
CMipsExecutor(CMIPS& context, uint32 maxAddress)
: m_emptyBlock(std::make_shared<CBasicBlock>(context, MIPS_INVALID_PC, MIPS_INVALID_PC))
, m_context(context)
......@@ -273,9 +220,7 @@ public:
CBasicBlock* FindBlockStartingAt(uint32 address) const
{
auto result = m_blockLookup.FindBlockAt(address);
if(result->GetBeginAddress() != address) return m_emptyBlock.get();
return result;
return m_blockLookup.FindBlockAt(address);
}
void DeleteBlock(CBasicBlock* block)
......@@ -339,44 +284,10 @@ protected:
void CreateBlock(uint32 start, uint32 end)
{
{
auto block = m_blockLookup.FindBlockAt(start);
if(!block->IsEmpty())
{
//If the block starts and ends at the same place, block already exists and doesn't need
//to be re-created
uint32 otherBegin = block->GetBeginAddress();
uint32 otherEnd = block->GetEndAddress();
if((otherBegin == start) && (otherEnd == end))
{
return;
}
if(otherEnd == end)
{
//Repartition the existing block if end of both blocks are the same
DeleteBlock(block);
CreateBlock(otherBegin, start - 4);
assert(!HasBlockAt(start));
}
else if(otherBegin == start)
{
DeleteBlock(block);
CreateBlock(end + 4, otherEnd);
assert(!HasBlockAt(end));
}
else
{
//Delete the currently existing block otherwise
DeleteBlock(block);
}
}
}
assert(!HasBlockAt(end));
{
auto block = BlockFactory(m_context, start, end);
m_blockLookup.AddBlock(block.get());
m_blocks.push_back(std::move(block));
}
assert(!HasBlockAt(start));
auto block = BlockFactory(m_context, start, end);
m_blockLookup.AddBlock(block.get());
m_blocks.push_back(std::move(block));
}
virtual BasicBlockPtr BlockFactory(CMIPS& context, uint32 start, uint32 end)
......@@ -386,107 +297,46 @@ protected:
return result;
}
virtual void PartitionFunction(uint32 functionAddress)
virtual void PartitionFunction(uint32 startAddress)
{
typedef std::set<uint32> PartitionPointSet;
uint32 endAddress = 0;
PartitionPointSet partitionPoints;
//Insert begin point
partitionPoints.insert(functionAddress);
//Find the end
for(uint32 address = functionAddress;; address += 4)
uint32 endAddress = startAddress + MAX_BLOCK_SIZE;
for(uint32 address = startAddress; address < endAddress; address += 4)
{
//Probably going too far...
if((address - functionAddress) > 0x10000)
{
endAddress = address;
partitionPoints.insert(endAddress);
break;
}
uint32 opcode = m_context.m_pMemoryMap->GetInstruction(address);
if(opcode == 0x03E00008)
auto branchType = m_context.m_pArch->IsInstructionBranch(&m_context, address, opcode);
if(branchType == MIPS_BRANCH_NORMAL)
{
//+4 for delay slot
endAddress = address + 4;
partitionPoints.insert(endAddress + 4);
break;
}
}
//Find partition points within the function
for(uint32 address = functionAddress; address <= endAddress; address += 4)
{
uint32 opcode = m_context.m_pMemoryMap->GetInstruction(address);
MIPS_BRANCH_TYPE branchType = m_context.m_pArch->IsInstructionBranch(&m_context, address, opcode);
if(branchType == MIPS_BRANCH_NORMAL)
{
partitionPoints.insert(address + 8);
uint32 target = m_context.m_pArch->GetInstructionEffectiveAddress(&m_context, address, opcode);
if(target > functionAddress && target < endAddress)
{
partitionPoints.insert(target);
}
}
else if(branchType == MIPS_BRANCH_NODELAY)
{
partitionPoints.insert(address + 4);
}
//Check if there's a block already exising that this address
if(address != endAddress)
{
auto possibleBlock = FindBlockStartingAt(address);
if(!possibleBlock->IsEmpty())
{
//assert(possibleBlock->GetEndAddress() <= endAddress);
//Add its beginning and end in the partition points
partitionPoints.insert(possibleBlock->GetBeginAddress());
partitionPoints.insert(possibleBlock->GetEndAddress() + 4);
}
endAddress = address;
break;
}
}
assert((endAddress - startAddress) <= MAX_BLOCK_SIZE);
CreateBlock(startAddress, endAddress);
}
//Check if blocks are too big
{
uint32 currentPoint = -1;
for(PartitionPointSet::const_iterator pointIterator(partitionPoints.begin());
pointIterator != partitionPoints.end(); ++pointIterator)
{
if(currentPoint != -1)
{
uint32 startPos = currentPoint;
uint32 endPos = *pointIterator;
uint32 distance = (endPos - startPos);
if(distance > 0x400)
{
uint32 middlePos = ((endPos + startPos) / 2) & ~0x03;
pointIterator = partitionPoints.insert(middlePos).first;
--pointIterator;
continue;
}
}
currentPoint = *pointIterator;
}
}
void ClearActiveBlocksInRangeInternal(uint32 start, uint32 end, CBasicBlock* protectedBlock)
{
//Widen scan range since blocks starting before the range can end in the range
uint32 scanStart = static_cast<uint32>(std::max<int64>(0, static_cast<uint64>(start) - MAX_BLOCK_SIZE));
uint32 scanEnd = end;
assert(scanEnd > scanStart);
//Create blocks
std::set<CBasicBlock*> clearedBlocks;
for(uint32 address = scanStart; address <= scanEnd; address += 4)
{
uint32 currentPoint = -1;
for(auto partitionPoint : partitionPoints)
{
if(currentPoint != -1)
{
CreateBlock(currentPoint, partitionPoint - 4);
}
currentPoint = partitionPoint;
}
auto block = m_blockLookup.FindBlockAt(address);
if(block->IsEmpty()) continue;
if(block == protectedBlock) continue;
if(!IsInsideRange(block->GetBeginAddress(), start, end) && !IsInsideRange(block->GetEndAddress(), start, end)) continue;
clearedBlocks.insert(block);
m_blockLookup.DeleteBlock(block);
}
}
void ClearActiveBlocksInRangeInternal(uint32 start, uint32 end, CBasicBlock* protectedBlock)
{
auto clearedBlocks = m_blockLookup.ClearInRange(start, end, protectedBlock);
if(!clearedBlocks.empty())
{
m_blocks.remove_if([&](const BasicBlockPtr& block) { return clearedBlocks.find(block.get()) != std::end(clearedBlocks); });
......
......@@ -15,31 +15,7 @@ void CVuBasicBlock::CompileRange(CMipsJitter* jitter)
assert(((m_end + 4) & 0x07) == 0);
auto arch = static_cast<CMA_VU*>(m_context.m_pArch);
uint32 fixedEnd = m_end;
bool needsPcAdjust = false;
//Make sure the delay slot instruction is present in the block.
//CVuExecutor can sometimes cut the blocks in a way that removes the delay slot instruction for branches.
{
uint32 addressLo = fixedEnd - 4;
uint32 addressHi = fixedEnd - 0;
uint32 opcodeLo = m_context.m_pMemoryMap->GetInstruction(addressLo);
uint32 opcodeHi = m_context.m_pMemoryMap->GetInstruction(addressHi);
//Check for LOI
if((opcodeHi & 0x80000000) == 0)
{
auto branchType = arch->IsInstructionBranch(&m_context, addressLo, opcodeLo);
if(branchType == MIPS_BRANCH_NORMAL)
{
fixedEnd += 8;
needsPcAdjust = true;
}
}
}
auto integerBranchDelayInfo = GetIntegerBranchDelayInfo(fixedEnd);
auto integerBranchDelayInfo = GetIntegerBranchDelayInfo();
bool hasPendingXgKick = false;
const auto clearPendingXgKick =
......@@ -49,7 +25,7 @@ void CVuBasicBlock::CompileRange(CMipsJitter* jitter)
hasPendingXgKick = false;
};
for(uint32 address = m_begin; address <= fixedEnd; address += 8)
for(uint32 address = m_begin; address <= m_end; address += 8)
{
uint32 relativePipeTime = (address - m_begin) / 8;
......@@ -170,27 +146,13 @@ void CVuBasicBlock::CompileRange(CMipsJitter* jitter)
//Increment pipeTime
{
uint32 timeInc = ((fixedEnd - m_begin) / 8) + 1;
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));
}
//Adjust PC to make sure we don't execute the delay slot at the next block
if(needsPcAdjust)
{
jitter->PushCst(MIPS_INVALID_PC);
jitter->PushRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
jitter->BeginIf(Jitter::CONDITION_EQ);
{
jitter->PushCst(fixedEnd + 0x4);
jitter->PullRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
}
jitter->EndIf();
}
CompileProlog(jitter);
}
......@@ -201,7 +163,7 @@ bool CVuBasicBlock::IsConditionalBranch(uint32 opcodeLo)
return (id >= 0x28) && (id < 0x30);
}
CVuBasicBlock::INTEGER_BRANCH_DELAY_INFO CVuBasicBlock::GetIntegerBranchDelayInfo(uint32 fixedEnd) const
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.
......@@ -210,7 +172,7 @@ CVuBasicBlock::INTEGER_BRANCH_DELAY_INFO CVuBasicBlock::GetIntegerBranchDelayInf
INTEGER_BRANCH_DELAY_INFO result;
auto arch = static_cast<CMA_VU*>(m_context.m_pArch);
uint32 adjustedEnd = fixedEnd - 4;
uint32 adjustedEnd = m_end - 4;
// Check if we have a conditional branch instruction.
uint32 branchOpcodeAddr = adjustedEnd - 8;
......@@ -232,7 +194,7 @@ CVuBasicBlock::INTEGER_BRANCH_DELAY_INFO CVuBasicBlock::GetIntegerBranchDelayInf
{
//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(fixedEnd, priorLoOps.writeI);
bool isSpecialLoop = CheckIsSpecialIntegerLoop(priorLoOps.writeI);
if(!isSpecialLoop)
{
// we need to use the value of intReg 4 steps prior or use initial value.
......@@ -247,7 +209,7 @@ CVuBasicBlock::INTEGER_BRANCH_DELAY_INFO CVuBasicBlock::GetIntegerBranchDelayInf
return result;
}
bool CVuBasicBlock::CheckIsSpecialIntegerLoop(uint32 fixedEnd, unsigned int regI) const
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
......@@ -255,7 +217,7 @@ bool CVuBasicBlock::CheckIsSpecialIntegerLoop(uint32 fixedEnd, unsigned int regI
//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 = (fixedEnd - m_begin) / 8;
uint32 length = (m_end - m_begin) / 8;
if(length != 4) return false;
for(uint32 index = 0; index <= length; index++)
{
......
......@@ -21,7 +21,7 @@ private:
static bool IsConditionalBranch(uint32);
INTEGER_BRANCH_DELAY_INFO GetIntegerBranchDelayInfo(uint32) const;
bool CheckIsSpecialIntegerLoop(uint32, unsigned int) const;
INTEGER_BRANCH_DELAY_INFO GetIntegerBranchDelayInfo() const;
bool CheckIsSpecialIntegerLoop(unsigned int) const;
static void EmitXgKick(CMipsJitter*);
};
......@@ -55,91 +55,36 @@ BasicBlockPtr CVuExecutor::BlockFactory(CMIPS& context, uint32 begin, uint32 end
return result;
}
void CVuExecutor::PartitionFunction(uint32 functionAddress)
{
typedef std::set<uint32> PartitionPointSet;
uint32 endAddress = 0;
PartitionPointSet partitionPoints;
//Insert begin point
partitionPoints.insert(functionAddress);
#define VU_UPPEROP_BIT_I (0x80000000)
#define VU_UPPEROP_BIT_E (0x40000000)
//Find the end
for(uint32 address = functionAddress;; address += 4)
void CVuExecutor::PartitionFunction(uint32 startAddress)
{
uint32 endAddress = startAddress + MAX_BLOCK_SIZE;
for(uint32 address = startAddress; address < endAddress; address += 8)
{
//Probably going too far...
if(address >= m_maxAddress)
uint32 addrLo = address + 0;
uint32 addrHi = address + 4;
uint32 lowerOp = m_context.m_pMemoryMap->GetInstruction(address + 0);
uint32 upperOp = m_context.m_pMemoryMap->GetInstruction(address + 4);
auto branchType = m_context.m_pArch->IsInstructionBranch(&m_context, addrLo, lowerOp);
if(upperOp & VU_UPPEROP_BIT_E)
{
endAddress = address;
partitionPoints.insert(endAddress);
endAddress = address + 0xC;
assert(branchType == MIPS_BRANCH_NONE);
break;
}
uint32 opcode = m_context.m_pMemoryMap->GetInstruction(address);
//If we find the E bit in an upper instruction
if((address & 0x04) && (opcode & 0x40000000))
else if(branchType == MIPS_BRANCH_NORMAL)
{
endAddress = address + 8;
partitionPoints.insert(endAddress + 4);
endAddress = address + 0xC;
break;
}
}
//Find partition points within the function
for(uint32 address = functionAddress; address <= endAddress; address += 4)
{
uint32 opcode = m_context.m_pMemoryMap->GetInstruction(address);
MIPS_BRANCH_TYPE branchType = m_context.m_pArch->IsInstructionBranch(&m_context, address, opcode);
if(branchType == MIPS_BRANCH_NORMAL)
else if(branchType == MIPS_BRANCH_NODELAY)
{
assert((address & 0x07) == 0x00);
partitionPoints.insert(address + 0x10);
uint32 target = m_context.m_pArch->GetInstructionEffectiveAddress(&m_context, address, opcode);
if(target > functionAddress && target < endAddress)
{
assert((target & 0x07) == 0x00);
partitionPoints.insert(target);
}
//Should never happen
assert(false);
}
//Check if there's a block already exising that this address
if(address != endAddress)
{
auto possibleBlock = FindBlockStartingAt(address);
if(!possibleBlock->IsEmpty())
{
assert(possibleBlock->GetEndAddress() <= endAddress);
//Add its beginning and end in the partition points
partitionPoints.insert(possibleBlock->GetBeginAddress());
partitionPoints.insert(possibleBlock->GetEndAddress() + 4);
}
}
}
uint32 currentPoint = MIPS_INVALID_PC;
for(const auto& point : partitionPoints)
{
if(currentPoint != MIPS_INVALID_PC)
{
uint32 beginAddress = currentPoint;
uint32 endAddress = point - 4;
//Sanity checks
assert((beginAddress & 0x07) == 0x00);
assert((endAddress & 0x07) == 0x04);
CreateBlock(beginAddress, endAddress);
}
currentPoint = point;
}
/*
//Convenient cutting for debugging purposes
for(uint32 address = functionAddress; address <= endAddress; address += 8)
{
uint32 beginAddress = address;
uint32 endAddress = address + 4;
//Sanity checks
assert((beginAddress & 0x07) == 0x00);
assert((endAddress & 0x07) == 0x04);