[clang] [clang][dataflow] In the CFG visualization, mark converged blocks. (PR #79999)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 30 05:01:55 PST 2024


https://github.com/martinboehme created https://github.com/llvm/llvm-project/pull/79999

None

>From 418f6a64dd9f9b9d3895ca4e88150fcd0ac99fa3 Mon Sep 17 00:00:00 2001
From: Martin Braenne <mboehme at google.com>
Date: Tue, 30 Jan 2024 13:01:12 +0000
Subject: [PATCH] [clang][dataflow] In the CFG visualization, mark converged
 blocks.

---
 .../lib/Analysis/FlowSensitive/HTMLLogger.cpp | 100 ++++++++++--------
 1 file changed, 58 insertions(+), 42 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
index 2a7bfce53501..556409d057ff 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
@@ -158,13 +158,17 @@ class HTMLLogger : public Logger {
 
   StreamFactory Streams;
   std::unique_ptr<llvm::raw_ostream> OS;
-  std::optional<llvm::json::OStream> JOS;
+  std::string JSON;
+  llvm::raw_string_ostream JStringStream{JSON};
+  llvm::json::OStream JOS{JStringStream, /*Indent=*/2};
 
   const ControlFlowContext *CFG;
   // Timeline of iterations of CFG block visitation.
   std::vector<Iteration> Iters;
   // Indexes  in `Iters` of the iterations for each block.
   llvm::DenseMap<const CFGBlock *, llvm::SmallVector<size_t>> BlockIters;
+  // IDs of blocks that converged (on the last iteration).
+  llvm::DenseSet<unsigned> ConvergedBlocks;
   // 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.
@@ -191,37 +195,37 @@ class HTMLLogger : public Logger {
     *OS << "<script>" << HTMLLogger_js << "</script>\n";
 
     writeCode();
-    writeCFG();
-
-    *OS << "<script>var HTMLLoggerData = \n";
-    JOS.emplace(*OS, /*Indent=*/2);
-    JOS->objectBegin();
-    JOS->attributeBegin("states");
-    JOS->objectBegin();
+    JOS.objectBegin();
+    JOS.attributeBegin("states");
+    JOS.objectBegin();
   }
   // Between beginAnalysis() and endAnalysis() we write all the states for
   // particular analysis points into the `timeline` array.
   void endAnalysis() override {
-    JOS->objectEnd();
-    JOS->attributeEnd();
+    JOS.objectEnd();
+    JOS.attributeEnd();
 
-    JOS->attributeArray("timeline", [&] {
+    JOS.attributeArray("timeline", [&] {
       for (const auto &E : Iters) {
-        JOS->object([&] {
-          JOS->attribute("block", blockID(E.Block->getBlockID()));
-          JOS->attribute("iter", E.Iter);
-          JOS->attribute("post_visit", E.PostVisit);
-          JOS->attribute("converged", E.Converged);
+        JOS.object([&] {
+          JOS.attribute("block", blockID(E.Block->getBlockID()));
+          JOS.attribute("iter", E.Iter);
+          JOS.attribute("post_visit", E.PostVisit);
+          JOS.attribute("converged", E.Converged);
         });
       }
     });
-    JOS->attributeObject("cfg", [&] {
+    JOS.attributeObject("cfg", [&] {
       for (const auto &E : BlockIters)
         writeBlock(*E.first, E.second);
     });
 
-    JOS->objectEnd();
-    JOS.reset();
+    JOS.objectEnd();
+
+    writeCFG();
+
+    *OS << "<script>var HTMLLoggerData = \n";
+    *OS << JSON;
     *OS << ";\n</script>\n";
     *OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").second;
   }
@@ -231,6 +235,8 @@ class HTMLLogger : public Logger {
     unsigned IterNum = BIter.size() + 1;
     BIter.push_back(Iters.size());
     Iters.push_back({&B, IterNum, PostVisit, /*Converged=*/false});
+    if (!PostVisit)
+      ConvergedBlocks.erase(B.getBlockID());
     ElementIndex = 0;
   }
   void enterElement(const CFGElement &E) override {
@@ -261,11 +267,11 @@ class HTMLLogger : public Logger {
     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);
+    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) {
@@ -274,28 +280,31 @@ class HTMLLogger : public Logger {
         if (const Expr *E = S ? llvm::dyn_cast<Expr>(S->getStmt()) : nullptr) {
           if (E->isPRValue()) {
             if (auto *V = State.Env.getValue(*E))
-              JOS->attributeObject(
-                  "value", [&] { ModelDumper(*JOS, State.Env).dump(*V); });
+              JOS.attributeObject(
+                  "value", [&] { ModelDumper(JOS, State.Env).dump(*V); });
           } else {
             if (auto *Loc = State.Env.getStorageLocation(*E))
-              JOS->attributeObject(
-                  "value", [&] { ModelDumper(*JOS, State.Env).dump(*Loc); });
+              JOS.attributeObject(
+                  "value", [&] { ModelDumper(JOS, State.Env).dump(*Loc); });
           }
         }
       }
       if (!ContextLogs.empty()) {
-        JOS->attribute("logs", ContextLogs);
+        JOS.attribute("logs", ContextLogs);
         ContextLogs.clear();
       }
       {
         std::string BuiltinLattice;
         llvm::raw_string_ostream BuiltinLatticeS(BuiltinLattice);
         State.Env.dump(BuiltinLatticeS);
-        JOS->attribute("builtinLattice", BuiltinLattice);
+        JOS.attribute("builtinLattice", BuiltinLattice);
       }
     });
   }
-  void blockConverged() override { Iters.back().Converged = true; }
+  void blockConverged() override {
+    Iters.back().Converged = true;
+    ConvergedBlocks.insert(Iters.back().Block->getBlockID());
+  }
 
   void logText(llvm::StringRef S) override {
     ContextLogs.append(S.begin(), S.end());
@@ -307,23 +316,23 @@ class HTMLLogger : public Logger {
   // 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, llvm::ArrayRef<size_t> ItersForB) {
-    JOS->attributeObject(blockID(B.getBlockID()), [&] {
-      JOS->attributeArray("iters", [&] {
+    JOS.attributeObject(blockID(B.getBlockID()), [&] {
+      JOS.attributeArray("iters", [&] {
         for (size_t IterIdx : ItersForB) {
           const Iteration &Iter = Iters[IterIdx];
-          JOS->object([&] {
-            JOS->attribute("iter", Iter.Iter);
-            JOS->attribute("post_visit", Iter.PostVisit);
-            JOS->attribute("converged", Iter.Converged);
+          JOS.object([&] {
+            JOS.attribute("iter", Iter.Iter);
+            JOS.attribute("post_visit", Iter.PostVisit);
+            JOS.attribute("converged", Iter.Converged);
           });
         }
       });
-      JOS->attributeArray("elements", [&] {
+      JOS.attributeArray("elements", [&] {
         for (const auto &Elt : B.Elements) {
           std::string Dump;
           llvm::raw_string_ostream DumpS(Dump);
           Elt.dumpToStream(DumpS);
-          JOS->value(Dump);
+          JOS.value(Dump);
         }
       });
     });
@@ -477,7 +486,7 @@ class HTMLLogger : public Logger {
   }
 
   // Produce a graphviz description of a CFG.
-  static std::string buildCFGDot(const clang::CFG &CFG) {
+  std::string buildCFGDot(const clang::CFG &CFG) {
     std::string Graph;
     llvm::raw_string_ostream GraphS(Graph);
     // Graphviz likes to add unhelpful tooltips everywhere, " " suppresses.
@@ -486,8 +495,15 @@ class HTMLLogger : public Logger {
       node[class=bb, shape=square, fontname="sans-serif", tooltip=" "]
       edge[tooltip = " "]
 )";
-    for (unsigned I = 0; I < CFG.getNumBlockIDs(); ++I)
-      GraphS << "  " << blockID(I) << " [id=" << blockID(I) << "]\n";
+    for (unsigned I = 0; I < CFG.getNumBlockIDs(); ++I) {
+      std::string Name = blockID(I);
+      // Rightwards arrow, vertical line
+      char ConvergenceMarker[] = u8"\\n\u2192\u007c";
+      if (ConvergedBlocks.contains(I))
+        Name += ConvergenceMarker;
+      GraphS << "  " << blockID(I) << " [id=" << blockID(I) << " label=\""
+             << Name << "\"]\n";
+    }
     for (const auto *Block : CFG) {
       for (const auto &Succ : Block->succs()) {
         if (Succ.getReachableBlock())



More information about the cfe-commits mailing list