clang 24.0.0git
SMTConstraintManager.h
Go to the documentation of this file.
1//== SMTConstraintManager.h -------------------------------------*- C++ -*--==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines a SMT generic API, which will be the base class for
10// every SMT solver specific class.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
15#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
16
22#include "llvm/ADT/BitVector.h"
23#include "llvm/ADT/DenseMap.h"
24#include "llvm/ADT/DenseSet.h"
25#include "llvm/ADT/STLExtras.h"
26#include <optional>
27
28typedef llvm::ImmutableSet<
29 std::pair<clang::ento::SymbolRef, const llvm::SMTExpr *>>
32
33namespace clang {
34namespace ento {
35
37 using ConstraintEntry = std::pair<SymbolRef, const llvm::SMTExpr *>;
38 mutable llvm::SMTSolverRef Solver = llvm::CreateZ3Solver();
39
40public:
43 : SimpleConstraintManager(EE, SB) {
44 Solver->setBoolParam("model", true); // Enable model finding
45 Solver->setUnsignedParam("timeout", 15000 /*milliseconds*/);
46 }
47 virtual ~SMTConstraintManager() = default;
48
49 //===------------------------------------------------------------------===//
50 // Implementation for interface from SimpleConstraintManager.
51 //===------------------------------------------------------------------===//
52
54 bool Assumption) override {
56
57 QualType RetTy;
58 bool hasComparison;
59
60 std::optional<llvm::SMTExprRef> Exp =
61 SMTConv::getExpr(Solver, Ctx, Sym, RetTy, &hasComparison);
62 if (!Exp)
63 return assumeSymUnsupported(State, Sym, Assumption);
64 // Create zero comparison for implicit boolean cast, with reversed
65 // assumption
66 if (!hasComparison && !RetTy->isBooleanType())
67 return assumeExpr(
68 State, Sym,
69 SMTConv::getZeroExpr(Solver, Ctx, Exp.value(), RetTy, !Assumption));
70
71 return assumeExpr(State, Sym,
72 Assumption ? Exp.value() : Solver->mkNot(Exp.value()));
73 }
74
76 const llvm::APSInt &From,
77 const llvm::APSInt &To,
78 bool InRange) override {
80 std::optional<llvm::SMTExprRef> Expr =
81 SMTConv::getRangeExpr(Solver, Ctx, Sym, From, To, InRange);
82 if (!Expr)
83 return assumeSymUnsupported(State, Sym, false);
84 return assumeExpr(State, Sym, Expr.value());
85 }
86
88 bool Assumption) override {
89 // Skip anything that is unsupported
90 return State;
91 }
92
93 //===------------------------------------------------------------------===//
94 // Implementation for interface from ConstraintManager.
95 //===------------------------------------------------------------------===//
96
99
100 QualType RetTy;
101 // The expression may be casted, so we cannot call getZ3DataExpr() directly
102 std::optional<llvm::SMTExprRef> VarExp =
103 SMTConv::getExpr(Solver, Ctx, Sym, RetTy);
104 if (!VarExp)
105 return ConditionTruthVal();
106 llvm::SMTExprRef Exp = SMTConv::getZeroExpr(Solver, Ctx, VarExp.value(),
107 RetTy, /*Assumption=*/true);
108
109 // Negate the constraint
110 llvm::SMTExprRef NotExp = SMTConv::getZeroExpr(Solver, Ctx, VarExp.value(),
111 RetTy, /*Assumption=*/false);
112
113 ConditionTruthVal isSat = checkModel(State, Sym, Exp);
114 ConditionTruthVal isNotSat = checkModel(State, Sym, NotExp);
115
116 // Zero is the only possible solution
117 if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse())
118 return true;
119
120 // Zero is not a solution
121 if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue())
122 return false;
123
124 // Zero may be a solution
125 return ConditionTruthVal();
126 }
127
128 const llvm::APSInt *getSymVal(ProgramStateRef State,
129 SymbolRef Sym) const override {
131 ASTContext &Ctx = BVF.getContext();
132
133 if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
134 QualType Ty = Sym->getType();
135 assert(!Ty->isRealFloatingType());
136 llvm::APSInt Value(SMTConv::getSMTBitWidth(Ctx, Ty),
138
139 // TODO: this should call checkModel so we can use the cache, however,
140 // this method tries to get the interpretation (the actual value) from
141 // the solver, which is currently not cached.
142
143 llvm::SMTExprRef Exp = SMTConv::fromData(Solver, Ctx, SD);
144
145 Solver->reset();
146 addStateConstraints(State);
147
148 // Constraints are unsatisfiable
149 std::optional<bool> isSat = Solver->check();
150 if (!isSat || !*isSat)
151 return nullptr;
152
153 // Model does not assign interpretation
154 if (!Solver->getInterpretation(Exp, Value))
155 return nullptr;
156
157 // A value has been obtained, check if it is the only value
158 llvm::SMTExprRef NotExp = SMTConv::fromBinOp(
159 Solver, Exp, BO_NE,
160 Ty->isBooleanType() ? Solver->mkBoolean(Value.getBoolValue())
161 : Solver->mkBitvector(Value, Value.getBitWidth()),
162 /*isSigned=*/false);
163
164 Solver->addConstraint(NotExp);
165
166 std::optional<bool> isNotSat = Solver->check();
167 if (!isNotSat || *isNotSat)
168 return nullptr;
169
170 // This is the only solution, store it
171 return BVF.getValue(Value).get();
172 }
173
174 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
175 SymbolRef CastSym = SC->getOperand();
176 QualType CastTy = SC->getType();
177 // Skip the void type
178 if (CastTy->isVoidType())
179 return nullptr;
180
181 const llvm::APSInt *Value;
182 if (!(Value = getSymVal(State, CastSym)))
183 return nullptr;
184 return BVF.Convert(SC->getType(), *Value).get();
185 }
186
187 if (const auto *USE = dyn_cast<UnarySymExpr>(Sym)) {
188 const llvm::APSInt *Value;
189 if (!(Value = getSymVal(State, USE->getOperand())))
190 return nullptr;
191 std::optional<APSIntPtr> Res = BVF.evalAPSInt(USE->getOpcode(), *Value);
192 return Res ? Res.value().get() : nullptr;
193 }
194
195 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
196 const llvm::APSInt *LHS, *RHS;
197 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
198 LHS = getSymVal(State, SIE->getLHS());
199 RHS = SIE->getRHS().get();
200 } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
201 LHS = ISE->getLHS().get();
202 RHS = getSymVal(State, ISE->getRHS());
203 } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
204 // Early termination to avoid expensive call
205 LHS = getSymVal(State, SSM->getLHS());
206 RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr;
207 } else {
208 llvm_unreachable("Unsupported binary expression to get symbol value!");
209 }
210
211 if (!LHS || !RHS)
212 return nullptr;
213
214 llvm::APSInt ConvertedLHS, ConvertedRHS;
215 QualType LTy, RTy;
216 std::tie(ConvertedLHS, LTy) = SMTConv::fixAPSInt(Ctx, *LHS);
217 std::tie(ConvertedRHS, RTy) = SMTConv::fixAPSInt(Ctx, *RHS);
219 Solver, Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy);
220 std::optional<APSIntPtr> Res =
221 BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
222 return Res ? Res.value().get() : nullptr;
223 }
224
225 llvm_unreachable("Unsupported expression to get symbol value!");
226 }
227
229 SymbolReaper &SymReaper) override {
230 ConstraintSMTType CZ = State->get<ConstraintSMT>();
231 ConstraintSMTType::Factory &CZFactory = State->get_context<ConstraintSMT>();
232 llvm::SmallVector<ConstraintEntry> Constraints(CZ.begin(), CZ.end());
233 llvm::DenseMap<SymbolRef, SmallVector<size_t>> ConstraintsBySym;
234 llvm::DenseSet<SymbolRef> TraversedSymbols;
236 llvm::BitVector RetainedConstraints(Constraints.size());
237
238 for (auto [Idx, Entry] : llvm::enumerate(Constraints)) {
239 for (auto Symbol : Entry.first->symbols()) {
240 if (SymReaper.isLive(Symbol) && TraversedSymbols.insert(Symbol).second)
241 WorkList.push_back(Symbol);
242 ConstraintsBySym[Symbol].push_back(Idx);
243 }
244 }
245
246 while (WorkList.size()) {
247 SymbolRef Item = WorkList.pop_back_val();
248 for (auto Idx : ConstraintsBySym[Item]) {
249 if (RetainedConstraints.test(Idx))
250 continue;
251
252 RetainedConstraints.set(Idx);
253
254 for (auto Symbol : Constraints[Idx].first->symbols()) {
255 if (TraversedSymbols.insert(Symbol).second)
256 WorkList.push_back(Symbol);
257 }
258 }
259 }
260
261 for (auto [Idx, Entry] : llvm::enumerate(Constraints)) {
262 if (!RetainedConstraints.test(Idx))
263 CZ = CZFactory.remove(CZ, Entry);
264 }
265
266 return State->set<ConstraintSMT>(CZ);
267 }
268
269 void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
270 unsigned int Space = 0, bool IsDot = false) const override {
271 ConstraintSMTType Constraints = State->get<ConstraintSMT>();
272
273 Indent(Out, Space, IsDot) << "\"constraints\": ";
274 if (Constraints.isEmpty()) {
275 Out << "null," << NL;
276 return;
277 }
278
279 ++Space;
280 Out << '[' << NL;
281 for (ConstraintSMTType::iterator I = Constraints.begin();
282 I != Constraints.end(); ++I) {
283 Indent(Out, Space, IsDot)
284 << "{ \"symbol\": \"" << I->first << "\", \"range\": \"";
285 I->second->print(Out);
286 Out << "\" }";
287
288 if (std::next(I) != Constraints.end())
289 Out << ',';
290 Out << NL;
291 }
292
293 --Space;
294 Indent(Out, Space, IsDot) << "],";
295 }
296
298 ProgramStateRef S2) const override {
299 return S1->get<ConstraintSMT>() == S2->get<ConstraintSMT>();
300 }
301
302 bool canReasonAbout(SVal X) const override {
304
305 std::optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
306 if (!SymVal)
307 return true;
308
309 const SymExpr *Sym = SymVal->getSymbol();
310 QualType Ty = Sym->getType();
311
312 // Complex types are not modeled
313 if (Ty->isComplexType() || Ty->isComplexIntegerType())
314 return false;
315
316 // Non-IEEE 754 floating-point types are not modeled
317 if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) &&
318 (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() ||
319 &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble())))
320 return false;
321
322 if (Ty->isRealFloatingType())
323 return Solver->isFPSupported();
324
325 if (isa<SymbolData>(Sym))
326 return true;
327
329
330 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
331 return canReasonAbout(SVB.makeSymbolVal(SC->getOperand()));
332
333 if (const auto *USE = dyn_cast<UnarySymExpr>(Sym))
334 return canReasonAbout(SVB.makeSymbolVal(USE->getOperand()));
335
336 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
337 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE))
338 return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS()));
339
340 if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE))
341 return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS()));
342
343 if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE))
344 return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) &&
345 canReasonAbout(SVB.makeSymbolVal(SSE->getRHS()));
346 }
347
348 llvm_unreachable("Unsupported expression to reason about!");
349 }
350
351#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
352 /// Dumps SMT formula
353 LLVM_DUMP_METHOD void dump() const { Solver->dump(); }
354#endif
355
356protected:
357 // Check whether a new model is satisfiable, and update the program state.
359 const llvm::SMTExprRef &Exp) {
360 // Check the model, avoid simplifying AST to save time
361 if (checkModel(State, Sym, Exp).isConstrainedTrue())
362 return State->add<ConstraintSMT>(std::make_pair(Sym, Exp));
363
364 return nullptr;
365 }
366
367 /// Given a program state, construct the logical conjunction and add it to
368 /// the solver
369 virtual void addStateConstraints(ProgramStateRef State) const {
370 // TODO: Don't add all the constraints, only the relevant ones
371 auto CZ = State->get<ConstraintSMT>();
372 auto I = CZ.begin(), IE = CZ.end();
373
374 // Construct the logical AND of all the constraints
375 if (I != IE) {
376 llvm::SMTExprRef Constraint = I++->second;
377 while (I != IE) {
378 Constraint = Solver->mkAnd(Constraint, I++->second);
379 }
380
381 Solver->addConstraint(Constraint);
382 }
383 }
384
385 // Generate and check a Z3 model, using the given constraint.
387 const llvm::SMTExprRef &Exp) const {
388 ProgramStateRef NewState =
389 State->add<ConstraintSMT>(std::make_pair(Sym, Exp));
390
391 llvm::FoldingSetNodeID ID;
392 NewState->get<ConstraintSMT>().Profile(ID);
393
394 unsigned hash = ID.ComputeHash();
395 auto I = Cached.find(hash);
396 if (I != Cached.end())
397 return I->second;
398
399 Solver->reset();
400 addStateConstraints(NewState);
401
402 std::optional<bool> res = Solver->check();
403 return Cached[hash] = res ? ConditionTruthVal(*res) : ConditionTruthVal();
404 }
405
406 // Cache the result of an SMT query (true, false, unknown). The key is the
407 // hash of the constraints in a state
408 mutable llvm::DenseMap<unsigned, ConditionTruthVal> Cached;
409}; // end class SMTConstraintManager
410
411} // namespace ento
412} // namespace clang
413
414#endif
#define X(type, name)
Definition Value.h:97
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
llvm::ImmutableSet< std::pair< clang::ento::SymbolRef, const llvm::SMTExpr * > > ConstraintSMTType
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:223
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:943
This represents one expression.
Definition Expr.h:113
A (possibly-)qualified type.
Definition TypeBase.h:938
Exposes information about the current target.
Definition TargetInfo.h:226
const llvm::fltSemantics & getLongDoubleFormat() const
Definition TargetInfo.h:803
bool isVoidType() const
Definition TypeBase.h:9111
bool isBooleanType() const
Definition TypeBase.h:9248
bool isSignedIntegerOrEnumerationType() const
Determines whether this is an integer type that is signed or an enumeration types whose underlying ty...
Definition Type.cpp:2319
bool isComplexType() const
isComplexType() does not include complex integers (a GCC extension).
Definition Type.cpp:761
bool isSpecificBuiltinType(unsigned K) const
Test for a particular builtin type.
Definition TypeBase.h:9080
bool isComplexIntegerType() const
Definition Type.cpp:767
bool isRealFloatingType() const
Floating point categories.
Definition Type.cpp:2437
LLVM_ATTRIBUTE_RETURNS_NONNULL const APSInt * get() const
Definition APSIntPtr.h:36
std::optional< APSIntPtr > evalAPSInt(UnaryOperator::Opcode Op, const llvm::APSInt &V1)
APSIntPtr Convert(const llvm::APSInt &To, const llvm::APSInt &From)
Convert - Create a new persistent APSInt with the same value as 'From' but with the bitwidth and sign...
Represents a symbolic expression involving a binary operator.
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
SMTConstraintManager(clang::ento::ExprEngine *EE, clang::ento::SValBuilder &SB)
virtual void addStateConstraints(ProgramStateRef State) const
Given a program state, construct the logical conjunction and add it to the solver.
bool canReasonAbout(SVal X) const override
canReasonAbout - Not all ConstraintManagers can accurately reason about all SVal values.
ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, bool InRange) override
Given a symbolic expression within the range [From, To], assume that it is true/false and generate th...
LLVM_DUMP_METHOD void dump() const
Dumps SMT formula.
ConditionTruthVal checkModel(ProgramStateRef State, SymbolRef Sym, const llvm::SMTExprRef &Exp) const
const llvm::APSInt * getSymVal(ProgramStateRef State, SymbolRef Sym) const override
If a symbol is perfectly constrained to a constant, attempt to return the concrete value.
ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override
Scan all symbols referenced by the constraints.
ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override
Returns whether or not a symbol is known to be null ("true"), known to be non-null ("false"),...
ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, bool Assumption) override
Given a symbolic expression that can be reasoned about, assume that it is true/false and generate the...
llvm::DenseMap< unsigned, ConditionTruthVal > Cached
virtual ~SMTConstraintManager()=default
virtual ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym, const llvm::SMTExprRef &Exp)
void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL="\n", unsigned int Space=0, bool IsDot=false) const override
ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, bool Assumption) override
Given a symbolic expression that cannot be reasoned about, assume that it is zero/nonzero and add it ...
bool haveEqualConstraints(ProgramStateRef S1, ProgramStateRef S2) const override
static std::optional< llvm::SMTExprRef > getRangeExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, bool InRange)
Definition SMTConv.h:599
static llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &Exp, QualType Ty, bool Assumption)
Definition SMTConv.h:568
static void doIntTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS, QualType &LTy, T &RHS, QualType &RTy)
Definition SMTConv.h:735
static std::optional< llvm::SMTExprRef > getExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, QualType &RetTy, bool *hasComparison=nullptr)
Definition SMTConv.h:558
static llvm::SMTExprRef fromData(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const SymbolData *Sym)
Construct an SMTSolverRef from a SymbolData.
Definition SMTConv.h:342
static llvm::SMTExprRef fromBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS, const BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS, bool isSigned)
Construct an SMTSolverRef from a binary operator.
Definition SMTConv.h:103
static std::pair< llvm::APSInt, QualType > fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int)
Definition SMTConv.h:651
static uint64_t getSMTBitWidth(ASTContext &Ctx, QualType Ty)
Definition SMTConv.h:28
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:57
SimpleConstraintManager(ExprEngine *exprengine, SValBuilder &SB)
Symbolic value.
Definition SymExpr.h:32
virtual QualType getType() const =0
Represents a cast expression.
A symbol representing data which can be stored in a memory location (region).
Definition SymExpr.h:138
A class responsible for cleaning up unused symbols.
bool isLive(SymbolRef sym)
Represents symbolic expression that isn't a location.
Definition SVals.h:285
Defines the clang::TargetInfo interface.
BinarySymExprImpl< APSIntPtr, const SymExpr *, SymExpr::Kind::IntSymExprKind > IntSymExpr
Represents a symbolic expression like 3 - 'x'.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
BinarySymExprImpl< const SymExpr *, const SymExpr *, SymExpr::Kind::SymSymExprKind > SymSymExpr
Represents a symbolic expression like 'x' + 'y'.
BinarySymExprImpl< const SymExpr *, APSIntPtr, SymExpr::Kind::SymIntExprKind > SymIntExpr
Represents a symbolic expression like 'x' + 3.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
raw_ostream & Indent(raw_ostream &Out, const unsigned int Space, bool IsDot)
Definition JsonSupport.h:21