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"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include "clang/AST/Type.h"
|
|
|
|
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
|
|
|
|
#include "clang/Analysis/FlowSensitive/Value.h"
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/DenseSet.h"
|
2022-08-16 18:27:41 +02:00
|
|
|
#include "llvm/ADT/STLExtras.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-11-03 00:36:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
llvm_unreachable("All cases covered in switch");
|
|
|
|
}
|
|
|
|
|
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-11-03 00:36:58 +00:00
|
|
|
static Value *mergeDistinctValues(QualType Type, Value &Val1,
|
|
|
|
const Environment &Env1, Value &Val2,
|
2022-04-14 13:42:02 +00:00
|
|
|
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.
|
2023-01-13 18:33:52 +00:00
|
|
|
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:
|
|
|
|
// ```
|
2022-12-29 14:40:40 +08:00
|
|
|
// std::optional<bool> o;
|
|
|
|
// int x;
|
2023-01-13 18:33:52 +00:00
|
|
|
// if (o.has_value())
|
2022-12-29 14:40:40 +08:00
|
|
|
// x = o.value();
|
2023-01-13 18:33:52 +00:00
|
|
|
// ```
|
2022-12-29 14:40:40 +08:00
|
|
|
auto *Expr1 = cast<BoolValue>(&Val1);
|
2022-11-03 00:36:58 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Consider destroying `MergedValue` immediately if `ValueModel::merge`
|
|
|
|
// returns false to avoid storing unneeded values in `DACtx`.
|
2023-01-13 18:33:52 +00:00
|
|
|
// FIXME: Creating the value based on the type alone creates misshapen values
|
|
|
|
// for lvalues, since the type does not reflect the need for `ReferenceValue`.
|
2022-04-14 13:42:02 +00:00
|
|
|
if (Value *MergedVal = MergedEnv.createValue(Type))
|
2022-11-03 00:36:58 +00:00
|
|
|
if (Model.merge(Type, Val1, Env1, Val2, Env2, *MergedVal, MergedEnv))
|
2022-03-28 15:38:17 +00:00
|
|
|
return MergedVal;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-11-03 00:36:58 +00:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2022-02-23 13:38:51 +00:00
|
|
|
/// Initializes a global storage value.
|
2023-01-06 13:34:12 +00:00
|
|
|
static void insertIfGlobal(const Decl &D,
|
|
|
|
llvm::DenseSet<const VarDecl *> &Vars) {
|
2022-02-23 13:38:51 +00:00
|
|
|
if (auto *V = dyn_cast<VarDecl>(&D))
|
2023-01-06 13:34:12 +00:00
|
|
|
if (V->hasGlobalStorage())
|
|
|
|
Vars.insert(V);
|
|
|
|
}
|
|
|
|
|
2023-04-18 04:49:38 +00:00
|
|
|
static void insertIfFunction(const Decl &D,
|
|
|
|
llvm::DenseSet<const FunctionDecl *> &Funcs) {
|
|
|
|
if (auto *FD = dyn_cast<FunctionDecl>(&D))
|
|
|
|
Funcs.insert(FD);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
getFieldsGlobalsAndFuncs(const Decl &D,
|
|
|
|
llvm::DenseSet<const FieldDecl *> &Fields,
|
|
|
|
llvm::DenseSet<const VarDecl *> &Vars,
|
|
|
|
llvm::DenseSet<const FunctionDecl *> &Funcs) {
|
2023-04-11 10:21:25 +00:00
|
|
|
insertIfGlobal(D, Vars);
|
2023-04-18 04:49:38 +00:00
|
|
|
insertIfFunction(D, Funcs);
|
2023-01-06 13:34:12 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-04-18 04:49:38 +00:00
|
|
|
/// Traverses `S` and inserts into `Fields`, `Vars` and `Funcs` any fields,
|
|
|
|
/// global variables and functions that are declared in or referenced from
|
|
|
|
/// sub-statements.
|
|
|
|
static void
|
|
|
|
getFieldsGlobalsAndFuncs(const Stmt &S,
|
|
|
|
llvm::DenseSet<const FieldDecl *> &Fields,
|
|
|
|
llvm::DenseSet<const VarDecl *> &Vars,
|
|
|
|
llvm::DenseSet<const FunctionDecl *> &Funcs) {
|
2023-01-06 13:34:12 +00:00
|
|
|
for (auto *Child : S.children())
|
2022-02-23 13:38:51 +00:00
|
|
|
if (Child != nullptr)
|
2023-04-18 04:49:38 +00:00
|
|
|
getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs);
|
2022-02-23 13:38:51 +00:00
|
|
|
|
|
|
|
if (auto *DS = dyn_cast<DeclStmt>(&S)) {
|
2023-01-06 13:34:12 +00:00
|
|
|
if (DS->isSingleDecl())
|
2023-04-18 04:49:38 +00:00
|
|
|
getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs);
|
2023-01-06 13:34:12 +00:00
|
|
|
else
|
2022-02-23 13:38:51 +00:00
|
|
|
for (auto *D : DS->getDeclGroup())
|
2023-04-18 04:49:38 +00:00
|
|
|
getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs);
|
2022-02-23 13:38:51 +00:00
|
|
|
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
|
2023-04-11 10:21:25 +00:00
|
|
|
insertIfGlobal(*E->getDecl(), Vars);
|
2023-04-18 04:49:38 +00:00
|
|
|
insertIfFunction(*E->getDecl(), Funcs);
|
2022-02-23 13:38:51 +00:00
|
|
|
} else if (auto *E = dyn_cast<MemberExpr>(&S)) {
|
2023-01-06 13:34:12 +00:00
|
|
|
// FIXME: should we be using `E->getFoundDecl()`?
|
|
|
|
const ValueDecl *VD = E->getMemberDecl();
|
2023-04-11 10:21:25 +00:00
|
|
|
insertIfGlobal(*VD, Vars);
|
2023-04-18 04:49:38 +00:00
|
|
|
insertIfFunction(*VD, Funcs);
|
2023-01-06 13:34:12 +00:00
|
|
|
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.
|
2023-04-18 04:49:38 +00:00
|
|
|
void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
|
2023-04-03 07:02:48 +00:00
|
|
|
assert(FuncDecl->getBody() != nullptr);
|
|
|
|
|
|
|
|
llvm::DenseSet<const FieldDecl *> Fields;
|
|
|
|
llvm::DenseSet<const VarDecl *> Vars;
|
2023-04-18 04:49:38 +00:00
|
|
|
llvm::DenseSet<const FunctionDecl *> Funcs;
|
2023-04-03 07:02:48 +00:00
|
|
|
|
|
|
|
// Look for global variable and field 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);
|
2023-04-18 04:49:38 +00:00
|
|
|
getFieldsGlobalsAndFuncs(*E, Fields, Vars, Funcs);
|
2023-04-03 07:02:48 +00:00
|
|
|
}
|
|
|
|
// Add all fields mentioned in default member initializers.
|
|
|
|
for (const FieldDecl *F : CtorDecl->getParent()->fields())
|
|
|
|
if (const auto *I = F->getInClassInitializer())
|
2023-04-18 04:49:38 +00:00
|
|
|
getFieldsGlobalsAndFuncs(*I, Fields, Vars, Funcs);
|
2023-04-03 07:02:48 +00:00
|
|
|
}
|
2023-04-18 04:49:38 +00:00
|
|
|
getFieldsGlobalsAndFuncs(*FuncDecl->getBody(), Fields, Vars, Funcs);
|
2023-04-03 07:02:48 +00:00
|
|
|
|
|
|
|
// These have to be added before the lines that follow to ensure that
|
|
|
|
// `create*` work correctly for structs.
|
|
|
|
DACtx->addModeledFields(Fields);
|
|
|
|
|
2023-01-06 13:34:12 +00:00
|
|
|
for (const VarDecl *D : Vars) {
|
2023-05-08 06:38:42 +00:00
|
|
|
if (getStorageLocation(*D) != nullptr)
|
2023-01-06 13:34:12 +00:00
|
|
|
continue;
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
auto &Loc = createStorageLocation(D->getType().getNonReferenceType());
|
2023-01-06 13:34:12 +00:00
|
|
|
setStorageLocation(*D, Loc);
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
if (auto *Val = createValue(D->getType().getNonReferenceType()))
|
2023-01-06 13:34:12 +00:00
|
|
|
setValue(Loc, *Val);
|
2022-02-23 13:38:51 +00:00
|
|
|
}
|
2023-04-18 04:49:38 +00:00
|
|
|
|
|
|
|
for (const FunctionDecl *FD : Funcs) {
|
2023-05-08 06:38:42 +00:00
|
|
|
if (getStorageLocation(*FD) != nullptr)
|
2023-04-18 04:49:38 +00:00
|
|
|
continue;
|
|
|
|
auto &Loc = createStorageLocation(FD->getType());
|
|
|
|
setStorageLocation(*FD, Loc);
|
|
|
|
}
|
2022-02-23 13:38:51 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 15:23:42 +00:00
|
|
|
Environment::Environment(DataflowAnalysisContext &DACtx)
|
2023-04-17 20:35:20 +02:00
|
|
|
: DACtx(&DACtx),
|
|
|
|
FlowConditionToken(&DACtx.arena().makeFlowConditionToken()) {}
|
2022-04-25 15:23:42 +00:00
|
|
|
|
|
|
|
Environment::Environment(const Environment &Other)
|
2022-08-15 19:58:23 +00:00
|
|
|
: DACtx(Other.DACtx), CallStack(Other.CallStack),
|
|
|
|
ReturnLoc(Other.ReturnLoc), ThisPointeeLoc(Other.ThisPointeeLoc),
|
|
|
|
DeclToLoc(Other.DeclToLoc), ExprToLoc(Other.ExprToLoc),
|
|
|
|
LocToVal(Other.LocToVal), MemberLocToStruct(Other.MemberLocToStruct),
|
2022-04-25 15:23:42 +00:00
|
|
|
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,
|
2022-08-15 19:58:23 +00:00
|
|
|
const DeclContext &DeclCtx)
|
2022-01-11 12:15:53 +00:00
|
|
|
: Environment(DACtx) {
|
2022-08-15 19:58:23 +00:00
|
|
|
CallStack.push_back(&DeclCtx);
|
2022-08-03 14:48:49 +00:00
|
|
|
|
2022-08-15 19:58:23 +00:00
|
|
|
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
|
2022-02-23 13:38:51 +00:00
|
|
|
assert(FuncDecl->getBody() != nullptr);
|
2023-01-06 13:34:12 +00:00
|
|
|
|
2023-04-18 04:49:38 +00:00
|
|
|
initFieldsGlobalsAndFuncs(FuncDecl);
|
2023-01-09 22:54:53 +00:00
|
|
|
|
2022-01-11 12:15:53 +00:00
|
|
|
for (const auto *ParamDecl : FuncDecl->parameters()) {
|
|
|
|
assert(ParamDecl != nullptr);
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
// References aren't objects, so the reference itself doesn't have a
|
|
|
|
// storage location. Instead, the storage location for a reference refers
|
|
|
|
// directly to an object of the referenced type -- so strip off any
|
|
|
|
// reference from the type.
|
|
|
|
auto &ParamLoc =
|
|
|
|
createStorageLocation(ParamDecl->getType().getNonReferenceType());
|
2022-01-11 12:15:53 +00:00
|
|
|
setStorageLocation(*ParamDecl, ParamLoc);
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
if (Value *ParamVal =
|
|
|
|
createValue(ParamDecl->getType().getNonReferenceType()))
|
|
|
|
setValue(ParamLoc, *ParamVal);
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
2022-08-04 17:42:01 +00:00
|
|
|
|
|
|
|
QualType ReturnType = FuncDecl->getReturnType();
|
|
|
|
ReturnLoc = &createStorageLocation(ReturnType);
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
|
2022-08-15 19:58:23 +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());
|
|
|
|
|
2022-12-30 14:09:40 +00:00
|
|
|
// FIXME: Initialize the ThisPointeeLoc of lambdas too.
|
2022-05-25 20:15:46 +00:00
|
|
|
if (MethodDecl && !MethodDecl->isStatic()) {
|
2022-01-11 12:15:53 +00:00
|
|
|
QualType ThisPointeeType = MethodDecl->getThisObjectType();
|
2022-12-30 14:09:40 +00:00
|
|
|
ThisPointeeLoc = &createStorageLocation(ThisPointeeType);
|
|
|
|
if (Value *ThisPointeeVal = createValue(ThisPointeeType))
|
|
|
|
setValue(*ThisPointeeLoc, *ThisPointeeVal);
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-15 19:58:23 +00:00
|
|
|
bool Environment::canDescend(unsigned MaxDepth,
|
|
|
|
const DeclContext *Callee) const {
|
2022-08-16 18:27:41 +02:00
|
|
|
return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee);
|
2022-08-15 19:58:23 +00:00
|
|
|
}
|
|
|
|
|
2022-07-26 17:54:13 +00:00
|
|
|
Environment Environment::pushCall(const CallExpr *Call) const {
|
|
|
|
Environment Env(*this);
|
2022-08-03 14:48:49 +00:00
|
|
|
|
2022-08-10 14:01:18 +00:00
|
|
|
// FIXME: Support references here.
|
|
|
|
Env.ReturnLoc = getStorageLocation(*Call, SkipPast::Reference);
|
2022-08-10 14:01:18 +00:00
|
|
|
|
2022-08-10 14:20:49 -07:00
|
|
|
if (const auto *MethodCall = dyn_cast<CXXMemberCallExpr>(Call)) {
|
|
|
|
if (const Expr *Arg = MethodCall->getImplicitObjectArgument()) {
|
2022-09-22 12:42:23 +00:00
|
|
|
if (!isa<CXXThisExpr>(Arg))
|
|
|
|
Env.ThisPointeeLoc = getStorageLocation(*Arg, SkipPast::Reference);
|
|
|
|
// Otherwise (when the argument is `this`), retain the current
|
|
|
|
// environment's `ThisPointeeLoc`.
|
2022-08-10 14:20:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-10 14:01:18 +00:00
|
|
|
Env.pushCallInternal(Call->getDirectCallee(),
|
2023-01-06 16:56:23 +01:00
|
|
|
llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
|
2022-08-10 14:01:18 +00:00
|
|
|
|
|
|
|
return Env;
|
|
|
|
}
|
|
|
|
|
|
|
|
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(),
|
2023-01-06 16:56:23 +01:00
|
|
|
llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
|
2022-08-10 14:01:18 +00:00
|
|
|
|
|
|
|
return Env;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
|
|
|
|
ArrayRef<const Expr *> Args) {
|
2022-08-15 19:58:23 +00:00
|
|
|
CallStack.push_back(FuncDecl);
|
2022-08-10 14:01:18 +00:00
|
|
|
|
2023-04-18 04:49:38 +00:00
|
|
|
initFieldsGlobalsAndFuncs(FuncDecl);
|
2023-01-09 22:54:53 +00:00
|
|
|
|
2023-01-06 13:34:12 +00:00
|
|
|
const auto *ParamIt = FuncDecl->param_begin();
|
2022-07-26 17:54:13 +00:00
|
|
|
|
|
|
|
// FIXME: Parameters don't always map to arguments 1:1; examples include
|
|
|
|
// overloaded operators implemented as member functions, and parameter packs.
|
2022-08-10 14:01:18 +00:00
|
|
|
for (unsigned ArgIndex = 0; ArgIndex < Args.size(); ++ParamIt, ++ArgIndex) {
|
2022-07-26 21:02:31 -07:00
|
|
|
assert(ParamIt != FuncDecl->param_end());
|
2022-07-26 17:54:13 +00:00
|
|
|
|
2022-08-10 14:01:18 +00:00
|
|
|
const Expr *Arg = Args[ArgIndex];
|
|
|
|
auto *ArgLoc = getStorageLocation(*Arg, SkipPast::Reference);
|
2022-08-10 17:50:14 +00:00
|
|
|
if (ArgLoc == nullptr)
|
|
|
|
continue;
|
2022-07-29 19:39:52 +00:00
|
|
|
|
|
|
|
const VarDecl *Param = *ParamIt;
|
|
|
|
|
|
|
|
QualType ParamType = Param->getType();
|
|
|
|
if (ParamType->isReferenceType()) {
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
setStorageLocation(*Param, *ArgLoc);
|
|
|
|
} else {
|
|
|
|
auto &Loc = createStorageLocation(*Param);
|
|
|
|
setStorageLocation(*Param, Loc);
|
|
|
|
|
|
|
|
if (auto *ArgVal = getValue(*ArgLoc)) {
|
|
|
|
setValue(Loc, *ArgVal);
|
|
|
|
} else if (Value *Val = createValue(ParamType)) {
|
|
|
|
setValue(Loc, *Val);
|
|
|
|
}
|
2022-07-29 19:39:52 +00:00
|
|
|
}
|
2022-07-26 17:54:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 19:39:52 +00:00
|
|
|
void Environment::popCall(const Environment &CalleeEnv) {
|
2022-08-04 17:42:01 +00:00
|
|
|
// We ignore `DACtx` because it's already the same in both. We don't want the
|
2022-08-03 14:48:49 +00:00
|
|
|
// 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.
|
2022-07-29 19:39:52 +00:00
|
|
|
this->LocToVal = std::move(CalleeEnv.LocToVal);
|
|
|
|
this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
|
|
|
|
this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-08-04 17:42:01 +00:00
|
|
|
if (ReturnLoc != Other.ReturnLoc)
|
|
|
|
return false;
|
|
|
|
|
2022-08-04 17:45:30 +00:00
|
|
|
if (ThisPointeeLoc != Other.ThisPointeeLoc)
|
|
|
|
return false;
|
|
|
|
|
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-10-14 12:10:52 +00:00
|
|
|
if (!areEquivalentValues(*Val, *It->second) &&
|
2022-11-03 00:36:58 +00:00
|
|
|
!compareDistinctValues(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-11-03 00:36:58 +00:00
|
|
|
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;
|
|
|
|
|
2022-12-19 14:47:35 +00:00
|
|
|
// 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.
|
2022-11-03 00:36:58 +00:00
|
|
|
//
|
2022-12-19 14:47:35 +00:00
|
|
|
// 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.
|
2022-11-03 00:36:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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);
|
2022-08-04 17:42:01 +00:00
|
|
|
assert(ReturnLoc == Other.ReturnLoc);
|
2022-08-04 17:45:30 +00:00
|
|
|
assert(ThisPointeeLoc == Other.ThisPointeeLoc);
|
2022-08-15 19:58:23 +00:00
|
|
|
assert(CallStack == Other.CallStack);
|
2021-12-29 11:31:02 +00:00
|
|
|
|
|
|
|
auto Effect = LatticeJoinEffect::Unchanged;
|
|
|
|
|
2022-04-14 13:42:02 +00:00
|
|
|
Environment JoinedEnv(*DACtx);
|
|
|
|
|
2022-08-15 19:58:23 +00:00
|
|
|
JoinedEnv.CallStack = CallStack;
|
2022-08-04 17:42:01 +00:00
|
|
|
JoinedEnv.ReturnLoc = ReturnLoc;
|
2022-08-04 17:45:30 +00:00
|
|
|
JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
|
2022-08-04 17:42:01 +00:00
|
|
|
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
// FIXME: Once we're able to remove declarations from `DeclToLoc` when their
|
|
|
|
// lifetime ends, add an assertion that there aren't any entries in
|
|
|
|
// `DeclToLoc` and `Other.DeclToLoc` that map the same declaration to
|
|
|
|
// different storage locations.
|
2022-04-14 13:42:02 +00:00
|
|
|
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-11-03 00:36:58 +00:00
|
|
|
// FIXME: update join to detect backedges and simplify the flow condition
|
|
|
|
// accordingly.
|
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);
|
|
|
|
|
2022-10-14 12:10:52 +00:00
|
|
|
if (areEquivalentValues(*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-11-03 00:36:58 +00:00
|
|
|
if (Value *MergedVal =
|
|
|
|
mergeDistinctValues(Loc->getType(), *Val, *this, *It->second, Other,
|
|
|
|
JoinedEnv, Model)) {
|
2022-04-14 13:42:02 +00:00
|
|
|
JoinedEnv.LocToVal.insert({Loc, MergedVal});
|
2022-11-03 00:36:58 +00:00
|
|
|
Effect = LatticeJoinEffect::Changed;
|
|
|
|
}
|
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-08-02 21:05:48 +00:00
|
|
|
return DACtx->createStorageLocation(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) {
|
2023-03-15 18:06:34 -07:00
|
|
|
assert(!DeclToLoc.contains(&D));
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
assert(!isa_and_nonnull<ReferenceValue>(getValue(Loc)));
|
2021-12-29 11:31:02 +00:00
|
|
|
DeclToLoc[&D] = &Loc;
|
|
|
|
}
|
|
|
|
|
2023-05-08 06:38:42 +00:00
|
|
|
StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const {
|
2021-12-29 11:31:02 +00:00
|
|
|
auto It = DeclToLoc.find(&D);
|
[clang][dataflow] Eliminate intermediate `ReferenceValue`s from `Environment::DeclToLoc`.
For the wider context of this change, see the RFC at
https://discourse.llvm.org/t/70086.
After this change, global and local variables of reference type are associated
directly with the `StorageLocation` of the referenced object instead of the
`StorageLocation` of a `ReferenceValue`.
Some tests that explicitly check for an existence of `ReferenceValue` for a
variable of reference type have been modified accordingly.
As discussed in the RFC, I have added an assertion to `Environment::join()` to
check that if both environments contain an entry for the same declaration in
`DeclToLoc`, they both map to the same `StorageLocation`. As discussed in
https://discourse.llvm.org/t/70086/5, this also necessitates removing
declarations from `DeclToLoc` when they go out of scope.
In the RFC, I proposed a gradual migration for this change, but it appears
that all of the callers of `Environment::setStorageLocation(const ValueDecl &,
SkipPast` are in the dataflow framework itself, and that there are only a few of
them.
As this is the function whose semantics are changing in a way that callers
potentially need to adapt to, I've decided to change the semantics of the
function directly.
The semantics of `getStorageLocation(const ValueDecl &, SkipPast SP` now no
longer depend on the behavior of the `SP` parameter. (There don't appear to be
any callers that use `SkipPast::ReferenceThenPointer`, so I've added an
assertion that forbids this usage.)
This patch adds a default argument for the `SP` parameter and removes the
explicit `SP` argument at the callsites that are touched by this change. A
followup patch will remove the argument from the remaining callsites,
allowing the `SkipPast` parameter to be removed entirely. (I don't want to do
that in this patch so that semantics-changing changes can be reviewed separately
from semantics-neutral changes.)
Reviewed By: ymandel, xazax.hun, gribozavr2
Differential Revision: https://reviews.llvm.org/D149144
2023-05-04 07:42:05 +00:00
|
|
|
if (It == DeclToLoc.end())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
StorageLocation *Loc = It->second;
|
|
|
|
|
|
|
|
assert(!isa_and_nonnull<ReferenceValue>(getValue(*Loc)));
|
|
|
|
|
|
|
|
return Loc;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
|
2022-05-04 21:08:43 +00:00
|
|
|
const Expr &CanonE = ignoreCFGOmittedNodes(E);
|
2023-03-15 18:06:34 -07:00
|
|
|
assert(!ExprToLoc.contains(&CanonE));
|
2022-05-04 21:08:43 +00:00
|
|
|
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 {
|
2022-08-04 17:45:30 +00:00
|
|
|
return ThisPointeeLoc;
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
|
2022-08-04 17:42:01 +00:00
|
|
|
StorageLocation *Environment::getReturnStorageLocation() const {
|
|
|
|
return ReturnLoc;
|
|
|
|
}
|
|
|
|
|
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();
|
2023-04-05 12:32:13 +00:00
|
|
|
assert(Type->isRecordType());
|
2022-01-13 13:53:52 +00:00
|
|
|
|
2023-01-06 13:34:12 +00:00
|
|
|
for (const FieldDecl *Field : DACtx->getReferencedFields(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
|
|
|
|
2022-07-25 20:23:23 +02:00
|
|
|
auto It = MemberLocToStruct.find(&Loc);
|
|
|
|
if (It != MemberLocToStruct.end()) {
|
2022-02-23 15:39:26 +00:00
|
|
|
// `Loc` is the location of a struct member so we need to also update the
|
|
|
|
// value of the member in the corresponding `StructValue`.
|
|
|
|
|
2022-07-25 20:23:23 +02:00
|
|
|
assert(It->second.first != nullptr);
|
|
|
|
StructValue &StructVal = *It->second.first;
|
2022-02-23 15:39:26 +00:00
|
|
|
|
2022-07-25 20:23:23 +02:00
|
|
|
assert(It->second.second != nullptr);
|
|
|
|
const ValueDecl &Member = *It->second.second;
|
2022-02-23 15:39:26 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:08:36 +00:00
|
|
|
Value *Environment::getValue(const ValueDecl &D) const {
|
2023-05-08 06:38:42 +00:00
|
|
|
auto *Loc = getStorageLocation(D);
|
2022-01-04 13:47:14 +00:00
|
|
|
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-11-03 00:36:58 +00:00
|
|
|
// FIXME: consider instead `return nullptr`, given that we do nothing useful
|
|
|
|
// with integers, and so distinguishing them serves no purpose, but could
|
|
|
|
// prevent convergence.
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2023-05-02 00:08:30 +00:00
|
|
|
return &DACtx->arena().create<IntegerValue>();
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2023-04-05 11:20:33 +00:00
|
|
|
if (Type->isReferenceType() || Type->isPointerType()) {
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2023-04-05 11:20:33 +00:00
|
|
|
QualType PointeeType = Type->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
|
|
|
}
|
|
|
|
|
2023-04-05 11:20:33 +00:00
|
|
|
if (Type->isReferenceType())
|
2023-05-02 00:08:30 +00:00
|
|
|
return &DACtx->arena().create<ReferenceValue>(PointeeLoc);
|
2023-04-05 11:20:33 +00:00
|
|
|
else
|
2023-05-02 00:08:30 +00:00
|
|
|
return &DACtx->arena().create<PointerValue>(PointeeLoc);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2023-04-05 12:32:13 +00:00
|
|
|
if (Type->isRecordType()) {
|
2022-02-24 20:02:00 +00:00
|
|
|
CreatedValuesCount++;
|
2021-12-29 11:31:02 +00:00
|
|
|
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
|
2023-01-06 13:34:12 +00:00
|
|
|
for (const FieldDecl *Field : DACtx->getReferencedFields(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());
|
|
|
|
}
|
|
|
|
|
2023-05-02 00:08:30 +00:00
|
|
|
return &DACtx->arena().create<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;
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-01-13 19:26:57 +00:00
|
|
|
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)
|
2023-04-21 21:07:17 +02:00
|
|
|
OS << " [" << D->getNameAsString() << ", " << L << "]\n";
|
2023-01-13 19:26:57 +00:00
|
|
|
|
|
|
|
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";
|
2023-03-21 15:34:31 +01:00
|
|
|
DACtx->dumpFlowCondition(*FlowConditionToken, OS);
|
2022-07-23 01:23:17 +02:00
|
|
|
}
|
|
|
|
|
2023-01-13 19:26:57 +00:00
|
|
|
void Environment::dump() const {
|
|
|
|
dump(llvm::dbgs());
|
|
|
|
}
|
|
|
|
|
2023-05-12 11:59:21 +00:00
|
|
|
AggregateStorageLocation *
|
|
|
|
getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
|
|
|
|
const Environment &Env) {
|
|
|
|
Expr *ImplicitObject = MCE.getImplicitObjectArgument();
|
|
|
|
if (ImplicitObject == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
StorageLocation *Loc =
|
|
|
|
Env.getStorageLocation(*ImplicitObject, SkipPast::Reference);
|
|
|
|
if (Loc == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
if (ImplicitObject->getType()->isPointerType()) {
|
|
|
|
if (auto *Val = cast_or_null<PointerValue>(Env.getValue(*Loc)))
|
|
|
|
return &cast<AggregateStorageLocation>(Val->getPointeeLoc());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return cast<AggregateStorageLocation>(Loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
|
|
|
|
const Environment &Env) {
|
|
|
|
Expr *Base = ME.getBase();
|
|
|
|
if (Base == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
StorageLocation *Loc = Env.getStorageLocation(*Base, SkipPast::Reference);
|
|
|
|
if (Loc == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
if (ME.isArrow()) {
|
|
|
|
if (auto *Val = cast_or_null<PointerValue>(Env.getValue(*Loc)))
|
|
|
|
return &cast<AggregateStorageLocation>(Val->getPointeeLoc());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return cast<AggregateStorageLocation>(Loc);
|
|
|
|
}
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
} // namespace dataflow
|
|
|
|
} // namespace clang
|