ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc
Luke Serné 8303061629 Many typo's
These were found using the command below searching for duplicated words,
and manually going through the results to remove the false positives and
reword the true positives. Sometimes I removed the doubled word and
sometimes I replaced the duplicated word.

The grep command:
grep -nIEr '\b([a-zA-Z]+)[[:space:]*]+\1\b' ./Ghidra
2025-04-19 18:06:41 +02:00

2605 lines
76 KiB
C++

/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rangeutil.hh"
#include "block.hh"
namespace ghidra {
const char CircleRange::arrange[] = "gcgbegdagggggggeggggcgbggggggggcdfgggggggegdggggbgggfggggcgbegda";
/// All the instantiations where left == right represent the same set. We
/// normalize the representation so we can compare sets easily.
void CircleRange::normalize(void)
{
if (left == right) {
if (step != 1)
left = left % step;
else
left = 0;
right = left;
}
}
/// This method \b only works if \b step is 1
void CircleRange::complement(void)
{
if (isempty) {
left=0;
right=0;
isempty = false;
return;
}
if (left==right) {
isempty = true;
return;
}
uintb tmp = left;
left = right;
right = tmp;
}
/// If the original range contained
/// - 0 and 1 => the new range is [0,2)
/// - 0 only => the new range is [0,1)
/// - 1 only => the new range is [1,2)
/// - neither 0 or 1 => the new range is empty
///
/// \return \b true if the range contains both 0 and 1
bool CircleRange::convertToBoolean(void)
{
if (isempty) return false;
bool contains_zero = contains(0);
bool contains_one = contains(1);
mask = 0xff;
step = 1;
if (contains_zero && contains_one) {
left = 0;
right = 2;
isempty = false;
return true;
}
else if (contains_zero) {
left = 0;
right = 1;
isempty = false;
}
else if (contains_one) {
left = 1;
right = 2;
isempty = false;
}
else
isempty = true;
return false;
}
/// \brief Recalculate range based on new stride
///
/// Restrict a left/right specified range to a new stride, given the step and
/// remainder it needs to match. This assumes the specified range is not empty.
/// \param mask is the domain mask
/// \param step is the new stride
/// \param oldStep is the original step (always smaller)
/// \param rem is the given remainder to match
/// \param myleft is a reference to the left boundary of the specified range
/// \param myright is a reference to the right boundary of the specified range
/// \return \b true if result is empty
bool CircleRange::newStride(uintb mask,int4 step,int4 oldStep,uint4 rem,uintb &myleft,uintb &myright)
{
if (oldStep != 1) {
uint4 oldRem = (uint4)(myleft % oldStep);
if (oldRem != (rem % oldStep))
return true; // Step is completely off
}
bool origOrder = (myleft < myright);
uint4 leftRem = (uint4)(myleft % step);
uint4 rightRem = (uint4)(myright % step);
if (leftRem > rem)
myleft += rem + step - leftRem;
else
myleft += rem - leftRem;
if (rightRem > rem)
myright += rem + step - rightRem;
else
myright += rem - rightRem;
myleft &= mask;
myright &= mask;
bool newOrder = (myleft < myright);
if (origOrder != newOrder)
return true;
return false; // not empty
}
/// \brief Make \b this range fit in a new domain
///
/// Truncate any part of the range outside of the new domain.
/// If the original range is completely outside of the new domain,
/// return \b true (empty). Step information is preserved.
/// \param newMask is the mask for the new domain
/// \param newStep is the step associated with the range
/// \param myleft is a reference to the left edge of the range to fit
/// \param myright is a reference to the right edge of the range to fit
/// \return \b true if the truncated domain is empty
bool CircleRange::newDomain(uintb newMask,int4 newStep,uintb &myleft,uintb &myright)
{
uintb rem;
if (newStep != 1)
rem = myleft % newStep;
else
rem = 0;
if (myleft > newMask) {
if (myright > newMask) { // Both bounds out of range of newMask
if (myleft < myright) return true; // Old range is completely out of bounds of new mask
myleft = rem;
myright = rem; // Old range contained everything in newMask
return false;
}
myleft = rem; // Take everything up to left edge of new range
}
if (myright > newMask) {
myright = rem; // Take everything up to right edge of new range
}
if (myleft == myright) {
myleft = rem; // Normalize the everything
myright = rem;
}
return false; // not empty
}
/// Give specific left/right boundaries and step information.
/// The first element in the set is given left boundary. The sequence
/// then proceeds by the given \e step up to (but not including) the given
/// right boundary. Care should be taken to make sure the remainders of the
/// left and right boundaries modulo the step are equal.
/// \param lft is the left boundary of the range
/// \param rgt is the right boundary of the range
/// \param size is the domain size in bytes (1,2,4,8,..)
/// \param stp is the desired step (1,2,4,8,..)
CircleRange::CircleRange(uintb lft,uintb rgt,int4 size,int4 stp)
{
mask = calc_mask(size);
step = stp;
left = lft;
right = rgt;
isempty = false;
}
/// The range contains only a single integer, 0 or 1, depending on the boolean parameter.
/// \param val is the boolean parameter
CircleRange::CircleRange(bool val)
{
mask = 0xff;
step = 1;
left = val ? 1: 0;
right = val + 1;
isempty = false;
}
/// A size specifies the number of bytes (*8 to get number of bits) in the mask.
/// The stride is assumed to be 1.
/// \param val is the single value
/// \param size is the size of the mask in bytes
CircleRange::CircleRange(uintb val,int4 size)
{
mask = calc_mask(size);
step = 1;
left = val;
right = (left+1)&mask;
isempty = false;
}
/// \param lft is the left boundary of the range
/// \param rgt is the right boundary of the range
/// \param size is the size of the range domain in bytes
/// \param stp is the step/stride of the range
void CircleRange::setRange(uintb lft,uintb rgt,int4 size,int4 stp)
{
mask = calc_mask(size);
left = lft;
right = rgt;
step = stp;
isempty = false;
}
/// A size specifies the number of bytes (*8 to get number of bits) in the mask.
/// The stride is assumed to be 1.
/// \param val is the single value
/// \param size is the size of the mask in bytes
void CircleRange::setRange(uintb val,int4 size)
{
mask = calc_mask(size);
step = 1;
left = val;
right = (left+1)&mask;
isempty = false;
}
/// Make a range of values that holds everything.
/// \param size is the size (in bytes) of the range
void CircleRange::setFull(int4 size)
{
mask = calc_mask(size);
step = 1;
left = 0;
right = 0;
isempty = false;
}
/// \return the number of integers contained in this range
uintb CircleRange::getSize(void) const
{
if (isempty) return 0;
uintb val;
if (left < right)
val = (right-left) / step;
else {
val = (mask - (left-right) + step) / step;
if (val == 0) { // This is an overflow, when all uintb values are in the range
val = mask; // We lie by one, which shouldn't matter for our jumptable application
if (step > 1) {
val = val / step;
val += 1;
}
}
}
return val;
}
/// In this context, the information content of a value is the index (+1) of the
/// most significant non-zero bit (of the absolute value). This routine returns
/// the maximum information across all values in the range.
/// \return the maximum information
int4 CircleRange::getMaxInfo(void) const
{
uintb halfPoint = mask ^ (mask >> 1);
if (contains(halfPoint))
return 8*sizeof(uintb) - count_leading_zeros(halfPoint);
int4 sizeLeft,sizeRight;
if ((halfPoint & left) == 0)
sizeLeft = count_leading_zeros(left);
else
sizeLeft = count_leading_zeros(~left & mask);
if ((halfPoint & right) == 0)
sizeRight = count_leading_zeros(right);
else
sizeRight = count_leading_zeros(~right & mask);
int4 size1 = 8*sizeof(uintb) - (sizeRight < sizeLeft ? sizeRight : sizeLeft);
return size1;
}
/// \param op2 is the specific range to test for containment.
/// \return \b true if \b this contains the interval \b op2
bool CircleRange::contains(const CircleRange &op2) const
{
if (isempty)
return op2.isempty;
if (op2.isempty)
return true;
if (step > op2.step) {
// This must have a smaller or equal step to op2 or containment is impossible
// except in the corner case where op2 consists of a single element (its step is meaningless)
if (!op2.isSingle())
return false;
}
if (left == right) return true;
if (op2.left == op2.right) return false;
if (left % step != op2.left % step) return false; // Wrong phase
if (left == op2.left && right == op2.right) return true;
char overlapCode = encodeRangeOverlaps(left, right, op2.left, op2.right);
if (overlapCode == 'c')
return true;
if (overlapCode == 'b' && (right == op2.right))
return true;
return false;
// Missing one case where op2.step > this->step, and the boundaries don't show containment,
// but there is containment because the lower step size UP TO right still contains the edge points
}
/// Check if a specific integer is a member of \b this range.
/// \param val is the specific integer
/// \return \b true if it is contained in \b this
bool CircleRange::contains(uintb val) const
{
if (isempty) return false;
if (step != 1) {
if ((left % step)!=(val%step))
return false; // Phase is wrong
}
if (left < right) {
if (val < left) return false;
if (right <= val) return false;
}
else if (right < left) {
if (val<right) return true;
if (val>=left) return true;
return false;
}
return true;
}
/// Set \b this to the union of \b this and \b op2 as a single interval.
/// Return 0 if the result is valid.
/// Return 2 if the union is two pieces.
/// If result is not zero, \b this is not modified.
/// \param op2 is the range to union with
/// \return the result code
int4 CircleRange::circleUnion(const CircleRange &op2)
{
if (op2.isempty) return 0;
if (isempty) {
*this = op2;
return 0;
}
if (mask != op2.mask) return 2; // Cannot do proper union with different domains
uintb aRight = right;
uintb bRight = op2.right;
int4 newStep = step;
if (step < op2.step) {
if (isSingle()) {
newStep = op2.step;
aRight = (left + newStep) & mask;
}
else
return 2;
}
else if (op2.step < step) {
if (op2.isSingle()) {
newStep = step;
bRight = (op2.left + newStep) & mask;
}
else
return 2;
}
uintb rem;
if (newStep != 1) {
rem = left % newStep;
if (rem != (op2.left % newStep))
return 2;
}
else
rem = 0;
if ((left==aRight)||(op2.left==bRight)) {
left = rem;
right = rem;
step = newStep;
return 0;
}
char overlapCode = encodeRangeOverlaps(left, aRight, op2.left, bRight);
switch(overlapCode) {
case 'a': // order (l r op2.l op2.r)
case 'f': // order (op2.l op2.r l r)
if (aRight==op2.left) {
right = bRight;
step = newStep;
return 0;
}
if (left==bRight) {
left = op2.left;
right = aRight;
step = newStep;
return 0;
}
return 2; // 2 pieces;
case 'b': // order (l op2.l r op2.r)
right = bRight;
step = newStep;
return 0;
case 'c': // order (l op2.l op2.r r)
right = aRight;
step = newStep;
return 0;
case 'd': // order (op2.l l r op2.r)
left = op2.left;
right = bRight;
step = newStep;
return 0;
case 'e': // order (op2.l l op2.r r)
left = op2.left;
right = aRight;
step = newStep;
return 0;
case 'g': // either impossible or covers whole circle
left = rem;
right = rem;
step = newStep;
return 0; // entire circle is covered
}
return -1; // Never reach here
}
/// Turn \b this into a range that contains both the original range and
/// the other given range. The resulting range may contain values that were in neither
/// of the original ranges (not a strict union). But the number of added values will be
/// minimal. This method will create a range with step if the input ranges hold single values
/// and the distance between them is a power of 2 and less or equal than a given bound.
/// \param op2 is the other given range to combine with \b this
/// \param maxStep is the step bound that can be induced for a container with two singles
/// \return \b true if the container is everything (full)
bool CircleRange::minimalContainer(const CircleRange &op2,int4 maxStep)
{
if (isSingle() && op2.isSingle()) {
uintb min,max;
if (getMin() < op2.getMin()) {
min = getMin();
max = op2.getMin();
}
else {
min = op2.getMin();
max = getMin();
}
uintb diff = max - min;
if (diff > 0 && diff <= maxStep) {
if (leastsigbit_set(diff) == mostsigbit_set(diff)) {
step = (int4) diff;
left = min;
right = (max + step) & mask;
return false;
}
}
}
uintb aRight = right - step + 1; // Treat original ranges as having step=1
uintb bRight = op2.right - op2.step + 1;
step = 1;
mask |= op2.mask;
uintb vacantSize1,vacantSize2;
char overlapCode = encodeRangeOverlaps(left, aRight, op2.left, bRight);
switch(overlapCode) {
case 'a': // order (l r op2.l op2.r)
vacantSize1 = left + (mask - bRight) + 1;
vacantSize2 = op2.left - aRight;
if (vacantSize1 < vacantSize2) {
left = op2.left;
right = aRight;
}
else {
right = bRight;
}
break;
case 'f': // order (op2.l op2.r l r)
vacantSize1 = op2.left + (mask-aRight) + 1;
vacantSize2 = left - bRight;
if (vacantSize1 < vacantSize2) {
right = bRight;
}
else {
left = op2.left;
right = aRight;
}
break;
case 'b': // order (l op2.l r op2.r)
right = bRight;
break;
case 'c': // order (l op2.l op2.r r)
right = aRight;
break;
case 'd': // order (op2.l l r op2.r)
left = op2.left;
right = bRight;
break;
case 'e': // order (op2.l l op2.r r)
left = op2.left;
right = aRight;
break;
case 'g': // order (l op2.r op2.l r)
left = 0; // Entire circle is covered
right = 0;
break;
}
normalize();
return (left == right);
}
/// Convert range to its complement. The step is automatically converted to 1 first.
/// \return the original step size
int4 CircleRange::invert(void)
{
int4 res = step;
step = 1;
complement();
return res;
}
/// Set \b this to the intersection of \b this and \b op2 as a
/// single interval if possible.
/// Return 0 if the result is valid
/// Return 2 if the intersection is two pieces
/// If result is not zero, \b this is not modified
/// \param op2 is the second range
/// \return the intersection code
int4 CircleRange::intersect(const CircleRange &op2)
{
int4 retval,newStep;
uintb newMask,myleft,myright,op2left,op2right;
if (isempty) return 0; // Intersection with empty is empty
if (op2.isempty) {
isempty = true;
return 0;
}
myleft = left;
myright = right;
op2left = op2.left;
op2right = op2.right;
if (step < op2.step) {
newStep = op2.step;
uint4 rem = (uint4)(op2left % newStep);
if (newStride(mask,newStep,step,rem,myleft,myright)) { // Increase the smaller stride
isempty = true;
return 0;
}
}
else if (op2.step < step) {
newStep = step;
uint4 rem = (uint4)(myleft % newStep);
if (newStride(op2.mask,newStep,op2.step,rem,op2left,op2right)) {
isempty = true;
return 0;
}
}
else
newStep = step;
newMask = mask & op2.mask;
if (mask != newMask) {
if (newDomain(newMask,newStep,myleft,myright)) {
isempty = true;
return 0;
}
}
else if (op2.mask != newMask) {
if (newDomain(newMask,newStep,op2left,op2right)) {
isempty = true;
return 0;
}
}
if (myleft==myright) { // Intersect with this everything
left = op2left;
right = op2right;
retval = 0;
}
else if (op2left == op2right) { // Intersect with op2 everything
left = myleft;
right = myright;
retval = 0;
}
else {
char overlapCode = encodeRangeOverlaps(myleft, myright, op2left, op2right);
switch(overlapCode) {
case 'a': // order (l r op2.l op2.r)
case 'f': // order (op2.l op2.r l r)
isempty = true;
retval = 0; // empty set
break;
case 'b': // order (l op2.l r op2.r)
left = op2left;
right = myright;
if (left==right)
isempty = true;
retval = 0;
break;
case 'c': // order (l op2.l op2.r r)
left = op2left;
right = op2right;
retval = 0;
break;
case 'd': // order (op2.l l r op2.r)
left = myleft;
right = myright;
retval = 0;
break;
case 'e': // order (op2.l l op2.r r)
left = myleft;
right = op2right;
if (left==right)
isempty = true;
retval = 0;
break;
case 'g': // order (l op2.r op2.l r)
if (myleft==op2right) {
left = op2left;
right = myright;
if (left==right)
isempty = true;
retval = 0;
}
else if (op2left==myright) {
left = myleft;
right = op2right;
if (left==right)
isempty = true;
retval = 0;
}
else
retval = 2; // 2 pieces
break;
default:
retval = 2; // Will never reach here
break;
}
}
if (retval != 0) return retval;
mask = newMask;
step = newStep;
return 0;
}
/// Try to create a range given a value that is not necessarily a valid mask.
/// If the mask is valid, range is set to all possible values that whose non-zero
/// bits are contained in the mask. If the mask is invalid, \b this range is not modified.
/// \param nzmask is the putative mask
/// \param size is a maximum size (in bytes) for the mask
/// \return \b true if the mask is valid
bool CircleRange::setNZMask(uintb nzmask,int4 size)
{
int4 trans = bit_transitions(nzmask,size);
if (trans>2) return false; // Too many transitions to form a valid range
bool hasstep = ((nzmask&1)==0);
if ((!hasstep)&&(trans==2)) return false; // Two sections of non-zero bits
isempty = false;
if (trans == 0) {
mask = calc_mask(size);
if (hasstep) { // All zeros
step = 1;
left = 0;
right = 1; // Range containing only zero
}
else { // All ones
step = 1;
left = 0;
right = 0; // Everything
}
return true;
}
int4 shift = leastsigbit_set(nzmask);
step = 1;
step <<= shift;
mask = calc_mask(size);
left = 0;
right = (nzmask + step) & mask;
return true;
}
/// This method changes the step for \b this range, i.e. elements are removed.
/// The boundaries of the range do not change except for the remainder modulo the new step.
/// \param newStep is the new step amount
/// \param rem is the desired phase (remainder of the values modulo the step)
void CircleRange::setStride(int4 newStep,uintb rem)
{
bool iseverything = (!isempty) && (left==right);
if (newStep == step) return;
uintb aRight = right - step;
step = newStep;
if (step == 1) return; // No remainder to fill in
uintb curRem = left % step;
left = (left - curRem) + rem;
curRem = aRight % step;
aRight = (aRight - curRem) + rem;
right = aRight + step;
if ((!iseverything)&&(left == right))
isempty = true;
}
/// \param opc is the OpCode to pull the range back through
/// \param inSize is the storage size in bytes of the resulting input
/// \param outSize is the storage size in bytes of the range to pull-back
/// \return \b true if a valid range is formed in the pull-back
bool CircleRange::pullBackUnary(OpCode opc,int4 inSize,int4 outSize)
{
uintb val;
// If there is nothing in the output set, no input will map to it
if (isempty) return true;
switch(opc) {
case CPUI_BOOL_NEGATE:
if (convertToBoolean())
break; // Both outputs possible => both inputs possible
left = left ^ 1; // Flip the boolean range
right = left +1;
break;
case CPUI_COPY:
break; // Identity transform on range
case CPUI_INT_2COMP:
val = (~left + 1 + step) & mask;
left = (~right + 1 + step) & mask;
right = val;
break;
case CPUI_INT_NEGATE:
val = (~left + step) & mask;
left = (~right + step) & mask;
right = val;
break;
case CPUI_INT_ZEXT:
{
val = calc_mask(inSize); // (smaller) input mask
uintb rem = left % step;
CircleRange zextrange;
zextrange.left = rem;
zextrange.right = val + 1 + rem; // Biggest possible range of ZEXT
zextrange.mask = mask;
zextrange.step = step; // Keep the same stride
zextrange.isempty = false;
if (0 != intersect(zextrange))
return false;
left &= val;
right &= val;
mask &= val; // Preserve the stride
break;
}
case CPUI_INT_SEXT:
{
val = calc_mask(inSize); // (smaller) input mask
uintb rem = left & step;
CircleRange sextrange;
sextrange.left = val ^ (val >> 1); // High order bit for (small) input space
sextrange.left += rem;
sextrange.right = sign_extend(sextrange.left, inSize, outSize);
sextrange.mask = mask;
sextrange.step = step; // Keep the same stride
sextrange.isempty = false;
if (sextrange.intersect(*this) != 0)
return false;
else {
if (!sextrange.isEmpty())
return false;
else {
left &= val;
right &= val;
mask &= val; // Preserve the stride
}
}
break;
}
default:
return false;
}
return true;
}
/// \param opc is the OpCode to pull the range back through
/// \param val is the constant value of the other input parameter (if present)
/// \param slot is the slot of the input variable whose range gets produced
/// \param inSize is the storage size in bytes of the resulting input
/// \param outSize is the storage size in bytes of the range to pull-back
/// \return \b true if a valid range is formed in the pull-back
bool CircleRange::pullBackBinary(OpCode opc,uintb val,int4 slot,int4 inSize,int4 outSize)
{
bool yescomplement;
bool bothTrueFalse;
// If there is nothing in the output set, no input will map to it
if (isempty) return true;
switch(opc) {
case CPUI_INT_EQUAL:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse)
break; // All possible outs => all possible ins
yescomplement = (left == 0);
left = val;
right = (val + 1) & mask;
if (yescomplement)
complement();
break;
case CPUI_INT_NOTEQUAL:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse) break; // All possible outs => all possible ins
yescomplement = (left==0);
left = (val+1)&mask;
right = val;
if (yescomplement)
complement();
break;
case CPUI_INT_LESS:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse) break; // All possible outs => all possible ins
yescomplement = (left==0);
if (slot==0) {
if (val==0)
isempty = true; // X < 0 is always false
else {
left = 0;
right = val;
}
}
else {
if (val==mask)
isempty = true; // 0xffff < X is always false
else {
left = (val+1)&mask;
right = 0;
}
}
if (yescomplement)
complement();
break;
case CPUI_INT_LESSEQUAL:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse) break; // All possible outs => all possible ins
yescomplement = (left==0);
if (slot==0) {
left = 0;
right = (val+1)&mask;
}
else {
left = val;
right = 0;
}
if (yescomplement)
complement();
break;
case CPUI_INT_SLESS:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse) break; // All possible outs => all possible ins
yescomplement = (left==0);
if (slot==0) {
if (val == (mask>>1)+1)
isempty = true; // X < -infinity, is always false
else {
left = (mask >> 1)+1; // -infinity
right = val;
}
}
else {
if ( val == (mask>>1) )
isempty = true; // infinity < X, is always false
else {
left = (val+1)&mask;
right = (mask >> 1)+1; // -infinity
}
}
if (yescomplement)
complement();
break;
case CPUI_INT_SLESSEQUAL:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse) break; // All possible outs => all possible ins
yescomplement = (left==0);
if (slot==0) {
left = (mask >> 1)+1; // -infinity
right = (val+1)&mask;
}
else {
left = val;
right = (mask >> 1)+1; // -infinity
}
if (yescomplement)
complement();
break;
case CPUI_INT_CARRY:
bothTrueFalse = convertToBoolean();
mask = calc_mask(inSize);
if (bothTrueFalse) break; // All possible outs => all possible ins
yescomplement = (left==0);
if (val==0)
isempty = true; // Nothing carries adding zero
else {
left = ((mask-val)+1)&mask;
right = 0;
}
if (yescomplement)
complement();
break;
case CPUI_INT_ADD:
left = (left-val)&mask;
right = (right-val)&mask;
break;
case CPUI_INT_SUB:
if (slot==0) {
left = (left+val)&mask;
right = (right+val)&mask;
}
else {
left = (val-left)&mask;
right = (val-right)&mask;
}
break;
case CPUI_INT_RIGHT:
{
if (step == 1) {
uintb rightBound = (calc_mask(inSize) >> val) + 1; // The maximal right bound
if (((left >= rightBound) && (right >= rightBound) && (left >= right))
|| ((left == 0) && (right >= rightBound)) || (left == right)) {
// covers everything in range of shift
left = 0; // So domain is everything
right = 0;
}
else {
if (left > rightBound)
left = rightBound;
if (right > rightBound)
right = 0;
left = (left << val) & mask;
right = (right << val) & mask;
if (left == right)
isempty = true;
}
}
else
return false;
break;
}
case CPUI_INT_SRIGHT:
{
if (step == 1) {
uintb rightb = calc_mask(inSize);
uintb leftb = rightb >> (val + 1);
rightb = leftb ^ rightb; // Smallest negative possible
leftb += 1; // Biggest positive (+1) possible
if (((left >= leftb) && (left <= rightb) && (right >= leftb)
&& (right <= rightb) && (left >= right)) || (left == right)) {
// covers everything in range of shift
left = 0; // So domain is everything
right = 0;
}
else {
if ((left > leftb) && (left < rightb))
left = leftb;
if ((right > leftb) && (right < rightb))
right = rightb;
left = (left << val) & mask;
right = (right << val) & mask;
if (left == right)
isempty = true;
}
}
else
return false;
break;
}
default:
return false;
}
return true;
}
/// The pull-back is performed through a given p-code \b op and set \b this
/// to the resulting range (if possible).
/// If there is a single unknown input, and the set of values
/// for this input that cause the output of \b op to fall
/// into \b this form a range, then set \b this to the
/// range (the "pullBack") and return the unknown varnode.
/// Return null otherwise.
///
/// We may know something about the input varnode in the form of its NZMASK, which can further
/// restrict the range we return. If \b usenzmask is true, and NZMASK forms a range, intersect
/// \b this with the result.
///
/// If there is Symbol markup on any constant passed into the op, pass that information back.
/// \param op is the given PcodeOp
/// \param constMarkup is the reference for passing back the constant relevant to the pull-back
/// \param usenzmask specifies whether to use the NZMASK
/// \return the input Varnode or NULL
Varnode *CircleRange::pullBack(PcodeOp *op,Varnode **constMarkup,bool usenzmask)
{
Varnode *res;
if (op->numInput() == 1) {
res = op->getIn(0);
if (res->isConstant()) return (Varnode *)0;
if (!pullBackUnary(op->code(),res->getSize(),op->getOut()->getSize()))
return (Varnode *)0;
}
else if (op->numInput() == 2) {
Varnode *constvn;
uintb val;
// Find non-constant varnode input, and slot
// Make sure second input is constant
int4 slot = 0;
res = op->getIn(slot);
constvn = op->getIn(1 - slot);
if (res->isConstant()) {
slot = 1;
constvn = res;
res = op->getIn(slot);
if (res->isConstant())
return (Varnode *) 0;
}
else if (!constvn->isConstant())
return (Varnode *) 0;
val = constvn->getOffset();
OpCode opc = op->code();
if (!pullBackBinary(opc, val, slot, res->getSize(), op->getOut()->getSize())) {
if (usenzmask && opc == CPUI_SUBPIECE && val == 0) {
// If everything we are truncating is known to be zero, we may still have a range
int4 msbset = mostsigbit_set(res->getNZMask());
msbset = (msbset + 8) / 8;
if (op->getOut()->getSize() < msbset) // Some bytes we are chopping off might not be zero
return (Varnode *) 0;
else {
mask = calc_mask(res->getSize()); // Keep the range but make the mask bigger
// If the range wraps (left>right) then, increasing the mask adds all the new space into
// the range, and it would be an inaccurate pullback by itself, but with the nzmask intersection
// all the new space will get intersected away again.
}
}
else
return (Varnode *) 0;
}
if (constvn->getSymbolEntry() != (SymbolEntry *) 0)
*constMarkup = constvn;
}
else // Neither unary or binary
return (Varnode *)0;
if (usenzmask) {
CircleRange nzrange;
if (!nzrange.setNZMask(res->getNZMask(),res->getSize()))
return res;
intersect(nzrange);
// If the intersect does not succeed (i.e. produces 2 pieces) the original range is
// preserved and we still consider this pullback successful.
}
return res;
}
/// Push all values in the given range through a p-code operator.
/// If the output set of values forms a range, then set \b this to the range and return \b true.
/// \param opc is the given p-code operator
/// \param in1 is the given input range
/// \param inSize is the storage space in bytes for the input
/// \param outSize is the storage space in bytes for the output
/// \return \b true if the result is known and forms a range
bool CircleRange::pushForwardUnary(OpCode opc,const CircleRange &in1,int4 inSize,int4 outSize)
{
if (in1.isempty) {
isempty = true;
return true;
}
switch(opc) {
case CPUI_CAST:
case CPUI_COPY:
*this = in1;
break;
case CPUI_INT_ZEXT:
isempty = false;
step = in1.step;
mask = calc_mask(outSize);
if (in1.left == in1.right) {
left = in1.left % step;
right = in1.mask + 1 + left;
}
else {
left = in1.left;
right = (in1.right - in1.step) & in1.mask;
if (right < left)
return false; // Extending causes 2 pieces
right += step; // Impossible for it to wrap with bigger mask
}
break;
case CPUI_INT_SEXT:
isempty = false;
step = in1.step;
mask = calc_mask(outSize);
if (in1.left == in1.right) {
uintb rem = in1.left % step;
right = calc_mask(inSize) >> 1;
left = (calc_mask(outSize) ^ right) + rem;
right = right + 1 + rem;
}
else {
left = sign_extend(in1.left, inSize, outSize);
right = sign_extend((in1.right - in1.step)&in1.mask, inSize, outSize);
if ((intb)right < (intb)left)
return false; // Extending causes 2 pieces
right = (right + step) & mask;
}
break;
case CPUI_INT_2COMP:
isempty = false;
step = in1.step;
mask = in1.mask;
right = (~in1.left + 1 + step) & mask;
left = (~in1.right + 1 + step) & mask;
normalize();
break;
case CPUI_INT_NEGATE:
isempty = false;
step = in1.step;
mask = in1.mask;
left = (~in1.right + step) & mask;
right =(~in1.left + step) & mask;
normalize();
break;
case CPUI_BOOL_NEGATE:
case CPUI_FLOAT_NAN:
isempty = false;
mask = 0xff;
step = 1;
left = 0;
right = 2;
break;
default:
return false;
}
return true;
}
/// \brief Push \b this range forward through a binary operation
///
/// Push all values in the given ranges through a binary p-code operator.
/// If the output set of values forms a range, then set \b this to the range and return \b true.
/// \param opc is the given p-code operator
/// \param in1 is the first given input range
/// \param in2 is the second given input range
/// \param inSize is the storage space in bytes for the input
/// \param outSize is the storage space in bytes for the output
/// \param maxStep is the maximum to allow step to grow via multiplication
/// \return \b true if the result is known and forms a range
bool CircleRange::pushForwardBinary(OpCode opc,const CircleRange &in1,const CircleRange &in2,int4 inSize,int4 outSize,int4 maxStep)
{
if (in1.isempty || in2.isempty) {
isempty = true;
return true;
}
switch(opc) {
case CPUI_PTRSUB:
case CPUI_INT_ADD:
isempty = false;
mask = in1.mask | in2.mask;
if (in1.left == in1.right || in2.left == in2.right) {
step = (in1.step < in2.step) ? in1.step : in2.step; // Smaller step
left = (in1.left + in2.left) % step;
right = left;
}
else if (in2.isSingle()) {
step = in1.step;
left = (in1.left + in2.left) & mask;
right = (in1.right + in2.left) & mask;
}
else if (in1.isSingle()) {
step = in2.step;
left = (in2.left + in1.left) & mask;
right = (in2.right +in1.left) & mask;
}
else {
step = (in1.step < in2.step) ? in1.step : in2.step; // Smaller step
uintb size1 = (in1.left < in1.right) ? (in1.right-in1.left) : (in1.mask - (in1.left-in1.right) + in1.step);
left = (in1.left + in2.left) & mask;
right = (in1.right - in1.step + in2.right - in2.step + step) & mask;
uintb sizenew = (left < right) ? (right-left) : (mask - (left-right) + step);
if (sizenew < size1) {
right = left; // Over-flow, we covered everything
}
normalize();
}
break;
case CPUI_INT_MULT:
{
isempty = false;
mask = in1.mask | in2.mask;
uintb constVal;
if (in1.isSingle()) {
constVal = in1.getMin();
step = in2.step;
}
else if (in2.isSingle()) {
constVal = in2.getMin();
step = in1.step;
}
else
return false;
uint4 tmp = (uint4)constVal;
while(step < maxStep) {
if ((tmp & 1) != 0) break;
step <<= 1;
tmp >>= 1;
}
int4 wholeSize = 8*sizeof(uintb) - count_leading_zeros(mask);
if (in1.getMaxInfo() + in2.getMaxInfo() > wholeSize) {
left = (in1.left * in2.left) % step;
right = left; // Covered everything
normalize();
return true;
}
if ((constVal & (mask ^ (mask >> 1))) != 0) { // Multiplying by negative number
left = ((in1.right - in1.step) * (in2.right - in2.step)) & mask;
right = ((in1.left * in2.left) + step) & mask;
}
else {
left = (in1.left * in2.left)&mask;
right = ((in1.right - in1.step) * (in2.right - in2.step) + step) & mask;
}
break;
}
case CPUI_INT_LEFT:
{
if (!in2.isSingle()) return false;
isempty = false;
mask = in1.mask;
step = in1.step;
uint4 sa = (uint4)in2.getMin();
uint4 tmp = sa;
while(step < maxStep && tmp > 0) {
step <<= 1;
tmp -= 1;
}
left = (in1.left << sa)&mask;
right = (in1.right << sa)&mask;
int4 wholeSize = 8*sizeof(uintb) - count_leading_zeros(mask);
if (in1.getMaxInfo() + sa > wholeSize) {
right = left; // Covered everything
normalize();
return true;
}
break;
}
case CPUI_SUBPIECE:
{
if (!in2.isSingle()) return false;
isempty = false;
int4 sa = (int4)in2.left * 8;
mask = calc_mask(outSize);
step = (sa == 0) ? in1.step : 1;
uintb range = (in1.left < in1.right) ? in1.right-in1.left : in1.left - in1.right;
if (range == 0 || ((range >> sa) > mask )) {
left = right = 0; // We cover everything
}
else {
left = in1.left >> sa;
right = ((in1.right - in1.step) >> sa) + step;
left &= mask;
right &= mask;
normalize();
}
break;
}
case CPUI_INT_RIGHT:
{
if (!in2.isSingle()) return false;
isempty = false;
int4 sa = (int4)in2.left;
mask = calc_mask(outSize);
step = 1; // Lose any step
if (in1.left < in1.right) {
left = in1.left >> sa;
right = ((in1.right - in1.step) >> sa) + 1;
}
else {
left = 0;
right = in1.mask >> sa;
}
if (left == right) // Don't truncate accidentally to everything
right = (left + 1)&mask;
break;
}
case CPUI_INT_SRIGHT:
{
if (!in2.isSingle()) return false;
isempty = false;
int4 sa = (int4)in2.left;
mask = calc_mask(outSize);
step = 1; // Lose any step
int4 bitPos = 8*inSize - 1;
intb valLeft = sign_extend(in1.left,bitPos);
intb valRight = sign_extend(in1.right,bitPos);
if (valLeft >= valRight) {
valRight = (intb)(mask >> 1); // Max positive
valLeft = valRight + 1; // Min negative
valLeft = sign_extend(valLeft,bitPos);
}
left = (valLeft >> sa) & mask;
right = (((valRight - in1.step) >> sa) + 1) & mask;
if (left == right) // Don't truncate accidentally to everything
right = (left + 1)&mask;
break;
}
case CPUI_INT_EQUAL:
case CPUI_INT_NOTEQUAL:
case CPUI_INT_SLESS:
case CPUI_INT_SLESSEQUAL:
case CPUI_INT_LESS:
case CPUI_INT_LESSEQUAL:
case CPUI_INT_CARRY:
case CPUI_INT_SCARRY:
case CPUI_INT_SBORROW:
case CPUI_BOOL_XOR:
case CPUI_BOOL_AND:
case CPUI_BOOL_OR:
case CPUI_FLOAT_EQUAL:
case CPUI_FLOAT_NOTEQUAL:
case CPUI_FLOAT_LESS:
case CPUI_FLOAT_LESSEQUAL:
// Ops with boolean outcome. We don't try to eliminate outcomes here.
isempty = false;
mask = 0xff;
step = 1;
left = 0; // Both true and false are possible
right = 2;
break;
default:
return false;
}
return true;
}
/// \brief Push \b this range forward through a trinary operation
///
/// Push all values in the given ranges through a trinary p-code operator (currenly only CPUI_PTRADD).
/// If the output set of values forms a range, then set \b this to the range and return \b true.
/// \param opc is the given p-code operator
/// \param in1 is the first given input range
/// \param in2 is the second given input range
/// \param in3 is the third given input range
/// \param inSize is the storage space in bytes for the input
/// \param outSize is the storage space in bytes for the output
/// \param maxStep is the maximum to allow step to grow via multiplication
/// \return \b true if the result is known and forms a range
bool CircleRange::pushForwardTrinary(OpCode opc,const CircleRange &in1,const CircleRange &in2,const CircleRange &in3,
int4 inSize,int4 outSize,int4 maxStep)
{
if (opc != CPUI_PTRADD) return false;
CircleRange tmpRange;
if (!tmpRange.pushForwardBinary(CPUI_INT_MULT, in2, in3, inSize, inSize, maxStep))
return false;
return pushForwardBinary(CPUI_INT_ADD, in1, tmpRange, inSize, outSize, maxStep);
}
/// Widen \b this range so at least one of the boundaries matches with the given
/// range, which must contain \b this.
/// \param op2 is the given containing range
/// \param leftIsStable is \b true if we want to match right boundaries
void CircleRange::widen(const CircleRange &op2,bool leftIsStable)
{
if (leftIsStable) {
uintb lmod = left % step;
uintb mod = op2.right % step;
if (mod <= lmod)
right = op2.right + (lmod - mod);
else
right = op2.right - (mod - lmod);
right &= mask;
}
else {
left = op2.left & mask;
}
normalize();
}
/// Recover parameters for a comparison PcodeOp, that returns true for
/// input values exactly in \b this range.
/// Return:
/// - 0 on success
/// - 1 if all inputs must return true
/// - 2 if this is not possible
/// - 3 if no inputs must return true
/// \param opc will contain the OpCode for the comparison PcodeOp
/// \param c will contain the constant input to the op
/// \param cslot will indicate the slot holding the constant
/// \return the success code
int4 CircleRange::translate2Op(OpCode &opc,uintb &c,int4 &cslot) const
{
if (isempty) return 3;
if (step != 1) return 2; // Not possible with a stride
if (right==((left+1)&mask)) { // Single value
opc = CPUI_INT_EQUAL;
cslot = 0;
c = left;
return 0;
}
if (left==((right+1)&mask)) { // All but one value
opc = CPUI_INT_NOTEQUAL;
cslot = 0;
c = right;
return 0;
}
if (left==right) return 1; // All outputs are possible
if (left==0) {
opc = CPUI_INT_LESS;
cslot = 1;
c = right;
return 0;
}
if (right==0) {
opc = CPUI_INT_LESS;
cslot = 0;
c = (left-1)&mask;
return 0;
}
if (left==(mask>>1)+1) {
opc = CPUI_INT_SLESS;
cslot = 1;
c = right;
return 0;
}
if (right==(mask>>1)+1) {
opc = CPUI_INT_SLESS;
cslot = 0;
c = (left-1)&mask;
return 0;
}
return 2; // Cannot represent
}
/// \param s is the stream to write to
void CircleRange::printRaw(ostream &s) const
{
if (isempty) {
s << "(empty)";
return;
}
if (left == right) {
s << "(full";
if (step != 1)
s << ',' << dec << step;
s << ')';
}
else if (right == ((left+1)&mask)) {
s << '[' << hex << left << ']';
}
else {
s << '[' << hex << left << ',' << right;
if (step != 1)
s << ',' << dec << step;
s << ')';
}
}
const int4 ValueSet::MAX_STEP = 32;
/// The initial values in \b this are set based on the type of Varnode:
/// - Constant gets the single value
/// - Input gets all possible values
/// - Other Varnodes that are written start with an empty set
///
/// \param v is the given Varnode to attach to
/// \param tCode indicates whether to treat values as constants are relative offsets
void ValueSet::setVarnode(Varnode *v,int4 tCode)
{
typeCode = tCode;
vn = v;
vn->setValueSet(this);
if (typeCode != 0) {
opCode = CPUI_MAX;
numParams = 0;
range.setRange(0,vn->getSize()); // Treat as offset of 0 relative to special value
leftIsStable = true;
rightIsStable = true;
}
else if (vn->isWritten()) {
PcodeOp *op = vn->getDef();
opCode = op->code();
if (opCode == CPUI_INDIRECT) { // Treat CPUI_INDIRECT as CPUI_COPY
numParams = 1;
opCode = CPUI_COPY;
}
else
numParams = op->numInput();
leftIsStable = false;
rightIsStable = false;
}
else if (vn->isConstant()) {
opCode = CPUI_MAX;
numParams = 0;
range.setRange(vn->getOffset(),vn->getSize());
leftIsStable = true;
rightIsStable = true;
}
else { // Some other form of input
opCode = CPUI_MAX;
numParams = 0;
typeCode = 0;
range.setFull(vn->getSize());
leftIsStable = false;
rightIsStable = false;
}
}
/// Equations are stored as an array of (slot,range) pairs, ordered on slot.
/// \param slot is the given slot
/// \param type is the constraint characteristic
/// \param constraint is the given range
void ValueSet::addEquation(int4 slot,int4 type,const CircleRange &constraint)
{
vector<Equation>::iterator iter;
iter = equations.begin();
while(iter != equations.end()) {
if ((*iter).slot > slot)
break;
++iter;
}
equations.insert(iter,Equation(slot,type,constraint));
}
/// 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
/// returning \b true. The Varnode attached to \b this must have a defining op.
/// \return \b true if there is an indeterminate combination
bool ValueSet::computeTypeCode(void)
{
int4 relCount = 0;
int4 lastTypeCode = 0;
PcodeOp *op = vn->getDef();
for(int4 i=0;i<numParams;++i) {
ValueSet *valueSet = op->getIn(i)->getValueSet();
if (valueSet->typeCode != 0) {
relCount += 1;
lastTypeCode = valueSet->typeCode;
}
}
if (relCount == 0) {
typeCode = 0;
return false;
}
// Only certain operations can propagate a relative value set
switch(opCode) {
case CPUI_PTRSUB:
case CPUI_PTRADD:
case CPUI_INT_ADD:
case CPUI_INT_SUB:
if (relCount == 1)
typeCode = lastTypeCode;
else
return true;
break;
case CPUI_CAST:
case CPUI_COPY:
case CPUI_INDIRECT:
case CPUI_MULTIEQUAL:
typeCode = lastTypeCode;
break;
default:
return true;
}
return false;
}
/// Recalculate \b this value set by grabbing the value sets of the inputs to the
/// 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(Widener &widener)
{
if (!vn->isWritten()) return false;
if (widener.checkFreeze(*this)) return false;
if (count == 0) {
if (computeTypeCode()) {
setFull();
return true;
}
}
count += 1; // Count this iteration
CircleRange res;
PcodeOp *op = vn->getDef();
int4 eqPos = 0;
if (opCode == CPUI_MULTIEQUAL) {
int4 pieces = 0;
for(int4 i=0;i<numParams;++i) {
ValueSet *inSet = op->getIn(i)->getValueSet();
if (doesEquationApply(eqPos, i)) {
CircleRange rangeCopy(inSet->range);
if (0 !=rangeCopy.intersect(equations[eqPos].range)) {
rangeCopy = equations[eqPos].range;
}
pieces = res.circleUnion(rangeCopy);
eqPos += 1; // Equation was used
}
else {
pieces = res.circleUnion(inSet->range);
}
if (pieces == 2) {
if (res.minimalContainer(inSet->range,MAX_STEP)) // Could not get clean union, force it
break;
}
}
if (0 != res.circleUnion(range)) { // Union with the previous iteration's set
res.minimalContainer(range,MAX_STEP);
}
if (!range.isEmpty() && !res.isEmpty()) {
leftIsStable = range.getMin() == res.getMin();
rightIsStable = range.getEnd() == res.getEnd();
}
}
else if (numParams == 1) {
ValueSet *inSet1 = op->getIn(0)->getValueSet();
if (doesEquationApply(eqPos, 0)) {
CircleRange rangeCopy(inSet1->range);
if (0 != rangeCopy.intersect(equations[eqPos].range)) {
rangeCopy = equations[eqPos].range;
}
if (!res.pushForwardUnary(opCode, rangeCopy, inSet1->vn->getSize(), vn->getSize())) {
setFull();
return true;
}
eqPos += 1;
}
else if (!res.pushForwardUnary(opCode, inSet1->range, inSet1->vn->getSize(), vn->getSize())) {
setFull();
return true;
}
leftIsStable = inSet1->leftIsStable;
rightIsStable = inSet1->rightIsStable;
}
else if (numParams == 2) {
ValueSet *inSet1 = op->getIn(0)->getValueSet();
ValueSet *inSet2 = op->getIn(1)->getValueSet();
if (equations.size() == 0) {
if (!res.pushForwardBinary(opCode, inSet1->range, inSet2->range, inSet1->vn->getSize(), vn->getSize(), MAX_STEP)) {
setFull();
return true;
}
}
else {
CircleRange range1 = inSet1->range;
CircleRange range2 = inSet2->range;
if (doesEquationApply(eqPos, 0)) {
if (0 != range1.intersect(equations[eqPos].range))
range1 = equations[eqPos].range;
eqPos += 1;
}
if (doesEquationApply(eqPos, 1)) {
if (0 != range2.intersect(equations[eqPos].range))
range2 = equations[eqPos].range;
}
if (!res.pushForwardBinary(opCode, range1, range2, inSet1->vn->getSize(), vn->getSize(), MAX_STEP)) {
setFull();
return true;
}
}
leftIsStable = inSet1->leftIsStable && inSet2->leftIsStable;
rightIsStable = inSet1->rightIsStable && inSet2->rightIsStable;
}
else if (numParams == 3) {
ValueSet *inSet1 = op->getIn(0)->getValueSet();
ValueSet *inSet2 = op->getIn(1)->getValueSet();
ValueSet *inSet3 = op->getIn(2)->getValueSet();
CircleRange range1 = inSet1->range;
CircleRange range2 = inSet2->range;
if (doesEquationApply(eqPos, 0)) {
if (0 != range1.intersect(equations[eqPos].range))
range1 = equations[eqPos].range;
eqPos += 1;
}
if (doesEquationApply(eqPos, 1)) {
if (0 != range2.intersect(equations[eqPos].range))
range2 = equations[eqPos].range;
}
if (!res.pushForwardTrinary(opCode, range1, range2, inSet3->range, inSet1->vn->getSize(), vn->getSize(), MAX_STEP)) {
setFull();
return true;
}
leftIsStable = inSet1->leftIsStable && inSet2->leftIsStable;
rightIsStable = inSet1->rightIsStable && inSet2->rightIsStable;
}
else
return false; // No way to change this value set
if (res == range)
return false;
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
{
// Any equation can serve as a landmark. We prefer the one restricting the
// value of an input branch, as these usually give a tighter approximation
// of the stable point.
for(int4 i=0;i<equations.size();++i) {
if (equations[i].typeCode == typeCode)
return &equations[i].range;
}
return (const CircleRange *)0;
}
/// \param s is the stream to print to
void ValueSet::printRaw(ostream &s) const
{
if (vn == (Varnode *)0)
s << "root";
else
vn->printRaw(s);
if (typeCode == 0)
s << " absolute";
else
s << " stackptr";
if (opCode == CPUI_MAX) {
if (vn->isConstant())
s << " const";
else
s << " input";
}
else
s << ' ' << get_opname(opCode);
s << ' ';
range.printRaw(s);
}
/// \param o is the PcodeOp reading the value set
/// \param slt is the input slot the values are coming in from
void ValueSetRead::setPcodeOp(PcodeOp *o,int4 slt)
{
typeCode = 0;
op = o;
slot = slt;
equationTypeCode = -1;
}
/// \param slt is the given slot
/// \param type is the constraint characteristic
/// \param constraint is the given range
void ValueSetRead::addEquation(int4 slt,int4 type,const CircleRange &constraint)
{
if (slot == slt) {
equationTypeCode = type;
equationConstraint = constraint;
}
}
/// This value set will be the same as the ValueSet of the Varnode being read but may
/// be modified due to additional control-flow constraints
void ValueSetRead::compute(void)
{
Varnode *vn = op->getIn(slot);
ValueSet *valueSet = vn->getValueSet();
typeCode = valueSet->getTypeCode();
range = valueSet->getRange();
leftIsStable = valueSet->isLeftStable();
rightIsStable = valueSet->isRightStable();
if (typeCode == equationTypeCode) {
if (0 != range.intersect(equationConstraint)) {
range = equationConstraint;
}
}
}
/// \param s is the stream to print to
void ValueSetRead::printRaw(ostream &s) const
{
s << "Read: " << get_opname(op->code());
s << '(' << op->getSeqNum() << ')';
if (typeCode == 0)
s << " absolute ";
else
s << " stackptr ";
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
/// of the descendant ops of the Varnode attached to the given node, but this
/// allows for an artificial root node so we can simulate multiple input nodes.
/// \param node is the given ValueSet node (NULL if this is the simulated root)
/// \param roots is the list of input ValueSets to use for the simulated root
ValueSetSolver::ValueSetEdge::ValueSetEdge(ValueSet *node,const vector<ValueSet *> &roots)
{
vn = node->getVarnode();
if (vn == (Varnode *)0) { // Assume this is the simulated root
rootEdges = &roots; // Set up for simulated edges
rootPos = 0;
}
else {
rootEdges = (const vector<ValueSet *> *)0;
iter = vn->beginDescend();
}
}
/// \brief Get the ValueSet pointed to by this iterator and advance the iterator
///
/// This method assumes all Varnodes with an attached ValueSet have been marked.
/// \return the next ValueSet or NULL if the end of the list is reached
ValueSet *ValueSetSolver::ValueSetEdge::getNext(void)
{
if (vn == (Varnode *)0) {
if (rootPos < rootEdges->size()) {
ValueSet *res = (*rootEdges)[rootPos];
rootPos += 1;
return res;
}
return (ValueSet *)0;
}
while(iter != vn->endDescend()) {
PcodeOp *op = *iter;
++iter;
Varnode *outVn = op->getOut();
if (outVn != (Varnode *)0 && outVn->isMark()) {
return outVn->getValueSet();
}
}
return (ValueSet *)0;
}
/// The new ValueSet is attached to the given Varnode
/// \param vn is the given Varnode
/// \param tCode is the type to associate with the Varnode
void ValueSetSolver::newValueSet(Varnode *vn,int4 tCode)
{
valueNodes.emplace_back();
valueNodes.back().setVarnode(vn, tCode);
}
/// This method saves a Partition to permanent storage. It marks the
/// starting node of the partition and sets up for the iterating algorithm.
/// \param part is the partition to store
void ValueSetSolver::partitionSurround(Partition &part)
{
recordStorage.push_back(part);
part.startNode->partHead = &recordStorage.back();
}
/// Knowing that the given Varnode is the head of a partition, generate
/// the partition recursively and generate the formal Partition object.
/// \param vertex is the given ValueSet (attached to the head Varnode)
/// \param part will hold the constructed Partition
void ValueSetSolver::component(ValueSet *vertex,Partition &part)
{
ValueSetEdge edgeIterator(vertex,rootNodes);
ValueSet *succ = edgeIterator.getNext();
while(succ != (ValueSet *)0) {
if (succ->count == 0)
visit(succ,part);
succ = edgeIterator.getNext();
}
partitionPrepend(vertex, part);
partitionSurround(part);
}
/// \param vertex is the current Varnode being walked
/// \param part is the current Partition being constructed
/// \return the index of calculated head ValueSet for the current Parition
int4 ValueSetSolver::visit(ValueSet *vertex,Partition &part)
{
nodeStack.push_back(vertex);
depthFirstIndex += 1;
vertex->count = depthFirstIndex;
int4 head = depthFirstIndex;
bool loop = false;
ValueSetEdge edgeIterator(vertex,rootNodes);
ValueSet *succ = edgeIterator.getNext();
while(succ != (ValueSet *)0) {
int4 min;
if (succ->count == 0)
min = visit(succ,part);
else
min = succ->count;
if (min <= head) {
head = min;
loop = true;
}
succ = edgeIterator.getNext();
}
if (head == vertex->count) {
vertex->count = 0x7fffffff; // Set to "infinity"
ValueSet *element = nodeStack.back();
nodeStack.pop_back();
if (loop) {
while(element != vertex) {
element->count = 0;
element = nodeStack.back();
nodeStack.pop_back();
}
Partition compPart; // empty partition
component(vertex,compPart);
partitionPrepend(compPart, part);
}
else {
partitionPrepend(vertex, part);
}
}
return head;
}
/// \brief Establish the recursive node ordering for iteratively solving the value set system.
///
/// This algorithm is based on "Efficient chaotic iteration strategies with widenings" by
/// Francois Bourdoncle. The Varnodes in the system are ordered and a set of nested
/// Partition components are generated. Iterating the ValueSets proceeds in this order,
/// looping through the components recursively until a fixed point is reached.
/// This implementation assumes all Varnodes in the system are distinguished by
/// Varnode::isMark() returning \b true.
void ValueSetSolver::establishTopologicalOrder(void)
{
for(list<ValueSet>::iterator iter=valueNodes.begin();iter!=valueNodes.end();++iter) {
(*iter).count = 0;
(*iter).next = (ValueSet *)0;
(*iter).partHead = (Partition *)0;
}
ValueSet rootNode;
rootNode.vn = (Varnode *)0;
depthFirstIndex = 0;
visit(&rootNode,orderPartition);
orderPartition.startNode = orderPartition.startNode->next; // Remove simulated root
}
/// \brief Generate an equation given a \b true constraint and the input/output Varnodes it affects
///
/// The equation is expressed as: only \b true values can reach the indicated input to a specific PcodeOp.
/// The equation is attached to the output of the PcodeOp.
/// \param vn is the output Varnode the equation will be attached to
/// \param op is the specific PcodeOp
/// \param slot is the input slot of the constrained input Varnode
/// \param type is the type of values
/// \param range is the range of \b true values
void ValueSetSolver::generateTrueEquation(Varnode *vn,PcodeOp *op,int4 slot,int4 type,const CircleRange &range)
{
if (vn != (Varnode *) 0)
vn->getValueSet()->addEquation(slot, type, range);
else
readNodes[op->getSeqNum()].addEquation(slot, type, range);// Special read site
}
/// \brief Generate the complementary equation given a \b true constraint and the input/output Varnodes it affects
///
/// The equation is expressed as: only \b false values can reach the indicated input to a specific PcodeOp.
/// The equation is attached to the output of the PcodeOp.
/// \param vn is the output Varnode the equation will be attached to
/// \param op is the specific PcodeOp
/// \param slot is the input slot of the constrained input Varnode
/// \param type is the type of values
/// \param range is the range of \b true values, which must be complemented
void ValueSetSolver::generateFalseEquation(Varnode *vn,PcodeOp *op,int4 slot,int4 type,const CircleRange &range)
{
CircleRange falseRange(range);
falseRange.invert();
if (vn != (Varnode *) 0)
vn->getValueSet()->addEquation(slot, type, falseRange);
else
readNodes[op->getSeqNum()].addEquation(slot, type, falseRange);// Special read site
}
/// \brief Look for PcodeOps where the given constraint range applies and instantiate an equation
///
/// If a read of the given Varnode is in a basic block dominated by the condition producing the
/// constraint, then either the constraint or its complement applies to the PcodeOp reading
/// the Varnode. An equation holding the constraint is added to the ValueSet of the Varnode
/// output of the PcodeOp.
/// \param vn is the given Varnode
/// \param type is the constraint characteristic
/// \param range is the known constraint (assuming the \b true branch was taken)
/// \param cbranch is conditional branch creating the constraint
void ValueSetSolver::applyConstraints(Varnode *vn,int4 type,const CircleRange &range,PcodeOp *cbranch)
{
FlowBlock *splitPoint = cbranch->getParent();
FlowBlock *trueBlock,*falseBlock;
if (cbranch->isBooleanFlip()) {
trueBlock = splitPoint->getFalseOut();
falseBlock = splitPoint->getTrueOut();
}
else {
trueBlock = splitPoint->getTrueOut();
falseBlock = splitPoint->getFalseOut();
}
// Check if the only path to trueBlock or falseBlock is via a splitPoint out-edge induced by the condition
bool trueIsRestricted = trueBlock->restrictedByConditional(splitPoint);
bool falseIsRestricted = falseBlock->restrictedByConditional(splitPoint);
list<PcodeOp *>::const_iterator iter;
if (vn->isWritten()) {
ValueSet *vSet = vn->getValueSet();
if (vSet->opCode == CPUI_MULTIEQUAL) {
vSet->addLandmark(type,range); // Leave landmark for widening
}
}
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
Varnode *outVn = (Varnode *)0;
if (!op->isMark()) { // If this is not a special read site
outVn = op->getOut(); // Make sure there is a Varnode in the system
if (outVn == (Varnode *)0) continue;
if (!outVn->isMark()) continue;
}
FlowBlock *curBlock = op->getParent();
int4 slot = op->getSlot(vn);
if (op->code() == CPUI_MULTIEQUAL) {
if (curBlock == trueBlock) {
// If its possible that both the true and false edges can reach trueBlock
// then the only input we can restrict is a MULTIEQUAL input along the exact true edge
if (trueIsRestricted || trueBlock->getIn(slot) == splitPoint)
generateTrueEquation(outVn, op, slot, type, range);
continue;
}
else if (curBlock == falseBlock) {
// If its possible that both the true and false edges can reach falseBlock
// then the only input we can restrict is a MULTIEQUAL input along the exact false edge
if (falseIsRestricted || falseBlock->getIn(slot) == splitPoint)
generateFalseEquation(outVn, op, slot, type, range);
continue;
}
else
curBlock = curBlock->getIn(slot); // MULTIEQUAL input is really only from one in-block
}
for(;;) {
if (curBlock == trueBlock) {
if (trueIsRestricted)
generateTrueEquation(outVn, op, slot, type, range);
break;
}
else if (curBlock == falseBlock) {
if (falseIsRestricted)
generateFalseEquation(outVn, op, slot, type, range);
break;
}
else if (curBlock == splitPoint || curBlock == (FlowBlock *)0)
break;
curBlock = curBlock->getImmedDom();
}
}
}
/// \brief Generate constraints given a Varnode path
///
/// Knowing that there is a lifting path from the given starting Varnode to an ending Varnode
/// in the system, go ahead and lift the given range to a final constraint on the ending
/// Varnode. Then look for reads of the Varnode where the constraint applies.
/// \param type is the constraint characteristic
/// \param lift is the given range that will be lifted
/// \param startVn is the starting Varnode
/// \param endVn is the given ending Varnode in the system
/// \param cbranch is the PcodeOp causing the control-flow split
void ValueSetSolver::constraintsFromPath(int4 type,CircleRange &lift,Varnode *startVn,Varnode *endVn,PcodeOp *cbranch)
{
while(startVn != endVn) {
Varnode *constVn;
startVn = lift.pullBack(startVn->getDef(),&constVn,false);
if (startVn == (Varnode *)0) return; // Couldn't pull all the way back to our value set
}
for(;;) {
Varnode *constVn;
applyConstraints(endVn,type,lift,cbranch);
if (!endVn->isWritten()) break;
PcodeOp *op = endVn->getDef();
if (op->isCall() || op->isMarker()) break;
endVn = lift.pullBack(op,&constVn,false);
if (endVn == (Varnode *)0) break;
if (!endVn->isMark()) break;
}
}
/// Lift the set of values on the condition for the given CBRANCH to any
/// Varnode in the system, and label (the reads) of any such Varnode with
/// the constraint. If the values cannot be lifted or no Varnode in the system
/// is found, no constraints are generated.
/// \param cbranch is the given condition branch
void ValueSetSolver::constraintsFromCBranch(PcodeOp *cbranch)
{
Varnode *vn = cbranch->getIn(1); // Get Varnode deciding the condition
while(!vn->isMark()) {
if (!vn->isWritten()) break;
PcodeOp *op = vn->getDef();
if (op->isCall() || op->isMarker())
break;
int4 num = op->numInput();
if (num == 0 || num > 2) break;
vn = op->getIn(0);
if (num == 2) {
if (vn->isConstant())
vn = op->getIn(1);
else if (!op->getIn(1)->isConstant()) {
// If we reach here, both inputs are non-constant
generateRelativeConstraint(op, cbranch);
return;
}
// If we reach here, vn is non-constant, other input is constant
}
}
if (vn->isMark()) {
CircleRange lift(true);
Varnode *startVn = cbranch->getIn(1);
constraintsFromPath(0,lift,startVn,vn,cbranch);
}
}
/// Given a complete data-flow system of Varnodes, look for any \e constraint:
/// - For a particular Varnode
/// - A limited set of values
/// - Due to its involvement in a branch condition
/// - Which applies at a particular \e read of the Varnode
///
/// \param worklist is the set of Varnodes in the data-flow system (all marked)
/// \param reads is the additional set of PcodeOps that read a Varnode from the system
void ValueSetSolver::generateConstraints(const vector<Varnode *> &worklist,const vector<PcodeOp *> &reads)
{
vector<FlowBlock *> blockList;
// Collect all blocks that contain a system op (input) or dominate a container
for(int4 i=0;i<worklist.size();++i) {
PcodeOp *op = worklist[i]->getDef();
if (op == (PcodeOp *)0) continue;
FlowBlock *bl = op->getParent();
if (op->code() == CPUI_MULTIEQUAL) {
for(int4 j=0;j<bl->sizeIn();++j) {
FlowBlock *curBl = bl->getIn(j);
do {
if (curBl->isMark()) break;
curBl->setMark();
blockList.push_back(curBl);
curBl = curBl->getImmedDom();
} while(curBl != (FlowBlock *)0);
}
}
else {
do {
if (bl->isMark()) break;
bl->setMark();
blockList.push_back(bl);
bl = bl->getImmedDom();
} while(bl != (FlowBlock *)0);
}
}
for(int4 i=0;i<reads.size();++i) {
FlowBlock *bl = reads[i]->getParent();
do {
if (bl->isMark()) break;
bl->setMark();
blockList.push_back(bl);
bl = bl->getImmedDom();
} while(bl != (FlowBlock *)0);
}
for(int4 i=0;i<blockList.size();++i)
blockList[i]->clearMark();
vector<FlowBlock *> finalList;
// Now go through input blocks to the previously calculated blocks
for(int4 i=0;i<blockList.size();++i) {
FlowBlock *bl = blockList[i];
for(int4 j=0;j<bl->sizeIn();++j) {
BlockBasic *splitPoint = (BlockBasic *)bl->getIn(j);
if (splitPoint->isMark()) continue;
if (splitPoint->sizeOut() != 2) continue;
PcodeOp *lastOp = splitPoint->lastOp();
if (lastOp != (PcodeOp *)0 && lastOp->code() == CPUI_CBRANCH) {
splitPoint->setMark();
finalList.push_back(splitPoint);
constraintsFromCBranch(lastOp); // Try to generate constraints from this splitPoint
}
}
}
for(int4 i=0;i<finalList.size();++i)
finalList[i]->clearMark();
}
/// Verify that the given Varnode is produced by a straight line sequence of
/// COPYs, INT_ADDs with a constant, from the base register marked as \e relative
/// for our system.
/// \param vn is the given Varnode
/// \param typeCode will hold the base register code (if found)
/// \param value will hold the additive value relative to the base register (if found)
/// \return \b true if the Varnode is a \e relative constant
bool ValueSetSolver::checkRelativeConstant(Varnode *vn,int4 &typeCode,uintb &value) const
{
value = 0;
for(;;) {
if (vn->isMark()) {
ValueSet *valueSet = vn->getValueSet();
if (valueSet->typeCode != 0) {
typeCode = valueSet->typeCode;
break;
}
}
if (!vn->isWritten()) return false;
PcodeOp *op = vn->getDef();
OpCode opc = op->code();
if (opc == CPUI_COPY || opc == CPUI_INDIRECT)
vn = op->getIn(0);
else if (opc == CPUI_INT_ADD || opc == CPUI_PTRSUB) {
Varnode *constVn = op->getIn(1);
if (!constVn->isConstant())
return false;
value = (value + constVn->getOffset()) & calc_mask(constVn->getSize());
vn = op->getIn(0);
}
else
return false;
}
return true;
}
/// Given a binary PcodeOp producing a conditional branch, check if it can be interpreted
/// as a constraint relative to (the) base register specified for this system. If it can
/// be, a \e relative Equation is generated, which will apply to \e relative ValueSets.
/// \param compOp is the comparison PcodeOp
/// \param cbranch is the conditional branch
void ValueSetSolver::generateRelativeConstraint(PcodeOp *compOp,PcodeOp *cbranch)
{
OpCode opc = compOp->code();
switch(opc) {
case CPUI_INT_LESS:
opc = CPUI_INT_SLESS; // Treat unsigned pointer comparisons as signed relative to the base register
break;
case CPUI_INT_LESSEQUAL:
opc = CPUI_INT_SLESSEQUAL;
break;
case CPUI_INT_SLESS:
case CPUI_INT_SLESSEQUAL:
case CPUI_INT_EQUAL:
case CPUI_INT_NOTEQUAL:
break;
default:
return;
}
int4 typeCode;
uintb value;
Varnode *vn;
Varnode *inVn0 = compOp->getIn(0);
Varnode *inVn1 = compOp->getIn(1);
CircleRange lift(true);
if (checkRelativeConstant(inVn0, typeCode, value)) {
vn = inVn1;
if (!lift.pullBackBinary(opc, value, 1, vn->getSize(), 1))
return;
}
else if (checkRelativeConstant(inVn1,typeCode,value)) {
vn = inVn0;
if (!lift.pullBackBinary(opc, value, 0, vn->getSize(), 1))
return;
}
else
return; // Neither side looks like a relative constant
Varnode *endVn = vn;
while(!endVn->isMark()) {
if (!endVn->isWritten()) return;
PcodeOp *op = endVn->getDef();
opc = op->code();
if (opc == CPUI_COPY || opc == CPUI_PTRSUB) {
endVn = op->getIn(0);
}
else if (opc == CPUI_INT_ADD) { // Can pull-back through INT_ADD
if (!op->getIn(1)->isConstant()) // if second param is constant
return;
endVn = op->getIn(0);
}
else
return;
}
constraintsFromPath(typeCode,lift,vn,endVn,cbranch);
}
/// \brief Build value sets for a data-flow system
///
/// Given a set of sinks, find all the Varnodes that flow directly into them and set up their
/// initial ValueSet objects.
/// \param sinks is the list terminating Varnodes
/// \param reads are add-on PcodeOps where we would like to know input ValueSets at the point of read
/// \param stackReg (if non-NULL) gives the stack pointer (for keeping track of relative offsets)
/// \param indirectAsCopy is \b true if solver should treat CPUI_INDIRECT as CPUI_COPY operations
void ValueSetSolver::establishValueSets(const vector<Varnode *> &sinks,const vector<PcodeOp *> &reads,Varnode *stackReg,
bool indirectAsCopy)
{
vector<Varnode *> worklist;
int4 workPos = 0;
if (stackReg != (Varnode *)0) {
newValueSet(stackReg,1); // Establish stack pointer as special
stackReg->setMark();
worklist.push_back(stackReg);
workPos += 1;
rootNodes.push_back(stackReg->getValueSet());
}
for(int4 i=0;i<sinks.size();++i) {
Varnode *vn = sinks[i];
newValueSet(vn,0);
vn->setMark();
worklist.push_back(vn);
}
while(workPos < worklist.size()) {
Varnode *vn = worklist[workPos];
workPos += 1;
if (!vn->isWritten()) {
if (vn->isConstant()) {
// Constant inputs to binary ops should not be treated as root nodes as they
// get picked up during iteration by the other input, except in the case of a
// a PTRSUB from a spacebase constant.
if (vn->isSpacebase() || vn->loneDescend()->numInput() == 1)
rootNodes.push_back(vn->getValueSet());
}
else
rootNodes.push_back(vn->getValueSet());
continue;
}
PcodeOp *op = vn->getDef();
switch(op->code()) { // Distinguish ops where we can never predict an integer range
case CPUI_INDIRECT:
if (indirectAsCopy || op->isIndirectStore()) {
Varnode *inVn = op->getIn(0);
if (!inVn->isMark()) {
newValueSet(inVn,0);
inVn->setMark();
worklist.push_back(inVn);
}
}
else {
vn->getValueSet()->setFull();
rootNodes.push_back(vn->getValueSet());
}
break;
case CPUI_CALL:
case CPUI_CALLIND:
case CPUI_CALLOTHER:
case CPUI_LOAD:
case CPUI_NEW:
case CPUI_SEGMENTOP:
case CPUI_CPOOLREF:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_DIV:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_INT2FLOAT:
case CPUI_FLOAT_FLOAT2FLOAT:
case CPUI_FLOAT_TRUNC:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
vn->getValueSet()->setFull();
rootNodes.push_back(vn->getValueSet());
break;
default:
for(int4 i=0;i<op->numInput();++i) {
Varnode *inVn = op->getIn(i);
if (inVn->isMark() || inVn->isAnnotation()) continue;
newValueSet(inVn,0);
inVn->setMark();
worklist.push_back(inVn);
}
break;
}
}
for(int4 i=0;i<reads.size();++i) {
PcodeOp *op = reads[i];
for(int4 slot=0;slot<op->numInput();++slot) {
Varnode *vn = op->getIn(slot);
if (vn->isMark()) {
readNodes[op->getSeqNum()].setPcodeOp(op, slot);
op->setMark(); // Mark read ops for equation generation stage
break; // Only 1 read allowed
}
}
}
generateConstraints(worklist,reads);
for(int4 i=0;i<reads.size();++i)
reads[i]->clearMark(); // Clear marks on read ops
establishTopologicalOrder();
for(int4 i=0;i<worklist.size();++i)
worklist[i]->clearMark();
}
/// 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
/// \param widener is the Widening strategy to use to accelerate stabilization
void ValueSetSolver::solve(int4 max,Widener &widener)
{
maxIterations = max;
numIterations = 0;
for(list<ValueSet>::iterator iter=valueNodes.begin();iter!=valueNodes.end();++iter)
(*iter).count = 0;
vector<Partition *> componentStack;
Partition *curComponent = (Partition *)0;
ValueSet *curSet = orderPartition.startNode;
while(curSet != (ValueSet *)0) {
numIterations += 1;
if (numIterations > maxIterations) break; // Quit if max iterations exceeded
if (curSet->partHead != (Partition *)0 && curSet->partHead != curComponent) {
componentStack.push_back(curSet->partHead);
curComponent = curSet->partHead;
curComponent->isDirty = false;
// Reset component counter upon entry
curComponent->startNode->count = widener.determineIterationReset(*curComponent->startNode);
}
if (curComponent != (Partition *)0) {
if (curSet->iterate(widener))
curComponent->isDirty = true;
if (curComponent->stopNode != curSet) {
curSet = curSet->next;
}
else {
for(;;) {
if (curComponent->isDirty) {
curComponent->isDirty = false;
curSet = curComponent->startNode;
if (componentStack.size() > 1) { // Mark parent as dirty if we are restarting dirty child
componentStack[componentStack.size()-2]->isDirty = true;
}
break;
}
componentStack.pop_back();
if (componentStack.empty()) {
curComponent = (Partition *)0;
curSet = curSet->next;
break;
}
curComponent = componentStack.back();
if (curComponent->stopNode != curSet) {
curSet = curSet->next;
break;
}
}
}
}
else {
curSet->iterate(widener);
curSet = curSet->next;
}
}
map<SeqNum,ValueSetRead>::iterator riter;
for(riter=readNodes.begin();riter!=readNodes.end();++riter)
(*riter).second.compute(); // Calculate any follow-on value sets
}
#ifdef CPUI_DEBUG
void ValueSetSolver::dumpValueSets(ostream &s) const
{
list<ValueSet>::const_iterator iter;
for(iter=valueNodes.begin();iter!=valueNodes.end();++iter) {
(*iter).printRaw(s);
s << endl;
}
map<SeqNum,ValueSetRead>::const_iterator riter;
for(riter=readNodes.begin();riter!=readNodes.end();++riter) {
(*riter).second.printRaw(s);
s << endl;
}
}
#endif
} // End namespace ghidra