Skip to content

Commit 14a9c8b

Browse files
committed
[LifetimeSafety] Add expired loans analysis
1 parent 123045c commit 14a9c8b

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
@@ -735,6 +735,142 @@ class LifetimeDataflow {
735735
}
736736
};
737737

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

0 commit comments

Comments
 (0)