[PATCH] D135397: [clang][dataflow] Add support for a Top value in boolean formulas.

Dmitri Gribenko via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 6 15:09:30 PDT 2022


gribozavr2 added inline comments.


================
Comment at: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h:157
+  TopValue &createTop() {
+    return takeOwnership(std::make_unique<TopValue>());
+  }
----------------
Should we be creating a new top every time, or should it be a singleton like true and false?

It seems like we explicitly don't care about its identity (or else it would be isomorphic to AtomicBool).


================
Comment at: clang/include/clang/Analysis/FlowSensitive/Value.h:97
+/// formulas.
+class TopValue final : public BoolValue {
+public:
----------------
Here and throughout the patch: since we might have a top for other kinds of values, WDYT about renaming this type to TopBoolValue? Similarly createTop -> createTopBool ?


================
Comment at: clang/include/clang/Analysis/FlowSensitive/Value.h:102
+  static bool classof(const Value *Val) { return Val->getKind() == Kind::Top; }
+};
+
----------------
Since TopValue is a BoolValue, can we form say a ConjunctionValue where LHS or RHS is Top?

What if we create such a conjunction twice? It seems like such conjunctions would incorrectly compare equal, even though each Top will be replaced with a unique fresh variable.

Would it make sense to change our factory functions for boolean formulas to eagerly open Top?

Then we wouldn't need a recursive walk in unpackValue().


================
Comment at: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp:378
+    if (Val == It->second || (Val->getKind() == Value::Kind::Top &&
+                              It->second->getKind() == Value::Kind::Top)) {
       JoinedEnv.LocToVal.insert({Loc, Val});
----------------
isa<Top>


================
Comment at: clang/lib/Analysis/FlowSensitive/Transfer.cpp:76
+  case Value::Kind::AtomicBool:
+    return V;
+
----------------
llvm_unreachable(); because this can't happen, V is a BoolValue.


================
Comment at: clang/lib/Analysis/FlowSensitive/Transfer.cpp:123-124
+  return &UnpackedVal;
+}
+
+
----------------



================
Comment at: clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp:237-238
+      case Value::Kind::Top:
+        // Nothing more to do. Each `Top` instance will be mapped to a fresh
+        // variable and is thereafter anonymous.
+        break;
----------------



================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:469
 
+static bool isTop(Value &V) { return V.getKind() == Value::Kind::Top; }
+static bool isAtomic(Value &V) {
----------------
`isa<Top>(...)`? and `isa<AtomicBool>(...)` below?


================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:621
+        EXPECT_EQ(GetFooValue(Env3)->getProperty("has_value")->getKind(),
+                  Value::Kind::Top);
       });
----------------
`EXPECT_TRUE(isa<Top>(...))` ?


================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:1193
+                         const Environment &Env2) final {
+    // Changes to a sounds approximation, which allows us to test whether we can
+    // (soundly) converge for some loops.
----------------



================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:1220-1221
+
+    // void target(bool Foo) {
+    void target() {
+      bool Foo = makeTop();
----------------



================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:1292
+        ASSERT_THAT(FooVal2, NotNull());
+        EXPECT_TRUE(isTop(*FooVal2));
+
----------------



================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:1302-1311
+    void target(bool Cond) {
+      bool Foo = makeTop();
+      // Force a new block.
+      if (false) return;
+      (void)0;
+      /*[[p1]]*/
+
----------------
Similarly in tests below.

`if (false)` theoretically could be handled in a special way in future and could be folded away during CFG construction.


================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:1436-1440
+        const ValueDecl *Zab1Decl = findValueDecl(AO.ASTCtx, "Zab1");
+        ASSERT_THAT(Zab1Decl, NotNull());
+
+        const ValueDecl *Zab2Decl = findValueDecl(AO.ASTCtx, "Zab2");
+        ASSERT_THAT(Zab2Decl, NotNull());
----------------
Zab1Decl, Zab2Decl are unused apart from the NotNull check (which does not seem interesting to me).


================
Comment at: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp:1454
+      });
+}
+
----------------
Could you add a variant of this test?

```
    void target(bool Cond) {
      bool Foo = makeTop();
      // Force a new block.
      if (false) return;
      (void)0;
      /*[[p1]]*/

      bool Zab;
      if (Cond) {
        Zab = Foo;
      } else {
        Zab = Foo;
      }
      (void)0;
      if (Zab == Foo) { return; }
      /*[[p2]]*/
    }
```

Show the loss of precision by checking that the flow condition for p2 is satisfiable.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D135397/new/

https://reviews.llvm.org/D135397



More information about the cfe-commits mailing list