https://github.com/jvoung updated https://github.com/llvm/llvm-project/pull/179546
>From 1a4730f84e8229f20a46cc38df5162ef1be45a29 Mon Sep 17 00:00:00 2001 From: Jan Voung <[email protected]> Date: Tue, 3 Feb 2026 20:40:01 +0000 Subject: [PATCH 1/3] [clang][analysis][flowsensitive] Detect goto backedges to trigger Widen Currently, the ClangDataflowFramework only does Widen on backedges from structured loops. Missing some Widen calls (e.g., when there are backedges from gotos) could cause some analyses to iterate ~forever (until the max visits limit is hit). This adds a simple search for backedges, and triggers Widen on the additional backedge nodes. Fixes issue 179083. --- clang/include/clang/Analysis/CFGBackEdges.h | 27 ++ clang/lib/Analysis/CFGBackEdges.cpp | 68 +++++ clang/lib/Analysis/CMakeLists.txt | 1 + .../TypeErasedDataflowAnalysis.cpp | 63 ++++- clang/unittests/Analysis/CFGBackEdgesTest.cpp | 239 ++++++++++++++++++ clang/unittests/Analysis/CMakeLists.txt | 1 + .../TypeErasedDataflowAnalysisTest.cpp | 53 ++++ 7 files changed, 443 insertions(+), 9 deletions(-) create mode 100644 clang/include/clang/Analysis/CFGBackEdges.h create mode 100644 clang/lib/Analysis/CFGBackEdges.cpp create mode 100644 clang/unittests/Analysis/CFGBackEdgesTest.cpp diff --git a/clang/include/clang/Analysis/CFGBackEdges.h b/clang/include/clang/Analysis/CFGBackEdges.h new file mode 100644 index 0000000000000..497c8fea83358 --- /dev/null +++ b/clang/include/clang/Analysis/CFGBackEdges.h @@ -0,0 +1,27 @@ +//===- CFGBackEdges.h - Finds back edges in Clang CFGs -*- C++ ----------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_CFG_BACKEDGES_H +#define LLVM_CLANG_ANALYSIS_CFG_BACKEDGES_H + +#include "clang/Analysis/CFG.h" +#include "llvm/ADT/DenseMap.h" + +namespace clang { + +/// Finds and returns back edges in Clang CFGs. The CFG already has some +/// backedge information for structured loops (\c CFGBlock::getLoopTarget). +/// However, unstructured back edges from \c goto statements are not included. +/// This helps find back edges, whether the CFG is reducible or not. +/// This includes CFGBlock::getLoopTarget nodes, but one can filter those out. +llvm::DenseMap<const CFGBlock *, const CFGBlock *> +findCFGBackEdges(const CFG &CFG); + +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_CFG_BACKEDGES_H diff --git a/clang/lib/Analysis/CFGBackEdges.cpp b/clang/lib/Analysis/CFGBackEdges.cpp new file mode 100644 index 0000000000000..7cc4db2a907c1 --- /dev/null +++ b/clang/lib/Analysis/CFGBackEdges.cpp @@ -0,0 +1,68 @@ +//===- CFGBackEdges.cpp - Finds back edges in Clang CFGs ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <stack> +#include <utility> +#include <vector> + +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGBackEdges.h" +#include "llvm/ADT/DenseMap.h" + +namespace clang { + +namespace { +struct VisitClockTimes { + // Timestamp for when the node was visited / discovered. + int Pre = -1; + // Timestamp for when we finished visiting a node's successors. + int Post = -1; +}; +} // namespace + +llvm::DenseMap<const CFGBlock *, const CFGBlock *> +findCFGBackEdges(const clang::CFG &CFG) { + // Do a simple textbook DFS with pre-post numberings to find back edges. + llvm::DenseMap<const CFGBlock *, const CFGBlock *> BackEdges; + + std::vector<VisitClockTimes> VisitState; + VisitState.resize(CFG.getNumBlockIDs()); + std::stack<std::pair<const CFGBlock *, CFGBlock::const_succ_iterator>> + DFSStack; + int Clock = 0; + const CFGBlock &Entry = CFG.getEntry(); + VisitState[Entry.getBlockID()].Pre = Clock++; + DFSStack.push({&Entry, Entry.succ_begin()}); + + while (!DFSStack.empty()) { + auto &[Block, SuccIt] = DFSStack.top(); + if (SuccIt == Block->succ_end()) { + VisitState[Block->getBlockID()].Post = Clock++; + DFSStack.pop(); + continue; + } + + const CFGBlock::AdjacentBlock &AdjacentSucc = *SuccIt++; + const CFGBlock *Succ = AdjacentSucc.getReachableBlock(); + // Skip unreachable blocks. + if (Succ == nullptr) + continue; + + VisitClockTimes &SuccVisitState = VisitState[Succ->getBlockID()]; + if (SuccVisitState.Pre != -1) { + if (SuccVisitState.Post == -1) + BackEdges.insert({Block, Succ}); + } else { + SuccVisitState.Pre = Clock++; + DFSStack.push({Succ, Succ->succ_begin()}); + } + } + return BackEdges; +} + +} // namespace clang diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 65f160e965d47..fef688424978d 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_library(clangAnalysis BodyFarm.cpp CalledOnceCheck.cpp CFG.cpp + CFGBackEdges.cpp CFGReachabilityAnalysis.cpp CFGStmtMap.cpp CallGraph.cpp diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 1113bbe7f4d9c..51fc1c3ccd459 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -19,18 +19,23 @@ #include "clang/AST/ASTDumper.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGBackEdges.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" #include "clang/Analysis/FlowSensitive/Transfer.h" #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/Value.h" +#include "clang/Basic/LLVM.h" #include "clang/Support/Compiler.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Error.h" @@ -64,14 +69,23 @@ static int blockIndexInPredecessor(const CFGBlock &Pred, return BlockPos - Pred.succ_begin(); } -// A "backedge" node is a block introduced in the CFG exclusively to indicate a -// loop backedge. They are exactly identified by the presence of a non-null -// pointer to the entry block of the loop condition. Note that this is not -// necessarily the block with the loop statement as terminator, because -// short-circuit operators will result in multiple blocks encoding the loop -// condition, only one of which will contain the loop statement as terminator. -static bool isBackedgeNode(const CFGBlock &B) { - return B.getLoopTarget() != nullptr; +// A "backedge node" is a node that is the source of a backedge in the CFG +// (given backedge from U to V, U is the "backedge node"). +// This can be either: +// - A block introduced in the CFG exclusively to indicate a structured loop's +// backedge. They are exactly identified by the presence of a non-null +// pointer to the entry block of the loop condition. Note that this is not +// necessarily the block with the loop statement as terminator, because +// short-circuit operators will result in multiple blocks encoding the loop +// condition, only one of which will contain the loop statement as terminator. +// - Or, a block that is part of a backedge in a CFG with unstructured loops +// (e.g., a CFG with a `goto` statement). Note that this is not +// necessarily the block with the goto statement as terminator. The choice +// depends on how blocks and edges are ordered. +static bool isBackedgeNode( + const CFGBlock &B, + const llvm::SmallDenseSet<const CFGBlock *> &NonLoopBackedgeNodes) { + return B.getLoopTarget() != nullptr || NonLoopBackedgeNodes.contains(&B); } namespace { @@ -484,6 +498,35 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC, return State; } +// Returns true if the CFG contains any goto statements (direct or indirect). +static bool hasGotoInCFG(const clang::CFG &CFG) { + for (const CFGBlock *Block : CFG) { + const Stmt *Term = Block->getTerminatorStmt(); + if (Term == nullptr) continue; + if (isa<GotoStmt>(Term) || isa<IndirectGotoStmt>(Term)) + return true; + } + return false; +} + +// Returns a set of CFG blocks that is the source of a backedge and is not +// tracked part of a structured loop (according to `CFGBlock::getLoopTarget`). +static llvm::SmallDenseSet<const CFGBlock *> +findNonLoopBackedgeNodes(const clang::CFG &CFG) { + llvm::SmallDenseSet<const CFGBlock *> NonLoopBackedgeNodes; + // We should only need this if the function has gotos. + if (!hasGotoInCFG(CFG)) + return NonLoopBackedgeNodes; + + llvm::DenseMap<const CFGBlock *, const CFGBlock *> BackEdges = + findCFGBackEdges(CFG); + for (const auto &[From, To] : BackEdges) { + if (From->getLoopTarget() == nullptr) + NonLoopBackedgeNodes.insert(From); + } + return NonLoopBackedgeNodes; +} + llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>> runTypeErasedDataflowAnalysis( const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis, @@ -503,6 +546,8 @@ runTypeErasedDataflowAnalysis( const clang::CFG &CFG = ACFG.getCFG(); PostOrderCFGView POV(&CFG); ForwardDataflowWorklist Worklist(CFG, &POV); + llvm::SmallDenseSet<const CFGBlock *> NonLoopBackedgeNodes = + findNonLoopBackedgeNodes(CFG); std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates( CFG.size()); @@ -537,7 +582,7 @@ runTypeErasedDataflowAnalysis( llvm::errs() << "Old Env:\n"; OldBlockState->Env.dump(); }); - if (isBackedgeNode(*Block)) { + if (isBackedgeNode(*Block, NonLoopBackedgeNodes)) { LatticeJoinEffect Effect1 = Analysis.widenTypeErased( NewBlockState.Lattice, OldBlockState->Lattice); LatticeJoinEffect Effect2 = diff --git a/clang/unittests/Analysis/CFGBackEdgesTest.cpp b/clang/unittests/Analysis/CFGBackEdgesTest.cpp new file mode 100644 index 0000000000000..6ab8ccce4cdd0 --- /dev/null +++ b/clang/unittests/Analysis/CFGBackEdgesTest.cpp @@ -0,0 +1,239 @@ +//===- unittests/Analysis/CFGBackEdgesTest.cpp - CFG backedges tests ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/CFGBackEdges.h" +#include "CFGBuildResult.h" +#include "clang/AST/Stmt.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/LLVM.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace analysis { +namespace { + +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; + +TEST(CFGBackEdgesTest, NoBackedgesLinear) { + const char *Code = R"cc( + int f(int x) { + l1: + x++; + l2: + x++; + return x; + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_TRUE(BackEdges.empty()); +} + +TEST(CFGBackEdgesTest, NoBackedgesOnlyCrossEdge) { + const char *Code = R"cc( + int f(int x) { + if (x > 0) + x++; + else + x--; + return x; + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_TRUE(BackEdges.empty()); +} + +TEST(CFGBackEdgesTest, NoBackedgesWithUnreachableSuccessorForSwitch) { + const char *Code = R"cc( + enum class Kind { A, B }; + + void f(Kind kind) { + switch(kind) { + case Kind::A: return 0; + case Kind::B: break; + } + return 1; + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_TRUE(BackEdges.empty()); +} + +TEST(CFGBackEdgesTest, ForLoop) { + const char *Code = R"cc( + void f(int n) { + for (int i = 0; i < n; ++i) {} + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + // Finds one backedge, which is the one looping back to the loop header + // (has a loop target). + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(1)); + EXPECT_THAT(BackEdges.begin()->first->getLoopTarget(), NotNull()); +} + +TEST(CFGBackEdgesTest, WhileLoop) { + const char *Code = R"cc( + void f(int n) { + int i = 0; + while (i < n) { ++i; } + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(1)); + EXPECT_THAT(BackEdges.begin()->first->getLoopTarget(), NotNull()); +} + +TEST(CFGBackEdgesTest, DoWhileLoop) { + const char *Code = R"cc( + void f(int n) { + int i = 0; + do { ++i; } while (i < n); + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(1)); + EXPECT_THAT(BackEdges.begin()->first->getLoopTarget(), NotNull()); +} + +TEST(CFGBackEdgesTest, GotoLoop) { + const char *Code = R"cc( + void f(int n) { + int i = 0; + loop: + if (i < n) { + ++i; + goto loop; + } + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + // Finds one backedge, but since it's an unstructured loop, the loop target is + // null. Instead, the node has a goto terminator. + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(1)); + EXPECT_THAT(BackEdges.begin()->first->getLoopTarget(), IsNull()); + EXPECT_TRUE(isa<GotoStmt>(BackEdges.begin()->first->getTerminatorStmt())); +} + +TEST(CFGBackEdgesTest, WhileWithContinueLoop) { + const char *Code = R"cc( + void f(int n) { + int i = 0; + while (i < n) { + ++i; + if (i == 5) continue; + if (i == 10) break; + i *= 2; + } + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(testing::Gt(0))); + for (const auto &[From, To] : BackEdges) + EXPECT_THAT(From->getLoopTarget(), NotNull()); +} + +TEST(CFGBackEdgesTest, NestedForLoop) { + const char *Code = R"cc( + void f(int n) { + for (int i = 0; i < n; ++i) { + for (int j = i; j < n; ++j) {} + } + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + // Finds one backedge, which is the one looping back to the loop header + // (has a loop target). + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(2)); + auto it = BackEdges.begin(); + EXPECT_THAT(it->first->getLoopTarget(), NotNull()); + ++it; + EXPECT_THAT(it->first->getLoopTarget(), NotNull()); +} + +TEST(CFGBackEdgesTest, IrreducibleCFG) { + const char *Code = R"cc( + void f(int cond) { + if (cond) goto L1; + L0: + goto L1; + L1: + goto L0; + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + // In an irreducible CFG, we still expect to find a back edge. + EXPECT_THAT(BackEdges, SizeIs(1)); + EXPECT_TRUE(isa<GotoStmt>(BackEdges.begin()->first->getTerminatorStmt())); +} + +TEST(CFGBackEdgesTest, FirstBackedgeIsNotGoto) { + const char *Code = R"cc( + void f(int x, int y) { + if (x > y) { + } else { + L1: + --x; + if (x == 0) return; + } + goto L1; + })cc"; + BuildResult Result = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus()); + CFG *Cfg = Result.getCFG(); + + auto BackEdges = findCFGBackEdges(*Cfg); + EXPECT_THAT(BackEdges, SizeIs(1)); + // We might find a backedge where the source block doesn't terminate with + // a `goto`, due to the DFS search order. For example: + // + // B_entry: `if (x > y)` + // \--then--> B1: `<empty>` + // --> B2: `goto L1` + // --> B3: `--x; if (x == 0)` + // \--then--> B4 `return` --> B_exit. + // \--else--> B2: ... (the `if`'s else is a backedge from B3 to B2!) + // \--else--> B3: ... + EXPECT_FALSE(isa<GotoStmt>(BackEdges.begin()->first->getTerminatorStmt())); +} + +} // namespace +} // namespace analysis +} // namespace clang diff --git a/clang/unittests/Analysis/CMakeLists.txt b/clang/unittests/Analysis/CMakeLists.txt index 97e768b11db69..7cefa2caf3c91 100644 --- a/clang/unittests/Analysis/CMakeLists.txt +++ b/clang/unittests/Analysis/CMakeLists.txt @@ -1,4 +1,5 @@ add_clang_unittest(ClangAnalysisTests + CFGBackEdgesTest.cpp CFGDominatorTree.cpp CFGTest.cpp CloneDetectionTest.cpp diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp index d1dd4ff3ea33e..976ed3d82d378 100644 --- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp @@ -1145,6 +1145,34 @@ TEST_F(WideningTest, DistinctValuesWithDifferentPropertiesWidenedToTop) { }); } +TEST_F(WideningTest, + DistinctValuesWithDifferentPropertiesWidenedToTopGotoInsteadOfWhile) { + std::string Code = R"cc( + void target(bool Cond) { + int *Foo; + int i = 0; + Foo = nullptr; + start: + if (Cond) { + Foo = &i; + goto start; + } + (void)0; + /*[[p]]*/ + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + const auto &FooVal = getValueForDecl<Value>(ASTCtx, Env, "Foo"); + ASSERT_THAT(FooVal.getProperty("is_null"), NotNull()); + EXPECT_TRUE(areEquivalentValues(*FooVal.getProperty("is_null"), + Env.makeTopBoolValue())); + }); +} + class FlowConditionTest : public Test { protected: template <typename Matcher> @@ -1275,6 +1303,31 @@ TEST_F(FlowConditionTest, WhileStmtWithAssignmentInCondition) { }); } +TEST_F(FlowConditionTest, GotoLoopWithAssignmentInCondition) { + std::string Code = R"cc( + void target(bool Foo) { + // This test checks whether the analysis preserves the connection between + // the value of `Foo` and the assignment expression, despite widening. + // The equality operator generates a fresh boolean variable on each + // interpretation, which forces use of widening. + start: + if ((Foo = (3 == 4))) { + (void)0; + /*[[p]]*/ + goto start; + } + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + auto &FooVal = getValueForDecl<BoolValue>(ASTCtx, Env, "Foo").formula(); + EXPECT_TRUE(Env.proves(FooVal)); + }); +} + TEST_F(FlowConditionTest, Conjunction) { std::string Code = R"( void target(bool Foo, bool Bar) { >From b9062faad98f139822d6a7d9ed7f3846f1f59e3a Mon Sep 17 00:00:00 2001 From: Jan Voung <[email protected]> Date: Tue, 3 Feb 2026 20:46:19 +0000 Subject: [PATCH 2/3] clang-format --- clang/include/clang/Analysis/CFGBackEdges.h | 2 +- .../lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Analysis/CFGBackEdges.h b/clang/include/clang/Analysis/CFGBackEdges.h index 497c8fea83358..46c86657502e2 100644 --- a/clang/include/clang/Analysis/CFGBackEdges.h +++ b/clang/include/clang/Analysis/CFGBackEdges.h @@ -24,4 +24,4 @@ findCFGBackEdges(const CFG &CFG); } // namespace clang -#endif // LLVM_CLANG_ANALYSIS_CFG_BACKEDGES_H +#endif // LLVM_CLANG_ANALYSIS_CFG_BACKEDGES_H diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 51fc1c3ccd459..ca120678346b8 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -502,7 +502,8 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC, static bool hasGotoInCFG(const clang::CFG &CFG) { for (const CFGBlock *Block : CFG) { const Stmt *Term = Block->getTerminatorStmt(); - if (Term == nullptr) continue; + if (Term == nullptr) + continue; if (isa<GotoStmt>(Term) || isa<IndirectGotoStmt>(Term)) return true; } >From 7796176533123ca0ac1a47dff4c81375ffa20f04 Mon Sep 17 00:00:00 2001 From: Jan Voung <[email protected]> Date: Wed, 4 Feb 2026 02:20:03 +0000 Subject: [PATCH 3/3] cleanup --- clang/lib/Analysis/CFGBackEdges.cpp | 2 +- .../TypeErasedDataflowAnalysis.cpp | 40 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/clang/lib/Analysis/CFGBackEdges.cpp b/clang/lib/Analysis/CFGBackEdges.cpp index 7cc4db2a907c1..303b2e44d8787 100644 --- a/clang/lib/Analysis/CFGBackEdges.cpp +++ b/clang/lib/Analysis/CFGBackEdges.cpp @@ -27,7 +27,7 @@ struct VisitClockTimes { llvm::DenseMap<const CFGBlock *, const CFGBlock *> findCFGBackEdges(const clang::CFG &CFG) { - // Do a simple textbook DFS with pre-post numberings to find back edges. + // Do a simple textbook DFS with pre and post numberings to find back edges. llvm::DenseMap<const CFGBlock *, const CFGBlock *> BackEdges; std::vector<VisitClockTimes> VisitState; diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index ca120678346b8..37daf1b254001 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -69,23 +69,23 @@ static int blockIndexInPredecessor(const CFGBlock &Pred, return BlockPos - Pred.succ_begin(); } -// A "backedge node" is a node that is the source of a backedge in the CFG -// (given backedge from U to V, U is the "backedge node"). -// This can be either: +// Given a backedge from B1 to B2, B1 is a "backedge node" in a CFG. +// It can be: // - A block introduced in the CFG exclusively to indicate a structured loop's // backedge. They are exactly identified by the presence of a non-null // pointer to the entry block of the loop condition. Note that this is not // necessarily the block with the loop statement as terminator, because // short-circuit operators will result in multiple blocks encoding the loop // condition, only one of which will contain the loop statement as terminator. -// - Or, a block that is part of a backedge in a CFG with unstructured loops -// (e.g., a CFG with a `goto` statement). Note that this is not -// necessarily the block with the goto statement as terminator. The choice -// depends on how blocks and edges are ordered. +// - A block that is part of a backedge in a CFG with unstructured loops +// (e.g., a CFG with a `goto` statement). Note that this is not necessarily +// the block with the goto statement as terminator. The choice depends on how +// blocks and edges are ordered. static bool isBackedgeNode( const CFGBlock &B, - const llvm::SmallDenseSet<const CFGBlock *> &NonLoopBackedgeNodes) { - return B.getLoopTarget() != nullptr || NonLoopBackedgeNodes.contains(&B); + const llvm::SmallDenseSet<const CFGBlock *> &NonStructLoopBackedgeNodes) { + return B.getLoopTarget() != nullptr || + NonStructLoopBackedgeNodes.contains(&B); } namespace { @@ -511,21 +511,21 @@ static bool hasGotoInCFG(const clang::CFG &CFG) { } // Returns a set of CFG blocks that is the source of a backedge and is not -// tracked part of a structured loop (according to `CFGBlock::getLoopTarget`). +// tracked as part of a structured loop (with `CFGBlock::getLoopTarget`). static llvm::SmallDenseSet<const CFGBlock *> -findNonLoopBackedgeNodes(const clang::CFG &CFG) { - llvm::SmallDenseSet<const CFGBlock *> NonLoopBackedgeNodes; +findNonStructuredLoopBackedgeNodes(const clang::CFG &CFG) { + llvm::SmallDenseSet<const CFGBlock *> NonStructLoopBackedgeNodes; // We should only need this if the function has gotos. if (!hasGotoInCFG(CFG)) - return NonLoopBackedgeNodes; + return NonStructLoopBackedgeNodes; - llvm::DenseMap<const CFGBlock *, const CFGBlock *> BackEdges = + llvm::DenseMap<const CFGBlock *, const CFGBlock *> Backedges = findCFGBackEdges(CFG); - for (const auto &[From, To] : BackEdges) { + for (const auto &[From, To] : Backedges) { if (From->getLoopTarget() == nullptr) - NonLoopBackedgeNodes.insert(From); + NonStructLoopBackedgeNodes.insert(From); } - return NonLoopBackedgeNodes; + return NonStructLoopBackedgeNodes; } llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>> @@ -547,8 +547,8 @@ runTypeErasedDataflowAnalysis( const clang::CFG &CFG = ACFG.getCFG(); PostOrderCFGView POV(&CFG); ForwardDataflowWorklist Worklist(CFG, &POV); - llvm::SmallDenseSet<const CFGBlock *> NonLoopBackedgeNodes = - findNonLoopBackedgeNodes(CFG); + llvm::SmallDenseSet<const CFGBlock *> NonStructLoopBackedgeNodes = + findNonStructuredLoopBackedgeNodes(CFG); std::vector<std::optional<TypeErasedDataflowAnalysisState>> BlockStates( CFG.size()); @@ -583,7 +583,7 @@ runTypeErasedDataflowAnalysis( llvm::errs() << "Old Env:\n"; OldBlockState->Env.dump(); }); - if (isBackedgeNode(*Block, NonLoopBackedgeNodes)) { + if (isBackedgeNode(*Block, NonStructLoopBackedgeNodes)) { LatticeJoinEffect Effect1 = Analysis.widenTypeErased( NewBlockState.Lattice, OldBlockState->Lattice); LatticeJoinEffect Effect2 = _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
