https://github.com/martinboehme updated https://github.com/llvm/llvm-project/pull/79999
>From 771d63d99968fb96417e818ee6ba7ab4bdf8f764 Mon Sep 17 00:00:00 2001 From: Martin Braenne <mboe...@google.com> Date: Wed, 31 Jan 2024 07:29:59 +0000 Subject: [PATCH] [clang][dataflow] In the CFG visualization, mark converged blocks. --- .../lib/Analysis/FlowSensitive/HTMLLogger.cpp | 102 ++++++++++-------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index 2a7bfce535015..1e098d8d23c42 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; + // For a given block ID, did the block converge (on the last iteration)? + llvm::BitVector BlockConverged; // 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. @@ -178,6 +182,8 @@ class HTMLLogger : public Logger { this->CFG = &CFG; *OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").first; + BlockConverged.resize(CFG.getCFG().getNumBlockIDs()); + const auto &D = CFG.getDecl(); const auto &SM = A.getASTContext().getSourceManager(); *OS << "<title>"; @@ -191,37 +197,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 +237,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) + BlockConverged[B.getBlockID()] = false; ElementIndex = 0; } void enterElement(const CFGElement &E) override { @@ -261,11 +269,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 +282,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; + BlockConverged[Iters.back().Block->getBlockID()] = true; + } void logText(llvm::StringRef S) override { ContextLogs.append(S.begin(), S.end()); @@ -307,23 +318,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 +488,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 +497,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 (BlockConverged[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()) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits