================
@@ -40,6 +44,481 @@
 namespace clang {
 namespace dataflow {
 
+namespace {
+
+class SimpleBoolModel : public Environment::BoolModel {
+public:
+  SimpleBoolModel() : BoolModel(BoolModelKind::Simple) {}
+
+  static bool classof(const BoolModel *M) {
+    return M->getKind() == BoolModelKind::Simple;
+  }
+
+  std::unique_ptr<BoolModel> clone() const override {
+    return std::make_unique<SimpleBoolModel>(*this);
+  }
+
+  void onFork() override {}
+
+  void assume(const Formula &F) override {
+    if (HasContradiction)
+      return;
+    unsigned PrevSize = AtomToVal.size();
+    // Given that F is assumed true, see if we can force the assignment
+    // of any atoms and ultimately reduce F to a literal (if possible).
+    // For example, if F is `A && B`, we will force `A` and `B` to be true.
+    // If F is `A || B`, we will only force an assignment if one of A or B is
+    // already known to be false.
+    assumeFormula(/*AssumedVal=*/true, F);
+    if (HasContradiction)
+      return;
+
+    // Retain `F` in `AssumedFormulas` only if it has not yet been reduced to a
+    // known truth value in `AtomToVal` (e.g., `A || B` or `A == B` where
+    // neither side is known yet).
+    //
+    // - Why save unresolved formulas: a compound formula (like
+    //   `EqVal => (LHS == RHS)` from a comparison expression) is often assumed
+    //   while its atoms are still unknown; when we later follow a branch that
+    //   fixes one of those atoms, the propagation loop below can reduce the
+    //   saved formula to concrete `AtomToVal` entries.
+    //
+    // - Skipping formulas that already evaluate to `true` avoids bloating
+    //   `AssumedFormulas` with single-atom and conjunctive assumptions that 
are
+    //   already fully represented by `AtomToVal` (though it also drops
+    //   disjunctive formulas that were reduced using a branch-local atom, such
+    //   as on the RHS of a short-circuit `&&` or `||`).
+    //   TODO(jvoung): We may want to retain some disjunctive formulas, but
+    //   would need to profile and see how much it helps vs how much it costs.
+    if (std::optional<bool> Val = getLiteralValue(F)) {
+      if (!*Val) {
+        HasContradiction = true;
+        return;
+      }
+    } else {
+      AssumedFormulas.insert(&F);
+    }
+
+    if (AtomToVal.size() == PrevSize)
+      return;
+
+    // Propagate newly learned atom values through any pending assumed 
formulas.
+    // Note: Do not remove formulas from `AssumedFormulas` when they evaluate 
to
+    // `true` in a branch, because the atoms that satisfied them may be
+    // branch-local and discarded at a subsequent `join`, where the formula
+    // itself still holds on both branches.
+    bool Changed = true;
+    while (Changed && !HasContradiction) {
+      Changed = false;
+      for (const Formula *Pending : AssumedFormulas) {
+        unsigned Before = AtomToVal.size();
+        assumeFormula(/*AssumedVal=*/true, *Pending);
+        if (HasContradiction)
+          return;
+        if (getLiteralValue(*Pending) == false) {
+          HasContradiction = true;
+          return;
+        }
+        if (AtomToVal.size() > Before)
+          Changed = true;
+      }
+    }
+  }
+
+  bool proves(const Formula &F) const override {
+    if (HasContradiction)
+      return true;
+    auto V = getLiteralValue(F);
+    return V.value_or(false);
+  }
+
+  bool allows(const Formula &F) const override {
+    if (HasContradiction)
+      return false;
+    auto V = getLiteralValue(F);
+    // We are conservative in the negative direction: if we can't determine the
+    // value, assume it is allowed.
+    return V.value_or(true);
+  }
+
+  BoolValue &joinBoolValues(BoolValue &Val1, const BoolModel &Model1,
+                            BoolValue &Val2, const BoolModel &Model2,
+                            Environment &JoinedEnv) override {
+    const auto &S1 = cast<SimpleBoolModel>(Model1);
+    const auto &S2 = cast<SimpleBoolModel>(Model2);
+    if (S1.HasContradiction && !S2.HasContradiction)
+      return Val2;
+    if (S2.HasContradiction && !S1.HasContradiction)
+      return Val1;
+    if (auto V1 = S1.getLiteralValue(Val1.formula());
+        V1.has_value() && V1 == S2.getLiteralValue(Val2.formula()))
+      return JoinedEnv.getBoolLiteralValue(*V1);
+    return JoinedEnv.makeAtomicBoolValue();
+  }
+
+  void join(const BoolModel &ModelA, const BoolModel &ModelB) override {
+    const auto &SA = cast<SimpleBoolModel>(ModelA);
+    const auto &SB = cast<SimpleBoolModel>(ModelB);
+    AtomToVal.clear();
+    AssumedFormulas.clear();
+    HasContradiction = false;
+
+    if (SA.HasContradiction && SB.HasContradiction) {
+      HasContradiction = true;
+    } else if (SA.HasContradiction) {
+      AtomToVal = SB.AtomToVal;
+      AssumedFormulas = SB.AssumedFormulas;
+    } else if (SB.HasContradiction) {
+      AtomToVal = SA.AtomToVal;
+      AssumedFormulas = SA.AssumedFormulas;
+    } else {
+      // Intersect the AtomToVal maps.
+      for (const auto &Entry : SA.AtomToVal) {
+        auto It = SB.AtomToVal.find(Entry.first);
+        if (It != SB.AtomToVal.end() && Entry.second == It->second)
+          AtomToVal.insert({Entry.first, Entry.second});
----------------
kazutakahirata wrote:

May I suggest structured bindings here?
```suggestion
      for (auto [A, V] : SA.AtomToVal) {
        auto It = SB.AtomToVal.find(A);
        if (It != SB.AtomToVal.end() && V == It->second)
          AtomToVal.insert({A, V});
```

https://github.com/llvm/llvm-project/pull/231061
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to