Newer
Older
//===-- GRConstants.cpp - Simple, Path-Sens. Constant Prop. ------*- C++ -*-==//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Constant Propagation via Graph Reachability
//
// This files defines a simple analysis that performs path-sensitive
// constant propagation within a function. An example use of this analysis
// is to perform simple checks for NULL dereferences.
//
//===----------------------------------------------------------------------===//
#include "RValues.h"
#include "ValueState.h"
#include "clang/Analysis/PathSensitive/GREngine.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ASTContext.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SmallPtrSet.h"
Ted Kremenek
committed
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
Ted Kremenek
committed
#include "llvm/Support/Streams.h"
#include <functional>
#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
#include <sstream>
#endif
using namespace clang;
using llvm::dyn_cast;
using llvm::cast;
//===----------------------------------------------------------------------===//
//
// FIXME: This checker logic should be eventually broken into two components.
// The first is the "meta"-level checking logic; the code that
// does the Stmt visitation, fetching values from the map, etc.
// The second part does the actual state manipulation. This way we
// get more of a separate of concerns of these two pieces, with the
// latter potentially being refactored back into the main checking
// logic.
//===----------------------------------------------------------------------===//
namespace {
Ted Kremenek
committed
class VISIBILITY_HIDDEN GRConstants {
public:
typedef ValueStateManager::StateTy StateTy;
typedef GRStmtNodeBuilder<GRConstants> StmtNodeBuilder;
typedef GRBranchNodeBuilder<GRConstants> BranchNodeBuilder;
typedef ExplodedGraph<GRConstants> GraphTy;
typedef GraphTy::NodeTy NodeTy;
Ted Kremenek
committed
class NodeSet {
typedef llvm::SmallVector<NodeTy*,3> ImplTy;
ImplTy Impl;
public:
NodeSet() {}
NodeSet(NodeTy* N) { assert (N && !N->isSink()); Impl.push_back(N); }
Ted Kremenek
committed
void Add(NodeTy* N) { if (N && !N->isSink()) Impl.push_back(N); }
Ted Kremenek
committed
typedef ImplTy::iterator iterator;
typedef ImplTy::const_iterator const_iterator;
unsigned size() const { return Impl.size(); }
bool empty() const { return Impl.empty(); }
Ted Kremenek
committed
iterator begin() { return Impl.begin(); }
iterator end() { return Impl.end(); }
const_iterator begin() const { return Impl.begin(); }
const_iterator end() const { return Impl.end(); }
};
protected:
/// G - the simulation graph.
GraphTy& G;
Ted Kremenek
committed
/// Liveness - live-variables information the ValueDecl* and block-level
/// Expr* in the CFG. Used to prune out dead state.
Ted Kremenek
committed
LiveVariables Liveness;
/// Builder - The current GRStmtNodeBuilder which is used when building the nodes
Ted Kremenek
committed
/// for a given statement.
StmtNodeBuilder* Builder;
Ted Kremenek
committed
/// StateMgr - Object that manages the data for all created states.
ValueStateManager StateMgr;
Ted Kremenek
committed
/// ValueMgr - Object that manages the data for all created RValues.
ValueManager& ValMgr;
/// SymMgr - Object that manages the symbol information.
SymbolManager& SymMgr;
Ted Kremenek
committed
/// StmtEntryNode - The immediate predecessor node.
NodeTy* StmtEntryNode;
/// CurrentStmt - The current block-level statement.
Stmt* CurrentStmt;
/// UninitBranches - Nodes in the ExplodedGraph that result from
/// taking a branch based on an uninitialized value.
typedef llvm::SmallPtrSet<NodeTy*,5> UninitBranchesTy;
UninitBranchesTy UninitBranches;
Ted Kremenek
committed
bool StateCleaned;
ASTContext& getContext() const { return G.getContext(); }
public:
Ted Kremenek
committed
GRConstants(GraphTy& g) : G(g), Liveness(G.getCFG(), G.getFunctionDecl()),
Builder(NULL),
StateMgr(G.getContext(), G.getAllocator()),
ValMgr(StateMgr.getValueManager()),
SymMgr(StateMgr.getSymbolManager()),
StmtEntryNode(NULL), CurrentStmt(NULL) {
// Compute liveness information.
Ted Kremenek
committed
Liveness.runOnCFG(G.getCFG());
Liveness.runOnAllBlocks(G.getCFG(), NULL, true);
/// getCFG - Returns the CFG associated with this analysis.
CFG& getCFG() { return G.getCFG(); }
Ted Kremenek
committed
/// getInitialState - Return the initial state used for the root vertex
/// in the ExplodedGraph.
StateTy getInitialState() {
StateTy St = StateMgr.getInitialState();
// Iterate the parameters.
FunctionDecl& F = G.getFunctionDecl();
for (FunctionDecl::param_iterator I=F.param_begin(), E=F.param_end();
I!=E; ++I)
St = SetValue(St, lval::DeclVal(*I), RValue::GetSymbolValue(SymMgr, *I));
bool isUninitControlFlow(const NodeTy* N) const {
return N->isSink() && UninitBranches.count(const_cast<NodeTy*>(N)) != 0;
}
Ted Kremenek
committed
/// ProcessStmt - Called by GREngine. Used to generate new successor
/// nodes by processing the 'effects' of a block-level statement.
void ProcessStmt(Stmt* S, StmtNodeBuilder& builder);
/// ProcessBranch - Called by GREngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.
void ProcessBranch(Expr* Condition, Stmt* Term, BranchNodeBuilder& builder);
Ted Kremenek
committed
/// RemoveDeadBindings - Return a new state that is the same as 'M' except
/// that all subexpression mappings are removed and that any
/// block-level expressions that are not live at 'S' also have their
/// mappings removed.
StateTy RemoveDeadBindings(Stmt* S, StateTy M);
StateTy SetValue(StateTy St, Stmt* S, const RValue& V);
Ted Kremenek
committed
StateTy SetValue(StateTy St, const Stmt* S, const RValue& V) {
return SetValue(St, const_cast<Stmt*>(S), V);
}
/// SetValue - This version of SetValue is used to batch process a set
/// of different possible RValues and return a set of different states.
const StateTy::BufferTy& SetValue(StateTy St, Stmt* S,
const RValue::BufferTy& V,
StateTy::BufferTy& RetBuf);
StateTy SetValue(StateTy St, const LValue& LV, const RValue& V);
inline RValue GetValue(const StateTy& St, Stmt* S) {
return StateMgr.GetValue(St, S);
}
inline RValue GetValue(const StateTy& St, Stmt* S, bool& hasVal) {
return StateMgr.GetValue(St, S, &hasVal);
}
inline RValue GetValue(const StateTy& St, const Stmt* S) {
return GetValue(St, const_cast<Stmt*>(S));
}
inline RValue GetValue(const StateTy& St, const LValue& LV) {
return StateMgr.GetValue(St, LV);
}
inline LValue GetLValue(const StateTy& St, Stmt* S) {
return StateMgr.GetLValue(St, S);
}
inline NonLValue GetRValueConstant(uint64_t X, Expr* E) {
return NonLValue::GetValue(ValMgr, X, E->getType(), E->getLocStart());
}
/// Assume - Create new state by assuming that a given expression
/// is true or false.
inline StateTy Assume(StateTy St, RValue Cond, bool Assumption,
bool& isFeasible) {
if (isa<LValue>(Cond))
return Assume(St, cast<LValue>(Cond), Assumption, isFeasible);
else
return Assume(St, cast<NonLValue>(Cond), Assumption, isFeasible);
}
StateTy Assume(StateTy St, LValue Cond, bool Assumption, bool& isFeasible);
StateTy Assume(StateTy St, NonLValue Cond, bool Assumption, bool& isFeasible);
Ted Kremenek
committed
StateTy AssumeSymNE(StateTy St, SymbolID sym, const llvm::APSInt& V,
bool& isFeasible);
StateTy AssumeSymEQ(StateTy St, SymbolID sym, const llvm::APSInt& V,
bool& isFeasible);
Ted Kremenek
committed
StateTy AssumeSymInt(StateTy St, bool Assumption, const SymIntConstraint& C,
bool& isFeasible);
Ted Kremenek
committed
void Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St);
/// Nodify - This version of Nodify is used to batch process a set of states.
/// The states are not guaranteed to be unique.
void Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, const StateTy::BufferTy& SB);
Ted Kremenek
committed
/// Visit - Transfer function logic for all statements. Dispatches to
/// other functions that handle specific kinds of statements.
void Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst);
/// VisitCast - Transfer function logic for all casts (implicit and explicit).
void VisitCast(Expr* CastE, Expr* E, NodeTy* Pred, NodeSet& Dst);
Ted Kremenek
committed
/// VisitUnaryOperator - Transfer function logic for unary operators.
void VisitUnaryOperator(UnaryOperator* B, NodeTy* Pred, NodeSet& Dst);
Ted Kremenek
committed
/// VisitBinaryOperator - Transfer function logic for binary operators.
void VisitBinaryOperator(BinaryOperator* B, NodeTy* Pred, NodeSet& Dst);
void VisitAssignmentLHS(Expr* E, NodeTy* Pred, NodeSet& Dst);
/// VisitDeclStmt - Transfer function logic for DeclStmts.
void VisitDeclStmt(DeclStmt* DS, NodeTy* Pred, NodeSet& Dst);
/// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose
void VisitGuardedExpr(Stmt* S, Stmt* LHS, Stmt* RHS,
NodeTy* Pred, NodeSet& Dst);
/// VisitLogicalExpr - Transfer function logic for '&&', '||'
void VisitLogicalExpr(BinaryOperator* B, NodeTy* Pred, NodeSet& Dst);
};
} // end anonymous namespace
Ted Kremenek
committed
GRConstants::StateTy
GRConstants::SetValue(StateTy St, Stmt* S, const RValue& V) {
if (!StateCleaned) {
St = RemoveDeadBindings(CurrentStmt, St);
StateCleaned = true;
}
bool isBlkExpr = false;
if (S == CurrentStmt) {
isBlkExpr = getCFG().isBlkExpr(S);
if (!isBlkExpr)
return St;
}
return StateMgr.SetValue(St, S, isBlkExpr, V);
}
const GRConstants::StateTy::BufferTy&
GRConstants::SetValue(StateTy St, Stmt* S, const RValue::BufferTy& RB,
StateTy::BufferTy& RetBuf) {
assert (RetBuf.empty());
for (RValue::BufferTy::const_iterator I=RB.begin(), E=RB.end(); I!=E; ++I)
RetBuf.push_back(SetValue(St, S, *I));
return RetBuf;
}
GRConstants::StateTy
GRConstants::SetValue(StateTy St, const LValue& LV, const RValue& V) {
if (!LV.isValid())
return St;
if (!StateCleaned) {
St = RemoveDeadBindings(CurrentStmt, St);
StateCleaned = true;
}
return StateMgr.SetValue(St, LV, V);
}
void GRConstants::ProcessBranch(Expr* Condition, Stmt* Term,
BranchNodeBuilder& builder) {
StateTy PrevState = builder.getState();
// Remove old bindings for subexpressions.
Ted Kremenek
committed
for (StateTy::vb_iterator I=PrevState.begin(), E=PrevState.end(); I!=E; ++I)
if (I.getKey().isSubExpr())
PrevState = StateMgr.Remove(PrevState, I.getKey());
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
// Remove terminator-specific bindings.
switch (Term->getStmtClass()) {
default: break;
case Stmt::BinaryOperatorClass: { // '&&', '||'
BinaryOperator* B = cast<BinaryOperator>(Term);
// FIXME: Liveness analysis should probably remove these automatically.
// Verify later when we converge to an 'optimization' stage.
PrevState = StateMgr.Remove(PrevState, B->getRHS());
break;
}
case Stmt::ConditionalOperatorClass: { // '?' operator
ConditionalOperator* C = cast<ConditionalOperator>(Term);
// FIXME: Liveness analysis should probably remove these automatically.
// Verify later when we converge to an 'optimization' stage.
if (Expr* L = C->getLHS()) PrevState = StateMgr.Remove(PrevState, L);
PrevState = StateMgr.Remove(PrevState, C->getRHS());
break;
}
case Stmt::ChooseExprClass: { // __builtin_choose_expr
ChooseExpr* C = cast<ChooseExpr>(Term);
// FIXME: Liveness analysis should probably remove these automatically.
// Verify later when we converge to an 'optimization' stage.
PrevState = StateMgr.Remove(PrevState, C->getRHS());
PrevState = StateMgr.Remove(PrevState, C->getRHS());
break;
}
}
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
RValue V = GetValue(PrevState, Condition);
switch (V.getBaseKind()) {
default:
break;
case RValue::InvalidKind:
builder.generateNode(PrevState, true);
builder.generateNode(PrevState, false);
return;
case RValue::UninitializedKind: {
NodeTy* N = builder.generateNode(PrevState, true);
if (N) {
N->markAsSink();
UninitBranches.insert(N);
}
builder.markInfeasible(false);
return;
}
}
// Process the true branch.
bool isFeasible = true;
StateTy St = Assume(PrevState, V, true, isFeasible);
if (isFeasible)
builder.generateNode(St, true);
else {
builder.markInfeasible(true);
isFeasible = true;
}
// Process the false branch.
St = Assume(PrevState, V, false, isFeasible);
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
if (isFeasible)
builder.generateNode(St, false);
else
builder.markInfeasible(false);
}
void GRConstants::VisitLogicalExpr(BinaryOperator* B, NodeTy* Pred,
NodeSet& Dst) {
bool hasR2;
StateTy PrevState = Pred->getState();
RValue R1 = GetValue(PrevState, B->getLHS());
RValue R2 = GetValue(PrevState, B->getRHS(), hasR2);
if (isa<InvalidValue>(R1) &&
(isa<InvalidValue>(R2) ||
isa<UninitializedValue>(R2))) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, R2));
return;
}
else if (isa<UninitializedValue>(R1)) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, R1));
return;
}
// R1 is an expression that can evaluate to either 'true' or 'false'.
if (B->getOpcode() == BinaryOperator::LAnd) {
// hasR2 == 'false' means that LHS evaluated to 'false' and that
// we short-circuited, leading to a value of '0' for the '&&' expression.
if (hasR2 == false) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(0U, B)));
return;
}
}
else {
assert (B->getOpcode() == BinaryOperator::LOr);
// hasR2 == 'false' means that the LHS evaluate to 'true' and that
// we short-circuited, leading to a value of '1' for the '||' expression.
if (hasR2 == false) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(1U, B)));
return;
}
}
// If we reach here we did not short-circuit. Assume R2 == true and
// R2 == false.
bool isFeasible;
StateTy St = Assume(PrevState, R2, true, isFeasible);
if (isFeasible)
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(1U, B)));
St = Assume(PrevState, R2, false, isFeasible);
if (isFeasible)
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(0U, B)));
}
void GRConstants::ProcessStmt(Stmt* S, StmtNodeBuilder& builder) {
Builder = &builder;
Ted Kremenek
committed
StmtEntryNode = builder.getLastNode();
CurrentStmt = S;
NodeSet Dst;
StateCleaned = false;
Visit(S, StmtEntryNode, Dst);
// If no nodes were generated, generate a new node that has all the
// dead mappings removed.
if (Dst.size() == 1 && *Dst.begin() == StmtEntryNode) {
StateTy St = RemoveDeadBindings(S, StmtEntryNode->getState());
builder.generateNode(S, St, StmtEntryNode);
}
Ted Kremenek
committed
CurrentStmt = NULL;
StmtEntryNode = NULL;
Builder = NULL;
Ted Kremenek
committed
GRConstants::StateTy GRConstants::RemoveDeadBindings(Stmt* Loc, StateTy M) {
// Note: in the code below, we can assign a new map to M since the
// iterators are iterating over the tree of the *original* map.
Ted Kremenek
committed
StateTy::vb_iterator I = M.begin(), E = M.end();
Ted Kremenek
committed
for (; I!=E && !I.getKey().isSymbol(); ++I) {
// Remove old bindings for subexpressions and "dead"
// block-level expressions.
if (I.getKey().isSubExpr() ||
I.getKey().isBlkExpr() && !Liveness.isLive(Loc,cast<Stmt>(I.getKey()))){
M = StateMgr.Remove(M, I.getKey());
Ted Kremenek
committed
}
else if (I.getKey().isDecl()) { // Remove bindings for "dead" decls.
if (VarDecl* V = dyn_cast<VarDecl>(cast<ValueDecl>(I.getKey())))
if (!Liveness.isLive(Loc, V))
M = StateMgr.Remove(M, I.getKey());
}
return M;
}
void GRConstants::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St) {
Ted Kremenek
committed
// If the state hasn't changed, don't generate a new node.
if (St == Pred->getState())
return;
Ted Kremenek
committed
Dst.Add(Builder->generateNode(S, St, Pred));
}
void GRConstants::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred,
const StateTy::BufferTy& SB) {
for (StateTy::BufferTy::const_iterator I=SB.begin(), E=SB.end(); I!=E; ++I)
Nodify(Dst, S, Pred, *I);
}
void GRConstants::VisitCast(Expr* CastE, Expr* E, NodeTy* Pred, NodeSet& Dst) {
QualType T = CastE->getType();
// Check for redundant casts.
if (E->getType() == T) {
Dst.Add(Pred);
return;
}
NodeSet S1;
Visit(E, Pred, S1);
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1) {
NodeTy* N = *I1;
StateTy St = N->getState();
const RValue& V = GetValue(St, E);
Nodify(Dst, CastE, N, SetValue(St, CastE, V.EvalCast(ValMgr, CastE)));
}
void GRConstants::VisitDeclStmt(DeclStmt* DS, GRConstants::NodeTy* Pred,
GRConstants::NodeSet& Dst) {
StateTy St = Pred->getState();
for (const ScopedDecl* D = DS->getDecl(); D; D = D->getNextDeclarator())
if (const VarDecl* VD = dyn_cast<VarDecl>(D)) {
const Expr* E = VD->getInit();
St = SetValue(St, lval::DeclVal(VD),
E ? GetValue(St, E) : UninitializedValue());
}
Nodify(Dst, DS, Pred, St);
if (Dst.empty())
Dst.Add(Pred);
}
void GRConstants::VisitGuardedExpr(Stmt* S, Stmt* LHS, Stmt* RHS,
NodeTy* Pred, NodeSet& Dst) {
StateTy St = Pred->getState();
RValue R = GetValue(St, LHS);
if (isa<InvalidValue>(R)) R = GetValue(St, RHS);
Nodify(Dst, S, Pred, SetValue(St, S, R));
}
void GRConstants::VisitUnaryOperator(UnaryOperator* U,
GRConstants::NodeTy* Pred,
GRConstants::NodeSet& Dst) {
NodeSet S1;
Visit(U->getSubExpr(), Pred, S1);
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1) {
NodeTy* N1 = *I1;
StateTy St = N1->getState();
switch (U->getOpcode()) {
case UnaryOperator::PostInc: {
const LValue& L1 = GetLValue(St, U->getSubExpr());
NonLValue R1 = cast<NonLValue>(GetValue(St, L1));
NonLValue Result = R1.EvalBinaryOp(ValMgr, BinaryOperator::Add,
GetRValueConstant(1U, U));
Nodify(Dst, U, N1, SetValue(SetValue(St, U, R1), L1, Result));
break;
}
case UnaryOperator::PostDec: {
const LValue& L1 = GetLValue(St, U->getSubExpr());
NonLValue R1 = cast<NonLValue>(GetValue(St, L1));
NonLValue Result = R1.EvalBinaryOp(ValMgr, BinaryOperator::Sub,
GetRValueConstant(1U, U));
Nodify(Dst, U, N1, SetValue(SetValue(St, U, R1), L1, Result));
break;
}
case UnaryOperator::PreInc: {
const LValue& L1 = GetLValue(St, U->getSubExpr());
NonLValue R1 = cast<NonLValue>(GetValue(St, L1));
NonLValue Result = R1.EvalBinaryOp(ValMgr, BinaryOperator::Add,
GetRValueConstant(1U, U));
Nodify(Dst, U, N1, SetValue(SetValue(St, U, Result), L1, Result));
break;
}
case UnaryOperator::PreDec: {
const LValue& L1 = GetLValue(St, U->getSubExpr());
NonLValue R1 = cast<NonLValue>(GetValue(St, L1));
NonLValue Result = R1.EvalBinaryOp(ValMgr, BinaryOperator::Sub,
GetRValueConstant(1U, U));
Nodify(Dst, U, N1, SetValue(SetValue(St, U, Result), L1, Result));
break;
}
case UnaryOperator::Minus: {
const NonLValue& R1 = cast<NonLValue>(GetValue(St, U->getSubExpr()));
Nodify(Dst, U, N1, SetValue(St, U, R1.EvalMinus(ValMgr, U)));
case UnaryOperator::Not: {
const NonLValue& R1 = cast<NonLValue>(GetValue(St, U->getSubExpr()));
Nodify(Dst, U, N1, SetValue(St, U, R1.EvalComplement(ValMgr)));
case UnaryOperator::LNot: {
// C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
//
// Note: technically we do "E == 0", but this is the same in the
// transfer functions as "0 == E".
RValue V1 = GetValue(St, U->getSubExpr());
if (isa<LValue>(V1)) {
const LValue& L1 = cast<LValue>(V1);
lval::ConcreteInt V2(ValMgr.getZeroWithPtrWidth());
Nodify(Dst, U, N1,
SetValue(St, U, L1.EvalBinaryOp(ValMgr, BinaryOperator::EQ,
V2)));
}
else {
const NonLValue& R1 = cast<NonLValue>(V1);
nonlval::ConcreteInt V2(ValMgr.getZeroWithPtrWidth());
Nodify(Dst, U, N1,
SetValue(St, U, R1.EvalBinaryOp(ValMgr, BinaryOperator::EQ,
V2)));
}
break;
}
case UnaryOperator::AddrOf: {
const LValue& L1 = GetLValue(St, U->getSubExpr());
Nodify(Dst, U, N1, SetValue(St, U, L1));
break;
}
case UnaryOperator::Deref: {
const LValue& L1 = cast<LValue>(GetValue(St, U->getSubExpr()));
Nodify(Dst, U, N1, SetValue(St, U, GetValue(St, L1)));
break;
}
default: ;
assert (false && "Not implemented.");
}
}
}
void GRConstants::VisitAssignmentLHS(Expr* E, GRConstants::NodeTy* Pred,
GRConstants::NodeSet& Dst) {
if (isa<DeclRefExpr>(E))
return;
if (UnaryOperator* U = dyn_cast<UnaryOperator>(E)) {
if (U->getOpcode() == UnaryOperator::Deref) {
Visit(U->getSubExpr(), Pred, Dst);
return;
}
}
Visit(E, Pred, Dst);
}
Ted Kremenek
committed
void GRConstants::VisitBinaryOperator(BinaryOperator* B,
GRConstants::NodeTy* Pred,
GRConstants::NodeSet& Dst) {
NodeSet S1;
if (B->isAssignmentOp())
VisitAssignmentLHS(B->getLHS(), Pred, S1);
else
Visit(B->getLHS(), Pred, S1);
Ted Kremenek
committed
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1) {
NodeTy* N1 = *I1;
Ted Kremenek
committed
// When getting the value for the LHS, check if we are in an assignment.
// In such cases, we want to (initially) treat the LHS as an LValue,
// so we use GetLValue instead of GetValue so that DeclRefExpr's are
// evaluated to LValueDecl's instead of to an NonLValue.
const RValue& V1 =
Ted Kremenek
committed
B->isAssignmentOp() ? GetLValue(N1->getState(), B->getLHS())
: GetValue(N1->getState(), B->getLHS());
Ted Kremenek
committed
NodeSet S2;
Visit(B->getRHS(), N1, S2);
for (NodeSet::iterator I2=S2.begin(), E2=S2.end(); I2 != E2; ++I2) {
Ted Kremenek
committed
NodeTy* N2 = *I2;
StateTy St = N2->getState();
const RValue& V2 = GetValue(St, B->getRHS());
Ted Kremenek
committed
BinaryOperator::Opcode Op = B->getOpcode();
if (Op <= BinaryOperator::Or) {
if (isa<InvalidValue>(V1) || isa<UninitializedValue>(V1)) {
Nodify(Dst, B, N2, SetValue(St, B, V1));
continue;
}
if (isa<LValue>(V1)) {
// FIXME: Add support for RHS being a non-lvalue.
const LValue& L1 = cast<LValue>(V1);
const LValue& L2 = cast<LValue>(V2);
Nodify(Dst, B, N2, SetValue(St, B, L1.EvalBinaryOp(ValMgr, Op, L2)));
else {
const NonLValue& R1 = cast<NonLValue>(V1);
const NonLValue& R2 = cast<NonLValue>(V2);
Nodify(Dst, B, N2, SetValue(St, B, R1.EvalBinaryOp(ValMgr, Op, R2)));
continue;
}
switch (Op) {
Ted Kremenek
committed
case BinaryOperator::Assign: {
const LValue& L1 = cast<LValue>(V1);
Nodify(Dst, B, N2, SetValue(SetValue(St, B, V2), L1, V2));
Ted Kremenek
committed
break;
}
default: { // Compound assignment operators.
assert (B->isCompoundAssignmentOp());
const LValue& L1 = cast<LValue>(V1);
RValue Result = cast<NonLValue>(InvalidValue());
Op = (BinaryOperator::Opcode)
(((unsigned) Op) - ((unsigned) BinaryOperator::MulAssign));
if (isa<LValue>(V2)) {
// FIXME: Add support for Non-LValues on RHS.
const LValue& L2 = cast<LValue>(V2);
Result = L1.EvalBinaryOp(ValMgr, Op, L2);
}
else {
const NonLValue& R1 = cast<NonLValue>(GetValue(N1->getState(), L1));
const NonLValue& R2 = cast<NonLValue>(V2);
Result = R1.EvalBinaryOp(ValMgr, Op, R2);
Nodify(Dst, B, N2, SetValue(SetValue(St, B, Result), L1, Result));
Ted Kremenek
committed
break;
Ted Kremenek
committed
}
}
}
}
Ted Kremenek
committed
void GRConstants::Visit(Stmt* S, GRConstants::NodeTy* Pred,
GRConstants::NodeSet& Dst) {
Ted Kremenek
committed
// FIXME: add metadata to the CFG so that we can disable
// this check when we KNOW that there is no block-level subexpression.
// The motivation is that this check requires a hashtable lookup.
Ted Kremenek
committed
if (S != CurrentStmt && getCFG().isBlkExpr(S)) {
Dst.Add(Pred);
return;
}
switch (S->getStmtClass()) {
case Stmt::BinaryOperatorClass:
if (cast<BinaryOperator>(S)->isLogicalOp()) {
VisitLogicalExpr(cast<BinaryOperator>(S), Pred, Dst);
break;
}
// Fall-through.
case Stmt::CompoundAssignOperatorClass:
Ted Kremenek
committed
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
break;
case Stmt::UnaryOperatorClass:
VisitUnaryOperator(cast<UnaryOperator>(S), Pred, Dst);
break;
Ted Kremenek
committed
case Stmt::ParenExprClass:
Visit(cast<ParenExpr>(S)->getSubExpr(), Pred, Dst);
break;
case Stmt::ImplicitCastExprClass: {
ImplicitCastExpr* C = cast<ImplicitCastExpr>(S);
VisitCast(C, C->getSubExpr(), Pred, Dst);
break;
}
case Stmt::CastExprClass: {
CastExpr* C = cast<CastExpr>(S);
VisitCast(C, C->getSubExpr(), Pred, Dst);
break;
}
case Stmt::ConditionalOperatorClass: { // '?' operator
ConditionalOperator* C = cast<ConditionalOperator>(S);
VisitGuardedExpr(S, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
case Stmt::ChooseExprClass: { // __builtin_choose_expr
ChooseExpr* C = cast<ChooseExpr>(S);
VisitGuardedExpr(S, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
case Stmt::ReturnStmtClass:
if (Expr* R = cast<ReturnStmt>(S)->getRetValue())
Visit(R, Pred, Dst);
else
Dst.Add(Pred);
break;
case Stmt::DeclStmtClass:
VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst);
break;
Ted Kremenek
committed
default:
Dst.Add(Pred); // No-op. Simply propagate the current state unchanged.
break;
}
//===----------------------------------------------------------------------===//
// "Assume" logic.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
GRConstants::StateTy GRConstants::Assume(StateTy St, LValue Cond,
bool Assumption,
bool& isFeasible) {
switch (Cond.getSubKind()) {
default:
Ted Kremenek
committed
assert (false && "'Assume' not implemented for this LValue.");
Ted Kremenek
committed
case lval::SymbolValKind:
if (Assumption)
return AssumeSymNE(St, cast<lval::SymbolVal>(Cond).getSymbol(),
ValMgr.getZeroWithPtrWidth(), isFeasible);
else
return AssumeSymEQ(St, cast<lval::SymbolVal>(Cond).getSymbol(),
ValMgr.getZeroWithPtrWidth(), isFeasible);
Ted Kremenek
committed
case lval::DeclValKind:
isFeasible = Assumption;
return St;
Ted Kremenek
committed
case lval::ConcreteIntKind: {
bool b = cast<lval::ConcreteInt>(Cond).getValue() != 0;
isFeasible = b ? Assumption : !Assumption;
return St;
}
}
Ted Kremenek
committed
GRConstants::StateTy GRConstants::Assume(StateTy St, NonLValue Cond,
bool Assumption,
bool& isFeasible) {
switch (Cond.getSubKind()) {
default:
assert (false && "'Assume' not implemented for this NonLValue.");
return St;
Ted Kremenek
committed
case nonlval::SymbolValKind: {
lval::SymbolVal& SV = cast<lval::SymbolVal>(Cond);
SymbolID sym = SV.getSymbol();
if (Assumption)
return AssumeSymNE(St, sym, ValMgr.getValue(0, SymMgr.getType(sym)),
isFeasible);
else
return AssumeSymEQ(St, sym, ValMgr.getValue(0, SymMgr.getType(sym)),
isFeasible);
}
Ted Kremenek
committed
case nonlval::SymIntConstraintValKind:
return
AssumeSymInt(St, Assumption,
cast<nonlval::SymIntConstraintVal>(Cond).getConstraint(),
isFeasible);
case nonlval::ConcreteIntKind: {
bool b = cast<nonlval::ConcreteInt>(Cond).getValue() != 0;
isFeasible = b ? Assumption : !Assumption;
return St;
}
}
}
Ted Kremenek
committed
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
GRConstants::StateTy
GRConstants::AssumeSymNE(StateTy St, SymbolID sym,
const llvm::APSInt& V, bool& isFeasible) {
// First, determine if sym == X, where X != V.
if (const llvm::APSInt* X = St.getSymVal(sym)) {
isFeasible = *X != V;
return St;
}
// Second, determine if sym != V.
if (St.isNotEqual(sym, V)) {
isFeasible = true;
return St;
}
// If we reach here, sym is not a constant and we don't know if it is != V.
// Make that assumption.
isFeasible = true;
return StateMgr.AddNE(St, sym, V);
}
GRConstants::StateTy
GRConstants::AssumeSymEQ(StateTy St, SymbolID sym,
const llvm::APSInt& V, bool& isFeasible) {
// First, determine if sym == X, where X != V.
if (const llvm::APSInt* X = St.getSymVal(sym)) {
isFeasible = *X == V;
return St;
}
// Second, determine if sym != V.
if (St.isNotEqual(sym, V)) {
isFeasible = false;
return St;
}
// If we reach here, sym is not a constant and we don't know if it is == V.
// Make that assumption.
isFeasible = true;
return StateMgr.AddEQ(St, sym, V);
}
Ted Kremenek
committed
GRConstants::StateTy
GRConstants::AssumeSymInt(StateTy St, bool Assumption,
const SymIntConstraint& C, bool& isFeasible) {
switch (C.getOpcode()) {
default:
// No logic yet for other operators.
return St;
case BinaryOperator::EQ:
if (Assumption)
return AssumeSymEQ(St, C.getSymbol(), C.getInt(), isFeasible);
else