fxos/lib/passes/cfg.cpp

81 lines
2.0 KiB
C++

//---
// fxos.passes.cfg: Control Flow Graph construction
//---
#include <fxos/disasm-passes/cfg.h>
#include <fxos/disassembly.h>
#include <fxos/errors.h>
#include <fxos/log.h>
#include <cassert>
using namespace FxOS::Log;
namespace FxOS {
CfgPass::CfgPass(Disassembly &disasm):
DisassemblyPass(disasm)
{
}
void CfgPass::analyze(uint32_t pc, ConcreteInstruction &ci)
{
/* Compute the jump target for jump instructions. This is easy because
they are all trivially computable. (...If they are not we dub them
"terminal" to avoid the computation!) */
uint32_t jmptarget = 0xffffffff;
if(ci.inst.isjump() || ci.inst.iscondjump())
{
auto &args = ci.inst.args;
if(args.size() != 1 || args[0].kind != Argument::PcJump)
throw LangError(pc, "invalid jump instruction");
jmptarget = (pc+4) + args[0].disp;
/* Make the target of the jump a leader */
ConcreteInstruction &target = m_disasm.readins(jmptarget);
target.leader = true;
/* Check that it's not in a delay slot */
if(target.delayslot)
throw LangError(pc, "jump into a delay slot!");
}
/* If this instruction is in a delay slot, check its type. A valid
delay slot has no branching properties on its own, so nothing new to
set in the properties. */
if(ci.delayslot)
{
if(!ci.inst.isvaliddelayslot())
throw LangError(pc, "invalid delay slot");
}
/* Handle normal instructions */
else if(!ci.inst.isdelayed())
{
ci.terminal = ci.inst.isterminal();
ci.jump = ci.inst.isjump();
ci.condjump = ci.inst.iscondjump();
ci.jmptarget = jmptarget;
}
/* Create a new delay slot */
else
{
ConcreteInstruction &slot = m_disasm.readins(pc+2);
if(slot.leader)
throw LangError(pc+2, "leader in a delay slot!");
if(!slot.inst.isvaliddelayslot())
throw LangError(pc+2, "invalid delay slot");
slot.delayslot = true;
slot.terminal = ci.inst.isterminal();
slot.jump = ci.inst.isjump();
slot.condjump = ci.inst.iscondjump();
slot.jmptarget = jmptarget;
}
enqueue_unseen_successors(pc, ci);
}
} /* namespace FxOS */