aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
Diffstat (limited to 'clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp')
-rw-r--r--clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp472
1 files changed, 368 insertions, 104 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index f6f71e34b892..cc3992805cc7 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -20,6 +20,7 @@
#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
@@ -48,43 +49,66 @@ llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
return Result;
}
-static bool areEquivalentIndirectionValues(Value *Val1, Value *Val2) {
- if (auto *IndVal1 = dyn_cast<ReferenceValue>(Val1)) {
- auto *IndVal2 = cast<ReferenceValue>(Val2);
- return &IndVal1->getReferentLoc() == &IndVal2->getReferentLoc();
- }
- if (auto *IndVal1 = dyn_cast<PointerValue>(Val1)) {
- auto *IndVal2 = cast<PointerValue>(Val2);
- return &IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc();
+static bool compareDistinctValues(QualType Type, Value &Val1,
+ const Environment &Env1, Value &Val2,
+ const Environment &Env2,
+ Environment::ValueModel &Model) {
+ // Note: Potentially costly, but, for booleans, we could check whether both
+ // can be proven equivalent in their respective environments.
+
+ // FIXME: move the reference/pointers logic from `areEquivalentValues` to here
+ // and implement separate, join/widen specific handling for
+ // reference/pointers.
+ switch (Model.compare(Type, Val1, Env1, Val2, Env2)) {
+ case ComparisonResult::Same:
+ return true;
+ case ComparisonResult::Different:
+ return false;
+ case ComparisonResult::Unknown:
+ switch (Val1.getKind()) {
+ case Value::Kind::Integer:
+ case Value::Kind::Reference:
+ case Value::Kind::Pointer:
+ case Value::Kind::Struct:
+ // FIXME: this choice intentionally introduces unsoundness to allow
+ // for convergence. Once we have widening support for the
+ // reference/pointer and struct built-in models, this should be
+ // `false`.
+ return true;
+ default:
+ return false;
+ }
}
- return false;
-}
-
-/// Returns true if and only if `Val1` is equivalent to `Val2`.
-static bool equivalentValues(QualType Type, Value *Val1,
- const Environment &Env1, Value *Val2,
- const Environment &Env2,
- Environment::ValueModel &Model) {
- return Val1 == Val2 || areEquivalentIndirectionValues(Val1, Val2) ||
- Model.compareEquivalent(Type, *Val1, Env1, *Val2, Env2);
+ llvm_unreachable("All cases covered in switch");
}
/// Attempts to merge distinct values `Val1` and `Val2` in `Env1` and `Env2`,
/// respectively, of the same type `Type`. Merging generally produces a single
/// value that (soundly) approximates the two inputs, although the actual
/// meaning depends on `Model`.
-static Value *mergeDistinctValues(QualType Type, Value *Val1,
- const Environment &Env1, Value *Val2,
+static Value *mergeDistinctValues(QualType Type, Value &Val1,
+ const Environment &Env1, Value &Val2,
const Environment &Env2,
Environment &MergedEnv,
Environment::ValueModel &Model) {
// Join distinct boolean values preserving information about the constraints
// in the respective path conditions.
- //
- // FIXME: Does not work for backedges, since the two (or more) paths will not
- // have mutually exclusive conditions.
- if (auto *Expr1 = dyn_cast<BoolValue>(Val1)) {
- auto *Expr2 = cast<BoolValue>(Val2);
+ if (isa<BoolValue>(&Val1) && isa<BoolValue>(&Val2)) {
+ // FIXME: Checking both values should be unnecessary, since they should have
+ // a consistent shape. However, right now we can end up with BoolValue's in
+ // integer-typed variables due to our incorrect handling of
+ // boolean-to-integer casts (we just propagate the BoolValue to the result
+ // of the cast). So, a join can encounter an integer in one branch but a
+ // bool in the other.
+ // For example:
+ // ```
+ // std::optional<bool> o;
+ // int x;
+ // if (o.has_value())
+ // x = o.value();
+ // ```
+ auto *Expr1 = cast<BoolValue>(&Val1);
+ auto *Expr2 = cast<BoolValue>(&Val2);
auto &MergedVal = MergedEnv.makeAtomicBoolValue();
MergedEnv.addToFlowCondition(MergedEnv.makeOr(
MergedEnv.makeAnd(Env1.getFlowConditionToken(),
@@ -94,59 +118,99 @@ static Value *mergeDistinctValues(QualType Type, Value *Val1,
return &MergedVal;
}
- // FIXME: add unit tests that cover this statement.
- if (areEquivalentIndirectionValues(Val1, Val2)) {
- return Val1;
- }
-
// FIXME: Consider destroying `MergedValue` immediately if `ValueModel::merge`
// returns false to avoid storing unneeded values in `DACtx`.
+ // FIXME: Creating the value based on the type alone creates misshapen values
+ // for lvalues, since the type does not reflect the need for `ReferenceValue`.
if (Value *MergedVal = MergedEnv.createValue(Type))
- if (Model.merge(Type, *Val1, Env1, *Val2, Env2, *MergedVal, MergedEnv))
+ if (Model.merge(Type, Val1, Env1, Val2, Env2, *MergedVal, MergedEnv))
return MergedVal;
return nullptr;
}
-/// Initializes a global storage value.
-static void initGlobalVar(const VarDecl &D, Environment &Env) {
- if (!D.hasGlobalStorage() ||
- Env.getStorageLocation(D, SkipPast::None) != nullptr)
- return;
+// When widening does not change `Current`, return value will equal `&Prev`.
+static Value &widenDistinctValues(QualType Type, Value &Prev,
+ const Environment &PrevEnv, Value &Current,
+ Environment &CurrentEnv,
+ Environment::ValueModel &Model) {
+ // Boolean-model widening.
+ if (isa<BoolValue>(&Prev)) {
+ assert(isa<BoolValue>(Current));
+ // Widen to Top, because we know they are different values. If previous was
+ // already Top, re-use that to (implicitly) indicate that no change occured.
+ if (isa<TopBoolValue>(Prev))
+ return Prev;
+ return CurrentEnv.makeTopBoolValue();
+ }
+
+ // FIXME: Add other built-in model widening.
- auto &Loc = Env.createStorageLocation(D);
- Env.setStorageLocation(D, Loc);
- if (auto *Val = Env.createValue(D.getType()))
- Env.setValue(Loc, *Val);
+ // Custom-model widening.
+ if (auto *W = Model.widen(Type, Prev, PrevEnv, Current, CurrentEnv))
+ return *W;
+
+ // Default of widening is a no-op: leave the current value unchanged.
+ return Current;
}
/// Initializes a global storage value.
-static void initGlobalVar(const Decl &D, Environment &Env) {
+static void insertIfGlobal(const Decl &D,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars) {
if (auto *V = dyn_cast<VarDecl>(&D))
- initGlobalVar(*V, Env);
-}
-
-/// Initializes global storage values that are declared or referenced from
-/// sub-statements of `S`.
-// FIXME: Add support for resetting globals after function calls to enable
-// the implementation of sound analyses.
-static void initGlobalVars(const Stmt &S, Environment &Env) {
- for (auto *Child : S.children()) {
+ if (V->hasGlobalStorage())
+ Vars.insert(V);
+}
+
+static void getFieldsAndGlobalVars(const Decl &D,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars) {
+ insertIfGlobal(D, Fields, Vars);
+ if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D))
+ for (const auto *B : Decomp->bindings())
+ if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()))
+ // FIXME: should we be using `E->getFoundDecl()`?
+ if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
+ Fields.insert(FD);
+}
+
+/// Traverses `S` and inserts into `Vars` any global storage values that are
+/// declared in or referenced from sub-statements.
+static void getFieldsAndGlobalVars(const Stmt &S,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars) {
+ for (auto *Child : S.children())
if (Child != nullptr)
- initGlobalVars(*Child, Env);
- }
+ getFieldsAndGlobalVars(*Child, Fields, Vars);
if (auto *DS = dyn_cast<DeclStmt>(&S)) {
- if (DS->isSingleDecl()) {
- initGlobalVar(*DS->getSingleDecl(), Env);
- } else {
+ if (DS->isSingleDecl())
+ getFieldsAndGlobalVars(*DS->getSingleDecl(), Fields, Vars);
+ else
for (auto *D : DS->getDeclGroup())
- initGlobalVar(*D, Env);
- }
+ getFieldsAndGlobalVars(*D, Fields, Vars);
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
- initGlobalVar(*E->getDecl(), Env);
+ insertIfGlobal(*E->getDecl(), Fields, Vars);
} else if (auto *E = dyn_cast<MemberExpr>(&S)) {
- initGlobalVar(*E->getMemberDecl(), Env);
+ // FIXME: should we be using `E->getFoundDecl()`?
+ const ValueDecl *VD = E->getMemberDecl();
+ insertIfGlobal(*VD, Fields, Vars);
+ if (const auto *FD = dyn_cast<FieldDecl>(VD))
+ Fields.insert(FD);
+ }
+}
+
+// FIXME: Add support for resetting globals after function calls to enable
+// the implementation of sound analyses.
+void Environment::initVars(llvm::DenseSet<const VarDecl *> Vars) {
+ for (const VarDecl *D : Vars) {
+ if (getStorageLocation(*D, SkipPast::None) != nullptr)
+ continue;
+ auto &Loc = createStorageLocation(*D);
+ setStorageLocation(*D, Loc);
+ if (auto *Val = createValue(D->getType()))
+ setValue(Loc, *Val);
}
}
@@ -154,9 +218,10 @@ Environment::Environment(DataflowAnalysisContext &DACtx)
: DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
Environment::Environment(const Environment &Other)
- : DACtx(Other.DACtx), DeclToLoc(Other.DeclToLoc),
- ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
- MemberLocToStruct(Other.MemberLocToStruct),
+ : DACtx(Other.DACtx), CallStack(Other.CallStack),
+ ReturnLoc(Other.ReturnLoc), ThisPointeeLoc(Other.ThisPointeeLoc),
+ DeclToLoc(Other.DeclToLoc), ExprToLoc(Other.ExprToLoc),
+ LocToVal(Other.LocToVal), MemberLocToStruct(Other.MemberLocToStruct),
FlowConditionToken(&DACtx->forkFlowCondition(*Other.FlowConditionToken)) {
}
@@ -169,9 +234,32 @@ Environment &Environment::operator=(const Environment &Other) {
Environment::Environment(DataflowAnalysisContext &DACtx,
const DeclContext &DeclCtx)
: Environment(DACtx) {
+ CallStack.push_back(&DeclCtx);
+
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
assert(FuncDecl->getBody() != nullptr);
- initGlobalVars(*FuncDecl->getBody(), *this);
+
+ llvm::DenseSet<const FieldDecl *> Fields;
+ llvm::DenseSet<const VarDecl *> Vars;
+
+ // Look for global variable references in the constructor-initializers.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&DeclCtx)) {
+ for (const auto *Init : CtorDecl->inits()) {
+ if (const auto *M = Init->getAnyMember())
+ Fields.insert(M);
+ const Expr *E = Init->getInit();
+ assert(E != nullptr);
+ getFieldsAndGlobalVars(*E, Fields, Vars);
+ }
+ }
+ getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
+
+ // These have to be added before the lines that follow to ensure that
+ // `create*` work correctly for structs.
+ DACtx.addModeledFields(Fields);
+
+ initVars(Vars);
+
for (const auto *ParamDecl : FuncDecl->parameters()) {
assert(ParamDecl != nullptr);
auto &ParamLoc = createStorageLocation(*ParamDecl);
@@ -179,6 +267,9 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
if (Value *ParamVal = createValue(ParamDecl->getType()))
setValue(ParamLoc, *ParamVal);
}
+
+ QualType ReturnType = FuncDecl->getReturnType();
+ ReturnLoc = &createStorageLocation(ReturnType);
}
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(&DeclCtx)) {
@@ -187,59 +278,132 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
if (Parent->isLambda())
MethodDecl = dyn_cast<CXXMethodDecl>(Parent->getDeclContext());
+ // FIXME: Initialize the ThisPointeeLoc of lambdas too.
if (MethodDecl && !MethodDecl->isStatic()) {
QualType ThisPointeeType = MethodDecl->getThisObjectType();
- // FIXME: Add support for union types.
- if (!ThisPointeeType->isUnionType()) {
- auto &ThisPointeeLoc = createStorageLocation(ThisPointeeType);
- DACtx.setThisPointeeStorageLocation(ThisPointeeLoc);
- if (Value *ThisPointeeVal = createValue(ThisPointeeType))
- setValue(ThisPointeeLoc, *ThisPointeeVal);
- }
+ ThisPointeeLoc = &createStorageLocation(ThisPointeeType);
+ if (Value *ThisPointeeVal = createValue(ThisPointeeType))
+ setValue(*ThisPointeeLoc, *ThisPointeeVal);
}
}
}
+bool Environment::canDescend(unsigned MaxDepth,
+ const DeclContext *Callee) const {
+ return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee);
+}
+
Environment Environment::pushCall(const CallExpr *Call) const {
Environment Env(*this);
- // FIXME: Currently this only works if the callee is never a method and the
- // same callee is never analyzed from multiple separate callsites. To
- // generalize this, we'll need to store a "context" field (probably a stack of
- // `const CallExpr *`s) in the `Environment`, and then change the
- // `DataflowAnalysisContext` class to hold a map from contexts to "frames",
- // where each frame stores its own version of what are currently the
- // `DeclToLoc`, `ExprToLoc`, and `ThisPointeeLoc` fields.
+ // FIXME: Support references here.
+ Env.ReturnLoc = getStorageLocation(*Call, SkipPast::Reference);
- const auto *FuncDecl = Call->getDirectCallee();
- assert(FuncDecl != nullptr);
- assert(FuncDecl->getBody() != nullptr);
- // FIXME: In order to allow the callee to reference globals, we probably need
- // to call `initGlobalVars` here in some way.
+ if (const auto *MethodCall = dyn_cast<CXXMemberCallExpr>(Call)) {
+ if (const Expr *Arg = MethodCall->getImplicitObjectArgument()) {
+ if (!isa<CXXThisExpr>(Arg))
+ Env.ThisPointeeLoc = getStorageLocation(*Arg, SkipPast::Reference);
+ // Otherwise (when the argument is `this`), retain the current
+ // environment's `ThisPointeeLoc`.
+ }
+ }
+
+ Env.pushCallInternal(Call->getDirectCallee(),
+ llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
+
+ return Env;
+}
- auto ParamIt = FuncDecl->param_begin();
- auto ArgIt = Call->arg_begin();
- auto ArgEnd = Call->arg_end();
+Environment Environment::pushCall(const CXXConstructExpr *Call) const {
+ Environment Env(*this);
+
+ // FIXME: Support references here.
+ Env.ReturnLoc = getStorageLocation(*Call, SkipPast::Reference);
+
+ Env.ThisPointeeLoc = Env.ReturnLoc;
+
+ Env.pushCallInternal(Call->getConstructor(),
+ llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
+
+ return Env;
+}
+
+void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
+ ArrayRef<const Expr *> Args) {
+ CallStack.push_back(FuncDecl);
+
+ // FIXME: Share this code with the constructor, rather than duplicating it.
+ llvm::DenseSet<const FieldDecl *> Fields;
+ llvm::DenseSet<const VarDecl *> Vars;
+ // Look for global variable references in the constructor-initializers.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FuncDecl)) {
+ for (const auto *Init : CtorDecl->inits()) {
+ if (const auto *M = Init->getAnyMember())
+ Fields.insert(M);
+ const Expr *E = Init->getInit();
+ assert(E != nullptr);
+ getFieldsAndGlobalVars(*E, Fields, Vars);
+ }
+ }
+ getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
+
+ // These have to be added before the lines that follow to ensure that
+ // `create*` work correctly for structs.
+ DACtx->addModeledFields(Fields);
+
+ initVars(Vars);
+
+ const auto *ParamIt = FuncDecl->param_begin();
// FIXME: Parameters don't always map to arguments 1:1; examples include
// overloaded operators implemented as member functions, and parameter packs.
- for (; ArgIt != ArgEnd; ++ParamIt, ++ArgIt) {
+ for (unsigned ArgIndex = 0; ArgIndex < Args.size(); ++ParamIt, ++ArgIndex) {
assert(ParamIt != FuncDecl->param_end());
+ const Expr *Arg = Args[ArgIndex];
+ auto *ArgLoc = getStorageLocation(*Arg, SkipPast::Reference);
+ if (ArgLoc == nullptr)
+ continue;
+
const VarDecl *Param = *ParamIt;
- const Expr *Arg = *ArgIt;
- auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference);
- assert(ArgLoc != nullptr);
- Env.setStorageLocation(*Param, *ArgLoc);
+ auto &Loc = createStorageLocation(*Param);
+ setStorageLocation(*Param, Loc);
+
+ QualType ParamType = Param->getType();
+ if (ParamType->isReferenceType()) {
+ auto &Val = takeOwnership(std::make_unique<ReferenceValue>(*ArgLoc));
+ setValue(Loc, Val);
+ } else if (auto *ArgVal = getValue(*ArgLoc)) {
+ setValue(Loc, *ArgVal);
+ } else if (Value *Val = createValue(ParamType)) {
+ setValue(Loc, *Val);
+ }
}
+}
- return Env;
+void Environment::popCall(const Environment &CalleeEnv) {
+ // We ignore `DACtx` because it's already the same in both. We don't want the
+ // callee's `DeclCtx`, `ReturnLoc` or `ThisPointeeLoc`. We don't bring back
+ // `DeclToLoc` and `ExprToLoc` because we want to be able to later analyze the
+ // same callee in a different context, and `setStorageLocation` requires there
+ // to not already be a storage location assigned. Conceptually, these maps
+ // capture information from the local scope, so when popping that scope, we do
+ // not propagate the maps.
+ this->LocToVal = std::move(CalleeEnv.LocToVal);
+ this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
+ this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
}
bool Environment::equivalentTo(const Environment &Other,
Environment::ValueModel &Model) const {
assert(DACtx == Other.DACtx);
+ if (ReturnLoc != Other.ReturnLoc)
+ return false;
+
+ if (ThisPointeeLoc != Other.ThisPointeeLoc)
+ return false;
+
if (DeclToLoc != Other.DeclToLoc)
return false;
@@ -259,21 +423,91 @@ bool Environment::equivalentTo(const Environment &Other,
continue;
assert(It->second != nullptr);
- if (!equivalentValues(Loc->getType(), Val, *this, It->second, Other, Model))
+ if (!areEquivalentValues(*Val, *It->second) &&
+ !compareDistinctValues(Loc->getType(), *Val, *this, *It->second, Other,
+ Model))
return false;
}
return true;
}
+LatticeJoinEffect Environment::widen(const Environment &PrevEnv,
+ Environment::ValueModel &Model) {
+ assert(DACtx == PrevEnv.DACtx);
+ assert(ReturnLoc == PrevEnv.ReturnLoc);
+ assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);
+ assert(CallStack == PrevEnv.CallStack);
+
+ auto Effect = LatticeJoinEffect::Unchanged;
+
+ // By the API, `PrevEnv` is a previous version of the environment for the same
+ // block, so we have some guarantees about its shape. In particular, it will
+ // be the result of a join or widen operation on previous values for this
+ // block. For `DeclToLoc` and `ExprToLoc`, join guarantees that these maps are
+ // subsets of the maps in `PrevEnv`. So, as long as we maintain this property
+ // here, we don't need change their current values to widen.
+ //
+ // FIXME: `MemberLocToStruct` does not share the above property, because
+ // `join` can cause the map size to increase (when we add fresh data in places
+ // of conflict). Once this issue with join is resolved, re-enable the
+ // assertion below or replace with something that captures the desired
+ // invariant.
+ assert(DeclToLoc.size() <= PrevEnv.DeclToLoc.size());
+ assert(ExprToLoc.size() <= PrevEnv.ExprToLoc.size());
+ // assert(MemberLocToStruct.size() <= PrevEnv.MemberLocToStruct.size());
+
+ llvm::DenseMap<const StorageLocation *, Value *> WidenedLocToVal;
+ for (auto &Entry : LocToVal) {
+ const StorageLocation *Loc = Entry.first;
+ assert(Loc != nullptr);
+
+ Value *Val = Entry.second;
+ assert(Val != nullptr);
+
+ auto PrevIt = PrevEnv.LocToVal.find(Loc);
+ if (PrevIt == PrevEnv.LocToVal.end())
+ continue;
+ assert(PrevIt->second != nullptr);
+
+ if (areEquivalentValues(*Val, *PrevIt->second)) {
+ WidenedLocToVal.insert({Loc, Val});
+ continue;
+ }
+
+ Value &WidenedVal = widenDistinctValues(Loc->getType(), *PrevIt->second,
+ PrevEnv, *Val, *this, Model);
+ WidenedLocToVal.insert({Loc, &WidenedVal});
+ if (&WidenedVal != PrevIt->second)
+ Effect = LatticeJoinEffect::Changed;
+ }
+ LocToVal = std::move(WidenedLocToVal);
+ // FIXME: update the equivalence calculation for `MemberLocToStruct`, once we
+ // have a systematic way of soundly comparing this map.
+ if (DeclToLoc.size() != PrevEnv.DeclToLoc.size() ||
+ ExprToLoc.size() != PrevEnv.ExprToLoc.size() ||
+ LocToVal.size() != PrevEnv.LocToVal.size() ||
+ MemberLocToStruct.size() != PrevEnv.MemberLocToStruct.size())
+ Effect = LatticeJoinEffect::Changed;
+
+ return Effect;
+}
+
LatticeJoinEffect Environment::join(const Environment &Other,
Environment::ValueModel &Model) {
assert(DACtx == Other.DACtx);
+ assert(ReturnLoc == Other.ReturnLoc);
+ assert(ThisPointeeLoc == Other.ThisPointeeLoc);
+ assert(CallStack == Other.CallStack);
auto Effect = LatticeJoinEffect::Unchanged;
Environment JoinedEnv(*DACtx);
+ JoinedEnv.CallStack = CallStack;
+ JoinedEnv.ReturnLoc = ReturnLoc;
+ JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
+
JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size())
Effect = LatticeJoinEffect::Changed;
@@ -288,6 +522,8 @@ LatticeJoinEffect Environment::join(const Environment &Other,
Effect = LatticeJoinEffect::Changed;
// FIXME: set `Effect` as needed.
+ // FIXME: update join to detect backedges and simplify the flow condition
+ // accordingly.
JoinedEnv.FlowConditionToken = &DACtx->joinFlowConditions(
*FlowConditionToken, *Other.FlowConditionToken);
@@ -303,14 +539,17 @@ LatticeJoinEffect Environment::join(const Environment &Other,
continue;
assert(It->second != nullptr);
- if (Val == It->second) {
+ if (areEquivalentValues(*Val, *It->second)) {
JoinedEnv.LocToVal.insert({Loc, Val});
continue;
}
- if (Value *MergedVal = mergeDistinctValues(
- Loc->getType(), Val, *this, It->second, Other, JoinedEnv, Model))
+ if (Value *MergedVal =
+ mergeDistinctValues(Loc->getType(), *Val, *this, *It->second, Other,
+ JoinedEnv, Model)) {
JoinedEnv.LocToVal.insert({Loc, MergedVal});
+ Effect = LatticeJoinEffect::Changed;
+ }
}
if (LocToVal.size() != JoinedEnv.LocToVal.size())
Effect = LatticeJoinEffect::Changed;
@@ -321,7 +560,7 @@ LatticeJoinEffect Environment::join(const Environment &Other,
}
StorageLocation &Environment::createStorageLocation(QualType Type) {
- return DACtx->getStableStorageLocation(Type);
+ return DACtx->createStorageLocation(Type);
}
StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
@@ -363,7 +602,11 @@ StorageLocation *Environment::getStorageLocation(const Expr &E,
}
StorageLocation *Environment::getThisPointeeStorageLocation() const {
- return DACtx->getThisPointeeStorageLocation();
+ return ThisPointeeLoc;
+}
+
+StorageLocation *Environment::getReturnStorageLocation() const {
+ return ReturnLoc;
}
PointerValue &Environment::getOrCreateNullPointerValue(QualType PointeeType) {
@@ -377,9 +620,9 @@ void Environment::setValue(const StorageLocation &Loc, Value &Val) {
auto &AggregateLoc = *cast<AggregateStorageLocation>(&Loc);
const QualType Type = AggregateLoc.getType();
- assert(Type->isStructureOrClassType());
+ assert(Type->isStructureOrClassType() || Type->isUnionType());
- for (const FieldDecl *Field : getObjectFields(Type)) {
+ for (const FieldDecl *Field : DACtx->getReferencedFields(Type)) {
assert(Field != nullptr);
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
@@ -450,6 +693,9 @@ Value *Environment::createValueUnlessSelfReferential(
}
if (Type->isIntegerType()) {
+ // FIXME: consider instead `return nullptr`, given that we do nothing useful
+ // with integers, and so distinguishing them serves no purpose, but could
+ // prevent convergence.
CreatedValuesCount++;
return &takeOwnership(std::make_unique<IntegerValue>());
}
@@ -488,12 +734,10 @@ Value *Environment::createValueUnlessSelfReferential(
return &takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
}
- if (Type->isStructureOrClassType()) {
+ if (Type->isStructureOrClassType() || Type->isUnionType()) {
CreatedValuesCount++;
- // FIXME: Initialize only fields that are accessed in the context that is
- // being analyzed.
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
- for (const FieldDecl *Field : getObjectFields(Type)) {
+ for (const FieldDecl *Field : DACtx->getReferencedFields(Type)) {
assert(Field != nullptr);
QualType FieldType = Field->getType();
@@ -546,9 +790,29 @@ bool Environment::flowConditionImplies(BoolValue &Val) const {
return DACtx->flowConditionImplies(*FlowConditionToken, Val);
}
-void Environment::dump() const {
+void Environment::dump(raw_ostream &OS) const {
+ // FIXME: add printing for remaining fields and allow caller to decide what
+ // fields are printed.
+ OS << "DeclToLoc:\n";
+ for (auto [D, L] : DeclToLoc)
+ OS << " [" << D->getName() << ", " << L << "]\n";
+
+ OS << "ExprToLoc:\n";
+ for (auto [E, L] : ExprToLoc)
+ OS << " [" << E << ", " << L << "]\n";
+
+ OS << "LocToVal:\n";
+ for (auto [L, V] : LocToVal) {
+ OS << " [" << L << ", " << V << ": " << *V << "]\n";
+ }
+
+ OS << "FlowConditionToken:\n";
DACtx->dumpFlowCondition(*FlowConditionToken);
}
+void Environment::dump() const {
+ dump(llvm::dbgs());
+}
+
} // namespace dataflow
} // namespace clang