ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/rangeutil.cc

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 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 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