li.zhe.hua updated this revision to Diff 451861.
li.zhe.hua marked 2 inline comments as done.
li.zhe.hua added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D131646

Files:
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -42,13 +42,17 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
+using ::llvm::HasValue;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
+using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Ref;
+using ::testing::SizeIs;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -222,6 +226,59 @@
             "maximum number of iterations reached");
 }
 
+struct ConvergesOnWidenLattice {
+  int State = 0;
+  bool Top = false;
+
+  bool operator==(const ConvergesOnWidenLattice &Other) const {
+    if (Top)
+      return Other.Top;
+    return State == Other.State;
+  }
+
+  LatticeJoinEffect join(const ConvergesOnWidenLattice &Other) {
+    auto Prev = *this;
+    Top = Top || Other.Top;
+    State += Other.State;
+    return Prev == *this ? LatticeJoinEffect::Unchanged
+                         : LatticeJoinEffect::Changed;
+  }
+
+  void widen(const ConvergesOnWidenLattice &Other) { Top = true; }
+
+  friend std::ostream &operator<<(std::ostream &OS,
+                                  const ConvergesOnWidenLattice &L) {
+    return OS << "{" << L.State << "," << (L.Top ? "true" : "false") << "}";
+  }
+};
+
+class ConvergesOnWidenAnalysis
+    : public DataflowAnalysis<ConvergesOnWidenAnalysis,
+                              ConvergesOnWidenLattice> {
+public:
+  explicit ConvergesOnWidenAnalysis(ASTContext &Context)
+      : DataflowAnalysis(Context,
+                         /*ApplyBuiltinTransfer=*/false) {}
+
+  static ConvergesOnWidenLattice initialElement() { return {}; }
+
+  void transfer(const Stmt *S, ConvergesOnWidenLattice &E, Environment &Env) {
+    ++E.State;
+  }
+};
+
+TEST(DataflowAnalysisTest, ConvergesOnWidenAnalysis) {
+  std::string Code = R"(
+    void target() {
+      while(true) {}
+    }
+  )";
+  EXPECT_THAT_EXPECTED(
+      runAnalysis<ConvergesOnWidenAnalysis>(
+          Code, [](ASTContext &C) { return ConvergesOnWidenAnalysis(C); }),
+      HasValue(AllOf(SizeIs(4), Each(Optional(_)))));
+}
+
 struct FunctionCallLattice {
   llvm::SmallSet<std::string, 8> CalledFunctions;
 
Index: clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -154,6 +154,32 @@
   TransferOptions TransferOpts;
 };
 
+// Returns whether `Block` is a "back edge" in the CFG. Such a block is empty
+// and has only one successor, the start of the loop.
+static bool isBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  return Block->LoopTarget != nullptr;
+}
+
+// Returns the predecessor to `Block` that is a "back edge", if one exists.
+//
+// If this function returns a non-null pointer, that means `Block` dominates the
+// back edge block. (That is, all paths from the entry block to the back edge
+// block must go through `Block`.) It also means that there are only two
+// predecessors; the other is along the path from the entry block to `Block`.
+static const CFGBlock *findBackEdge(const CFGBlock *Block) {
+  assert(Block != nullptr);
+  for (const auto &Pred : Block->preds()) {
+    if (!Pred.isReachable())
+      continue;
+    if (isBackEdge(Pred)) {
+      assert(Block->pred_size() == 2);
+      return Pred;
+    }
+  }
+  return nullptr;
+}
+
 /// Computes the input state for a given basic block by joining the output
 /// states of its predecessors.
 ///
@@ -199,6 +225,21 @@
     }
   }
 
+  // If we are at the start of a loop, we will have two precessors, but we don't
+  // want to join these two predecessors. Instead, we want to take the back edge
+  // block (i.e. the result of the previous iteration) and use that directly as
+  // the input block.
+  //
+  // For the first iteration of loop, the "zeroth" iteration state is set up by
+  // `prepareBackEdges`.
+  if (const CFGBlock *BackEdge = findBackEdge(&Block)) {
+    const llvm::Optional<TypeErasedDataflowAnalysisState> &MaybePredState =
+        BlockStates[BackEdge->getBlockID()];
+    assert(MaybePredState.has_value());
+    assert(BackEdge->getTerminatorStmt() == nullptr);
+    return *MaybePredState;
+  }
+
   llvm::Optional<TypeErasedDataflowAnalysisState> MaybeState;
   bool ApplyBuiltinTransfer = Analysis.applyBuiltinTransfer();
 
@@ -311,6 +352,23 @@
         HandleTransferredStmt) {
   TypeErasedDataflowAnalysisState State =
       computeBlockInputState(CFCtx, BlockStates, Block, InitEnv, Analysis);
+
+  // The back edge block is where we perform a widen, allowing certain analyses
+  // to converge in finite time. The `PrevState` of the previous iteration of
+  // the loop is widened to subsume the input `State`.
+  //
+  // FIXME: Allow users to configure a certain number of iterations to unroll,
+  // to allow higher precision in their analyses.
+  if (isBackEdge(&Block)) {
+    assert(Block.empty());
+    auto PrevState = BlockStates[Block.getBlockID()];
+    assert(PrevState.has_value());
+    Analysis.widenTypeErased(PrevState->Lattice, State.Lattice);
+    // FIXME: Add a widen operator to the `Environment`.
+    PrevState->Env.join(State.Env, Analysis);
+    return *PrevState;
+  }
+
   for (const CFGElement &Element : Block) {
     switch (Element.getKind()) {
     case CFGElement::Statement:
@@ -329,6 +387,34 @@
   return State;
 }
 
+// Given a `Block` with its state set in `BlockStates`, copies this state to the
+// back edge of all successors that are the first block of a loop.
+static void prepareBackEdges(
+    const CFGBlock &Block,
+    llvm::MutableArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>>
+        BlockStates) {
+  const auto &State = BlockStates[Block.getBlockID()];
+  assert(State.has_value());
+
+  for (const auto &Succ : Block.succs()) {
+    if (!Succ.isReachable())
+      continue;
+    if (const CFGBlock *BackEdge = findBackEdge(Succ);
+        BackEdge != nullptr && BackEdge != &Block) {
+      // `Succ` is the first block of the loop body, `Block` is the "entrance"
+      // to the loop, and `BackEdge` is the back-edge of the loop.
+      //
+      // `Block` represents the input state to the loop before any iterations
+      // have been executed. By copying this state to the back edge, we can
+      // consistently use the back edge block as the input state to the loop,
+      // even on its first iteration. This subsequently handles nested loops; if
+      // the outer loop requires additional passes to converge, the inner loop
+      // is correctly "reinitialized" once `Block` is transfered.
+      BlockStates[BackEdge->getBlockID()] = State;
+    }
+  }
+}
+
 llvm::Expected<std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>>
 runTypeErasedDataflowAnalysis(
     const ControlFlowContext &CFCtx, TypeErasedDataflowAnalysis &Analysis,
@@ -387,6 +473,9 @@
       continue;
 
     Worklist.enqueueSuccessors(Block);
+
+    // If we are about to enter into a loop, we set up our back edge blocks.
+    prepareBackEdges(*Block, BlockStates);
   }
   // FIXME: Consider evaluating unreachable basic blocks (those that have a
   // state set to `llvm::None` at this point) to also analyze dead code.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to