-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[LifetimeSafety] Reorganize code into modular components #162474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
usx95
merged 13 commits into
main
from
users/usx95/10-08-_lifetimesafety_refactor_the_analysis_into_smaller_files
Oct 10, 2025
Merged
Changes from 8 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6f87445
[LifetimeSafety] Refactor the analysis into smaller files
usx95 5d811c2
Apply changes from code browser
usx95 92eb377
Apply changes from code browser
usx95 39689c6
Apply changes from code browser
usx95 0818599
Apply suggested changes
usx95 a677b11
split into implementation files
usx95 52464b3
Apply changes from code browser
usx95 98bb91d
Apply changes from code browser
usx95 feeb663
nested namespaces
usx95 146a15c
Apply changes from code browser
usx95 5af65da
move dataflow to lib
usx95 c6e7bf0
more useful names and anonymous namespaces
usx95 039ba89
documentation updates
usx95 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===- Checker.h - C++ Lifetime Safety Analysis -*----------- 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file provides the entry point for lifetime checking, which detects | ||
| // use-after-free errors by checking if live origins hold loans that have | ||
| // expired. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H | ||
| #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H | ||
|
|
||
| #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" | ||
| #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h" | ||
| #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h" | ||
| #include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h" | ||
|
|
||
| namespace clang { | ||
| namespace lifetimes { | ||
| namespace internal { | ||
|
|
||
| /// Runs the lifetime checker, which detects use-after-free errors by | ||
| /// examining loan expiration points and checking if any live origins hold | ||
| /// the expired loan. | ||
| void runLifetimeChecker(LoanPropagationAnalysis &LoanPropagation, | ||
| LiveOriginAnalysis &LiveOrigins, FactManager &FactMgr, | ||
| AnalysisDeclContext &ADC, | ||
| LifetimeSafetyReporter *Reporter); | ||
|
|
||
| } // namespace internal | ||
| } // namespace lifetimes | ||
| } // namespace clang | ||
|
|
||
| #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H | ||
190 changes: 190 additions & 0 deletions
190
clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| //===- Dataflow.h - Generic Dataflow Analysis Framework --------*- 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This file defines a generic, policy-based driver for dataflow analyses. | ||
| // It provides a flexible framework that combines the dataflow runner and | ||
| // transfer functions, allowing derived classes to implement specific analyses | ||
| // by defining their lattice, join, and transfer functions. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H | ||
| #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H | ||
|
|
||
| #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" | ||
| #include "clang/Analysis/AnalysisDeclContext.h" | ||
| #include "clang/Analysis/CFG.h" | ||
| #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" | ||
| #include "llvm/Support/Debug.h" | ||
| #include "llvm/Support/ErrorHandling.h" | ||
| #include "llvm/Support/TimeProfiler.h" | ||
| #include <optional> | ||
|
|
||
| namespace clang::lifetimes { | ||
| namespace internal { | ||
|
|
||
| enum class Direction { Forward, Backward }; | ||
|
|
||
| /// A `ProgramPoint` identifies a location in the CFG by pointing to a specific | ||
| /// `Fact`. identified by a lifetime-related event (`Fact`). | ||
| /// | ||
| /// A `ProgramPoint` has "after" semantics: it represents the location | ||
| /// immediately after its corresponding `Fact`. | ||
| using ProgramPoint = const Fact *; | ||
|
|
||
| /// A generic, policy-based driver for dataflow analyses. It combines | ||
| /// the dataflow runner and the transferer logic into a single class hierarchy. | ||
| /// | ||
| /// The derived class is expected to provide: | ||
| /// - A `Lattice` type. | ||
| /// - `StringRef getAnalysisName() const` | ||
| /// - `Lattice getInitialState();` The initial state of the analysis. | ||
| /// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths. | ||
| /// - `Lattice transfer(Lattice, const FactType&);` Defines how a single | ||
| /// lifetime-relevant `Fact` transforms the lattice state. Only overloads | ||
| /// for facts relevant to the analysis need to be implemented. | ||
| /// | ||
| /// \tparam Derived The CRTP derived class that implements the specific | ||
| /// analysis. | ||
| /// \tparam LatticeType The dataflow lattice used by the analysis. | ||
| /// \tparam Dir The direction of the analysis (Forward or Backward). | ||
| /// TODO: Maybe use the dataflow framework! The framework might need changes | ||
| /// to support the current comparison done at block-entry. | ||
| template <typename Derived, typename LatticeType, Direction Dir> | ||
| class DataflowAnalysis { | ||
| public: | ||
| using Lattice = LatticeType; | ||
| using Base = DataflowAnalysis<Derived, Lattice, Dir>; | ||
|
|
||
| private: | ||
| const CFG &Cfg; | ||
| AnalysisDeclContext &AC; | ||
|
|
||
| /// The dataflow state before a basic block is processed. | ||
| llvm::DenseMap<const CFGBlock *, Lattice> InStates; | ||
| /// The dataflow state after a basic block is processed. | ||
| llvm::DenseMap<const CFGBlock *, Lattice> OutStates; | ||
| /// The dataflow state at a Program Point. | ||
| /// In a forward analysis, this is the state after the Fact at that point has | ||
| /// been applied, while in a backward analysis, it is the state before. | ||
| llvm::DenseMap<ProgramPoint, Lattice> PerPointStates; | ||
|
|
||
| static constexpr bool isForward() { return Dir == Direction::Forward; } | ||
|
|
||
| protected: | ||
| FactManager &AllFacts; | ||
|
|
||
| explicit DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC, | ||
| FactManager &F) | ||
| : Cfg(C), AC(AC), AllFacts(F) {} | ||
|
|
||
| public: | ||
| void run() { | ||
| Derived &D = static_cast<Derived &>(*this); | ||
| llvm::TimeTraceScope Time(D.getAnalysisName()); | ||
|
|
||
| using Worklist = | ||
| std::conditional_t<Dir == Direction::Forward, ForwardDataflowWorklist, | ||
| BackwardDataflowWorklist>; | ||
| Worklist W(Cfg, AC); | ||
|
|
||
| const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit(); | ||
| InStates[Start] = D.getInitialState(); | ||
| W.enqueueBlock(Start); | ||
|
|
||
| while (const CFGBlock *B = W.dequeue()) { | ||
| Lattice StateIn = *getInState(B); | ||
| Lattice StateOut = transferBlock(B, StateIn); | ||
| OutStates[B] = StateOut; | ||
| for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) { | ||
| if (!AdjacentB) | ||
| continue; | ||
| std::optional<Lattice> OldInState = getInState(AdjacentB); | ||
| Lattice NewInState = | ||
| !OldInState ? StateOut : D.join(*OldInState, StateOut); | ||
| // Enqueue the adjacent block if its in-state has changed or if we have | ||
| // never seen it. | ||
| if (!OldInState || NewInState != *OldInState) { | ||
| InStates[AdjacentB] = NewInState; | ||
| W.enqueueBlock(AdjacentB); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected: | ||
| Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); } | ||
|
|
||
| std::optional<Lattice> getInState(const CFGBlock *B) const { | ||
| auto It = InStates.find(B); | ||
| if (It == InStates.end()) | ||
| return std::nullopt; | ||
| return It->second; | ||
| } | ||
|
|
||
| Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } | ||
|
|
||
| void dump() const { | ||
| const Derived *D = static_cast<const Derived *>(this); | ||
| llvm::dbgs() << "==========================================\n"; | ||
| llvm::dbgs() << D->getAnalysisName() << " results:\n"; | ||
| llvm::dbgs() << "==========================================\n"; | ||
| const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry(); | ||
| getOutState(&B).dump(llvm::dbgs()); | ||
| } | ||
|
|
||
| private: | ||
| /// Computes the state at one end of a block by applying all its facts | ||
| /// sequentially to a given state from the other end. | ||
| Lattice transferBlock(const CFGBlock *Block, Lattice State) { | ||
| auto Facts = AllFacts.getFacts(Block); | ||
| if constexpr (isForward()) { | ||
| for (const Fact *F : Facts) { | ||
| State = transferFact(State, F); | ||
| PerPointStates[F] = State; | ||
| } | ||
| } else { | ||
| for (const Fact *F : llvm::reverse(Facts)) { | ||
| // In backward analysis, capture the state before applying the fact. | ||
| PerPointStates[F] = State; | ||
| State = transferFact(State, F); | ||
| } | ||
| } | ||
| return State; | ||
| } | ||
|
|
||
| Lattice transferFact(Lattice In, const Fact *F) { | ||
| assert(F); | ||
| Derived *D = static_cast<Derived *>(this); | ||
| switch (F->getKind()) { | ||
| case Fact::Kind::Issue: | ||
| return D->transfer(In, *F->getAs<IssueFact>()); | ||
| case Fact::Kind::Expire: | ||
| return D->transfer(In, *F->getAs<ExpireFact>()); | ||
| case Fact::Kind::OriginFlow: | ||
| return D->transfer(In, *F->getAs<OriginFlowFact>()); | ||
| case Fact::Kind::ReturnOfOrigin: | ||
| return D->transfer(In, *F->getAs<ReturnOfOriginFact>()); | ||
| case Fact::Kind::Use: | ||
| return D->transfer(In, *F->getAs<UseFact>()); | ||
| case Fact::Kind::TestPoint: | ||
| return D->transfer(In, *F->getAs<TestPointFact>()); | ||
| } | ||
| llvm_unreachable("Unknown fact kind"); | ||
| } | ||
|
|
||
| public: | ||
| Lattice transfer(Lattice In, const IssueFact &) { return In; } | ||
| Lattice transfer(Lattice In, const ExpireFact &) { return In; } | ||
| Lattice transfer(Lattice In, const OriginFlowFact &) { return In; } | ||
| Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; } | ||
| Lattice transfer(Lattice In, const UseFact &) { return In; } | ||
| Lattice transfer(Lattice In, const TestPointFact &) { return In; } | ||
| }; | ||
| } // namespace internal | ||
| } // namespace clang::lifetimes | ||
| #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.