[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