2021-12-29 11:31:02 +00:00
|
|
|
//===-- DataflowEnvironment.cpp ---------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defines an Environment class that is used by dataflow analyses
|
|
|
|
// that run over Control-Flow Graphs (CFGs) to keep track of the state of the
|
|
|
|
// program at given program points.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
|
|
|
#include "clang/AST/Decl.h"
|
2022-01-11 12:15:53 +00:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2022-05-02 21:36:04 +00:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include "clang/AST/Type.h"
|
|
|
|
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
|
|
|
|
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
|
|
|
|
#include "clang/Analysis/FlowSensitive/Value.h"
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/DenseSet.h"
|
2022-04-27 17:21:59 +00:00
|
|
|
#include "llvm/Support/Casting.h"
|
2022-01-04 13:47:14 +00:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2022-02-23 13:38:51 +00:00
|
|
|
#include <cassert>
|
2021-12-29 11:31:02 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace dataflow {
|
|
|
|
|
2022-02-24 20:02:00 +00:00
|
|
|
// FIXME: convert these to parameters of the analysis or environment. Current
|
|
|
|
// settings have been experimentaly validated, but only for a particular
|
|
|
|
// analysis.
|
|
|
|
static constexpr int MaxCompositeValueDepth = 3;
|
|
|
|
static constexpr int MaxCompositeValueSize = 1000;
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
/// Returns a map consisting of key-value entries that are present in both maps.
|
|
|
|
template <typename K, typename V>
|
|
|
|
llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
|
|
|
|
const llvm::DenseMap<K, V> &Map2) {
|
|
|
|
llvm::DenseMap<K, V> Result;
|
|
|
|
for (auto &Entry : Map1) {
|
|
|
|
auto It = Map2.find(Entry.first);
|
|
|
|
if (It != Map2.end() && Entry.second == It->second)
|
|
|
|
Result.insert({Entry.first, Entry.second});
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2022-06-09 01:15:51 +02:00
|
|
|
static bool areEquivalentIndirectionValues(Value *Val1, Value *Val2) {
|
|
|
|
if (auto *IndVal1 = dyn_cast<ReferenceValue>(Val1)) {
|
|
|
|
auto *IndVal2 = cast<ReferenceValue>(Val2);
|
2022-06-15 00:41:49 +02:00
|
|
|
return &IndVal1->getReferentLoc() == &IndVal2->getReferentLoc();
|
2022-06-09 01:15:51 +02:00
|
|
|
}
|
|
|
|
if (auto *IndVal1 = dyn_cast<PointerValue>(Val1)) {
|
|
|
|
auto *IndVal2 = cast<PointerValue>(Val2);
|
|
|
|
return &IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-01-31 10:43:07 +00:00
|
|
|
/// Returns true if and only if `Val1` is equivalent to `Val2`.
|
2022-03-04 11:03:29 +00:00
|
|
|
static bool equivalentValues(QualType Type, Value *Val1,
|
|
|
|
const Environment &Env1, Value *Val2,
|
|
|
|
const Environment &Env2,
|
2022-01-31 10:43:07 +00:00
|
|
|
Environment::ValueModel &Model) {
|
2022-06-09 01:15:51 +02:00
|
|
|
return Val1 == Val2 || areEquivalentIndirectionValues(Val1, Val2) ||
|
|
|
|
Model.compareEquivalent(Type, *Val1, Env1, *Val2, Env2);
|
2022-01-31 10:43:07 +00:00
|
|
|
}
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
/// Attempts to merge distinct values `Val1` and `Val2` in `Env1` and `Env2`,
|
2022-03-28 15:38:17 +00:00
|
|
|
/// 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`.
|
2022-04-14 13:42:02 +00:00
|
|
|
static Value *mergeDistinctValues(QualType Type, Value *Val1,
|
|
|
|
const Environment &Env1, Value *Val2,
|
|
|
|
const Environment &Env2,
|
|
|
|
Environment &MergedEnv,
|
2022-03-28 15:38:17 +00:00
|
|
|
Environment::ValueModel &Model) {
|
|
|
|
// Join distinct boolean values preserving information about the constraints
|
2022-04-25 15:23:42 +00:00
|
|
|
// in the respective path conditions.
|
[clang][dataflow] Weaken abstract comparison to enable loop termination.
Currently, when the framework is used with an analysis that does not override
`compareEquivalent`, it does not terminate for most loops. The root cause is the
interaction of (the default implementation of) environment comparison
(`compareEquivalent`) and the means by which locations and values are
allocated. Specifically, the creation of certain values (including: reference
and pointer values; merged values) results in allocations of fresh locations in
the environment. As a result, analysis of even trivial loop bodies produces
different (if isomorphic) environments, on identical inputs. At the same time,
the default analysis relies on strict equality (versus some relaxed notion of
equivalence). Together, when the analysis compares these isomorphic, yet
unequal, environments, to determine whether the successors of the given block
need to be (re)processed, the result is invariably "yes", thus preventing loop
analysis from reaching a fixed point.
There are many possible solutions to this problem, including equivalence that is
less than strict pointer equality (like structural equivalence) and/or the
introduction of an explicit widening operation. However, these solutions will
require care to be implemented correctly. While a high priority, it seems more
urgent that we fix the current default implentation to allow
termination. Therefore, this patch proposes, essentially, to change the default
comparison to trivally equate any two values. As a result, we can say precisely
that the analysis will process the loop exactly twice -- once to establish an
initial result state and the second to produce an updated result which will
(always) compare equal to the previous. While clearly unsound -- we are not
reaching a fix point of the transfer function, in practice, this level of
analysis will find many practical issues where a single iteration of the loop
impacts abstract program state.
Note, however, that the change to the default `merge` operation does not affect
soundness, because the framework already produces a fresh (sound) abstraction of
the value when the two values are distinct. The previous setting was overly
conservative.
Differential Revision: https://reviews.llvm.org/D123586
2022-04-05 19:23:13 +00:00
|
|
|
//
|
|
|
|
// FIXME: Does not work for backedges, since the two (or more) paths will not
|
|
|
|
// have mutually exclusive conditions.
|
2022-03-28 15:38:17 +00:00
|
|
|
if (auto *Expr1 = dyn_cast<BoolValue>(Val1)) {
|
|
|
|
auto *Expr2 = cast<BoolValue>(Val2);
|
2022-06-20 11:02:51 +00:00
|
|
|
auto &MergedVal = MergedEnv.makeAtomicBoolValue();
|
|
|
|
MergedEnv.addToFlowCondition(MergedEnv.makeOr(
|
|
|
|
MergedEnv.makeAnd(Env1.getFlowConditionToken(),
|
|
|
|
MergedEnv.makeIff(MergedVal, *Expr1)),
|
|
|
|
MergedEnv.makeAnd(Env2.getFlowConditionToken(),
|
|
|
|
MergedEnv.makeIff(MergedVal, *Expr2))));
|
|
|
|
return &MergedVal;
|
2022-03-28 15:38:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-27 17:21:59 +00:00
|
|
|
// FIXME: add unit tests that cover this statement.
|
2022-06-09 01:15:51 +02:00
|
|
|
if (areEquivalentIndirectionValues(Val1, Val2)) {
|
|
|
|
return Val1;
|
2022-04-27 17:21:59 +00:00
|
|
|
}
|
|
|
|
|
2022-03-28 15:38:17 +00:00
|
|
|
// FIXME: Consider destroying `MergedValue` immediately if `ValueModel::merge`
|
|
|
|
// returns false to avoid storing unneeded values in `DACtx`.
|
2022-04-14 13:42:02 +00:00
|
|
|
if (Value *MergedVal = MergedEnv.createValue(Type))
|
|
|
|
if (Model.merge(Type, *Val1, Env1, *Val2, Env2, *MergedVal, MergedEnv))
|
2022-03-28 15:38:17 +00:00
|
|
|
return MergedVal;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-02-23 13:38:51 +00:00
|
|
|
/// Initializes a global storage value.
|
|
|
|
static void initGlobalVar(const VarDecl &D, Environment &Env) {
|
|
|
|
if (!D.hasGlobalStorage() ||
|
|
|
|
Env.getStorageLocation(D, SkipPast::None) != nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(D);
|
|
|
|
Env.setStorageLocation(D, Loc);
|
|
|
|
if (auto *Val = Env.createValue(D.getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Initializes a global storage value.
|
|
|
|
static void initGlobalVar(const Decl &D, Environment &Env) {
|
|
|
|
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 (Child != nullptr)
|
|
|
|
initGlobalVars(*Child, Env);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto *DS = dyn_cast<DeclStmt>(&S)) {
|
|
|
|
if (DS->isSingleDecl()) {
|
|
|
|
initGlobalVar(*DS->getSingleDecl(), Env);
|
|
|
|
} else {
|
|
|
|
for (auto *D : DS->getDeclGroup())
|
|
|
|
initGlobalVar(*D, Env);
|
|
|
|
}
|
|
|
|
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
|
|
|
|
initGlobalVar(*E->getDecl(), Env);
|
|
|
|
} else if (auto *E = dyn_cast<MemberExpr>(&S)) {
|
|
|
|
initGlobalVar(*E->getMemberDecl(), Env);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-25 15:23:42 +00:00
|
|
|
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),
|
|
|
|
FlowConditionToken(&DACtx->forkFlowCondition(*Other.FlowConditionToken)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
Environment &Environment::operator=(const Environment &Other) {
|
|
|
|
Environment Copy(Other);
|
|
|
|
*this = std::move(Copy);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:15:53 +00:00
|
|
|
Environment::Environment(DataflowAnalysisContext &DACtx,
|
|
|
|
const DeclContext &DeclCtx)
|
|
|
|
: Environment(DACtx) {
|
|
|
|
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
|
2022-02-23 13:38:51 +00:00
|
|
|
assert(FuncDecl->getBody() != nullptr);
|
|
|
|
initGlobalVars(*FuncDecl->getBody(), *this);
|
2022-01-11 12:15:53 +00:00
|
|
|
for (const auto *ParamDecl : FuncDecl->parameters()) {
|
|
|
|
assert(ParamDecl != nullptr);
|
|
|
|
auto &ParamLoc = createStorageLocation(*ParamDecl);
|
|
|
|
setStorageLocation(*ParamDecl, ParamLoc);
|
2022-01-17 15:17:05 +00:00
|
|
|
if (Value *ParamVal = createValue(ParamDecl->getType()))
|
|
|
|
setValue(ParamLoc, *ParamVal);
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(&DeclCtx)) {
|
2022-05-25 20:15:46 +00:00
|
|
|
auto *Parent = MethodDecl->getParent();
|
|
|
|
assert(Parent != nullptr);
|
|
|
|
if (Parent->isLambda())
|
|
|
|
MethodDecl = dyn_cast<CXXMethodDecl>(Parent->getDeclContext());
|
|
|
|
|
|
|
|
if (MethodDecl && !MethodDecl->isStatic()) {
|
2022-01-11 12:15:53 +00:00
|
|
|
QualType ThisPointeeType = MethodDecl->getThisObjectType();
|
|
|
|
// FIXME: Add support for union types.
|
|
|
|
if (!ThisPointeeType->isUnionType()) {
|
|
|
|
auto &ThisPointeeLoc = createStorageLocation(ThisPointeeType);
|
|
|
|
DACtx.setThisPointeeStorageLocation(ThisPointeeLoc);
|
2022-01-17 15:17:05 +00:00
|
|
|
if (Value *ThisPointeeVal = createValue(ThisPointeeType))
|
|
|
|
setValue(ThisPointeeLoc, *ThisPointeeVal);
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-31 10:43:07 +00:00
|
|
|
bool Environment::equivalentTo(const Environment &Other,
|
|
|
|
Environment::ValueModel &Model) const {
|
2021-12-29 11:31:02 +00:00
|
|
|
assert(DACtx == Other.DACtx);
|
2022-01-31 10:43:07 +00:00
|
|
|
|
|
|
|
if (DeclToLoc != Other.DeclToLoc)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (ExprToLoc != Other.ExprToLoc)
|
|
|
|
return false;
|
|
|
|
|
[clang][dataflow] Weaken abstract comparison to enable loop termination.
Currently, when the framework is used with an analysis that does not override
`compareEquivalent`, it does not terminate for most loops. The root cause is the
interaction of (the default implementation of) environment comparison
(`compareEquivalent`) and the means by which locations and values are
allocated. Specifically, the creation of certain values (including: reference
and pointer values; merged values) results in allocations of fresh locations in
the environment. As a result, analysis of even trivial loop bodies produces
different (if isomorphic) environments, on identical inputs. At the same time,
the default analysis relies on strict equality (versus some relaxed notion of
equivalence). Together, when the analysis compares these isomorphic, yet
unequal, environments, to determine whether the successors of the given block
need to be (re)processed, the result is invariably "yes", thus preventing loop
analysis from reaching a fixed point.
There are many possible solutions to this problem, including equivalence that is
less than strict pointer equality (like structural equivalence) and/or the
introduction of an explicit widening operation. However, these solutions will
require care to be implemented correctly. While a high priority, it seems more
urgent that we fix the current default implentation to allow
termination. Therefore, this patch proposes, essentially, to change the default
comparison to trivally equate any two values. As a result, we can say precisely
that the analysis will process the loop exactly twice -- once to establish an
initial result state and the second to produce an updated result which will
(always) compare equal to the previous. While clearly unsound -- we are not
reaching a fix point of the transfer function, in practice, this level of
analysis will find many practical issues where a single iteration of the loop
impacts abstract program state.
Note, however, that the change to the default `merge` operation does not affect
soundness, because the framework already produces a fresh (sound) abstraction of
the value when the two values are distinct. The previous setting was overly
conservative.
Differential Revision: https://reviews.llvm.org/D123586
2022-04-05 19:23:13 +00:00
|
|
|
// Compare the contents for the intersection of their domains.
|
2022-01-31 10:43:07 +00:00
|
|
|
for (auto &Entry : LocToVal) {
|
|
|
|
const StorageLocation *Loc = Entry.first;
|
|
|
|
assert(Loc != nullptr);
|
|
|
|
|
|
|
|
Value *Val = Entry.second;
|
|
|
|
assert(Val != nullptr);
|
|
|
|
|
|
|
|
auto It = Other.LocToVal.find(Loc);
|
|
|
|
if (It == Other.LocToVal.end())
|
[clang][dataflow] Weaken abstract comparison to enable loop termination.
Currently, when the framework is used with an analysis that does not override
`compareEquivalent`, it does not terminate for most loops. The root cause is the
interaction of (the default implementation of) environment comparison
(`compareEquivalent`) and the means by which locations and values are
allocated. Specifically, the creation of certain values (including: reference
and pointer values; merged values) results in allocations of fresh locations in
the environment. As a result, analysis of even trivial loop bodies produces
different (if isomorphic) environments, on identical inputs. At the same time,
the default analysis relies on strict equality (versus some relaxed notion of
equivalence). Together, when the analysis compares these isomorphic, yet
unequal, environments, to determine whether the successors of the given block
need to be (re)processed, the result is invariably "yes", thus preventing loop
analysis from reaching a fixed point.
There are many possible solutions to this problem, including equivalence that is
less than strict pointer equality (like structural equivalence) and/or the
introduction of an explicit widening operation. However, these solutions will
require care to be implemented correctly. While a high priority, it seems more
urgent that we fix the current default implentation to allow
termination. Therefore, this patch proposes, essentially, to change the default
comparison to trivally equate any two values. As a result, we can say precisely
that the analysis will process the loop exactly twice -- once to establish an
initial result state and the second to produce an updated result which will
(always) compare equal to the previous. While clearly unsound -- we are not
reaching a fix point of the transfer function, in practice, this level of
analysis will find many practical issues where a single iteration of the loop
impacts abstract program state.
Note, however, that the change to the default `merge` operation does not affect
soundness, because the framework already produces a fresh (sound) abstraction of
the value when the two values are distinct. The previous setting was overly
conservative.
Differential Revision: https://reviews.llvm.org/D123586
2022-04-05 19:23:13 +00:00
|
|
|
continue;
|
2022-01-31 10:43:07 +00:00
|
|
|
assert(It->second != nullptr);
|
|
|
|
|
2022-03-04 11:03:29 +00:00
|
|
|
if (!equivalentValues(Loc->getType(), Val, *this, It->second, Other, Model))
|
2022-01-31 10:43:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-24 13:29:06 +00:00
|
|
|
LatticeJoinEffect Environment::join(const Environment &Other,
|
2022-01-31 10:43:07 +00:00
|
|
|
Environment::ValueModel &Model) {
|
2021-12-29 11:31:02 +00:00
|
|
|
assert(DACtx == Other.DACtx);
|
|
|
|
|
|
|
|
auto Effect = LatticeJoinEffect::Unchanged;
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
Environment JoinedEnv(*DACtx);
|
|
|
|
|
|
|
|
JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
|
|
|
|
if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size())
|
2021-12-29 11:31:02 +00:00
|
|
|
Effect = LatticeJoinEffect::Changed;
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
JoinedEnv.ExprToLoc = intersectDenseMaps(ExprToLoc, Other.ExprToLoc);
|
|
|
|
if (ExprToLoc.size() != JoinedEnv.ExprToLoc.size())
|
2022-01-20 09:28:25 +00:00
|
|
|
Effect = LatticeJoinEffect::Changed;
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
JoinedEnv.MemberLocToStruct =
|
2022-02-23 15:39:26 +00:00
|
|
|
intersectDenseMaps(MemberLocToStruct, Other.MemberLocToStruct);
|
2022-04-14 13:42:02 +00:00
|
|
|
if (MemberLocToStruct.size() != JoinedEnv.MemberLocToStruct.size())
|
2022-02-23 15:39:26 +00:00
|
|
|
Effect = LatticeJoinEffect::Changed;
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
// FIXME: set `Effect` as needed.
|
2022-04-25 15:23:42 +00:00
|
|
|
JoinedEnv.FlowConditionToken = &DACtx->joinFlowConditions(
|
|
|
|
*FlowConditionToken, *Other.FlowConditionToken);
|
2022-04-14 13:42:02 +00:00
|
|
|
|
|
|
|
for (auto &Entry : LocToVal) {
|
2022-01-24 13:29:06 +00:00
|
|
|
const StorageLocation *Loc = Entry.first;
|
|
|
|
assert(Loc != nullptr);
|
|
|
|
|
|
|
|
Value *Val = Entry.second;
|
|
|
|
assert(Val != nullptr);
|
|
|
|
|
|
|
|
auto It = Other.LocToVal.find(Loc);
|
|
|
|
if (It == Other.LocToVal.end())
|
|
|
|
continue;
|
|
|
|
assert(It->second != nullptr);
|
|
|
|
|
[clang][dataflow] Weaken abstract comparison to enable loop termination.
Currently, when the framework is used with an analysis that does not override
`compareEquivalent`, it does not terminate for most loops. The root cause is the
interaction of (the default implementation of) environment comparison
(`compareEquivalent`) and the means by which locations and values are
allocated. Specifically, the creation of certain values (including: reference
and pointer values; merged values) results in allocations of fresh locations in
the environment. As a result, analysis of even trivial loop bodies produces
different (if isomorphic) environments, on identical inputs. At the same time,
the default analysis relies on strict equality (versus some relaxed notion of
equivalence). Together, when the analysis compares these isomorphic, yet
unequal, environments, to determine whether the successors of the given block
need to be (re)processed, the result is invariably "yes", thus preventing loop
analysis from reaching a fixed point.
There are many possible solutions to this problem, including equivalence that is
less than strict pointer equality (like structural equivalence) and/or the
introduction of an explicit widening operation. However, these solutions will
require care to be implemented correctly. While a high priority, it seems more
urgent that we fix the current default implentation to allow
termination. Therefore, this patch proposes, essentially, to change the default
comparison to trivally equate any two values. As a result, we can say precisely
that the analysis will process the loop exactly twice -- once to establish an
initial result state and the second to produce an updated result which will
(always) compare equal to the previous. While clearly unsound -- we are not
reaching a fix point of the transfer function, in practice, this level of
analysis will find many practical issues where a single iteration of the loop
impacts abstract program state.
Note, however, that the change to the default `merge` operation does not affect
soundness, because the framework already produces a fresh (sound) abstraction of
the value when the two values are distinct. The previous setting was overly
conservative.
Differential Revision: https://reviews.llvm.org/D123586
2022-04-05 19:23:13 +00:00
|
|
|
if (Val == It->second) {
|
2022-04-14 13:42:02 +00:00
|
|
|
JoinedEnv.LocToVal.insert({Loc, Val});
|
2022-01-24 13:29:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
if (Value *MergedVal = mergeDistinctValues(
|
|
|
|
Loc->getType(), Val, *this, It->second, Other, JoinedEnv, Model))
|
|
|
|
JoinedEnv.LocToVal.insert({Loc, MergedVal});
|
2022-01-24 13:29:06 +00:00
|
|
|
}
|
2022-04-14 13:42:02 +00:00
|
|
|
if (LocToVal.size() != JoinedEnv.LocToVal.size())
|
2021-12-29 11:31:02 +00:00
|
|
|
Effect = LatticeJoinEffect::Changed;
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
*this = std::move(JoinedEnv);
|
2022-03-04 11:03:29 +00:00
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
return Effect;
|
|
|
|
}
|
|
|
|
|
|
|
|
StorageLocation &Environment::createStorageLocation(QualType Type) {
|
2022-06-27 11:12:37 +02:00
|
|
|
return DACtx->getStableStorageLocation(Type);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
|
|
|
|
// Evaluated declarations are always assigned the same storage locations to
|
|
|
|
// ensure that the environment stabilizes across loop iterations. Storage
|
|
|
|
// locations for evaluated declarations are stored in the analysis context.
|
2022-06-27 11:12:37 +02:00
|
|
|
return DACtx->getStableStorageLocation(D);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
StorageLocation &Environment::createStorageLocation(const Expr &E) {
|
|
|
|
// Evaluated expressions are always assigned the same storage locations to
|
|
|
|
// ensure that the environment stabilizes across loop iterations. Storage
|
|
|
|
// locations for evaluated expressions are stored in the analysis context.
|
2022-06-27 11:12:37 +02:00
|
|
|
return DACtx->getStableStorageLocation(E);
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {
|
|
|
|
assert(DeclToLoc.find(&D) == DeclToLoc.end());
|
|
|
|
DeclToLoc[&D] = &Loc;
|
|
|
|
}
|
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
StorageLocation *Environment::getStorageLocation(const ValueDecl &D,
|
|
|
|
SkipPast SP) const {
|
2021-12-29 11:31:02 +00:00
|
|
|
auto It = DeclToLoc.find(&D);
|
2022-01-04 13:47:14 +00:00
|
|
|
return It == DeclToLoc.end() ? nullptr : &skip(*It->second, SP);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
|
2022-05-04 21:08:43 +00:00
|
|
|
const Expr &CanonE = ignoreCFGOmittedNodes(E);
|
|
|
|
assert(ExprToLoc.find(&CanonE) == ExprToLoc.end());
|
|
|
|
ExprToLoc[&CanonE] = &Loc;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StorageLocation *Environment::getStorageLocation(const Expr &E,
|
|
|
|
SkipPast SP) const {
|
2022-03-16 22:20:05 +00:00
|
|
|
// FIXME: Add a test with parens.
|
2022-05-04 21:08:43 +00:00
|
|
|
auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
|
2022-01-04 13:47:14 +00:00
|
|
|
return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-11 12:15:53 +00:00
|
|
|
StorageLocation *Environment::getThisPointeeStorageLocation() const {
|
|
|
|
return DACtx->getThisPointeeStorageLocation();
|
|
|
|
}
|
|
|
|
|
2022-06-27 14:14:01 +02:00
|
|
|
PointerValue &Environment::getOrCreateNullPointerValue(QualType PointeeType) {
|
|
|
|
return DACtx->getOrCreateNullPointerValue(PointeeType);
|
|
|
|
}
|
|
|
|
|
2022-01-13 13:53:52 +00:00
|
|
|
void Environment::setValue(const StorageLocation &Loc, Value &Val) {
|
|
|
|
LocToVal[&Loc] = &Val;
|
|
|
|
|
|
|
|
if (auto *StructVal = dyn_cast<StructValue>(&Val)) {
|
|
|
|
auto &AggregateLoc = *cast<AggregateStorageLocation>(&Loc);
|
|
|
|
|
|
|
|
const QualType Type = AggregateLoc.getType();
|
|
|
|
assert(Type->isStructureOrClassType());
|
|
|
|
|
2022-05-25 17:57:27 +00:00
|
|
|
for (const FieldDecl *Field : getObjectFields(Type)) {
|
2022-01-13 13:53:52 +00:00
|
|
|
assert(Field != nullptr);
|
2022-02-23 15:39:26 +00:00
|
|
|
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
|
|
|
|
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
|
2022-03-07 21:16:28 +00:00
|
|
|
if (auto *FieldVal = StructVal->getChild(*Field))
|
|
|
|
setValue(FieldLoc, *FieldVal);
|
2022-01-13 13:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-23 15:39:26 +00:00
|
|
|
|
|
|
|
auto IT = MemberLocToStruct.find(&Loc);
|
|
|
|
if (IT != MemberLocToStruct.end()) {
|
|
|
|
// `Loc` is the location of a struct member so we need to also update the
|
|
|
|
// value of the member in the corresponding `StructValue`.
|
|
|
|
|
|
|
|
assert(IT->second.first != nullptr);
|
|
|
|
StructValue &StructVal = *IT->second.first;
|
|
|
|
|
|
|
|
assert(IT->second.second != nullptr);
|
|
|
|
const ValueDecl &Member = *IT->second.second;
|
|
|
|
|
|
|
|
StructVal.setChild(Member, Val);
|
|
|
|
}
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Value *Environment::getValue(const StorageLocation &Loc) const {
|
|
|
|
auto It = LocToVal.find(&Loc);
|
|
|
|
return It == LocToVal.end() ? nullptr : It->second;
|
|
|
|
}
|
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
Value *Environment::getValue(const ValueDecl &D, SkipPast SP) const {
|
|
|
|
auto *Loc = getStorageLocation(D, SP);
|
|
|
|
if (Loc == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
return getValue(*Loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
Value *Environment::getValue(const Expr &E, SkipPast SP) const {
|
|
|
|
auto *Loc = getStorageLocation(E, SP);
|
|
|
|
if (Loc == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
return getValue(*Loc);
|
|
|
|
}
|
|
|
|
|
2022-01-17 15:17:05 +00:00
|
|
|
Value *Environment::createValue(QualType Type) {
|
2021-12-29 11:31:02 +00:00
|
|
|
llvm::DenseSet<QualType> Visited;
|
2022-02-24 20:02:00 +00:00
|
|
|
int CreatedValuesCount = 0;
|
|
|
|
Value *Val = createValueUnlessSelfReferential(Type, Visited, /*Depth=*/0,
|
|
|
|
CreatedValuesCount);
|
|
|
|
if (CreatedValuesCount > MaxCompositeValueSize) {
|
2022-04-20 22:09:03 +01:00
|
|
|
llvm::errs() << "Attempting to initialize a huge value of type: " << Type
|
|
|
|
<< '\n';
|
2022-02-24 20:02:00 +00:00
|
|
|
}
|
|
|
|
return Val;
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-17 15:17:05 +00:00
|
|
|
Value *Environment::createValueUnlessSelfReferential(
|
2022-02-24 20:02:00 +00:00
|
|
|
QualType Type, llvm::DenseSet<QualType> &Visited, int Depth,
|
|
|
|
int &CreatedValuesCount) {
|
2021-12-29 11:31:02 +00:00
|
|
|
assert(!Type.isNull());
|
|
|
|
|
2022-02-24 20:02:00 +00:00
|
|
|
// Allow unlimited fields at depth 1; only cap at deeper nesting levels.
|
|
|
|
if ((Depth > 1 && CreatedValuesCount > MaxCompositeValueSize) ||
|
|
|
|
Depth > MaxCompositeValueDepth)
|
|
|
|
return nullptr;
|
|
|
|
|
2022-03-04 11:03:29 +00:00
|
|
|
if (Type->isBooleanType()) {
|
|
|
|
CreatedValuesCount++;
|
|
|
|
return &makeAtomicBoolValue();
|
|
|
|
}
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
if (Type->isIntegerType()) {
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2022-01-17 15:17:05 +00:00
|
|
|
return &takeOwnership(std::make_unique<IntegerValue>());
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Type->isReferenceType()) {
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2022-03-09 10:51:43 +00:00
|
|
|
QualType PointeeType = Type->castAs<ReferenceType>()->getPointeeType();
|
2021-12-29 11:31:02 +00:00
|
|
|
auto &PointeeLoc = createStorageLocation(PointeeType);
|
|
|
|
|
2022-06-18 10:41:26 -07:00
|
|
|
if (Visited.insert(PointeeType.getCanonicalType()).second) {
|
2022-02-24 20:02:00 +00:00
|
|
|
Value *PointeeVal = createValueUnlessSelfReferential(
|
|
|
|
PointeeType, Visited, Depth, CreatedValuesCount);
|
2021-12-29 11:31:02 +00:00
|
|
|
Visited.erase(PointeeType.getCanonicalType());
|
2022-01-17 15:17:05 +00:00
|
|
|
|
|
|
|
if (PointeeVal != nullptr)
|
|
|
|
setValue(PointeeLoc, *PointeeVal);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-17 15:17:05 +00:00
|
|
|
return &takeOwnership(std::make_unique<ReferenceValue>(PointeeLoc));
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Type->isPointerType()) {
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2022-03-09 10:51:43 +00:00
|
|
|
QualType PointeeType = Type->castAs<PointerType>()->getPointeeType();
|
2021-12-29 11:31:02 +00:00
|
|
|
auto &PointeeLoc = createStorageLocation(PointeeType);
|
|
|
|
|
2022-06-18 10:41:26 -07:00
|
|
|
if (Visited.insert(PointeeType.getCanonicalType()).second) {
|
2022-02-24 20:02:00 +00:00
|
|
|
Value *PointeeVal = createValueUnlessSelfReferential(
|
|
|
|
PointeeType, Visited, Depth, CreatedValuesCount);
|
2021-12-29 11:31:02 +00:00
|
|
|
Visited.erase(PointeeType.getCanonicalType());
|
2022-01-17 15:17:05 +00:00
|
|
|
|
|
|
|
if (PointeeVal != nullptr)
|
|
|
|
setValue(PointeeLoc, *PointeeVal);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-17 15:17:05 +00:00
|
|
|
return &takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Type->isStructureOrClassType()) {
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2022-01-11 12:15:53 +00:00
|
|
|
// FIXME: Initialize only fields that are accessed in the context that is
|
|
|
|
// being analyzed.
|
2021-12-29 11:31:02 +00:00
|
|
|
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
|
2022-05-25 17:57:27 +00:00
|
|
|
for (const FieldDecl *Field : getObjectFields(Type)) {
|
2021-12-29 11:31:02 +00:00
|
|
|
assert(Field != nullptr);
|
|
|
|
|
|
|
|
QualType FieldType = Field->getType();
|
|
|
|
if (Visited.contains(FieldType.getCanonicalType()))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Visited.insert(FieldType.getCanonicalType());
|
2022-03-07 21:16:28 +00:00
|
|
|
if (auto *FieldValue = createValueUnlessSelfReferential(
|
|
|
|
FieldType, Visited, Depth + 1, CreatedValuesCount))
|
|
|
|
FieldValues.insert({Field, FieldValue});
|
2021-12-29 11:31:02 +00:00
|
|
|
Visited.erase(FieldType.getCanonicalType());
|
|
|
|
}
|
|
|
|
|
2022-01-17 15:17:05 +00:00
|
|
|
return &takeOwnership(
|
|
|
|
std::make_unique<StructValue>(std::move(FieldValues)));
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const {
|
|
|
|
switch (SP) {
|
|
|
|
case SkipPast::None:
|
|
|
|
return Loc;
|
|
|
|
case SkipPast::Reference:
|
|
|
|
// References cannot be chained so we only need to skip past one level of
|
|
|
|
// indirection.
|
|
|
|
if (auto *Val = dyn_cast_or_null<ReferenceValue>(getValue(Loc)))
|
2022-06-15 00:41:49 +02:00
|
|
|
return Val->getReferentLoc();
|
2022-01-04 13:47:14 +00:00
|
|
|
return Loc;
|
2022-01-11 12:15:53 +00:00
|
|
|
case SkipPast::ReferenceThenPointer:
|
|
|
|
StorageLocation &LocPastRef = skip(Loc, SkipPast::Reference);
|
|
|
|
if (auto *Val = dyn_cast_or_null<PointerValue>(getValue(LocPastRef)))
|
|
|
|
return Val->getPointeeLoc();
|
|
|
|
return LocPastRef;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
llvm_unreachable("bad SkipPast kind");
|
|
|
|
}
|
|
|
|
|
|
|
|
const StorageLocation &Environment::skip(const StorageLocation &Loc,
|
|
|
|
SkipPast SP) const {
|
|
|
|
return skip(*const_cast<StorageLocation *>(&Loc), SP);
|
|
|
|
}
|
|
|
|
|
2022-03-01 11:19:00 +00:00
|
|
|
void Environment::addToFlowCondition(BoolValue &Val) {
|
2022-04-25 15:23:42 +00:00
|
|
|
DACtx->addFlowConditionConstraint(*FlowConditionToken, Val);
|
2022-03-01 11:19:00 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 11:03:29 +00:00
|
|
|
bool Environment::flowConditionImplies(BoolValue &Val) const {
|
2022-04-25 15:23:42 +00:00
|
|
|
return DACtx->flowConditionImplies(*FlowConditionToken, Val);
|
2022-03-01 11:19:00 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
} // namespace dataflow
|
|
|
|
} // namespace clang
|