|
17 | 17 | #include "clang/AST/DeclCXX.h"
|
18 | 18 | #include "clang/AST/Type.h"
|
19 | 19 | #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
|
20 |
| -#include "clang/Analysis/FlowSensitive/Transfer.h" |
21 | 20 | #include "clang/Analysis/FlowSensitive/Value.h"
|
22 | 21 | #include "llvm/ADT/DenseMap.h"
|
23 | 22 | #include "llvm/ADT/DenseSet.h"
|
|
30 | 29 | namespace clang {
|
31 | 30 | namespace dataflow {
|
32 | 31 |
|
| 32 | +namespace simple_bool_model { |
| 33 | +static std::optional<bool> getLiteralValue(const Formula &F, |
| 34 | + const Environment &Env) { |
| 35 | + switch (F.kind()) { |
| 36 | + case Formula::AtomRef: |
| 37 | + return Env.getAtomValue(F.getAtom()); |
| 38 | + case Formula::Literal: |
| 39 | + return F.literal(); |
| 40 | + case Formula::Not: { |
| 41 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 42 | + assert(Operands.size() == 1); |
| 43 | + if (std::optional<bool> Maybe = getLiteralValue(*Operands[0], Env)) |
| 44 | + return !*Maybe; |
| 45 | + return std::nullopt; |
| 46 | + } |
| 47 | + case Formula::And: { |
| 48 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 49 | + assert(Operands.size() == 2); |
| 50 | + auto &LHS = *Operands[0]; |
| 51 | + auto &RHS = *Operands[1]; |
| 52 | + if (std::optional<bool> Left = getLiteralValue(LHS, Env)) |
| 53 | + return !*Left ? Left : getLiteralValue(RHS, Env); |
| 54 | + if (std::optional<bool> Right = getLiteralValue(RHS, Env); |
| 55 | + Right.has_value() && !*Right) |
| 56 | + return Right; |
| 57 | + return std::nullopt; |
| 58 | + } |
| 59 | + case Formula::Or: { |
| 60 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 61 | + assert(Operands.size() == 2); |
| 62 | + auto &LHS = *Operands[0]; |
| 63 | + auto &RHS = *Operands[1]; |
| 64 | + if (std::optional<bool> Left = getLiteralValue(LHS, Env)) |
| 65 | + return *Left ? Left : getLiteralValue(RHS, Env); |
| 66 | + if (std::optional<bool> Right = getLiteralValue(RHS, Env); |
| 67 | + Right.has_value() && *Right) |
| 68 | + return Right; |
| 69 | + return std::nullopt; |
| 70 | + } |
| 71 | + case Formula::Equal: { |
| 72 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 73 | + assert(Operands.size() == 2); |
| 74 | + auto &LHS = *Operands[0]; |
| 75 | + auto &RHS = *Operands[1]; |
| 76 | + if (&LHS == &RHS) return true; |
| 77 | + auto V_L = getLiteralValue(LHS, Env); |
| 78 | + auto V_R = getLiteralValue(RHS, Env); |
| 79 | + return V_L.has_value() && V_R.has_value() && (*V_L == *V_R); |
| 80 | + } |
| 81 | + default: |
| 82 | + return std::nullopt; |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +// Updates atom settings in `Env` based on the formula. `AtomVal` indicates the |
| 87 | +// value to use for atoms encountered in the formula. |
| 88 | +static void assumeFormula(bool AtomVal, const Formula &F, Environment &Env) { |
| 89 | + // TODO: handle literals directly rather than calling getLiteralValue. |
| 90 | + std::optional<bool> Lit = getLiteralValue(F, Env); |
| 91 | + if (Lit.has_value()) |
| 92 | + // FIXME: Nothing to do. Could verify no contradiction, but not sure what |
| 93 | + // we'd do with that here. Need to poison the Env. |
| 94 | + return; |
| 95 | + |
| 96 | + switch (F.kind()) { |
| 97 | + case Formula::AtomRef: |
| 98 | + // FIXME: check for contradictions |
| 99 | + Env.setAtomValue(F.getAtom(), AtomVal); |
| 100 | + break; |
| 101 | + case Formula::Not: { |
| 102 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 103 | + assert(Operands.size() == 1); |
| 104 | + assumeFormula(!AtomVal, *Operands[0], Env); |
| 105 | + break; |
| 106 | + } |
| 107 | + case Formula::And: { |
| 108 | + if (AtomVal == true) { |
| 109 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 110 | + assert(Operands.size() == 2); |
| 111 | + assumeFormula(true, *Operands[0], Env); |
| 112 | + assumeFormula(true, *Operands[1], Env); |
| 113 | + } |
| 114 | + break; |
| 115 | + } |
| 116 | + case Formula::Or: { |
| 117 | + if (AtomVal == false) { |
| 118 | + // Interpret the negated "or" as "and" of negated operands. |
| 119 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 120 | + assert(Operands.size() == 2); |
| 121 | + assumeFormula(false, *Operands[0], Env); |
| 122 | + assumeFormula(false, *Operands[1], Env); |
| 123 | + } |
| 124 | + break; |
| 125 | + } |
| 126 | + case Formula::Equal: { |
| 127 | + ArrayRef<const Formula *> Operands = F.operands(); |
| 128 | + assert(Operands.size() == 2); |
| 129 | + auto &LHS = *Operands[0]; |
| 130 | + auto &RHS = *Operands[1]; |
| 131 | + if (auto V = getLiteralValue(LHS, Env)) { |
| 132 | + assumeFormula(AtomVal == *V, RHS, Env); |
| 133 | + } else if (auto V = getLiteralValue(RHS, Env)) { |
| 134 | + assumeFormula(AtomVal == *V, LHS, Env); |
| 135 | + } |
| 136 | + break; |
| 137 | + } |
| 138 | + default: |
| 139 | + break; |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +BoolValue &join(BoolValue &Val1, const Environment &Env1, BoolValue &Val2, |
| 144 | + const Environment &Env2, Environment &JoinedEnv) { |
| 145 | + if (auto V1 = getLiteralValue(Val1.formula(), Env1); |
| 146 | + V1.has_value() && V1 == getLiteralValue(Val2.formula(), Env2)) |
| 147 | + return JoinedEnv.getBoolLiteralValue(*V1); |
| 148 | + return JoinedEnv.makeAtomicBoolValue(); |
| 149 | +} |
| 150 | + |
| 151 | +void assume(Environment &Env, const Formula &F) { |
| 152 | + assumeFormula(/*AtomVal*/true, F, Env); |
| 153 | +} |
| 154 | + |
| 155 | +bool allows(const Environment &Env, const Formula &F) { |
| 156 | + auto V = getLiteralValue(F, Env); |
| 157 | + // We are conservative in the negative direction: if we can't determine the |
| 158 | + // value, assume its allowed. |
| 159 | + return !V.has_value() || *V; |
| 160 | +} |
| 161 | + |
| 162 | +bool proves(const Environment &Env, const Formula &F) { |
| 163 | + auto V = getLiteralValue(F, Env); |
| 164 | + return V.has_value() && *V; |
| 165 | +} |
| 166 | +} // namespace simple_bool_model |
| 167 | + |
| 168 | +namespace sat_bool_model { |
| 169 | +BoolValue &join(BoolValue &Val1, const Environment &Env1, BoolValue &Val2, |
| 170 | + const Environment &Env2, Environment &JoinedEnv) { |
| 171 | + auto &A = JoinedEnv.arena(); |
| 172 | + auto &JoinedVal = JoinedEnv.makeAtomicBoolValue(); |
| 173 | + auto &JoinedFormula = JoinedVal.formula(); |
| 174 | + JoinedEnv.assume( |
| 175 | + A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()), |
| 176 | + A.makeEquals(JoinedFormula, Val1.formula())), |
| 177 | + A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()), |
| 178 | + A.makeEquals(JoinedFormula, Val2.formula())))); |
| 179 | + return JoinedVal; |
| 180 | +} |
| 181 | + |
| 182 | +void assume(Environment &Env, const Formula &F) { |
| 183 | + Env.getDataflowAnalysisContext().addFlowConditionConstraint( |
| 184 | + Env.getFlowConditionToken(), F); |
| 185 | +} |
| 186 | + |
| 187 | +bool allows(const Environment &Env, const Formula &F) { |
| 188 | + return Env.getDataflowAnalysisContext().flowConditionAllows( |
| 189 | + Env.getFlowConditionToken(), F); |
| 190 | +} |
| 191 | + |
| 192 | +bool proves(const Environment &Env, const Formula &F) { |
| 193 | + return Env.getDataflowAnalysisContext().flowConditionImplies( |
| 194 | + Env.getFlowConditionToken(), F); |
| 195 | +} |
| 196 | +} // namespace sat_bool_model |
| 197 | + |
| 198 | +namespace bool_model = simple_bool_model; |
| 199 | + |
33 | 200 | // FIXME: convert these to parameters of the analysis or environment. Current
|
34 | 201 | // settings have been experimentaly validated, but only for a particular
|
35 | 202 | // analysis.
|
@@ -130,23 +297,9 @@ static Value *joinDistinctValues(QualType Type, Value &Val1,
|
130 | 297 | // if (o.has_value())
|
131 | 298 | // x = o.value();
|
132 | 299 | // ```
|
133 |
| - auto &Expr1 = cast<BoolValue>(Val1).formula(); |
134 |
| - auto &Expr2 = cast<BoolValue>(Val2).formula(); |
135 |
| - auto &A = JoinedEnv.arena(); |
136 |
| - |
137 |
| -#if 1 |
138 |
| - if (auto V1 = simple_bool_model::getLiteralValue(Expr1, Env1); |
139 |
| - V1.has_value() && V1 == simple_bool_model::getLiteralValue(Expr2, Env2)) |
140 |
| - return &JoinedEnv.getBoolLiteralValue(*V1); |
141 |
| -#endif |
142 |
| - |
143 |
| - auto &JoinedVal = A.makeAtomRef(A.makeAtom()); |
144 |
| - JoinedEnv.assume( |
145 |
| - A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()), |
146 |
| - A.makeEquals(JoinedVal, Expr1)), |
147 |
| - A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()), |
148 |
| - A.makeEquals(JoinedVal, Expr2)))); |
149 |
| - return &A.makeBoolValue(JoinedVal); |
| 300 | + auto &B1 = cast<BoolValue>(Val1); |
| 301 | + auto &B2 = cast<BoolValue>(Val2); |
| 302 | + return &bool_model::join(B1, Env1, B2, Env2, JoinedEnv); |
150 | 303 | }
|
151 | 304 |
|
152 | 305 | Value *JoinedVal = nullptr;
|
@@ -1071,22 +1224,15 @@ StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
|
1071 | 1224 | }
|
1072 | 1225 |
|
1073 | 1226 | void Environment::assume(const Formula &F) {
|
1074 |
| - DACtx->addFlowConditionConstraint(FlowConditionToken, F); |
| 1227 | + bool_model::assume(*this, F); |
1075 | 1228 | }
|
1076 | 1229 |
|
1077 |
| -#if 0 |
1078 | 1230 | bool Environment::proves(const Formula &F) const {
|
1079 |
| - return DACtx->flowConditionImplies(FlowConditionToken, F); |
1080 |
| -} |
1081 |
| -#else |
1082 |
| -bool Environment::proves(const Formula &F) const { |
1083 |
| - auto V = simple_bool_model::getLiteralValue(F, *this); |
1084 |
| - return V.has_value() && *V; |
| 1231 | + return bool_model::proves(*this, F); |
1085 | 1232 | }
|
1086 |
| -#endif |
1087 | 1233 |
|
1088 | 1234 | bool Environment::allows(const Formula &F) const {
|
1089 |
| - return DACtx->flowConditionAllows(FlowConditionToken, F); |
| 1235 | + return bool_model::allows(*this, F); |
1090 | 1236 | }
|
1091 | 1237 |
|
1092 | 1238 | void Environment::dump(raw_ostream &OS) const {
|
|
0 commit comments