Skip to content

Commit dd4dde8

Browse files
committed
[clang][dataflow] Add transfer functions for logical and, or, not.
This is part of the implementation of the dataflow analysis framework. See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev. Reviewed-by: xazax.hun Differential Revision: https://reviews.llvm.org/D119953
1 parent 25f1d50 commit dd4dde8

File tree

8 files changed

+334
-32
lines changed

8 files changed

+334
-32
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ namespace dataflow {
3434
class DataflowAnalysisContext {
3535
public:
3636
DataflowAnalysisContext()
37-
: TrueVal(&takeOwnership(std::make_unique<BoolValue>())),
38-
FalseVal(&takeOwnership(std::make_unique<BoolValue>())) {}
37+
: TrueVal(takeOwnership(std::make_unique<AtomicBoolValue>())),
38+
FalseVal(takeOwnership(std::make_unique<AtomicBoolValue>())) {}
3939

4040
/// Takes ownership of `Loc` and returns a reference to it.
4141
///
@@ -115,8 +115,8 @@ class DataflowAnalysisContext {
115115

116116
/// Returns a symbolic boolean value that models a boolean literal equal to
117117
/// `Value`.
118-
BoolValue &getBoolLiteralValue(bool Value) const {
119-
return Value ? *TrueVal : *FalseVal;
118+
AtomicBoolValue &getBoolLiteralValue(bool Value) const {
119+
return Value ? TrueVal : FalseVal;
120120
}
121121

122122
private:
@@ -135,8 +135,8 @@ class DataflowAnalysisContext {
135135
StorageLocation *ThisPointeeLoc = nullptr;
136136

137137
// FIXME: Add support for boolean expressions.
138-
BoolValue *TrueVal;
139-
BoolValue *FalseVal;
138+
AtomicBoolValue &TrueVal;
139+
AtomicBoolValue &FalseVal;
140140
};
141141

142142
} // namespace dataflow

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ class Environment {
226226

227227
/// Returns a symbolic boolean value that models a boolean literal equal to
228228
/// `Value`
229-
BoolValue &getBoolLiteralValue(bool Value) const {
229+
AtomicBoolValue &getBoolLiteralValue(bool Value) const {
230230
return DACtx->getBoolLiteralValue(Value);
231231
}
232232

clang/include/clang/Analysis/FlowSensitive/Transfer.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,23 @@
2020
namespace clang {
2121
namespace dataflow {
2222

23+
/// Maps statements to the environments of basic blocks that contain them.
24+
class StmtToEnvMap {
25+
public:
26+
virtual ~StmtToEnvMap() = default;
27+
28+
/// Returns the environment of the basic block that contains `S` or nullptr if
29+
/// there isn't one.
30+
/// FIXME: Ensure that the result can't be null and return a const reference.
31+
virtual const Environment *getEnvironment(const Stmt &S) const = 0;
32+
};
33+
2334
/// Evaluates `S` and updates `Env` accordingly.
2435
///
2536
/// Requirements:
2637
///
2738
/// The type of `S` must not be `ParenExpr`.
28-
void transfer(const Stmt &S, Environment &Env);
39+
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env);
2940

3041
} // namespace dataflow
3142
} // namespace clang

clang/include/clang/Analysis/FlowSensitive/Value.h

+94-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,19 @@ namespace dataflow {
2828
/// Base class for all values computed by abstract interpretation.
2929
class Value {
3030
public:
31-
enum class Kind { Bool, Integer, Reference, Pointer, Struct };
31+
enum class Kind {
32+
Integer,
33+
Reference,
34+
Pointer,
35+
Struct,
36+
37+
// Synthetic boolean values are either atomic values or composites that
38+
// represent conjunctions, disjunctions, and negations.
39+
AtomicBool,
40+
Conjunction,
41+
Disjunction,
42+
Negation
43+
};
3244

3345
explicit Value(Kind ValKind) : ValKind(ValKind) {}
3446

@@ -43,9 +55,88 @@ class Value {
4355
/// Models a boolean.
4456
class BoolValue : public Value {
4557
public:
46-
explicit BoolValue() : Value(Kind::Bool) {}
58+
explicit BoolValue(Kind ValueKind) : Value(ValueKind) {}
4759

48-
static bool classof(const Value *Val) { return Val->getKind() == Kind::Bool; }
60+
static bool classof(const Value *Val) {
61+
return Val->getKind() == Kind::AtomicBool ||
62+
Val->getKind() == Kind::Conjunction ||
63+
Val->getKind() == Kind::Disjunction ||
64+
Val->getKind() == Kind::Negation;
65+
}
66+
};
67+
68+
/// Models an atomic boolean.
69+
class AtomicBoolValue : public BoolValue {
70+
public:
71+
explicit AtomicBoolValue() : BoolValue(Kind::AtomicBool) {}
72+
73+
static bool classof(const Value *Val) {
74+
return Val->getKind() == Kind::AtomicBool;
75+
}
76+
};
77+
78+
/// Models a boolean conjunction.
79+
// FIXME: Consider representing binary and unary boolean operations similar
80+
// to how they are represented in the AST. This might become more pressing
81+
// when such operations need to be added for other data types.
82+
class ConjunctionValue : public BoolValue {
83+
public:
84+
explicit ConjunctionValue(BoolValue &LeftSubVal, BoolValue &RightSubVal)
85+
: BoolValue(Kind::Conjunction), LeftSubVal(LeftSubVal),
86+
RightSubVal(RightSubVal) {}
87+
88+
static bool classof(const Value *Val) {
89+
return Val->getKind() == Kind::Conjunction;
90+
}
91+
92+
/// Returns the left sub-value of the conjunction.
93+
BoolValue &getLeftSubValue() const { return LeftSubVal; }
94+
95+
/// Returns the right sub-value of the conjunction.
96+
BoolValue &getRightSubValue() const { return RightSubVal; }
97+
98+
private:
99+
BoolValue &LeftSubVal;
100+
BoolValue &RightSubVal;
101+
};
102+
103+
/// Models a boolean disjunction.
104+
class DisjunctionValue : public BoolValue {
105+
public:
106+
explicit DisjunctionValue(BoolValue &LeftSubVal, BoolValue &RightSubVal)
107+
: BoolValue(Kind::Disjunction), LeftSubVal(LeftSubVal),
108+
RightSubVal(RightSubVal) {}
109+
110+
static bool classof(const Value *Val) {
111+
return Val->getKind() == Kind::Disjunction;
112+
}
113+
114+
/// Returns the left sub-value of the disjunction.
115+
BoolValue &getLeftSubValue() const { return LeftSubVal; }
116+
117+
/// Returns the right sub-value of the disjunction.
118+
BoolValue &getRightSubValue() const { return RightSubVal; }
119+
120+
private:
121+
BoolValue &LeftSubVal;
122+
BoolValue &RightSubVal;
123+
};
124+
125+
/// Models a boolean negation.
126+
class NegationValue : public BoolValue {
127+
public:
128+
explicit NegationValue(BoolValue &SubVal)
129+
: BoolValue(Kind::Negation), SubVal(SubVal) {}
130+
131+
static bool classof(const Value *Val) {
132+
return Val->getKind() == Kind::Negation;
133+
}
134+
135+
/// Returns the sub-value of the negation.
136+
BoolValue &getSubVal() const { return SubVal; }
137+
138+
private:
139+
BoolValue &SubVal;
49140
};
50141

51142
/// Models an integer.

clang/lib/Analysis/FlowSensitive/Transfer.cpp

+63-8
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ static const Expr *skipExprWithCleanups(const Expr *E) {
3939

4040
class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
4141
public:
42-
TransferVisitor(Environment &Env) : Env(Env) {}
42+
TransferVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env)
43+
: StmtToEnv(StmtToEnv), Env(Env) {}
4344

4445
void VisitBinaryOperator(const BinaryOperator *S) {
45-
if (S->getOpcode() == BO_Assign) {
46+
switch (S->getOpcode()) {
47+
case BO_Assign: {
4648
// The CFG does not contain `ParenExpr` as top-level statements in basic
4749
// blocks, however sub-expressions can still be of that type.
4850
assert(S->getLHS() != nullptr);
@@ -51,7 +53,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
5153
assert(LHS != nullptr);
5254
auto *LHSLoc = Env.getStorageLocation(*LHS, SkipPast::Reference);
5355
if (LHSLoc == nullptr)
54-
return;
56+
break;
5557

5658
// The CFG does not contain `ParenExpr` as top-level statements in basic
5759
// blocks, however sub-expressions can still be of that type.
@@ -61,15 +63,57 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
6163
assert(RHS != nullptr);
6264
Value *RHSVal = Env.getValue(*RHS, SkipPast::Reference);
6365
if (RHSVal == nullptr)
64-
return;
66+
break;
6567

6668
// Assign a value to the storage location of the left-hand side.
6769
Env.setValue(*LHSLoc, *RHSVal);
6870

6971
// Assign a storage location for the whole expression.
7072
Env.setStorageLocation(*S, *LHSLoc);
73+
break;
74+
}
75+
case BO_LAnd:
76+
case BO_LOr: {
77+
const Expr *LHS = S->getLHS();
78+
assert(LHS != nullptr);
79+
80+
const Expr *RHS = S->getRHS();
81+
assert(RHS != nullptr);
82+
83+
BoolValue *LHSVal =
84+
dyn_cast_or_null<BoolValue>(Env.getValue(*LHS, SkipPast::Reference));
85+
86+
// `RHS` and `S` might be part of different basic blocks. We need to
87+
// access their values from the corresponding environments.
88+
BoolValue *RHSVal = nullptr;
89+
const Environment *RHSEnv = StmtToEnv.getEnvironment(*RHS);
90+
if (RHSEnv != nullptr)
91+
RHSVal = dyn_cast_or_null<BoolValue>(
92+
RHSEnv->getValue(*RHS, SkipPast::Reference));
93+
94+
// Create fresh values for unknown boolean expressions.
95+
// FIXME: Consider providing a `GetOrCreateFresh` util in case this style
96+
// is expected to be common or make sure that all expressions are assigned
97+
// values and drop this.
98+
if (LHSVal == nullptr)
99+
LHSVal = &Env.takeOwnership(std::make_unique<AtomicBoolValue>());
100+
if (RHSVal == nullptr)
101+
RHSVal = &Env.takeOwnership(std::make_unique<AtomicBoolValue>());
102+
103+
auto &Loc = Env.createStorageLocation(*S);
104+
Env.setStorageLocation(*S, Loc);
105+
if (S->getOpcode() == BO_LAnd)
106+
Env.setValue(Loc, Env.takeOwnership(std::make_unique<ConjunctionValue>(
107+
*LHSVal, *RHSVal)));
108+
else
109+
Env.setValue(Loc, Env.takeOwnership(std::make_unique<DisjunctionValue>(
110+
*LHSVal, *RHSVal)));
111+
break;
112+
}
113+
default:
114+
// FIXME: Add support for BO_EQ, BO_NE.
115+
break;
71116
}
72-
// FIXME: Add support for BO_EQ, BO_NE.
73117
}
74118

75119
void VisitDeclRefExpr(const DeclRefExpr *S) {
@@ -212,8 +256,18 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
212256
Env.setValue(PointerLoc, PointerVal);
213257
break;
214258
}
259+
case UO_LNot: {
260+
auto *SubExprVal =
261+
dyn_cast_or_null<BoolValue>(Env.getValue(*SubExpr, SkipPast::None));
262+
if (SubExprVal == nullptr)
263+
return;
264+
265+
auto &ExprLoc = Env.createStorageLocation(*S);
266+
Env.setStorageLocation(*S, ExprLoc);
267+
Env.setValue(ExprLoc, Env.takeOwnership(
268+
std::make_unique<NegationValue>(*SubExprVal)));
269+
}
215270
default:
216-
// FIXME: Add support for UO_LNot.
217271
break;
218272
}
219273
}
@@ -450,12 +504,13 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
450504
}
451505

452506
private:
507+
const StmtToEnvMap &StmtToEnv;
453508
Environment &Env;
454509
};
455510

456-
void transfer(const Stmt &S, Environment &Env) {
511+
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env) {
457512
assert(!isa<ParenExpr>(&S));
458-
TransferVisitor(Env).Visit(&S);
513+
TransferVisitor(StmtToEnv, Env).Visit(&S);
459514
}
460515

461516
} // namespace dataflow

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

+34-10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "clang/Analysis/FlowSensitive/Transfer.h"
2525
#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
2626
#include "clang/Analysis/FlowSensitive/Value.h"
27+
#include "llvm/ADT/ArrayRef.h"
2728
#include "llvm/ADT/DenseSet.h"
2829
#include "llvm/ADT/None.h"
2930
#include "llvm/ADT/Optional.h"
@@ -32,6 +33,27 @@
3233
namespace clang {
3334
namespace dataflow {
3435

36+
class StmtToEnvMapImpl : public StmtToEnvMap {
37+
public:
38+
StmtToEnvMapImpl(
39+
const ControlFlowContext &CFCtx,
40+
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>>
41+
BlockToState)
42+
: CFCtx(CFCtx), BlockToState(BlockToState) {}
43+
44+
const Environment *getEnvironment(const Stmt &S) const override {
45+
auto BlockIT = CFCtx.getStmtToBlock().find(&S);
46+
assert(BlockIT != CFCtx.getStmtToBlock().end());
47+
const auto &State = BlockToState[BlockIT->getSecond()->getBlockID()];
48+
assert(State.hasValue());
49+
return &State.getValue().Env;
50+
}
51+
52+
private:
53+
const ControlFlowContext &CFCtx;
54+
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockToState;
55+
};
56+
3557
/// Computes the input state for a given basic block by joining the output
3658
/// states of its predecessors.
3759
///
@@ -42,7 +64,7 @@ namespace dataflow {
4264
/// `llvm::None` represent basic blocks that are not evaluated yet.
4365
static TypeErasedDataflowAnalysisState computeBlockInputState(
4466
const ControlFlowContext &CFCtx,
45-
std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> &BlockStates,
67+
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockStates,
4668
const CFGBlock &Block, const Environment &InitEnv,
4769
TypeErasedDataflowAnalysis &Analysis) {
4870
llvm::DenseSet<const CFGBlock *> Preds;
@@ -111,17 +133,19 @@ static TypeErasedDataflowAnalysisState computeBlockInputState(
111133
/// Transfers `State` by evaluating `CfgStmt` in the context of `Analysis`.
112134
/// `HandleTransferredStmt` (if provided) will be applied to `CfgStmt`, after it
113135
/// is evaluated.
114-
static void
115-
transferCFGStmt(const CFGStmt &CfgStmt, TypeErasedDataflowAnalysis &Analysis,
116-
TypeErasedDataflowAnalysisState &State,
117-
std::function<void(const CFGStmt &,
118-
const TypeErasedDataflowAnalysisState &)>
119-
HandleTransferredStmt) {
136+
static void transferCFGStmt(
137+
const ControlFlowContext &CFCtx,
138+
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockStates,
139+
const CFGStmt &CfgStmt, TypeErasedDataflowAnalysis &Analysis,
140+
TypeErasedDataflowAnalysisState &State,
141+
std::function<void(const CFGStmt &,
142+
const TypeErasedDataflowAnalysisState &)>
143+
HandleTransferredStmt) {
120144
const Stmt *S = CfgStmt.getStmt();
121145
assert(S != nullptr);
122146

123147
if (Analysis.applyBuiltinTransfer())
124-
transfer(*S, State.Env);
148+
transfer(StmtToEnvMapImpl(CFCtx, BlockStates), *S, State.Env);
125149
Analysis.transferTypeErased(S, State.Lattice, State.Env);
126150

127151
if (HandleTransferredStmt != nullptr)
@@ -176,8 +200,8 @@ TypeErasedDataflowAnalysisState transferBlock(
176200
for (const CFGElement &Element : Block) {
177201
switch (Element.getKind()) {
178202
case CFGElement::Statement:
179-
transferCFGStmt(*Element.getAs<CFGStmt>(), Analysis, State,
180-
HandleTransferredStmt);
203+
transferCFGStmt(CFCtx, BlockStates, *Element.getAs<CFGStmt>(), Analysis,
204+
State, HandleTransferredStmt);
181205
break;
182206
case CFGElement::Initializer:
183207
if (Analysis.applyBuiltinTransfer())

0 commit comments

Comments
 (0)