public Block EnqueueJumpTarget(Address addrSrc, Address addrDest, Procedure proc, ProcessorState state)
{
Procedure procDest;
Block block = FindExactBlock(addrDest);
if (block == null)
{
// Target wasn't a block before. Make sure it exists.
block = FindContainingBlock(addrDest);
if (block != null)
{
block = SplitBlock(block, addrDest);
}
else
{
block = AddBlock(addrDest, proc, Block.GenerateName(addrDest));
}
if (proc == block.Procedure)
{
// Easy case: split a block in our own procedure.
var wi = CreateBlockWorkItem(addrDest, proc, state);
queue.Enqueue(PriorityJumpTarget, wi);
}
else if (IsBlockLinearProcedureExit(block))
{
block = CloneBlockIntoOtherProcedure(block, proc);
}
else
{
// We just created a block in a foreign procedure.
blocks.Remove(addrDest);
block.Procedure.RemoveBlock(block);
procDest = (Procedure) ScanProcedure(addrDest, null, state);
var blockThunk = CreateCallRetThunk(addrSrc, proc, procDest);
var wi = CreatePromoteWorkItem(addrDest, block, procDest);
queue.Enqueue(PriorityBlockPromote, wi);
block = blockThunk;
}
}
else if (block.Procedure != proc)
{
// Jumped to a block with a different procedure than the
// current one. Was the jump to the entry of an existing procedure?
if (program.Procedures.TryGetValue(addrDest, out procDest))
{
if (procDest == proc)
{
proc.Signature.StackDelta = block.Procedure.Signature.StackDelta;
proc.Signature.FpuStackDelta = block.Procedure.Signature.FpuStackDelta;
var wi = CreatePromoteWorkItem(addrDest, block, procDest);
queue.Enqueue(PriorityBlockPromote, wi);
}
else
{
// We jumped to the entry of a different procedure.
block = CreateCallRetThunk(addrSrc, proc, procDest);
}
}
else
{
// Jumped into the middle of another procedure. Is it worth
// promoting the destination block to a new procedure?
if (IsBlockLinearProcedureExit(block))
{
// No, just clone the block into the new procedure.
block = CloneBlockIntoOtherProcedure(block, proc);
}
else
{
// We jumped into a pre-existing block of another
// procedure which was hairy enough that we need to
// promote the block to a new procedure.
procDest = EnsureProcedure(addrDest, null);
var blockNew = CreateCallRetThunk(addrSrc, proc, procDest);
EstablishInitialState(addrDest, program.Architecture.CreateProcessorState(), procDest);
procDest.ControlGraph.AddEdge(procDest.EntryBlock, block);
InjectProcedureEntryInstructions(addrDest, procDest);
var wi = CreatePromoteWorkItem(addrDest, block, procDest);
queue.Enqueue(PriorityBlockPromote, wi);
return blockNew;
}
}
}
return block;
}