Skip to content

Commit

Permalink
Merge pull request #2763 from april_2021_analyzer_cherry_picks
Browse files Browse the repository at this point in the history
Static analyzer cherry picks #21
  • Loading branch information
SavchenkoValeriy authored Apr 1, 2021
2 parents 80c7803 + 4b749e5 commit df76373
Show file tree
Hide file tree
Showing 17 changed files with 1,194 additions and 387 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ enum class TrackingKind {
/// gathered about the tracked expression value as possible.
Thorough,
/// Specifies that a more moderate tracking should be used for the expression
/// value. This will essentially make sure that functions relevant to the it
/// value. This will essentially make sure that functions relevant to it
/// aren't pruned, but otherwise relies on the user reading the code or
/// following the arrows.
Condition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ class BasicValueFactory {

/// Returns the type of the APSInt used to store values of the given QualType.
APSIntType getAPSIntType(QualType T) const {
// For the purposes of the analysis and constraints, we treat atomics
// as their underlying types.
if (const AtomicType *AT = T->getAs<AtomicType>()) {
T = AT->getValueType();
}

assert(T->isIntegralOrEnumerationType() || Loc::isLocType(T));
return APSIntType(Ctx.getIntWidth(T),
!T->isSignedIntegerOrEnumerationType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/Support/Allocator.h"

namespace clang {

Expand All @@ -24,21 +26,19 @@ namespace ento {
/// A Range represents the closed range [from, to]. The caller must
/// guarantee that from <= to. Note that Range is immutable, so as not
/// to subvert RangeSet's immutability.
class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> {
class Range {
public:
Range(const llvm::APSInt &from, const llvm::APSInt &to)
: std::pair<const llvm::APSInt *, const llvm::APSInt *>(&from, &to) {
assert(from <= to);
Range(const llvm::APSInt &From, const llvm::APSInt &To) : Impl(&From, &To) {
assert(From <= To);
}

Range(const llvm::APSInt &point)
: std::pair<const llvm::APSInt *, const llvm::APSInt *>(&point, &point) {}
Range(const llvm::APSInt &Point) : Range(Point, Point) {}

bool Includes(const llvm::APSInt &v) const {
return *first <= v && v <= *second;
bool Includes(const llvm::APSInt &Point) const {
return From() <= Point && Point <= To();
}
const llvm::APSInt &From() const { return *first; }
const llvm::APSInt &To() const { return *second; }
const llvm::APSInt &From() const { return *Impl.first; }
const llvm::APSInt &To() const { return *Impl.second; }
const llvm::APSInt *getConcreteValue() const {
return &From() == &To() ? &From() : nullptr;
}
Expand All @@ -47,93 +47,264 @@ class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> {
ID.AddPointer(&From());
ID.AddPointer(&To());
}
};
void dump(raw_ostream &OS) const;

class RangeTrait : public llvm::ImutContainerInfo<Range> {
public:
// When comparing if one Range is less than another, we should compare
// the actual APSInt values instead of their pointers. This keeps the order
// consistent (instead of comparing by pointer values) and can potentially
// be used to speed up some of the operations in RangeSet.
static inline bool isLess(key_type_ref lhs, key_type_ref rhs) {
return *lhs.first < *rhs.first ||
(!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second);
}
// In order to keep non-overlapping ranges sorted, we can compare only From
// points.
bool operator<(const Range &RHS) const { return From() < RHS.From(); }

bool operator==(const Range &RHS) const { return Impl == RHS.Impl; }
bool operator!=(const Range &RHS) const { return !operator==(RHS); }

private:
std::pair<const llvm::APSInt *, const llvm::APSInt *> Impl;
};

/// RangeSet contains a set of ranges. If the set is empty, then
/// there the value of a symbol is overly constrained and there are no
/// possible values for that symbol.
/// @class RangeSet is a persistent set of non-overlapping ranges.
///
/// New RangeSet objects can be ONLY produced by RangeSet::Factory object, which
/// also supports the most common operations performed on range sets.
///
/// Empty set corresponds to an overly constrained symbol meaning that there
/// are no possible values for that symbol.
class RangeSet {
typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet;
PrimRangeSet ranges; // no need to make const, since it is an
// ImmutableSet - this allows default operator=
// to work.
public:
typedef PrimRangeSet::Factory Factory;
typedef PrimRangeSet::iterator iterator;

RangeSet(PrimRangeSet RS) : ranges(RS) {}

/// Create a new set with all ranges of this set and RS.
/// Possible intersections are not checked here.
RangeSet addRange(Factory &F, const RangeSet &RS) {
PrimRangeSet Ranges(RS.ranges);
for (const auto &range : ranges)
Ranges = F.add(Ranges, range);
return RangeSet(Ranges);
}

iterator begin() const { return ranges.begin(); }
iterator end() const { return ranges.end(); }
class Factory;

bool isEmpty() const { return ranges.isEmpty(); }
private:
// We use llvm::SmallVector as the underlying container for the following
// reasons:
//
// * Range sets are usually very simple, 1 or 2 ranges.
// That's why llvm::ImmutableSet is not perfect.
//
// * Ranges in sets are NOT overlapping, so it is natural to keep them
// sorted for efficient operations and queries. For this reason,
// llvm::SmallSet doesn't fit the requirements, it is not sorted when it
// is a vector.
//
// * Range set operations usually a bit harder than add/remove a range.
// Complex operations might do many of those for just one range set.
// Formerly it used to be llvm::ImmutableSet, which is inefficient for our
// purposes as we want to make these operations BOTH immutable AND
// efficient.
//
// * Iteration over ranges is widespread and a more cache-friendly
// structure is preferred.
using ImplType = llvm::SmallVector<Range, 4>;

struct ContainerType : public ImplType, public llvm::FoldingSetNode {
void Profile(llvm::FoldingSetNodeID &ID) const {
for (const Range &It : *this) {
It.Profile(ID);
}
}
};
// This is a non-owning pointer to an actual container.
// The memory is fully managed by the factory and is alive as long as the
// factory itself is alive.
// It is a pointer as opposed to a reference, so we can easily reassign
// RangeSet objects.
using UnderlyingType = const ContainerType *;
UnderlyingType Impl;

/// Construct a new RangeSet representing '{ [from, to] }'.
RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to)
: ranges(F.add(F.getEmptySet(), Range(from, to))) {}
public:
using const_iterator = ImplType::const_iterator;

const_iterator begin() const { return Impl->begin(); }
const_iterator end() const { return Impl->end(); }
size_t size() const { return Impl->size(); }

bool isEmpty() const { return Impl->empty(); }

class Factory {
public:
Factory(BasicValueFactory &BV) : ValueFactory(BV) {}

/// Create a new set with all ranges from both LHS and RHS.
/// Possible intersections are not checked here.
///
/// Complexity: O(N + M)
/// where N = size(LHS), M = size(RHS)
RangeSet add(RangeSet LHS, RangeSet RHS);
/// Create a new set with all ranges from the original set plus the new one.
/// Possible intersections are not checked here.
///
/// Complexity: O(N)
/// where N = size(Original)
RangeSet add(RangeSet Original, Range Element);
/// Create a new set with all ranges from the original set plus the point.
/// Possible intersections are not checked here.
///
/// Complexity: O(N)
/// where N = size(Original)
RangeSet add(RangeSet Original, const llvm::APSInt &Point);

RangeSet getEmptySet() { return &EmptySet; }

/// Create a new set with just one range.
/// @{
RangeSet getRangeSet(Range Origin);
RangeSet getRangeSet(const llvm::APSInt &From, const llvm::APSInt &To) {
return getRangeSet(Range(From, To));
}
RangeSet getRangeSet(const llvm::APSInt &Origin) {
return getRangeSet(Origin, Origin);
}
/// @}

/// Intersect the given range sets.
///
/// Complexity: O(N + M)
/// where N = size(LHS), M = size(RHS)
RangeSet intersect(RangeSet LHS, RangeSet RHS);
/// Intersect the given set with the closed range [Lower, Upper].
///
/// Unlike the Range type, this range uses modular arithmetic, corresponding
/// to the common treatment of C integer overflow. Thus, if the Lower bound
/// is greater than the Upper bound, the range is taken to wrap around. This
/// is equivalent to taking the intersection with the two ranges [Min,
/// Upper] and [Lower, Max], or, alternatively, /removing/ all integers
/// between Upper and Lower.
///
/// Complexity: O(N)
/// where N = size(What)
RangeSet intersect(RangeSet What, llvm::APSInt Lower, llvm::APSInt Upper);
/// Intersect the given range with the given point.
///
/// The result can be either an empty set or a set containing the given
/// point depending on whether the point is in the range set.
///
/// Complexity: O(logN)
/// where N = size(What)
RangeSet intersect(RangeSet What, llvm::APSInt Point);

/// Delete the given point from the range set.
///
/// Complexity: O(N)
/// where N = size(From)
RangeSet deletePoint(RangeSet From, const llvm::APSInt &Point);
/// Negate the given range set.
///
/// Turn all [A, B] ranges to [-B, -A], when "-" is a C-like unary minus
/// operation under the values of the type.
///
/// We also handle MIN because applying unary minus to MIN does not change
/// it.
/// Example 1:
/// char x = -128; // -128 is a MIN value in a range of 'char'
/// char y = -x; // y: -128
///
/// Example 2:
/// unsigned char x = 0; // 0 is a MIN value in a range of 'unsigned char'
/// unsigned char y = -x; // y: 0
///
/// And it makes us to separate the range
/// like [MIN, N] to [MIN, MIN] U [-N, MAX].
/// For instance, whole range is {-128..127} and subrange is [-128,-126],
/// thus [-128,-127,-126,...] negates to [-128,...,126,127].
///
/// Negate restores disrupted ranges on bounds,
/// e.g. [MIN, B] => [MIN, MIN] U [-B, MAX] => [MIN, B].
///
/// Negate is a self-inverse function, i.e. negate(negate(R)) == R.
///
/// Complexity: O(N)
/// where N = size(What)
RangeSet negate(RangeSet What);

private:
/// Return a persistent version of the given container.
RangeSet makePersistent(ContainerType &&From);
/// Construct a new persistent version of the given container.
ContainerType *construct(ContainerType &&From);

RangeSet intersect(const ContainerType &LHS, const ContainerType &RHS);

// Many operations include producing new APSInt values and that's why
// we need this factory.
BasicValueFactory &ValueFactory;
// Allocator for all the created containers.
// Containers might own their own memory and that's why it is specific
// for the type, so it calls container destructors upon deletion.
llvm::SpecificBumpPtrAllocator<ContainerType> Arena;
// Usually we deal with the same ranges and range sets over and over.
// Here we track all created containers and try not to repeat ourselves.
llvm::FoldingSet<ContainerType> Cache;
static ContainerType EmptySet;
};

RangeSet(const RangeSet &) = default;
RangeSet &operator=(const RangeSet &) = default;
RangeSet(RangeSet &&) = default;
RangeSet &operator=(RangeSet &&) = default;
~RangeSet() = default;

/// Construct a new RangeSet representing '{ [From, To] }'.
RangeSet(Factory &F, const llvm::APSInt &From, const llvm::APSInt &To)
: RangeSet(F.getRangeSet(From, To)) {}

/// Construct a new RangeSet representing the given point as a range.
RangeSet(Factory &F, const llvm::APSInt &point) : RangeSet(F, point, point) {}
RangeSet(Factory &F, const llvm::APSInt &Point)
: RangeSet(F.getRangeSet(Point)) {}

static void Profile(llvm::FoldingSetNodeID &ID, const RangeSet &RS) {
ID.AddPointer(RS.Impl);
}

/// Profile - Generates a hash profile of this RangeSet for use
/// by FoldingSet.
void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); }
void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, *this); }

/// getConcreteValue - If a symbol is contrained to equal a specific integer
/// constant then this method returns that value. Otherwise, it returns
/// NULL.
const llvm::APSInt *getConcreteValue() const {
return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr;
return Impl->size() == 1 ? begin()->getConcreteValue() : nullptr;
}

/// Get a minimal value covered by the ranges in the set
/// Get the minimal value covered by the ranges in the set.
///
/// Complexity: O(1)
const llvm::APSInt &getMinValue() const;
/// Get a maximal value covered by the ranges in the set
/// Get the maximal value covered by the ranges in the set.
///
/// Complexity: O(1)
const llvm::APSInt &getMaxValue() const;

private:
void IntersectInRange(BasicValueFactory &BV, Factory &F,
const llvm::APSInt &Lower, const llvm::APSInt &Upper,
PrimRangeSet &newRanges, PrimRangeSet::iterator &i,
PrimRangeSet::iterator &e) const;
/// Test whether the given point is contained by any of the ranges.
///
/// Complexity: O(logN)
/// where N = size(this)
bool contains(llvm::APSInt Point) const { return containsImpl(Point); }

void dump(raw_ostream &OS) const;

bool operator==(const RangeSet &Other) const { return *Impl == *Other.Impl; }
bool operator!=(const RangeSet &Other) const { return !(*this == Other); }

private:
/* implicit */ RangeSet(ContainerType *RawContainer) : Impl(RawContainer) {}
/* implicit */ RangeSet(UnderlyingType Ptr) : Impl(Ptr) {}

/// Pin given points to the type represented by the current range set.
///
/// This makes parameter points to be in-out parameters.
/// In order to maintain consistent types across all of the ranges in the set
/// and to keep all the operations to compare ONLY points of the same type, we
/// need to pin every point before any operation.
///
/// @Returns true if the given points can be converted to the target type
/// without changing the values (i.e. trivially) and false otherwise.
/// @{
bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const;
bool pin(llvm::APSInt &Point) const;
/// @}

public:
RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower,
llvm::APSInt Upper) const;
RangeSet Intersect(BasicValueFactory &BV, Factory &F,
const RangeSet &Other) const;
RangeSet Negate(BasicValueFactory &BV, Factory &F) const;
RangeSet Delete(BasicValueFactory &BV, Factory &F,
const llvm::APSInt &Point) const;

void print(raw_ostream &os) const;

bool operator==(const RangeSet &other) const {
return ranges == other.ranges;
}
// This version of this function modifies its arguments (pins it).
bool containsImpl(llvm::APSInt &Point) const;

friend class Factory;
};

using ConstraintMap = llvm::ImmutableMap<SymbolRef, RangeSet>;
Expand Down
Loading

0 comments on commit df76373

Please sign in to comment.