https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/148222
None >From 14a9c8b50df11ce48ce15d0fbe29568b3e23b5a6 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Fri, 11 Jul 2025 11:11:47 +0000 Subject: [PATCH] [LifetimeSafety] Add expired loans analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 140 ++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e72192aa92c1a..88ec70d000d2c 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -735,6 +735,142 @@ class LifetimeDataflow { } }; +// ========================================================================= // +// Expired Loans Analysis +// ========================================================================= // + +/// The lattice for tracking expired loans. It is a set of loan IDs. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() = default; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { + return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { + return !(*this == Other); + } + + /// Computes the union of two lattices. + ExpiredLattice join(const ExpiredLattice &Other, + LoanSet::Factory &Factory) const { + LoanSet JoinedSet = Expired; + for (LoanID LID : Other.Expired) + JoinedSet = Factory.add(JoinedSet, LID); + return ExpiredLattice(JoinedSet); + } + + void dump(llvm::raw_ostream &OS) const { + OS << "ExpiredLattice State:\n"; + if (Expired.isEmpty()) + OS << " <empty>\n"; + for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// Transfer function for the expired loans analysis. +class ExpiredLoansTransferer { + FactManager &AllFacts; + LoanSet::Factory &SetFactory; + +public: + explicit ExpiredLoansTransferer(FactManager &F, LoanSet::Factory &SF) + : AllFacts(F), SetFactory(SF) {} + + /// Computes the exit state of a block by applying all its facts sequentially + /// to a given entry state. + ExpiredLattice transferBlock(const CFGBlock *Block, + ExpiredLattice EntryState) { + ExpiredLattice BlockState = EntryState; + llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block); + + for (const Fact *F : Facts) { + BlockState = transferFact(BlockState, F); + } + return BlockState; + } + +private: + ExpiredLattice transferFact(ExpiredLattice In, const Fact *F) { + if (const auto *EF = F->getAs<ExpireFact>()) + return ExpiredLattice(SetFactory.add(In.Expired, EF->getLoanID())); + + if (const auto *IF = F->getAs<IssueFact>()) + return ExpiredLattice(SetFactory.remove(In.Expired, IF->getLoanID())); + + return In; + } +}; + +/// Dataflow analysis driver for tracking expired loans. +class ExpiredLoansAnalysis { + const CFG &Cfg; + AnalysisDeclContext &AC; + LoanSet::Factory SetFactory; + ExpiredLoansTransferer Xfer; + + llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockEntryStates; + llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockExitStates; + +public: + ExpiredLoansAnalysis(const CFG &C, FactManager &FS, AnalysisDeclContext &AC) + : Cfg(C), AC(AC), Xfer(FS, SetFactory) {} + + void run() { + llvm::TimeTraceScope TimeProfile("Expired Loans Analysis"); + ForwardDataflowWorklist Worklist(Cfg, AC); + const CFGBlock *Entry = &Cfg.getEntry(); + BlockEntryStates[Entry] = ExpiredLattice(SetFactory.getEmptySet()); + Worklist.enqueueBlock(Entry); + while (const CFGBlock *B = Worklist.dequeue()) { + ExpiredLattice EntryState = getEntryState(B); + ExpiredLattice ExitState = Xfer.transferBlock(B, EntryState); + BlockExitStates[B] = ExitState; + + for (const CFGBlock *Successor : B->succs()) { + auto SuccIt = BlockEntryStates.find(Successor); + ExpiredLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end()) + ? SuccIt->second + : ExpiredLattice{}; + ExpiredLattice NewSuccEntryState = + OldSuccEntryState.join(ExitState, SetFactory); + if (SuccIt == BlockEntryStates.end() || + NewSuccEntryState != OldSuccEntryState) { + BlockEntryStates[Successor] = NewSuccEntryState; + Worklist.enqueueBlock(Successor); + } + } + } + } + + void dump() const { + llvm::dbgs() << "==========================================\n"; + llvm::dbgs() << " Expired Loans Results:\n"; + llvm::dbgs() << "==========================================\n"; + const CFGBlock &B = Cfg.getExit(); + getExitState(&B).dump(llvm::dbgs()); + } + + ExpiredLattice getEntryState(const CFGBlock *B) const { + auto It = BlockEntryStates.find(B); + if (It != BlockEntryStates.end()) { + return It->second; + } + return ExpiredLattice(SetFactory.getEmptySet()); + } + + ExpiredLattice getExitState(const CFGBlock *B) const { + auto It = BlockExitStates.find(B); + if (It != BlockExitStates.end()) { + return It->second; + } + return ExpiredLattice(SetFactory.getEmptySet()); + } +}; + // ========================================================================= // // TODO: Analysing dataflow results and error reporting. // ========================================================================= // @@ -762,5 +898,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LifetimeDataflow Dataflow(Cfg, FactMgr, AC); Dataflow.run(); DEBUG_WITH_TYPE("LifetimeDataflow", Dataflow.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, FactMgr, AC); + ExpiredAnalysis.run(); + DEBUG_WITH_TYPE("ExpiredLoans", ExpiredAnalysis.dump()); } } // namespace clang _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits