Different widening strategies

This commit is contained in:
caheckman 2019-06-06 12:38:01 -04:00
parent 8565f43e06
commit 32793712fe
3 changed files with 173 additions and 64 deletions

View file

@ -2483,6 +2483,16 @@ void IfcAnalyzeRange::execute(istream &s)
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
bool useFullWidener;
string token;
s >> ws >> token;
if (token == "full")
useFullWidener = true;
else if (token == "partial") {
useFullWidener = false;
}
else
throw IfaceParseError("Must specify \"full\" or \"partial\" widening");
Varnode *vn = iface_read_varnode(dcp,s);
vector<Varnode *> sinks;
vector<PcodeOp *> reads;
@ -2495,7 +2505,14 @@ void IfcAnalyzeRange::execute(istream &s)
Varnode *stackReg = dcp->fd->findSpacebaseInput(dcp->conf->getStackSpace());
ValueSetSolver vsSolver;
vsSolver.establishValueSets(sinks, reads, stackReg, false);
vsSolver.solve(10000);
if (useFullWidener) {
WidenerFull widener;
vsSolver.solve(10000,widener);
}
else {
WidenerNone widener;
vsSolver.solve(10000,widener);
}
list<ValueSet>::const_iterator iter;
for(iter=vsSolver.beginValueSets();iter!=vsSolver.endValueSets();++iter) {
(*iter).printRaw(*status->optr);

View file

@ -1265,7 +1265,7 @@ bool CircleRange::pushForwardBinary(OpCode opc,const CircleRange &in1,const Circ
step = 1; // Lose any step
if (in1.left < in1.right) {
left = in1.left >> sa;
right = in1.right >> sa;
right = ((in1.right - in1.step) >> sa) + 1;
}
else {
left = 0;
@ -1483,56 +1483,6 @@ void ValueSet::addEquation(int4 slot,int4 type,const CircleRange &constraint)
equations.insert(iter,Equation(slot,type,constraint));
}
/// For an iteration that isn't stabilizing attempt to widen \b this value set
/// so that it stabilizes more rapidly on future iterations. For initial iterations,
/// targeted widening is attempted using landmarks, otherwise \b this is widened
/// to \e everything.
/// \param newRange is the changed range for the current iteration
void ValueSet::doWidening(const CircleRange &newRange)
{
if (count < 2) {
range = newRange;
return;
}
else if (count == 2) {
if (!equations.empty()) { // Look for landmark
const Equation &landmark(equations.back());
if (landmark.slot == numParams && typeCode == landmark.typeCode) {
bool leftIsStable = range.getMin() == newRange.getMin();
range = newRange; // Preserve any new step information
if (landmark.range.contains(range)) {
range.widen(landmark.range,leftIsStable);
return;
}
else {
CircleRange constraint = landmark.range;
constraint.invert();
if (constraint.contains(range)) {
range.widen(constraint,leftIsStable);
return;
}
}
}
}
}
else if (count <5) {
range = newRange;
return;
}
setFull(); // In all other cases expand to everything
}
/// Give \b this value set a chance to reset its counter do to looping
void ValueSet::looped(void)
{
if (count >= 2)
count = 2; // Reset to point just after any widening
else
count = 0; // Delay widening, if we haven't performed it yet
}
/// Examine the input value sets that determine \b this set and decide if it
/// is relative. In general, \b this will be relative if any of its inputs are.
/// Certain combinations are indeterminate, which this method flags by calling
@ -1578,11 +1528,11 @@ void ValueSet::computeTypeCode(void)
/// operator defining the Varnode attached to \b this value set and pushing them
/// forward through the operator.
/// \return \b true if there was a change to \b this value set
bool ValueSet::iterate(void)
bool ValueSet::iterate(Widener &widener)
{
if (!vn->isWritten()) return false;
if (range.isFull()) return false;
if (widener.checkFreeze(*this)) return false;
if (count == 0)
computeTypeCode();
count += 1; // Count this iteration
@ -1683,13 +1633,30 @@ bool ValueSet::iterate(void)
if (res == range)
return false;
if (partHead != (Partition *)0)
doWidening(res);
if (partHead != (Partition *)0) {
if (!widener.doWidening(*this, range, res))
setFull();
}
else
range = res;
return true;
}
/// If a landmark was associated with \b this value set, return its range,
/// otherwise return null.
/// \return the landmark range or null
const CircleRange *ValueSet::getLandMark(void) const
{
if (equations.empty())
return (const CircleRange *)0;
const Equation &landmark(equations.back());
if (landmark.slot != numParams || typeCode != landmark.typeCode)
return (const CircleRange *)0;
return &landmark.range;
}
/// \param s is the stream to print to
void ValueSet::printRaw(ostream &s) const
@ -1766,6 +1733,76 @@ void ValueSetRead::printRaw(ostream &s) const
range.printRaw(s);
}
int4 WidenerFull::determineIterationReset(const ValueSet &valueSet)
{
if (valueSet.getCount() >= widenIteration)
return widenIteration; // Reset to point just after any widening
return 0; // Delay widening, if we haven't performed it yet
}
bool WidenerFull::checkFreeze(const ValueSet &valueSet)
{
return valueSet.getRange().isFull();
}
bool WidenerFull::doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)
{
if (valueSet.getCount() < widenIteration) {
range = newRange;
return true;
}
else if (valueSet.getCount() == widenIteration) {
const CircleRange *landmark = valueSet.getLandMark();
if (landmark != (const CircleRange *)0) {
bool leftIsStable = range.getMin() == newRange.getMin();
range = newRange; // Preserve any new step information
if (landmark->contains(range)) {
range.widen(*landmark,leftIsStable);
return true;
}
else {
CircleRange constraint = *landmark;
constraint.invert();
if (constraint.contains(range)) {
range.widen(constraint,leftIsStable);
return true;
}
}
}
}
else if (valueSet.getCount() < fullIteration) {
range = newRange;
return true;
}
return false; // Indicate that constrained widening failed (set to full)
}
int4 WidenerNone::determineIterationReset(const ValueSet &valueSet)
{
if (valueSet.getCount() >= freezeIteration)
return freezeIteration; // Reset to point just after any widening
return valueSet.getCount();
}
bool WidenerNone::checkFreeze(const ValueSet &valueSet)
{
if (valueSet.getRange().isFull())
return true;
return (valueSet.getCount() >= freezeIteration);
}
bool WidenerNone::doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)
{
range = newRange;
return true;
}
/// \brief Construct an iterator over the outbound edges of the given ValueSet node
///
/// Mostly this just forwards the ValueSets attached to output Varnodes
@ -2324,7 +2361,8 @@ void ValueSetSolver::establishValueSets(const vector<Varnode *> &sinks,const vec
/// The ValueSets are recalculated in the established topological ordering, with looping
/// at various levels until a fixed point is reached.
/// \param max is the maximum number of iterations to allow before forcing termination
void ValueSetSolver::solve(int4 max)
/// \param widener is the Widening strategy to use to accelerate stabilization
void ValueSetSolver::solve(int4 max,Widener &widener)
{
maxIterations = max;
@ -2343,10 +2381,11 @@ void ValueSetSolver::solve(int4 max)
componentStack.push_back(curSet->partHead);
curComponent = curSet->partHead;
curComponent->isDirty = false;
curComponent->startNode->looped(); // Reset component counter upon entry
// Reset component counter upon entry
curComponent->startNode->count = widener.determineIterationReset(*curComponent->startNode);
}
if (curComponent != (Partition *)0) {
if (curSet->iterate())
if (curSet->iterate(widener))
curComponent->isDirty = true;
if (curComponent->stopNode != curSet) {
curSet = curSet->next;
@ -2377,7 +2416,7 @@ void ValueSetSolver::solve(int4 max)
}
}
else {
curSet->iterate();
curSet->iterate(widener);
curSet = curSet->next;
}
}

View file

@ -99,6 +99,7 @@ public:
};
class Partition; // Forward declaration
class Widener; // Forward declaration
/// \brief A range of values attached to a Varnode within a data-flow subsystem
///
@ -139,11 +140,11 @@ private:
void setVarnode(Varnode *v,int4 tCode); ///< Attach \b this to given Varnode and set initial values
void addEquation(int4 slot,int4 type,const CircleRange &constraint); ///< Insert an equation restricting \b this value set
void addLandmark(int4 type,const CircleRange &constraint) { addEquation(numParams,type,constraint); } ///< Add a widening landmark
void doWidening(const CircleRange &newRange); ///< Widen the value set so fixed point is reached sooner
void looped(void); ///< Mark that iteration has looped back to \b this
void computeTypeCode(void); ///< Figure out if \b this value set is absolute or relative
bool iterate(void); ///< Regenerate \b this value set from operator inputs
bool iterate(Widener &widener); ///< Regenerate \b this value set from operator inputs
public:
int4 getCount(void) const { return count; } ///< Get the current iteration count
const CircleRange *getLandMark(void) const; ///< Get any \e landmark range
int4 getTypeCode(void) const { return typeCode; } ///< Return '0' for normal constant, '1' for spacebase relative
Varnode *getVarnode(void) const { return vn; } ///< Get the Varnode attached to \b this ValueSet
const CircleRange &getRange(void) const { return range; } ///< Get the actual range of values
@ -185,6 +186,58 @@ public:
void printRaw(ostream &s) const; ///< Write a text description of \b to the given stream
};
class Widener {
public:
virtual ~Widener(void) {} ///< Destructor
/// \brief Upon entering a fresh partition, determine how the given ValueSet count should be reset
///
/// \param valueSet is the given value set
/// \return the value of the iteration counter to reset to
virtual int4 determineIterationReset(const ValueSet &valueSet)=0;
/// \brief Check if the given value set has been frozen for the remainder of the iteration process
///
/// \param valueSet is the given value set
/// \return \b true if the valueSet will no longer change
virtual bool checkFreeze(const ValueSet &valueSet)=0;
/// \brief For an iteration that isn't stabilizing attempt to widen the given ValueSet
///
/// Change the given range based on its previous iteration so that it stabilizes more
/// rapidly on future iterations.
/// \param valueSet is the given value set
/// \param range is the previous form of the given range (and storage for the widening result)
/// \param newRange is the current iteration of the given range
/// \return \b true if widening succeeded
virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)=0;
};
/// \brief Class for doing normal widening
///
/// Widening is attempted at a specific iteration. If a landmark is available, it is used
/// to do a controlled widening, holding the stable range boundary constant. Otherwise a
/// full range is produced. At a later iteration, a full range is produced automatically.
class WidenerFull : public Widener {
int4 widenIteration; ///< The iteration at which widening is attempted
int4 fullIteration; ///< The iteration at which a full range is produced
public:
WidenerFull(void) { widenIteration = 2; fullIteration = 5; } ///< Constructor with default iterations
WidenerFull(int4 wide,int4 full) { widenIteration = wide; fullIteration = full; } ///< Constructor specifying iterations
virtual int4 determineIterationReset(const ValueSet &valueSet);
virtual bool checkFreeze(const ValueSet &valueSet);
virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange);
};
class WidenerNone : public Widener {
int4 freezeIteration; ///< The iteration at which all change ceases
public:
WidenerNone(void) { freezeIteration = 3; }
virtual int4 determineIterationReset(const ValueSet &valueSet);
virtual bool checkFreeze(const ValueSet &valueSet);
virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange);
};
/// \brief Class the determines a ValueSet for each Varnode in a data-flow system
///
/// This class uses \e value \e set \e analysis to calculate (an overestimation of)
@ -235,7 +288,7 @@ class ValueSetSolver {
public:
void establishValueSets(const vector<Varnode *> &sinks,const vector<PcodeOp *> &reads,Varnode *stackReg,bool indirectAsCopy);
int4 getNumIterations(void) const { return numIterations; } ///< Get the current number of iterations
void solve(int4 max); ///< Iterate the ValueSet system until it stabilizes
void solve(int4 max,Widener &widener); ///< Iterate the ValueSet system until it stabilizes
list<ValueSet>::const_iterator beginValueSets(void) const { return valueNodes.begin(); } ///< Start of all ValueSets in the system
list<ValueSet>::const_iterator endValueSets(void) const { return valueNodes.end(); } ///< End of all ValueSets in the system
map<SeqNum,ValueSetRead>::const_iterator beginValueSetReads(void) const { return readNodes.begin(); } ///< Start of ValueSetReads