fxos/lib/passes/cfg.cpp
Lephenixnoir c8b28b447f
masive improvements to memory use by compacting core objects
* Store CpuRegister on a single byte
* Store operation sizes (0, 1, 2, 4) on a single byte
* Share the (disp) and (imm) fields of instruction arguments
* Store instructions as char[12] instead of std::string (>32B)
* Store instruction args in Argument[2], not std::vector (>24B)

Size changes:
  CpuRegister:    4B ->  1B
  Argument:      24B ->  8B
  Instruction:  >64B -> 32B

This reduced the malloc size from 3.3M to 177k after a standard 40-line
disassembly (this excludes OS files mapped to memory), and improved the
loading time for the SH3 instruction table by about 30% (100 ms -> 65
ms).
2021-03-16 13:37:55 +01:00

85 lines
2.2 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, "cfg")
{
}
void CfgPass::analyze(uint32_t pc, ConcreteInstruction &ci)
{
/* Don't explore successors if the instruction cannot be decoded, not
even pc+2. This will prevent wild overshoot. */
if(!ci.inst) return;
/* 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(ci.inst->arg_count != 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 LimitError("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 LimitError("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 */