Skip to content

[LifetimeSafety] Add loan expiry analysis #148712

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

Open
wants to merge 1 commit into
base: users/usx95/07-16-lifetime-safety-add-unit-tests
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions clang/include/clang/Analysis/Analyses/LifetimeSafety.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace internal {
class Fact;
class FactManager;
class LoanPropagationAnalysis;
class ExpiredLoansAnalysis;
struct LifetimeFactory;

/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
Expand All @@ -52,6 +53,10 @@ template <typename Tag> struct ID {
IDBuilder.AddInteger(Value);
}
};
template <typename Tag>
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
return OS << ID.Value;
}

using LoanID = ID<struct LoanTag>;
using OriginID = ID<struct OriginTag>;
Expand Down Expand Up @@ -81,6 +86,9 @@ class LifetimeSafetyAnalysis {
/// Returns the set of loans an origin holds at a specific program point.
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;

/// Returns the set of loans that have expired at a specific program point.
LoanSet getExpiredLoansAtPoint(ProgramPoint PP) const;

/// Finds the OriginID for a given declaration.
/// Returns a null optional if not found.
std::optional<OriginID> getOriginIDForDecl(const ValueDecl *D) const;
Expand All @@ -105,6 +113,7 @@ class LifetimeSafetyAnalysis {
std::unique_ptr<LifetimeFactory> Factory;
std::unique_ptr<FactManager> FactMgr;
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
std::unique_ptr<ExpiredLoansAnalysis> ExpiredLoans;
};
} // namespace internal
} // namespace clang::lifetimes
Expand Down
77 changes: 73 additions & 4 deletions clang/lib/Analysis/LifetimeSafety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/TimeProfiler.h"
#include <cstdint>
#include <memory>

namespace clang::lifetimes {
namespace internal {
namespace {
template <typename Tag>
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
return OS << ID.Value;
}
// template <typename Tag>
// inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
// return OS << ID.Value;
// }
} // namespace

/// Represents the storage location being borrowed, e.g., a specific stack
Expand Down Expand Up @@ -832,6 +833,65 @@ class LoanPropagationAnalysis
}
};

// ========================================================================= //
// Expired Loans Analysis
// ========================================================================= //

/// The dataflow lattice for tracking the set of expired loans.
struct ExpiredLattice {
LoanSet Expired;

ExpiredLattice() : Expired(nullptr) {};
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);
}

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";
}
};

/// The analysis that tracks which loans have expired.
class ExpiredLoansAnalysis
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have the expectation that we have a tight bound on this analysis. I wonder if there is a way to somehow add an assert to verify that the reality matches our expectations. Not super important but if it is not too complicated it could be nice.

We can also defer this to a later PR since we want to be able to add strict bounds to the number of iterations in the future and that might be required for us to easily assert on this.

: public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice,
Direction::Forward> {

LoanSet::Factory &Factory;

public:
ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
LifetimeFactory &Factory)
: DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {}

using Base::transfer;

StringRef getAnalysisName() const { return "ExpiredLoans"; }

Lattice getInitialState() { return Lattice(Factory.getEmptySet()); }

/// Merges two lattices by taking the union of the expired loan sets.
Lattice join(Lattice L1, Lattice L2) const {
return Lattice(utils::join(L1.Expired, L2.Expired, Factory));
}

Lattice transfer(Lattice In, const ExpireFact &F) {
return Lattice(Factory.add(In.Expired, F.getLoanID()));
}

Lattice transfer(Lattice In, const IssueFact &F) {
return Lattice(Factory.remove(In.Expired, F.getLoanID()));
}
};

// ========================================================================= //
// TODO:
// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)`
Expand Down Expand Up @@ -873,6 +933,10 @@ void LifetimeSafetyAnalysis::run() {
LoanPropagation =
std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
LoanPropagation->run();

ExpiredLoans =
std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
ExpiredLoans->run();
}

LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
Expand All @@ -881,6 +945,11 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
return LoanPropagation->getLoans(OID, PP);
}

LoanSet LifetimeSafetyAnalysis::getExpiredLoansAtPoint(ProgramPoint PP) const {
assert(ExpiredLoans && "ExpiredLoansAnalysis has not been run.");
return ExpiredLoans->getState(PP).Expired;
}

std::optional<OriginID>
LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
assert(FactMgr && "FactManager not initialized");
Expand Down
Loading