fxos/include/fxos/analysis.h

199 lines
5.7 KiB
C++

//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
// fxos/analysis: Static analysis for assembler programs
//
// TODO: Designed to be an abstract interpreter, still WIP
//---
#ifndef FXOS_ANALYSIS_H
#define FXOS_ANALYSIS_H
#include <fxos/util/types.h>
#include <fxos/ai/RelConst.h>
#include <fxos/function.h>
#include <memory>
#include <vector>
namespace FxOS {
struct ProgramStateDiff;
/* Full description of a program state for the analyzer, at a known PC. */
struct ProgramState
{
ProgramState()
{
setBottom();
}
RelConst getRegister(int n) const
{
return ((unsigned)n >= 16) ? RelConstDomain().bottom() : m_regs[n];
}
// TODO: More value in program state
/* Set to initial program state at entry of function. */
void setFunctionInit();
/* Set to initial non-entry-block state at entry of function (all bot). */
void setBottom();
/* Set to completely unknown. */
void setTop();
/* Apply a diff. */
void applyDiff(ProgramStateDiff const &diff);
/* Join with another program state. */
void joinWith(ProgramState const &other);
/* Lattice order. */
bool le(ProgramState const &other) const;
private:
/* Values for registers r0..r15 */
RelConst m_regs[16];
};
/* Change in program state. This describes the effect of either:
- A single instruction;
- A branch combined with its delay slot;
- A branch combined with a no-op.
The single instruction or delay slot is referred to as the "base
instruction". */
struct ProgramStateDiff
{
enum class BaseType : u8 {
/* Instruction can do anything, makes the entire state top. */
Anything,
/* Instruction has no effect on tracked state. */
NoOp,
/* Instruction updates a register. */
RegisterUpdate,
};
enum class BranchType : u8 {
/* No branch. */
None,
/* Branch to a different PC with no other effect on state. */
Branch,
/* Branch to SPC and restore SR to SSR. */
Rte,
/* Branch is a function call with standard calling convention. */
CallStandard,
};
/* Type of effect for the base instruction. */
BaseType baseType() const
{
return m_baseType;
}
/* Type of effect for the branch (None if there's no branch). */
BranchType branchType() const
{
return m_branchType;
}
/* For BaseType::RegisterUpdate, which register is targeted. */
CpuRegister registerName() const
{
assert(baseType() == BaseType::RegisterUpdate && "wrong accessor");
return m_register;
}
/* For BaseType::RegisterUpdate, new value of the register. */
RelConst registerValue() const
{
assert(baseType() == BaseType::RegisterUpdate && "wrong accessor");
return m_value;
}
/*** Pseudo-constructors ***/
/* Clear the base diff. */
void clearBase()
{
m_baseType = BaseType::NoOp;
}
/* Set the base diff to changing register r to new value v. */
void setRegisterUpdate(CpuRegister reg, RelConst v)
{
m_baseType = BaseType::RegisterUpdate;
m_register = reg;
m_value = v;
}
/* Set the base diff to changing register r to an unknown value. */
void setRegisterTouched(CpuRegister reg)
{
setRegisterUpdate(reg, RelConstDomain().top());
}
/* Set the base diff to changing no register state. */
void setNoOp()
{
m_baseType = BaseType::NoOp;
}
/* Set the dif to an unknown effect. */
void setAnything()
{
m_baseType = BaseType::Anything;
}
/* Clear the branch diff. */
void clearBranch()
{
m_branchType = BranchType::None;
}
/* Set the branch diff to a normal branch. */
void setBranch()
{
m_branchType = BranchType::Branch;
}
/* Set the branch diff to the rte instruction. */
void setRte()
{
m_branchType = BranchType::Rte;
}
/* Set the branch diff to a call with standard calling convention. */
void setCallStandard()
{
m_branchType = BranchType::CallStandard;
}
/* Merge a branch diff with a delay slot diff. The bound instance should be
the branch instruction diff and it can't have a base. The other one
should not have a branch. */
void mergeWithDelaySlot(ProgramStateDiff const &delaySlotDiff);
std::string baseStr() const;
std::string branchStr() const;
/* String representation. If optional is true, returns an empty string for
no-op diffs. Otherwise always returns an unambiguous string. */
std::string str(bool optional = false) const;
private:
BaseType m_baseType = BaseType::NoOp;
BranchType m_branchType = BranchType::None;
CpuRegister m_register;
RelConst m_value;
};
/* Function's storage of program states at every control point. */
struct StaticFunctionAnalysis
{
/* Information stored for each block */
struct Block
{
ProgramState entry;
std::vector<ProgramStateDiff> diffs;
};
std::vector<Block> blocks;
};
/* Analyze a function; returns analysis results if successful, a null pointer
on error. Does not store the results in f itself. */
std::unique_ptr<StaticFunctionAnalysis> interpretFunction(Function const &f);
} // namespace FxOS
#endif /* FXOS_ANALYSIS_H */