mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-15 20:51:35 +00:00
[analyzer][solver] Track symbol disequalities
Summary: This commmit adds another relation that we can track separately from range constraints. Symbol disequality can help us understand that two equivalence classes are not equal to each other. We can generalize this knowledge to classes because for every a,b,c, and d that a == b, c == d, and b != c it is true that a != d. As a result, we can reason about other equalities/disequalities of symbols that we know nothing else about, i.e. no constraint ranges associated with them. However, we also benefit from the knowledge of disequal symbols by following the rule: if a != b and b == C where C is a constant, a != C This information can refine associated ranges for different classes and reduce the number of false positives and paths to explore. Differential Revision: https://reviews.llvm.org/D83286
This commit is contained in:
parent
b13d9878b8
commit
e63b488f27
@ -401,6 +401,9 @@ REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass)
|
|||||||
REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
|
REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
|
||||||
REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
|
REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
|
||||||
|
|
||||||
|
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass)
|
||||||
|
REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/// This class encapsulates a set of symbols equal to each other.
|
/// This class encapsulates a set of symbols equal to each other.
|
||||||
///
|
///
|
||||||
@ -450,6 +453,26 @@ public:
|
|||||||
LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
|
LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
|
||||||
SymbolReaper &Reaper);
|
SymbolReaper &Reaper);
|
||||||
|
|
||||||
|
LLVM_NODISCARD static inline ProgramStateRef
|
||||||
|
markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
|
||||||
|
ProgramStateRef State, SymbolRef First, SymbolRef Second);
|
||||||
|
LLVM_NODISCARD static inline ProgramStateRef
|
||||||
|
markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
|
||||||
|
ProgramStateRef State, EquivalenceClass First,
|
||||||
|
EquivalenceClass Second);
|
||||||
|
LLVM_NODISCARD inline ProgramStateRef
|
||||||
|
markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
|
||||||
|
ProgramStateRef State, EquivalenceClass Other) const;
|
||||||
|
LLVM_NODISCARD static inline ClassSet
|
||||||
|
getDisequalClasses(ProgramStateRef State, SymbolRef Sym);
|
||||||
|
LLVM_NODISCARD inline ClassSet
|
||||||
|
getDisequalClasses(ProgramStateRef State) const;
|
||||||
|
LLVM_NODISCARD inline ClassSet
|
||||||
|
getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const;
|
||||||
|
|
||||||
|
LLVM_NODISCARD static inline Optional<bool>
|
||||||
|
areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
|
||||||
|
|
||||||
/// Check equivalence data for consistency.
|
/// Check equivalence data for consistency.
|
||||||
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
|
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
|
||||||
isClassDataConsistent(ProgramStateRef State);
|
isClassDataConsistent(ProgramStateRef State);
|
||||||
@ -496,6 +519,11 @@ private:
|
|||||||
ProgramStateRef State, SymbolSet Members,
|
ProgramStateRef State, SymbolSet Members,
|
||||||
EquivalenceClass Other,
|
EquivalenceClass Other,
|
||||||
SymbolSet OtherMembers);
|
SymbolSet OtherMembers);
|
||||||
|
static inline void
|
||||||
|
addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
|
||||||
|
BasicValueFactory &BV, RangeSet::Factory &F,
|
||||||
|
ProgramStateRef State, EquivalenceClass First,
|
||||||
|
EquivalenceClass Second);
|
||||||
|
|
||||||
/// This is a unique identifier of the class.
|
/// This is a unique identifier of the class.
|
||||||
uintptr_t ID;
|
uintptr_t ID;
|
||||||
@ -505,17 +533,6 @@ private:
|
|||||||
// Constraint functions
|
// Constraint functions
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State,
|
|
||||||
EquivalenceClass Class,
|
|
||||||
RangeSet Constraint) {
|
|
||||||
return State->set<ConstraintRange>(Class, Constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
LLVM_NODISCARD inline ProgramStateRef
|
|
||||||
setConstraint(ProgramStateRef State, SymbolRef Sym, RangeSet Constraint) {
|
|
||||||
return setConstraint(State, EquivalenceClass::find(State, Sym), Constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
|
LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
|
||||||
EquivalenceClass Class) {
|
EquivalenceClass Class) {
|
||||||
return State->get<ConstraintRange>(Class);
|
return State->get<ConstraintRange>(Class);
|
||||||
@ -692,10 +709,11 @@ LLVM_NODISCARD inline
|
|||||||
class SymbolicRangeInferrer
|
class SymbolicRangeInferrer
|
||||||
: public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
|
: public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
|
||||||
public:
|
public:
|
||||||
|
template <class SourceType>
|
||||||
static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
|
static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
|
||||||
ProgramStateRef State, SymbolRef Sym) {
|
ProgramStateRef State, SourceType Origin) {
|
||||||
SymbolicRangeInferrer Inferrer(BV, F, State);
|
SymbolicRangeInferrer Inferrer(BV, F, State);
|
||||||
return Inferrer.infer(Sym);
|
return Inferrer.infer(Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeSet VisitSymExpr(SymbolRef Sym) {
|
RangeSet VisitSymExpr(SymbolRef Sym) {
|
||||||
@ -760,12 +778,20 @@ private:
|
|||||||
// If Sym is a comparison expression (except <=>),
|
// If Sym is a comparison expression (except <=>),
|
||||||
// find any other comparisons with the same operands.
|
// find any other comparisons with the same operands.
|
||||||
// See function description.
|
// See function description.
|
||||||
if (Optional<RangeSet> CmpRangeSet = getRangeForComparisonSymbol(Sym))
|
if (Optional<RangeSet> CmpRangeSet = getRangeForComparisonSymbol(Sym)) {
|
||||||
return *CmpRangeSet;
|
return *CmpRangeSet;
|
||||||
|
}
|
||||||
|
|
||||||
return Visit(Sym);
|
return Visit(Sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangeSet infer(EquivalenceClass Class) {
|
||||||
|
if (const RangeSet *AssociatedConstraint = getConstraint(State, Class))
|
||||||
|
return *AssociatedConstraint;
|
||||||
|
|
||||||
|
return infer(Class.getType());
|
||||||
|
}
|
||||||
|
|
||||||
/// Infer range information solely from the type.
|
/// Infer range information solely from the type.
|
||||||
RangeSet infer(QualType T) {
|
RangeSet infer(QualType T) {
|
||||||
// Lazily generate a new RangeSet representing all possible values for the
|
// Lazily generate a new RangeSet representing all possible values for the
|
||||||
@ -1034,19 +1060,15 @@ private:
|
|||||||
if (!Equality)
|
if (!Equality)
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
|
|
||||||
EquivalenceClass LHS = EquivalenceClass::find(State, Equality->Left);
|
if (Optional<bool> AreEqual = EquivalenceClass::areEqual(
|
||||||
EquivalenceClass RHS = EquivalenceClass::find(State, Equality->Right);
|
State, Equality->Left, Equality->Right)) {
|
||||||
|
if (*AreEqual == Equality->IsEquality) {
|
||||||
if (LHS != RHS)
|
return getTrueRange(Sym->getType());
|
||||||
// Can't really say anything at this point.
|
}
|
||||||
// We can add more logic here if we track disequalities as well.
|
return getFalseRange(Sym->getType());
|
||||||
return llvm::None;
|
|
||||||
|
|
||||||
// At this point, operands of the equality operation are known to be equal.
|
|
||||||
if (Equality->IsEquality) {
|
|
||||||
return getTrueRange(Sym->getType());
|
|
||||||
}
|
}
|
||||||
return getFalseRange(Sym->getType());
|
|
||||||
|
return llvm::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeSet getTrueRange(QualType T) {
|
RangeSet getTrueRange(QualType T) {
|
||||||
@ -1303,6 +1325,7 @@ private:
|
|||||||
RangeSet::Factory F;
|
RangeSet::Factory F;
|
||||||
|
|
||||||
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
|
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
|
||||||
|
RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
|
||||||
|
|
||||||
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
|
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
@ -1356,12 +1379,42 @@ private:
|
|||||||
|
|
||||||
ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS,
|
ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS,
|
||||||
SymbolRef RHS) {
|
SymbolRef RHS) {
|
||||||
// TODO: track inequalities
|
return EquivalenceClass::markDisequal(getBasicVals(), F, State, LHS, RHS);
|
||||||
return State;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS,
|
ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS,
|
||||||
SymbolRef RHS);
|
SymbolRef RHS) {
|
||||||
|
return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State,
|
||||||
|
EquivalenceClass Class,
|
||||||
|
RangeSet Constraint) {
|
||||||
|
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
|
||||||
|
ConstraintRangeTy::Factory &CF = State->get_context<ConstraintRange>();
|
||||||
|
|
||||||
|
// Add new constraint.
|
||||||
|
Constraints = CF.add(Constraints, Class, Constraint);
|
||||||
|
|
||||||
|
// There is a chance that we might need to update constraints for the
|
||||||
|
// classes that are known to be disequal to Class.
|
||||||
|
//
|
||||||
|
// In order for this to be even possible, the new constraint should
|
||||||
|
// be simply a constant because we can't reason about range disequalities.
|
||||||
|
if (const llvm::APSInt *Point = Constraint.getConcreteValue())
|
||||||
|
for (EquivalenceClass DisequalClass : Class.getDisequalClasses(State)) {
|
||||||
|
RangeSet UpdatedConstraint =
|
||||||
|
getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point);
|
||||||
|
Constraints = CF.add(Constraints, DisequalClass, UpdatedConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return State->set<ConstraintRange>(Constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVM_NODISCARD inline ProgramStateRef
|
||||||
|
setConstraint(ProgramStateRef State, SymbolRef Sym, RangeSet Constraint) {
|
||||||
|
return setConstraint(State, EquivalenceClass::find(State, Sym), Constraint);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
@ -1496,11 +1549,15 @@ EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory,
|
|||||||
|
|
||||||
// 2. Get ALL equivalence-related maps
|
// 2. Get ALL equivalence-related maps
|
||||||
ClassMapTy Classes = State->get<ClassMap>();
|
ClassMapTy Classes = State->get<ClassMap>();
|
||||||
ClassMapTy::Factory &CF = State->get_context<ClassMap>();
|
ClassMapTy::Factory &CMF = State->get_context<ClassMap>();
|
||||||
|
|
||||||
ClassMembersTy Members = State->get<ClassMembers>();
|
ClassMembersTy Members = State->get<ClassMembers>();
|
||||||
ClassMembersTy::Factory &MF = State->get_context<ClassMembers>();
|
ClassMembersTy::Factory &MF = State->get_context<ClassMembers>();
|
||||||
|
|
||||||
|
DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
|
||||||
|
DisequalityMapTy::Factory &DF = State->get_context<DisequalityMap>();
|
||||||
|
|
||||||
|
ClassSet::Factory &CF = State->get_context<ClassSet>();
|
||||||
SymbolSet::Factory &F = getMembersFactory(State);
|
SymbolSet::Factory &F = getMembersFactory(State);
|
||||||
|
|
||||||
// 2. Merge members of the Other class into the current class.
|
// 2. Merge members of the Other class into the current class.
|
||||||
@ -1508,7 +1565,7 @@ EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory,
|
|||||||
for (SymbolRef Sym : OtherMembers) {
|
for (SymbolRef Sym : OtherMembers) {
|
||||||
NewClassMembers = F.add(NewClassMembers, Sym);
|
NewClassMembers = F.add(NewClassMembers, Sym);
|
||||||
// *this is now the class for all these new symbols.
|
// *this is now the class for all these new symbols.
|
||||||
Classes = CF.add(Classes, Sym, *this);
|
Classes = CMF.add(Classes, Sym, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Adjust member mapping.
|
// 3. Adjust member mapping.
|
||||||
@ -1518,7 +1575,34 @@ EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory,
|
|||||||
// Now only the current class is mapped to all the symbols.
|
// Now only the current class is mapped to all the symbols.
|
||||||
Members = MF.add(Members, *this, NewClassMembers);
|
Members = MF.add(Members, *this, NewClassMembers);
|
||||||
|
|
||||||
// 4. Update the state
|
// 4. Update disequality relations
|
||||||
|
ClassSet DisequalToOther = Other.getDisequalClasses(DisequalityInfo, CF);
|
||||||
|
if (!DisequalToOther.isEmpty()) {
|
||||||
|
ClassSet DisequalToThis = getDisequalClasses(DisequalityInfo, CF);
|
||||||
|
DisequalityInfo = DF.remove(DisequalityInfo, Other);
|
||||||
|
|
||||||
|
for (EquivalenceClass DisequalClass : DisequalToOther) {
|
||||||
|
DisequalToThis = CF.add(DisequalToThis, DisequalClass);
|
||||||
|
|
||||||
|
// Disequality is a symmetric relation meaning that if
|
||||||
|
// DisequalToOther not null then the set for DisequalClass is not
|
||||||
|
// empty and has at least Other.
|
||||||
|
ClassSet OriginalSetLinkedToOther =
|
||||||
|
*DisequalityInfo.lookup(DisequalClass);
|
||||||
|
|
||||||
|
// Other will be eliminated and we should replace it with the bigger
|
||||||
|
// united class.
|
||||||
|
ClassSet NewSet = CF.remove(OriginalSetLinkedToOther, Other);
|
||||||
|
NewSet = CF.add(NewSet, *this);
|
||||||
|
|
||||||
|
DisequalityInfo = DF.add(DisequalityInfo, DisequalClass, NewSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
DisequalityInfo = DF.add(DisequalityInfo, *this, DisequalToThis);
|
||||||
|
State = State->set<DisequalityMap>(DisequalityInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Update the state
|
||||||
State = State->set<ClassMap>(Classes);
|
State = State->set<ClassMap>(Classes);
|
||||||
State = State->set<ClassMembers>(Members);
|
State = State->set<ClassMembers>(Members);
|
||||||
|
|
||||||
@ -1549,6 +1633,121 @@ bool EquivalenceClass::isTriviallyDead(ProgramStateRef State,
|
|||||||
return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol());
|
return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF,
|
||||||
|
RangeSet::Factory &RF,
|
||||||
|
ProgramStateRef State,
|
||||||
|
SymbolRef First,
|
||||||
|
SymbolRef Second) {
|
||||||
|
return markDisequal(VF, RF, State, find(State, First), find(State, Second));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF,
|
||||||
|
RangeSet::Factory &RF,
|
||||||
|
ProgramStateRef State,
|
||||||
|
EquivalenceClass First,
|
||||||
|
EquivalenceClass Second) {
|
||||||
|
return First.markDisequal(VF, RF, State, Second);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ProgramStateRef
|
||||||
|
EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF,
|
||||||
|
ProgramStateRef State,
|
||||||
|
EquivalenceClass Other) const {
|
||||||
|
// If we know that two classes are equal, we can only produce an infeasible
|
||||||
|
// state.
|
||||||
|
if (*this == Other) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
|
||||||
|
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
|
||||||
|
|
||||||
|
// Disequality is a symmetric relation, so if we mark A as disequal to B,
|
||||||
|
// we should also mark B as disequalt to A.
|
||||||
|
addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this,
|
||||||
|
Other);
|
||||||
|
addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other,
|
||||||
|
*this);
|
||||||
|
|
||||||
|
State = State->set<DisequalityMap>(DisequalityInfo);
|
||||||
|
State = State->set<ConstraintRange>(Constraints);
|
||||||
|
|
||||||
|
return State;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void EquivalenceClass::addToDisequalityInfo(
|
||||||
|
DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
|
||||||
|
BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State,
|
||||||
|
EquivalenceClass First, EquivalenceClass Second) {
|
||||||
|
|
||||||
|
// 1. Get all of the required factories.
|
||||||
|
DisequalityMapTy::Factory &F = State->get_context<DisequalityMap>();
|
||||||
|
ClassSet::Factory &CF = State->get_context<ClassSet>();
|
||||||
|
ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
|
||||||
|
|
||||||
|
// 2. Add Second to the set of classes disequal to First.
|
||||||
|
const ClassSet *CurrentSet = Info.lookup(First);
|
||||||
|
ClassSet NewSet = CurrentSet ? *CurrentSet : CF.getEmptySet();
|
||||||
|
NewSet = CF.add(NewSet, Second);
|
||||||
|
|
||||||
|
Info = F.add(Info, First, NewSet);
|
||||||
|
|
||||||
|
// 3. If Second is known to be a constant, we can delete this point
|
||||||
|
// from the constraint asociated with First.
|
||||||
|
//
|
||||||
|
// So, if Second == 10, it means that First != 10.
|
||||||
|
// At the same time, the same logic does not apply to ranges.
|
||||||
|
if (const RangeSet *SecondConstraint = Constraints.lookup(Second))
|
||||||
|
if (const llvm::APSInt *Point = SecondConstraint->getConcreteValue()) {
|
||||||
|
|
||||||
|
RangeSet FirstConstraint = SymbolicRangeInferrer::inferRange(
|
||||||
|
VF, RF, State, First.getRepresentativeSymbol());
|
||||||
|
|
||||||
|
FirstConstraint = FirstConstraint.Delete(VF, RF, *Point);
|
||||||
|
Constraints = CRF.add(Constraints, First, FirstConstraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
|
||||||
|
SymbolRef FirstSym,
|
||||||
|
SymbolRef SecondSym) {
|
||||||
|
EquivalenceClass First = find(State, FirstSym);
|
||||||
|
EquivalenceClass Second = find(State, SecondSym);
|
||||||
|
|
||||||
|
// The same equivalence class => symbols are equal.
|
||||||
|
if (First == Second)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Let's check if we know anything about these two classes being not equal to
|
||||||
|
// each other.
|
||||||
|
ClassSet DisequalToFirst = First.getDisequalClasses(State);
|
||||||
|
if (DisequalToFirst.contains(Second))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// It is not clear.
|
||||||
|
return llvm::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ClassSet EquivalenceClass::getDisequalClasses(ProgramStateRef State,
|
||||||
|
SymbolRef Sym) {
|
||||||
|
return find(State, Sym).getDisequalClasses(State);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ClassSet
|
||||||
|
EquivalenceClass::getDisequalClasses(ProgramStateRef State) const {
|
||||||
|
return getDisequalClasses(State->get<DisequalityMap>(),
|
||||||
|
State->get_context<ClassSet>());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ClassSet
|
||||||
|
EquivalenceClass::getDisequalClasses(DisequalityMapTy Map,
|
||||||
|
ClassSet::Factory &Factory) const {
|
||||||
|
if (const ClassSet *DisequalClasses = Map.lookup(*this))
|
||||||
|
return *DisequalClasses;
|
||||||
|
|
||||||
|
return Factory.getEmptySet();
|
||||||
|
}
|
||||||
|
|
||||||
bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
|
bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
|
||||||
ClassMembersTy Members = State->get<ClassMembers>();
|
ClassMembersTy Members = State->get<ClassMembers>();
|
||||||
|
|
||||||
@ -1563,6 +1762,28 @@ bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisequalityMapTy Disequalities = State->get<DisequalityMap>();
|
||||||
|
for (std::pair<EquivalenceClass, ClassSet> DisequalityInfo : Disequalities) {
|
||||||
|
EquivalenceClass Class = DisequalityInfo.first;
|
||||||
|
ClassSet DisequalClasses = DisequalityInfo.second;
|
||||||
|
|
||||||
|
// There is no use in keeping empty sets in the map.
|
||||||
|
if (DisequalClasses.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Disequality is symmetrical, i.e. for every Class A and B that A != B,
|
||||||
|
// B != A should also be true.
|
||||||
|
for (EquivalenceClass DisequalClass : DisequalClasses) {
|
||||||
|
const ClassSet *DisequalToDisequalClasses =
|
||||||
|
Disequalities.lookup(DisequalClass);
|
||||||
|
|
||||||
|
// It should be a set of at least one element: Class
|
||||||
|
if (!DisequalToDisequalClasses ||
|
||||||
|
!DisequalToDisequalClasses->contains(Class))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1669,9 +1890,48 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
|
|||||||
ClassMapTy NewMap = Map;
|
ClassMapTy NewMap = Map;
|
||||||
ClassMapTy::Factory &ClassFactory = State->get_context<ClassMap>();
|
ClassMapTy::Factory &ClassFactory = State->get_context<ClassMap>();
|
||||||
|
|
||||||
|
DisequalityMapTy Disequalities = State->get<DisequalityMap>();
|
||||||
|
DisequalityMapTy::Factory &DisequalityFactory =
|
||||||
|
State->get_context<DisequalityMap>();
|
||||||
|
ClassSet::Factory &ClassSetFactory = State->get_context<ClassSet>();
|
||||||
|
|
||||||
bool ClassMapChanged = false;
|
bool ClassMapChanged = false;
|
||||||
bool MembersMapChanged = false;
|
bool MembersMapChanged = false;
|
||||||
bool ConstraintMapChanged = false;
|
bool ConstraintMapChanged = false;
|
||||||
|
bool DisequalitiesChanged = false;
|
||||||
|
|
||||||
|
auto removeDeadClass = [&](EquivalenceClass Class) {
|
||||||
|
// Remove associated constraint ranges.
|
||||||
|
Constraints = ConstraintFactory.remove(Constraints, Class);
|
||||||
|
ConstraintMapChanged = true;
|
||||||
|
|
||||||
|
// Update disequality information to not hold any information on the
|
||||||
|
// removed class.
|
||||||
|
ClassSet DisequalClasses =
|
||||||
|
Class.getDisequalClasses(Disequalities, ClassSetFactory);
|
||||||
|
if (!DisequalClasses.isEmpty()) {
|
||||||
|
for (EquivalenceClass DisequalClass : DisequalClasses) {
|
||||||
|
ClassSet DisequalToDisequalSet =
|
||||||
|
DisequalClass.getDisequalClasses(Disequalities, ClassSetFactory);
|
||||||
|
// DisequalToDisequalSet is guaranteed to be non-empty for consistent
|
||||||
|
// disequality info.
|
||||||
|
assert(!DisequalToDisequalSet.isEmpty());
|
||||||
|
ClassSet NewSet = ClassSetFactory.remove(DisequalToDisequalSet, Class);
|
||||||
|
|
||||||
|
// No need in keeping an empty set.
|
||||||
|
if (NewSet.isEmpty()) {
|
||||||
|
Disequalities =
|
||||||
|
DisequalityFactory.remove(Disequalities, DisequalClass);
|
||||||
|
} else {
|
||||||
|
Disequalities =
|
||||||
|
DisequalityFactory.add(Disequalities, DisequalClass, NewSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove the data for the class
|
||||||
|
Disequalities = DisequalityFactory.remove(Disequalities, Class);
|
||||||
|
DisequalitiesChanged = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 1. Let's see if dead symbols are trivial and have associated constraints.
|
// 1. Let's see if dead symbols are trivial and have associated constraints.
|
||||||
for (std::pair<EquivalenceClass, RangeSet> ClassConstraintPair :
|
for (std::pair<EquivalenceClass, RangeSet> ClassConstraintPair :
|
||||||
@ -1679,8 +1939,7 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
|
|||||||
EquivalenceClass Class = ClassConstraintPair.first;
|
EquivalenceClass Class = ClassConstraintPair.first;
|
||||||
if (Class.isTriviallyDead(State, SymReaper)) {
|
if (Class.isTriviallyDead(State, SymReaper)) {
|
||||||
// If this class is trivial, we can remove its constraints right away.
|
// If this class is trivial, we can remove its constraints right away.
|
||||||
Constraints = ConstraintFactory.remove(Constraints, Class);
|
removeDeadClass(Class);
|
||||||
ConstraintMapChanged = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1698,6 +1957,7 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
|
|||||||
// and their constraints.
|
// and their constraints.
|
||||||
for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair :
|
for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair :
|
||||||
ClassMembersMap) {
|
ClassMembersMap) {
|
||||||
|
EquivalenceClass Class = ClassMembersPair.first;
|
||||||
SymbolSet LiveMembers = ClassMembersPair.second;
|
SymbolSet LiveMembers = ClassMembersPair.second;
|
||||||
bool MembersChanged = false;
|
bool MembersChanged = false;
|
||||||
|
|
||||||
@ -1716,17 +1976,14 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
|
|||||||
|
|
||||||
if (LiveMembers.isEmpty()) {
|
if (LiveMembers.isEmpty()) {
|
||||||
// The class is dead now, we need to wipe it out of the members map...
|
// The class is dead now, we need to wipe it out of the members map...
|
||||||
NewClassMembersMap =
|
NewClassMembersMap = EMFactory.remove(NewClassMembersMap, Class);
|
||||||
EMFactory.remove(NewClassMembersMap, ClassMembersPair.first);
|
|
||||||
|
|
||||||
// ...and remove all of its constraints.
|
// ...and remove all of its constraints.
|
||||||
Constraints =
|
removeDeadClass(Class);
|
||||||
ConstraintFactory.remove(Constraints, ClassMembersPair.first);
|
|
||||||
ConstraintMapChanged = true;
|
|
||||||
} else {
|
} else {
|
||||||
// We need to change the members associated with the class.
|
// We need to change the members associated with the class.
|
||||||
NewClassMembersMap = EMFactory.add(NewClassMembersMap,
|
NewClassMembersMap =
|
||||||
ClassMembersPair.first, LiveMembers);
|
EMFactory.add(NewClassMembersMap, Class, LiveMembers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1742,6 +1999,9 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
|
|||||||
if (ConstraintMapChanged)
|
if (ConstraintMapChanged)
|
||||||
State = State->set<ConstraintRange>(Constraints);
|
State = State->set<ConstraintRange>(Constraints);
|
||||||
|
|
||||||
|
if (DisequalitiesChanged)
|
||||||
|
State = State->set<DisequalityMap>(Disequalities);
|
||||||
|
|
||||||
assert(EquivalenceClass::isClassDataConsistent(State));
|
assert(EquivalenceClass::isClassDataConsistent(State));
|
||||||
|
|
||||||
return State;
|
return State;
|
||||||
@ -1752,6 +2012,11 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
|
|||||||
return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym);
|
return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
|
||||||
|
EquivalenceClass Class) {
|
||||||
|
return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Class);
|
||||||
|
}
|
||||||
|
|
||||||
//===------------------------------------------------------------------------===
|
//===------------------------------------------------------------------------===
|
||||||
// assumeSymX methods: protected interface for RangeConstraintManager.
|
// assumeSymX methods: protected interface for RangeConstraintManager.
|
||||||
//===------------------------------------------------------------------------===/
|
//===------------------------------------------------------------------------===/
|
||||||
@ -1976,12 +2241,6 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
|
|||||||
return New.isEmpty() ? nullptr : setConstraint(State, Sym, New);
|
return New.isEmpty() ? nullptr : setConstraint(State, Sym, New);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef RangeConstraintManager::trackEquality(ProgramStateRef State,
|
|
||||||
SymbolRef LHS,
|
|
||||||
SymbolRef RHS) {
|
|
||||||
return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS);
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Pretty-printing.
|
// Pretty-printing.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -23,9 +23,8 @@ void zeroImpliesEquality(int a, int b) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clang_analyzer_eval((a - b) == 0); // expected-warning{{FALSE}}
|
clang_analyzer_eval((a - b) == 0); // expected-warning{{FALSE}}
|
||||||
// FIXME: we should track disequality information as well
|
clang_analyzer_eval(b == a); // expected-warning{{FALSE}}
|
||||||
clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
|
clang_analyzer_eval(b != a); // expected-warning{{TRUE}}
|
||||||
clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void zeroImpliesReversedEqual(int a, int b) {
|
void zeroImpliesReversedEqual(int a, int b) {
|
||||||
@ -36,9 +35,8 @@ void zeroImpliesReversedEqual(int a, int b) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clang_analyzer_eval((b - a) == 0); // expected-warning{{FALSE}}
|
clang_analyzer_eval((b - a) == 0); // expected-warning{{FALSE}}
|
||||||
// FIXME: we should track disequality information as well
|
clang_analyzer_eval(b == a); // expected-warning{{FALSE}}
|
||||||
clang_analyzer_eval(b == a); // expected-warning{{UNKNOWN}}
|
clang_analyzer_eval(b != a); // expected-warning{{TRUE}}
|
||||||
clang_analyzer_eval(b != a); // expected-warning{{UNKNOWN}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void canonicalEqual(int a, int b) {
|
void canonicalEqual(int a, int b) {
|
||||||
@ -73,8 +71,7 @@ void test(int a, int b, int c, int d) {
|
|||||||
if (a != b && b == c) {
|
if (a != b && b == c) {
|
||||||
if (c == 42) {
|
if (c == 42) {
|
||||||
clang_analyzer_eval(b == 42); // expected-warning{{TRUE}}
|
clang_analyzer_eval(b == 42); // expected-warning{{TRUE}}
|
||||||
// FIXME: we should track disequality information as well
|
clang_analyzer_eval(a != 42); // expected-warning{{TRUE}}
|
||||||
clang_analyzer_eval(a != 42); // expected-warning{{UNKNOWN}}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,8 +141,31 @@ void testPointers(int *a, int *b, int *c, int *d) {
|
|||||||
|
|
||||||
if (a != b && b == c) {
|
if (a != b && b == c) {
|
||||||
if (c == NULL) {
|
if (c == NULL) {
|
||||||
// FIXME: we should track disequality information as well
|
clang_analyzer_eval(a != NULL); // expected-warning{{TRUE}}
|
||||||
clang_analyzer_eval(a != NULL); // expected-warning{{UNKNOWN}}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDisequalitiesAfter(int a, int b, int c) {
|
||||||
|
if (a >= 10 && b <= 42) {
|
||||||
|
if (a == b && c == 15 && c != a) {
|
||||||
|
clang_analyzer_eval(b != c); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(a != 15); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(b != 15); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(b >= 10); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(a <= 42); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDisequalitiesBefore(int a, int b, int c) {
|
||||||
|
if (a >= 10 && b <= 42 && c == 15) {
|
||||||
|
if (a == b && c != a) {
|
||||||
|
clang_analyzer_eval(b != c); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(a != 15); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(b != 15); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(b >= 10); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(a <= 42); // expected-warning{{TRUE}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
clang/test/Analysis/mutually_exclusive_null_fp.cpp
Normal file
26
clang/test/Analysis/mutually_exclusive_null_fp.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
|
||||||
|
|
||||||
|
// rdar://problem/56586853
|
||||||
|
// expected-no-diagnostics
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
int x;
|
||||||
|
Data *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
int compare(Data &a, Data &b) {
|
||||||
|
Data *aData = a.data;
|
||||||
|
Data *bData = b.data;
|
||||||
|
|
||||||
|
// Covers the cases where both pointers are null as well as both pointing to the same buffer.
|
||||||
|
if (aData == bData)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (aData && !bData)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!aData && bData)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return compare(*aData, *bData); // no-warning
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user