From b76c9160a608f6908119298001ec15599abaf051 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 21:24:11 +0000 Subject: [PATCH] add-backward-analysis-capability --- clang/lib/Analysis/LifetimeSafety.cpp | 108 ++++++++++++++------------ 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 9c623e3a5693b..e3a03cf93880e 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -499,13 +499,16 @@ class FactGenerator : public ConstStmtVisitor { // ========================================================================= // // Generic Dataflow Analysis // ========================================================================= // -/// A generic, policy-based driver for forward dataflow analyses. It combines + +enum class Direction { Forward, Backward }; + +/// 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 at the function entry. +/// - `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 @@ -514,18 +517,23 @@ class FactGenerator : public ConstStmtVisitor { /// \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 class DataflowAnalysis { +template +class DataflowAnalysis { public: using Lattice = LatticeType; + using Base = DataflowAnalysis; private: const CFG &Cfg; AnalysisDeclContext &AC; - llvm::DenseMap BlockEntryStates; - llvm::DenseMap BlockExitStates; + llvm::DenseMap InStates; + llvm::DenseMap OutStates; + + static constexpr bool isForward() { return Dir == Direction::Forward; } protected: FactManager &AllFacts; @@ -539,75 +547,76 @@ template class DataflowAnalysis { Derived &D = static_cast(*this); llvm::TimeTraceScope Time(D.getAnalysisName()); - ForwardDataflowWorklist Worklist(Cfg, AC); - const CFGBlock *Entry = &Cfg.getEntry(); - BlockEntryStates[Entry] = D.getInitialState(); - Worklist.enqueueBlock(Entry); - llvm::SmallBitVector Visited; - Visited.resize(Cfg.getNumBlockIDs() + 1); - - while (const CFGBlock *B = Worklist.dequeue()) { - Lattice EntryState = getEntryState(B); - Lattice ExitState = transferBlock(B, EntryState); - BlockExitStates[B] = ExitState; - Visited.set(B->getBlockID()); + using Worklist = + std::conditional_t; + Worklist W(Cfg, AC); + + const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit(); + InStates[Start] = D.getInitialState(); + W.enqueueBlock(Start); - for (const CFGBlock *Successor : B->succs()) { - Lattice OldSuccEntryState = getEntryState(Successor); - Lattice NewSuccEntryState = D.join(OldSuccEntryState, ExitState); + llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1); - // Enqueue the successor if its entry state has changed or if we have + while (const CFGBlock *B = W.dequeue()) { + Lattice StateIn = getInState(B); + Lattice StateOut = transferBlock(B, StateIn); + OutStates[B] = StateOut; + Visited.set(B->getBlockID()); + for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) { + Lattice OldInState = getInState(AdjacentB); + Lattice NewInState = D.join(OldInState, StateOut); + // Enqueue the adjacent block if its in-state has changed or if we have // never visited it. - if (!Visited.test(Successor->getBlockID()) || - NewSuccEntryState != OldSuccEntryState) { - BlockEntryStates[Successor] = NewSuccEntryState; - Worklist.enqueueBlock(Successor); + if (!Visited.test(AdjacentB->getBlockID()) || + NewInState != OldInState) { + InStates[AdjacentB] = NewInState; + W.enqueueBlock(AdjacentB); } } } } - Lattice getEntryState(const CFGBlock *B) const { - return BlockEntryStates.lookup(B); - } + Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); } - Lattice getExitState(const CFGBlock *B) const { - return BlockExitStates.lookup(B); - } + Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } void dump() const { const Derived *D = static_cast(this); llvm::dbgs() << "==========================================\n"; llvm::dbgs() << D->getAnalysisName() << " results:\n"; llvm::dbgs() << "==========================================\n"; - const CFGBlock &B = Cfg.getExit(); - getExitState(&B).dump(llvm::dbgs()); + const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry(); + getOutState(&B).dump(llvm::dbgs()); } -private: - /// Computes the exit state of a block by applying all its facts sequentially - /// to a given entry state. + /// Computes the state at one end of a block by applying all its facts + /// sequentially to a given state from the other end. /// TODO: We might need to store intermediate states per-fact in the block for /// later analysis. - Lattice transferBlock(const CFGBlock *Block, Lattice EntryState) { - Lattice BlockState = EntryState; - for (const Fact *F : AllFacts.getFacts(Block)) { - BlockState = transferFact(BlockState, F); - } - return BlockState; + Lattice transferBlock(const CFGBlock *Block, Lattice State) { + auto Facts = AllFacts.getFacts(Block); + if constexpr (isForward()) + for (const Fact *F : Facts) + State = transferFact(State, F); + else + for (const Fact *F : llvm::reverse(Facts)) + State = transferFact(State, F); + return State; } Lattice transferFact(Lattice In, const Fact *F) { - Derived *d = static_cast(this); + assert(F); + Derived *D = static_cast(this); switch (F->getKind()) { case Fact::Kind::Issue: - return d->transfer(In, *F->getAs()); + return D->transfer(In, *F->getAs()); case Fact::Kind::Expire: - return d->transfer(In, *F->getAs()); + return D->transfer(In, *F->getAs()); case Fact::Kind::AssignOrigin: - return d->transfer(In, *F->getAs()); + return D->transfer(In, *F->getAs()); case Fact::Kind::ReturnOfOrigin: - return d->transfer(In, *F->getAs()); + return D->transfer(In, *F->getAs()); } llvm_unreachable("Unknown fact kind"); } @@ -715,7 +724,8 @@ struct LoanPropagationLattice { /// The analysis that tracks which loans belong to which origins. class LoanPropagationAnalysis - : public DataflowAnalysis { + : public DataflowAnalysis { LifetimeFactory &Factory; @@ -724,7 +734,7 @@ class LoanPropagationAnalysis LifetimeFactory &Factory) : DataflowAnalysis(C, AC, F), Factory(Factory) {} - using DataflowAnalysis::transfer; + using Base::transfer; StringRef getAnalysisName() const { return "LoanPropagation"; }