Skip to content

Commit eb33d75

Browse files
committed
[LifetimeSafety] Add expired loans analysis
1 parent 19d033c commit eb33d75

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,142 @@ class LifetimeDataflow {
729729
}
730730
};
731731

732+
// ========================================================================= //
733+
// Expired Loans Analysis
734+
// ========================================================================= //
735+
736+
/// The lattice for tracking expired loans. It is a set of loan IDs.
737+
struct ExpiredLattice {
738+
LoanSet Expired;
739+
740+
ExpiredLattice() = default;
741+
explicit ExpiredLattice(LoanSet S) : Expired(S) {}
742+
743+
bool operator==(const ExpiredLattice &Other) const {
744+
return Expired == Other.Expired;
745+
}
746+
bool operator!=(const ExpiredLattice &Other) const {
747+
return !(*this == Other);
748+
}
749+
750+
/// Computes the union of two lattices.
751+
ExpiredLattice join(const ExpiredLattice &Other,
752+
LoanSet::Factory &Factory) const {
753+
LoanSet JoinedSet = Expired;
754+
for (LoanID LID : Other.Expired)
755+
JoinedSet = Factory.add(JoinedSet, LID);
756+
return ExpiredLattice(JoinedSet);
757+
}
758+
759+
void dump(llvm::raw_ostream &OS) const {
760+
OS << "ExpiredLattice State:\n";
761+
if (Expired.isEmpty())
762+
OS << " <empty>\n";
763+
for (const LoanID &LID : Expired)
764+
OS << " Loan " << LID << " is expired\n";
765+
}
766+
};
767+
768+
/// Transfer function for the expired loans analysis.
769+
class ExpiredLoansTransferer {
770+
FactManager &AllFacts;
771+
LoanSet::Factory &SetFactory;
772+
773+
public:
774+
explicit ExpiredLoansTransferer(FactManager &F, LoanSet::Factory &SF)
775+
: AllFacts(F), SetFactory(SF) {}
776+
777+
/// Computes the exit state of a block by applying all its facts sequentially
778+
/// to a given entry state.
779+
ExpiredLattice transferBlock(const CFGBlock *Block,
780+
ExpiredLattice EntryState) {
781+
ExpiredLattice BlockState = EntryState;
782+
llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block);
783+
784+
for (const Fact *F : Facts) {
785+
BlockState = transferFact(BlockState, F);
786+
}
787+
return BlockState;
788+
}
789+
790+
private:
791+
ExpiredLattice transferFact(ExpiredLattice In, const Fact *F) {
792+
if (const auto *EF = F->getAs<ExpireFact>())
793+
return ExpiredLattice(SetFactory.add(In.Expired, EF->getLoanID()));
794+
795+
if (const auto *IF = F->getAs<IssueFact>())
796+
return ExpiredLattice(SetFactory.remove(In.Expired, IF->getLoanID()));
797+
798+
return In;
799+
}
800+
};
801+
802+
/// Dataflow analysis driver for tracking expired loans.
803+
class ExpiredLoansAnalysis {
804+
const CFG &Cfg;
805+
AnalysisDeclContext &AC;
806+
LoanSet::Factory SetFactory;
807+
ExpiredLoansTransferer Xfer;
808+
809+
llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockEntryStates;
810+
llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockExitStates;
811+
812+
public:
813+
ExpiredLoansAnalysis(const CFG &C, FactManager &FS, AnalysisDeclContext &AC)
814+
: Cfg(C), AC(AC), Xfer(FS, SetFactory) {}
815+
816+
void run() {
817+
llvm::TimeTraceScope TimeProfile("Expired Loans Analysis");
818+
ForwardDataflowWorklist Worklist(Cfg, AC);
819+
const CFGBlock *Entry = &Cfg.getEntry();
820+
BlockEntryStates[Entry] = ExpiredLattice(SetFactory.getEmptySet());
821+
Worklist.enqueueBlock(Entry);
822+
while (const CFGBlock *B = Worklist.dequeue()) {
823+
ExpiredLattice EntryState = getEntryState(B);
824+
ExpiredLattice ExitState = Xfer.transferBlock(B, EntryState);
825+
BlockExitStates[B] = ExitState;
826+
827+
for (const CFGBlock *Successor : B->succs()) {
828+
auto SuccIt = BlockEntryStates.find(Successor);
829+
ExpiredLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())
830+
? SuccIt->second
831+
: ExpiredLattice{};
832+
ExpiredLattice NewSuccEntryState =
833+
OldSuccEntryState.join(ExitState, SetFactory);
834+
if (SuccIt == BlockEntryStates.end() ||
835+
NewSuccEntryState != OldSuccEntryState) {
836+
BlockEntryStates[Successor] = NewSuccEntryState;
837+
Worklist.enqueueBlock(Successor);
838+
}
839+
}
840+
}
841+
}
842+
843+
void dump() const {
844+
llvm::dbgs() << "==========================================\n";
845+
llvm::dbgs() << " Expired Loans Results:\n";
846+
llvm::dbgs() << "==========================================\n";
847+
const CFGBlock &B = Cfg.getExit();
848+
getExitState(&B).dump(llvm::dbgs());
849+
}
850+
851+
ExpiredLattice getEntryState(const CFGBlock *B) const {
852+
auto It = BlockEntryStates.find(B);
853+
if (It != BlockEntryStates.end()) {
854+
return It->second;
855+
}
856+
return ExpiredLattice(SetFactory.getEmptySet());
857+
}
858+
859+
ExpiredLattice getExitState(const CFGBlock *B) const {
860+
auto It = BlockExitStates.find(B);
861+
if (It != BlockExitStates.end()) {
862+
return It->second;
863+
}
864+
return ExpiredLattice(SetFactory.getEmptySet());
865+
}
866+
};
867+
732868
// ========================================================================= //
733869
// TODO: Analysing dataflow results and error reporting.
734870
// ========================================================================= //
@@ -756,5 +892,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
756892
LifetimeDataflow Dataflow(Cfg, FactMgr, AC);
757893
Dataflow.run();
758894
DEBUG_WITH_TYPE("LifetimeDataflow", Dataflow.dump());
895+
896+
ExpiredLoansAnalysis ExpiredAnalysis(Cfg, FactMgr, AC);
897+
ExpiredAnalysis.run();
898+
DEBUG_WITH_TYPE("ExpiredLoans", ExpiredAnalysis.dump());
759899
}
760900
} // namespace clang

0 commit comments

Comments
 (0)