[clang] ed65ced - [clang][dataflow] Identify post-visit state changes in the HTML logger. (#66746)

via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 20 06:19:02 PDT 2023


Author: martinboehme
Date: 2023-09-20T15:18:57+02:00
New Revision: ed65ced22af8a31ab374179d3d56b1e7c1456069

URL: https://github.com/llvm/llvm-project/commit/ed65ced22af8a31ab374179d3d56b1e7c1456069
DIFF: https://github.com/llvm/llvm-project/commit/ed65ced22af8a31ab374179d3d56b1e7c1456069.diff

LOG: [clang][dataflow] Identify post-visit state changes in the HTML logger. (#66746)

Previously, post-visit state changes were indistinguishable from
ordinary
iterations, which could give a confusing picture of how many iterations
a block
needs to converge.

Now, post-visit state changes are marked with "post-visit" instead of an
iteration number:


![screenshot](https://github.com/llvm/llvm-project/assets/29098113/5e9553d6-dfaa-45d3-8ea4-e623a14ee4c5))

Added: 
    

Modified: 
    clang/include/clang/Analysis/FlowSensitive/Logger.h
    clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
    clang/lib/Analysis/FlowSensitive/HTMLLogger.html
    clang/lib/Analysis/FlowSensitive/Logger.cpp
    clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
    clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Analysis/FlowSensitive/Logger.h b/clang/include/clang/Analysis/FlowSensitive/Logger.h
index 6836488003a97de..f4bd39f6ed49ef7 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Logger.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Logger.h
@@ -50,7 +50,9 @@ class Logger {
   /// Called when we start (re-)processing a block in the CFG.
   /// The target program point is the entry to the specified block.
   /// Calls to log() describe transferBranch(), join() etc.
-  virtual void enterBlock(const CFGBlock &) {}
+  /// `PostVisit` specifies whether we're processing the block for the
+  /// post-visit callback.
+  virtual void enterBlock(const CFGBlock &, bool PostVisit) {}
   /// Called when we start processing an element in the current CFG block.
   /// The target program point is after the specified element.
   /// Calls to log() describe the transfer() function.

diff  --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
index a5f64021eb6ba4b..47915958750d1f4 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
@@ -146,15 +146,21 @@ class ModelDumper {
 };
 
 class HTMLLogger : public Logger {
+  struct Iteration {
+    const CFGBlock *Block;
+    unsigned Iter;
+    bool PostVisit;
+  };
+
   StreamFactory Streams;
   std::unique_ptr<llvm::raw_ostream> OS;
   std::optional<llvm::json::OStream> JOS;
 
   const ControlFlowContext *CFG;
   // Timeline of iterations of CFG block visitation.
-  std::vector<std::pair<const CFGBlock *, unsigned>> Iters;
+  std::vector<Iteration> Iters;
   // Number of times each CFG block has been seen.
-  llvm::DenseMap<const CFGBlock *, unsigned> BlockIters;
+  llvm::DenseMap<const CFGBlock *, llvm::SmallVector<Iteration>> BlockIters;
   // The messages logged in the current context but not yet written.
   std::string ContextLogs;
   // The number of elements we have visited within the current CFG block.
@@ -198,8 +204,9 @@ class HTMLLogger : public Logger {
     JOS->attributeArray("timeline", [&] {
       for (const auto &E : Iters) {
         JOS->object([&] {
-          JOS->attribute("block", blockID(E.first->getBlockID()));
-          JOS->attribute("iter", E.second);
+          JOS->attribute("block", blockID(E.Block->getBlockID()));
+          JOS->attribute("iter", E.Iter);
+          JOS->attribute("post_visit", E.PostVisit);
         });
       }
     });
@@ -214,8 +221,11 @@ class HTMLLogger : public Logger {
     *OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").second;
   }
 
-  void enterBlock(const CFGBlock &B) override {
-    Iters.emplace_back(&B, ++BlockIters[&B]);
+  void enterBlock(const CFGBlock &B, bool PostVisit) override {
+    llvm::SmallVector<Iteration> &BIter = BlockIters[&B];
+    unsigned IterNum = BIter.size() + 1;
+    BIter.push_back({&B, IterNum, PostVisit});
+    Iters.push_back({&B, IterNum, PostVisit});
     ElementIndex = 0;
   }
   void enterElement(const CFGElement &E) override {
@@ -243,17 +253,19 @@ class HTMLLogger : public Logger {
   //  - meaningful names for values
   //  - which boolean values are implied true/false by the flow condition
   void recordState(TypeErasedDataflowAnalysisState &State) override {
-    unsigned Block = Iters.back().first->getBlockID();
-    unsigned Iter = Iters.back().second;
+    unsigned Block = Iters.back().Block->getBlockID();
+    unsigned Iter = Iters.back().Iter;
+    bool PostVisit = Iters.back().PostVisit;
     JOS->attributeObject(elementIterID(Block, Iter, ElementIndex), [&] {
       JOS->attribute("block", blockID(Block));
       JOS->attribute("iter", Iter);
+      JOS->attribute("post_visit", PostVisit);
       JOS->attribute("element", ElementIndex);
 
       // If this state immediately follows an Expr, show its built-in model.
       if (ElementIndex > 0) {
         auto S =
-            Iters.back().first->Elements[ElementIndex - 1].getAs<CFGStmt>();
+            Iters.back().Block->Elements[ElementIndex - 1].getAs<CFGStmt>();
         if (const Expr *E = S ? llvm::dyn_cast<Expr>(S->getStmt()) : nullptr) {
           if (E->isPRValue()) {
             if (auto *V = State.Env.getValue(*E))
@@ -289,9 +301,16 @@ class HTMLLogger : public Logger {
   // Write the CFG block details.
   // Currently this is just the list of elements in execution order.
   // FIXME: an AST dump would be a useful view, too.
-  void writeBlock(const CFGBlock &B, unsigned Iters) {
+  void writeBlock(const CFGBlock &B, llvm::ArrayRef<Iteration> ItersForB) {
     JOS->attributeObject(blockID(B.getBlockID()), [&] {
-      JOS->attribute("iters", Iters);
+      JOS->attributeArray("iters", [&] {
+        for (const auto &Iter : ItersForB) {
+          JOS->object([&] {
+            JOS->attribute("iter", Iter.Iter);
+            JOS->attribute("post_visit", Iter.PostVisit);
+          });
+        }
+      });
       JOS->attributeArray("elements", [&] {
         for (const auto &Elt : B.Elements) {
           std::string Dump;

diff  --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
index a60259a99cce066..87695623cb318b2 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
@@ -41,7 +41,11 @@
 <section id="timeline" data-selection="">
 <header>Timeline</header>
 <template data-for="entry in timeline">
-  <div id="{{entry.block}}:{{entry.iter}}" data-bb="{{entry.block}}" class="entry">{{entry.block}} ({{entry.iter}})</div>
+  <div id="{{entry.block}}:{{entry.iter}}" data-bb="{{entry.block}}" class="entry">
+    {{entry.block}}
+    <template data-if="entry.post_visit">(post-visit)</template>
+    <template data-if="!entry.post_visit">({{entry.iter}})</template>
+  </div>
 </template>
 </section>
 
@@ -54,8 +58,11 @@
 <section id="block" data-selection="bb">
 <header><template>Block {{selection.bb}}</template></header>
 <div id="iterations">
-  <template data-for="i in Array(cfg[selection.bb].iters).keys()">
-    <a class="chooser {{selection.bb}}:{{i+1}}" data-iter="{{selection.bb}}:{{i+1}}">Iteration {{i+1}}</a>
+  <template data-for="iter in cfg[selection.bb].iters">
+    <a class="chooser {{selection.bb}}:{{iter.iter}}" data-iter="{{selection.bb}}:{{iter.iter}}">
+      <template data-if="iter.post_visit">Post-visit</template>
+      <template data-if="!iter.post_visit">Iteration {{iter.iter}}</template>
+    </a>
   </template>
 </div>
 <table id="bb-elements">
@@ -77,8 +84,10 @@
 <section id="element" data-selection="iter,elt">
 <template data-let="state = states[selection.iter + '_' + selection.elt]">
 <header>
-  <template data-if="state.element == 0">{{state.block}} (iteration {{state.iter}}) initial state</template>
-  <template data-if="state.element != 0">Element {{selection.elt}} (iteration {{state.iter}})</template>
+  <template data-if="state.element == 0">{{state.block}} initial state</template>
+  <template data-if="state.element != 0">Element {{selection.elt}}</template>
+  <template data-if="state.post_visit"> (post-visit)</template>
+  <template data-if="!state.post_visit"> (iteration {{state.iter}})</template>
 </header>
 <template data-if="state.value" data-let="v = state.value">
   <h2>Value</h2>

diff  --git a/clang/lib/Analysis/FlowSensitive/Logger.cpp b/clang/lib/Analysis/FlowSensitive/Logger.cpp
index 28557638522e8f3..8c401df62e4459c 100644
--- a/clang/lib/Analysis/FlowSensitive/Logger.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Logger.cpp
@@ -57,12 +57,16 @@ struct TextualLogger final : Logger {
     llvm::errs() << "=== Finished analysis: " << Blocks << " blocks in "
                  << Steps << " total steps ===\n";
   }
-  virtual void enterBlock(const CFGBlock &Block) override {
+  virtual void enterBlock(const CFGBlock &Block, bool PostVisit) override {
     unsigned Count = ++VisitCount[&Block];
     {
       llvm::WithColor Header(OS, llvm::raw_ostream::Colors::RED, /*Bold=*/true);
-      OS << "=== Entering block B" << Block.getBlockID() << " (iteration "
-         << Count << ") ===\n";
+      OS << "=== Entering block B" << Block.getBlockID();
+      if (PostVisit)
+        OS << " (post-visit)";
+      else
+        OS << " (iteration " << Count << ")";
+      OS << " ===\n";
     }
     Block.print(OS, CurrentCFG, CurrentAnalysis->getASTContext().getLangOpts(),
                 ShowColors);

diff  --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index 900aa97e9f6de64..01b397787430ac6 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -469,7 +469,7 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
                  std::function<void(const CFGElement &,
                                     const TypeErasedDataflowAnalysisState &)>
                      PostVisitCFG = nullptr) {
-  AC.Log.enterBlock(Block);
+  AC.Log.enterBlock(Block, PostVisitCFG != nullptr);
   auto State = computeBlockInputState(Block, AC);
   AC.Log.recordState(State);
   int ElementIdx = 1;

diff  --git a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
index 7201771e0b11822..a60dbe1f61f6d6e 100644
--- a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
@@ -64,8 +64,9 @@ class TestLogger : public Logger {
   }
   void endAnalysis() override { logText("\nendAnalysis()"); }
 
-  void enterBlock(const CFGBlock &B) override {
-    OS << "\nenterBlock(" << B.BlockID << ")\n";
+  void enterBlock(const CFGBlock &B, bool PostVisit) override {
+    OS << "\nenterBlock(" << B.BlockID << ", " << (PostVisit ? "true" : "false")
+       << ")\n";
   }
   void enterElement(const CFGElement &E) override {
     // we don't want the trailing \n
@@ -114,7 +115,7 @@ TEST(LoggerTest, Sequence) {
 
   EXPECT_EQ(Log, R"(beginAnalysis()
 
-enterBlock(4)
+enterBlock(4, false)
 recordState(Elements=0, Branches=0, Joins=0)
 enterElement(b)
 transfer()
@@ -123,21 +124,21 @@ enterElement(b (ImplicitCastExpr, LValueToRValue, _Bool))
 transfer()
 recordState(Elements=2, Branches=0, Joins=0)
 
-enterBlock(3)
+enterBlock(3, false)
 transferBranch(0)
 recordState(Elements=2, Branches=1, Joins=0)
 enterElement(q)
 transfer()
 recordState(Elements=3, Branches=1, Joins=0)
 
-enterBlock(2)
+enterBlock(2, false)
 transferBranch(1)
 recordState(Elements=2, Branches=1, Joins=0)
 enterElement(p)
 transfer()
 recordState(Elements=3, Branches=1, Joins=0)
 
-enterBlock(1)
+enterBlock(1, false)
 recordState(Elements=6, Branches=2, Joins=1)
 enterElement(b ? p : q)
 transfer()
@@ -149,7 +150,7 @@ enterElement(return b ? p : q;)
 transfer()
 recordState(Elements=9, Branches=2, Joins=1)
 
-enterBlock(0)
+enterBlock(0, false)
 recordState(Elements=9, Branches=2, Joins=1)
 
 endAnalysis()


        


More information about the cfe-commits mailing list