Path: blob/main/contrib/llvm-project/clang/lib/Analysis/LifetimeSafety.cpp
213766 views
//===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- C++-*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7#include "clang/Analysis/Analyses/LifetimeSafety.h"8#include "clang/AST/Decl.h"9#include "clang/AST/Expr.h"10#include "clang/AST/StmtVisitor.h"11#include "clang/AST/Type.h"12#include "clang/Analysis/Analyses/PostOrderCFGView.h"13#include "clang/Analysis/AnalysisDeclContext.h"14#include "clang/Analysis/CFG.h"15#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"16#include "llvm/ADT/FoldingSet.h"17#include "llvm/ADT/ImmutableMap.h"18#include "llvm/ADT/ImmutableSet.h"19#include "llvm/ADT/PointerUnion.h"20#include "llvm/ADT/SmallVector.h"21#include "llvm/Support/Debug.h"22#include "llvm/Support/TimeProfiler.h"23#include <cstdint>2425namespace clang {26namespace {2728/// Represents the storage location being borrowed, e.g., a specific stack29/// variable.30/// TODO: Model access paths of other types, e.g., s.field, heap and globals.31struct AccessPath {32const clang::ValueDecl *D;3334AccessPath(const clang::ValueDecl *D) : D(D) {}35};3637/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.38/// Used for giving ID to loans and origins.39template <typename Tag> struct ID {40uint32_t Value = 0;4142bool operator==(const ID<Tag> &Other) const { return Value == Other.Value; }43bool operator!=(const ID<Tag> &Other) const { return !(*this == Other); }44bool operator<(const ID<Tag> &Other) const { return Value < Other.Value; }45ID<Tag> operator++(int) {46ID<Tag> Tmp = *this;47++Value;48return Tmp;49}50void Profile(llvm::FoldingSetNodeID &IDBuilder) const {51IDBuilder.AddInteger(Value);52}53};5455template <typename Tag>56inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {57return OS << ID.Value;58}5960using LoanID = ID<struct LoanTag>;61using OriginID = ID<struct OriginTag>;6263/// Information about a single borrow, or "Loan". A loan is created when a64/// reference or pointer is created.65struct Loan {66/// TODO: Represent opaque loans.67/// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it68/// is represented as empty LoanSet69LoanID ID;70AccessPath Path;71SourceLocation IssueLoc;7273Loan(LoanID id, AccessPath path, SourceLocation loc)74: ID(id), Path(path), IssueLoc(loc) {}75};7677/// An Origin is a symbolic identifier that represents the set of possible78/// loans a pointer-like object could hold at any given time.79/// TODO: Enhance the origin model to handle complex types, pointer80/// indirection and reborrowing. The plan is to move from a single origin per81/// variable/expression to a "list of origins" governed by the Type.82/// For example, the type 'int**' would have two origins.83/// See discussion:84/// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r213764423885struct Origin {86OriginID ID;87/// A pointer to the AST node that this origin represents. This union88/// distinguishes between origins from declarations (variables or parameters)89/// and origins from expressions.90llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;9192Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}93Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}9495const clang::ValueDecl *getDecl() const {96return Ptr.dyn_cast<const clang::ValueDecl *>();97}98const clang::Expr *getExpr() const {99return Ptr.dyn_cast<const clang::Expr *>();100}101};102103/// Manages the creation, storage and retrieval of loans.104class LoanManager {105public:106LoanManager() = default;107108Loan &addLoan(AccessPath Path, SourceLocation Loc) {109AllLoans.emplace_back(getNextLoanID(), Path, Loc);110return AllLoans.back();111}112113const Loan &getLoan(LoanID ID) const {114assert(ID.Value < AllLoans.size());115return AllLoans[ID.Value];116}117llvm::ArrayRef<Loan> getLoans() const { return AllLoans; }118119private:120LoanID getNextLoanID() { return NextLoanID++; }121122LoanID NextLoanID{0};123/// TODO(opt): Profile and evaluate the usefullness of small buffer124/// optimisation.125llvm::SmallVector<Loan> AllLoans;126};127128/// Manages the creation, storage, and retrieval of origins for pointer-like129/// variables and expressions.130class OriginManager {131public:132OriginManager() = default;133134Origin &addOrigin(OriginID ID, const clang::ValueDecl &D) {135AllOrigins.emplace_back(ID, &D);136return AllOrigins.back();137}138Origin &addOrigin(OriginID ID, const clang::Expr &E) {139AllOrigins.emplace_back(ID, &E);140return AllOrigins.back();141}142143OriginID get(const Expr &E) {144// Origin of DeclRefExpr is that of the declaration it refers to.145if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))146return get(*DRE->getDecl());147auto It = ExprToOriginID.find(&E);148// TODO: This should be an assert(It != ExprToOriginID.end()). The current149// implementation falls back to getOrCreate to avoid crashing on150// yet-unhandled pointer expressions, creating an empty origin for them.151if (It == ExprToOriginID.end())152return getOrCreate(E);153154return It->second;155}156157OriginID get(const ValueDecl &D) {158auto It = DeclToOriginID.find(&D);159// TODO: This should be an assert(It != DeclToOriginID.end()). The current160// implementation falls back to getOrCreate to avoid crashing on161// yet-unhandled pointer expressions, creating an empty origin for them.162if (It == DeclToOriginID.end())163return getOrCreate(D);164165return It->second;166}167168OriginID getOrCreate(const Expr &E) {169auto It = ExprToOriginID.find(&E);170if (It != ExprToOriginID.end())171return It->second;172173if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) {174// Origin of DeclRefExpr is that of the declaration it refers to.175return getOrCreate(*DRE->getDecl());176}177OriginID NewID = getNextOriginID();178addOrigin(NewID, E);179ExprToOriginID[&E] = NewID;180return NewID;181}182183const Origin &getOrigin(OriginID ID) const {184assert(ID.Value < AllOrigins.size());185return AllOrigins[ID.Value];186}187188llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }189190OriginID getOrCreate(const ValueDecl &D) {191auto It = DeclToOriginID.find(&D);192if (It != DeclToOriginID.end())193return It->second;194OriginID NewID = getNextOriginID();195addOrigin(NewID, D);196DeclToOriginID[&D] = NewID;197return NewID;198}199200private:201OriginID getNextOriginID() { return NextOriginID++; }202203OriginID NextOriginID{0};204/// TODO(opt): Profile and evaluate the usefullness of small buffer205/// optimisation.206llvm::SmallVector<Origin> AllOrigins;207llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;208llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;209};210211/// An abstract base class for a single, atomic lifetime-relevant event.212class Fact {213214public:215enum class Kind : uint8_t {216/// A new loan is issued from a borrow expression (e.g., &x).217Issue,218/// A loan expires as its underlying storage is freed (e.g., variable goes219/// out of scope).220Expire,221/// An origin is propagated from a source to a destination (e.g., p = q).222AssignOrigin,223/// An origin escapes the function by flowing into the return value.224ReturnOfOrigin225};226227private:228Kind K;229230protected:231Fact(Kind K) : K(K) {}232233public:234virtual ~Fact() = default;235Kind getKind() const { return K; }236237template <typename T> const T *getAs() const {238if (T::classof(this))239return static_cast<const T *>(this);240return nullptr;241}242243virtual void dump(llvm::raw_ostream &OS) const {244OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";245}246};247248class IssueFact : public Fact {249LoanID LID;250OriginID OID;251252public:253static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }254255IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}256LoanID getLoanID() const { return LID; }257OriginID getOriginID() const { return OID; }258void dump(llvm::raw_ostream &OS) const override {259OS << "Issue (LoanID: " << getLoanID() << ", OriginID: " << getOriginID()260<< ")\n";261}262};263264class ExpireFact : public Fact {265LoanID LID;266267public:268static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }269270ExpireFact(LoanID LID) : Fact(Kind::Expire), LID(LID) {}271LoanID getLoanID() const { return LID; }272void dump(llvm::raw_ostream &OS) const override {273OS << "Expire (LoanID: " << getLoanID() << ")\n";274}275};276277class AssignOriginFact : public Fact {278OriginID OIDDest;279OriginID OIDSrc;280281public:282static bool classof(const Fact *F) {283return F->getKind() == Kind::AssignOrigin;284}285286AssignOriginFact(OriginID OIDDest, OriginID OIDSrc)287: Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}288OriginID getDestOriginID() const { return OIDDest; }289OriginID getSrcOriginID() const { return OIDSrc; }290void dump(llvm::raw_ostream &OS) const override {291OS << "AssignOrigin (DestID: " << getDestOriginID()292<< ", SrcID: " << getSrcOriginID() << ")\n";293}294};295296class ReturnOfOriginFact : public Fact {297OriginID OID;298299public:300static bool classof(const Fact *F) {301return F->getKind() == Kind::ReturnOfOrigin;302}303304ReturnOfOriginFact(OriginID OID) : Fact(Kind::ReturnOfOrigin), OID(OID) {}305OriginID getReturnedOriginID() const { return OID; }306void dump(llvm::raw_ostream &OS) const override {307OS << "ReturnOfOrigin (OriginID: " << getReturnedOriginID() << ")\n";308}309};310311class FactManager {312public:313llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {314auto It = BlockToFactsMap.find(B);315if (It != BlockToFactsMap.end())316return It->second;317return {};318}319320void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {321if (!NewFacts.empty())322BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());323}324325template <typename FactType, typename... Args>326FactType *createFact(Args &&...args) {327void *Mem = FactAllocator.Allocate<FactType>();328return new (Mem) FactType(std::forward<Args>(args)...);329}330331void dump(const CFG &Cfg, AnalysisDeclContext &AC) const {332llvm::dbgs() << "==========================================\n";333llvm::dbgs() << " Lifetime Analysis Facts:\n";334llvm::dbgs() << "==========================================\n";335if (const Decl *D = AC.getDecl())336if (const auto *ND = dyn_cast<NamedDecl>(D))337llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";338// Print blocks in the order as they appear in code for a stable ordering.339for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {340llvm::dbgs() << " Block B" << B->getBlockID() << ":\n";341auto It = BlockToFactsMap.find(B);342if (It != BlockToFactsMap.end()) {343for (const Fact *F : It->second) {344llvm::dbgs() << " ";345F->dump(llvm::dbgs());346}347}348llvm::dbgs() << " End of Block\n";349}350}351352LoanManager &getLoanMgr() { return LoanMgr; }353OriginManager &getOriginMgr() { return OriginMgr; }354355private:356LoanManager LoanMgr;357OriginManager OriginMgr;358llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>359BlockToFactsMap;360llvm::BumpPtrAllocator FactAllocator;361};362363class FactGenerator : public ConstStmtVisitor<FactGenerator> {364365public:366FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)367: FactMgr(FactMgr), AC(AC) {}368369void run() {370llvm::TimeTraceScope TimeProfile("FactGenerator");371// Iterate through the CFG blocks in reverse post-order to ensure that372// initializations and destructions are processed in the correct sequence.373for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {374CurrentBlockFacts.clear();375for (unsigned I = 0; I < Block->size(); ++I) {376const CFGElement &Element = Block->Elements[I];377if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())378Visit(CS->getStmt());379else if (std::optional<CFGAutomaticObjDtor> DtorOpt =380Element.getAs<CFGAutomaticObjDtor>())381handleDestructor(*DtorOpt);382}383FactMgr.addBlockFacts(Block, CurrentBlockFacts);384}385}386387void VisitDeclStmt(const DeclStmt *DS) {388for (const Decl *D : DS->decls())389if (const auto *VD = dyn_cast<VarDecl>(D))390if (hasOrigin(VD->getType()))391if (const Expr *InitExpr = VD->getInit())392addAssignOriginFact(*VD, *InitExpr);393}394395void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {396/// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized397/// pointers can use the same type of loan.398FactMgr.getOriginMgr().getOrCreate(*N);399}400401void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {402if (!hasOrigin(ICE->getType()))403return;404Visit(ICE->getSubExpr());405// An ImplicitCastExpr node itself gets an origin, which flows from the406// origin of its sub-expression (after stripping its own parens/casts).407// TODO: Consider if this is actually useful in practice. Alternatively, we408// could directly use the sub-expression's OriginID instead of creating a409// new one.410addAssignOriginFact(*ICE, *ICE->getSubExpr());411}412413void VisitUnaryOperator(const UnaryOperator *UO) {414if (UO->getOpcode() == UO_AddrOf) {415const Expr *SubExpr = UO->getSubExpr();416if (const auto *DRE = dyn_cast<DeclRefExpr>(SubExpr)) {417if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {418// Check if it's a local variable.419if (VD->hasLocalStorage()) {420OriginID OID = FactMgr.getOriginMgr().getOrCreate(*UO);421AccessPath AddrOfLocalVarPath(VD);422const Loan &L = FactMgr.getLoanMgr().addLoan(AddrOfLocalVarPath,423UO->getOperatorLoc());424CurrentBlockFacts.push_back(425FactMgr.createFact<IssueFact>(L.ID, OID));426}427}428}429}430}431432void VisitReturnStmt(const ReturnStmt *RS) {433if (const Expr *RetExpr = RS->getRetValue()) {434if (hasOrigin(RetExpr->getType())) {435OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);436CurrentBlockFacts.push_back(437FactMgr.createFact<ReturnOfOriginFact>(OID));438}439}440}441442void VisitBinaryOperator(const BinaryOperator *BO) {443if (BO->isAssignmentOp()) {444const Expr *LHSExpr = BO->getLHS();445const Expr *RHSExpr = BO->getRHS();446447// We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`448// LHS must be a pointer/reference type that can be an origin.449// RHS must also represent an origin (either another pointer/ref or an450// address-of).451if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))452if (const auto *VD_LHS =453dyn_cast<ValueDecl>(DRE_LHS->getDecl()->getCanonicalDecl());454VD_LHS && hasOrigin(VD_LHS->getType()))455addAssignOriginFact(*VD_LHS, *RHSExpr);456}457}458459private:460// Check if a type has an origin.461bool hasOrigin(QualType QT) { return QT->isPointerOrReferenceType(); }462463template <typename Destination, typename Source>464void addAssignOriginFact(const Destination &D, const Source &S) {465OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);466OriginID SrcOID = FactMgr.getOriginMgr().get(S);467CurrentBlockFacts.push_back(468FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));469}470471void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {472/// TODO: Also handle trivial destructors (e.g., for `int`473/// variables) which will never have a CFGAutomaticObjDtor node.474/// TODO: Handle loans to temporaries.475/// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the476/// lifetime ends.477const VarDecl *DestructedVD = DtorOpt.getVarDecl();478if (!DestructedVD)479return;480// Iterate through all loans to see if any expire.481/// TODO(opt): Do better than a linear search to find loans associated with482/// 'DestructedVD'.483for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {484const AccessPath &LoanPath = L.Path;485// Check if the loan is for a stack variable and if that variable486// is the one being destructed.487if (LoanPath.D == DestructedVD)488CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(L.ID));489}490}491492FactManager &FactMgr;493AnalysisDeclContext &AC;494llvm::SmallVector<Fact *> CurrentBlockFacts;495};496497// ========================================================================= //498// The Dataflow Lattice499// ========================================================================= //500501// Using LLVM's immutable collections is efficient for dataflow analysis502// as it avoids deep copies during state transitions.503// TODO(opt): Consider using a bitset to represent the set of loans.504using LoanSet = llvm::ImmutableSet<LoanID>;505using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;506507/// An object to hold the factories for immutable collections, ensuring508/// that all created states share the same underlying memory management.509struct LifetimeFactory {510OriginLoanMap::Factory OriginMapFactory;511LoanSet::Factory LoanSetFact;512513/// Creates a singleton set containing only the given loan ID.514LoanSet createLoanSet(LoanID LID) {515return LoanSetFact.add(LoanSetFact.getEmptySet(), LID);516}517};518519/// LifetimeLattice represents the state of our analysis at a given program520/// point. It is an immutable object, and all operations produce a new521/// instance rather than modifying the existing one.522struct LifetimeLattice {523/// The map from an origin to the set of loans it contains.524/// The lattice has a finite height: An origin's loan set is bounded by the525/// total number of loans in the function.526/// TODO(opt): To reduce the lattice size, propagate origins of declarations,527/// not expressions, because expressions are not visible across blocks.528OriginLoanMap Origins = OriginLoanMap(nullptr);529530explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {}531LifetimeLattice() = default;532533bool operator==(const LifetimeLattice &Other) const {534return Origins == Other.Origins;535}536bool operator!=(const LifetimeLattice &Other) const {537return !(*this == Other);538}539540LoanSet getLoans(OriginID OID) const {541if (auto *Loans = Origins.lookup(OID))542return *Loans;543return LoanSet(nullptr);544}545546/// Computes the union of two lattices by performing a key-wise join of547/// their OriginLoanMaps.548// TODO(opt): This key-wise join is a performance bottleneck. A more549// efficient merge could be implemented using a Patricia Trie or HAMT550// instead of the current AVL-tree-based ImmutableMap.551// TODO(opt): Keep the state small by removing origins which become dead.552LifetimeLattice join(const LifetimeLattice &Other,553LifetimeFactory &Factory) const {554/// Merge the smaller map into the larger one ensuring we iterate over the555/// smaller map.556if (Origins.getHeight() < Other.Origins.getHeight())557return Other.join(*this, Factory);558559OriginLoanMap JoinedState = Origins;560// For each origin in the other map, union its loan set with ours.561for (const auto &Entry : Other.Origins) {562OriginID OID = Entry.first;563LoanSet OtherLoanSet = Entry.second;564JoinedState = Factory.OriginMapFactory.add(565JoinedState, OID, join(getLoans(OID), OtherLoanSet, Factory));566}567return LifetimeLattice(JoinedState);568}569570LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const {571/// Merge the smaller set into the larger one ensuring we iterate over the572/// smaller set.573if (a.getHeight() < b.getHeight())574std::swap(a, b);575LoanSet Result = a;576for (LoanID LID : b) {577/// TODO(opt): Profiling shows that this loop is a major performance578/// bottleneck. Investigate using a BitVector to represent the set of579/// loans for improved join performance.580Result = Factory.LoanSetFact.add(Result, LID);581}582return Result;583}584585void dump(llvm::raw_ostream &OS) const {586OS << "LifetimeLattice State:\n";587if (Origins.isEmpty())588OS << " <empty>\n";589for (const auto &Entry : Origins) {590if (Entry.second.isEmpty())591OS << " Origin " << Entry.first << " contains no loans\n";592for (const LoanID &LID : Entry.second)593OS << " Origin " << Entry.first << " contains Loan " << LID << "\n";594}595}596};597598// ========================================================================= //599// The Transfer Function600// ========================================================================= //601class Transferer {602FactManager &AllFacts;603LifetimeFactory &Factory;604605public:606explicit Transferer(FactManager &F, LifetimeFactory &Factory)607: AllFacts(F), Factory(Factory) {}608609/// Computes the exit state of a block by applying all its facts sequentially610/// to a given entry state.611/// TODO: We might need to store intermediate states per-fact in the block for612/// later analysis.613LifetimeLattice transferBlock(const CFGBlock *Block,614LifetimeLattice EntryState) {615LifetimeLattice BlockState = EntryState;616llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block);617618for (const Fact *F : Facts) {619BlockState = transferFact(BlockState, F);620}621return BlockState;622}623624private:625LifetimeLattice transferFact(LifetimeLattice In, const Fact *F) {626switch (F->getKind()) {627case Fact::Kind::Issue:628return transfer(In, *F->getAs<IssueFact>());629case Fact::Kind::AssignOrigin:630return transfer(In, *F->getAs<AssignOriginFact>());631// Expire and ReturnOfOrigin facts don't modify the Origins and the State.632case Fact::Kind::Expire:633case Fact::Kind::ReturnOfOrigin:634return In;635}636llvm_unreachable("Unknown fact kind");637}638639/// A new loan is issued to the origin. Old loans are erased.640LifetimeLattice transfer(LifetimeLattice In, const IssueFact &F) {641OriginID OID = F.getOriginID();642LoanID LID = F.getLoanID();643return LifetimeLattice(Factory.OriginMapFactory.add(644In.Origins, OID, Factory.createLoanSet(LID)));645}646647/// The destination origin's loan set is replaced by the source's.648/// This implicitly "resets" the old loans of the destination.649LifetimeLattice transfer(LifetimeLattice InState, const AssignOriginFact &F) {650OriginID DestOID = F.getDestOriginID();651OriginID SrcOID = F.getSrcOriginID();652LoanSet SrcLoans = InState.getLoans(SrcOID);653return LifetimeLattice(654Factory.OriginMapFactory.add(InState.Origins, DestOID, SrcLoans));655}656};657658// ========================================================================= //659// Dataflow analysis660// ========================================================================= //661662/// Drives the intra-procedural dataflow analysis.663///664/// Orchestrates the analysis by iterating over the CFG using a worklist665/// algorithm. It computes a fixed point by propagating the LifetimeLattice666/// state through each block until the state no longer changes.667/// TODO: Maybe use the dataflow framework! The framework might need changes668/// to support the current comparison done at block-entry.669class LifetimeDataflow {670const CFG &Cfg;671AnalysisDeclContext &AC;672LifetimeFactory LifetimeFact;673674Transferer Xfer;675676/// Stores the merged analysis state at the entry of each CFG block.677llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockEntryStates;678/// Stores the analysis state at the exit of each CFG block, after the679/// transfer function has been applied.680llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockExitStates;681682public:683LifetimeDataflow(const CFG &C, FactManager &FS, AnalysisDeclContext &AC)684: Cfg(C), AC(AC), Xfer(FS, LifetimeFact) {}685686void run() {687llvm::TimeTraceScope TimeProfile("Lifetime Dataflow");688ForwardDataflowWorklist Worklist(Cfg, AC);689const CFGBlock *Entry = &Cfg.getEntry();690BlockEntryStates[Entry] = LifetimeLattice{};691Worklist.enqueueBlock(Entry);692while (const CFGBlock *B = Worklist.dequeue()) {693LifetimeLattice EntryState = getEntryState(B);694LifetimeLattice ExitState = Xfer.transferBlock(B, EntryState);695BlockExitStates[B] = ExitState;696697for (const CFGBlock *Successor : B->succs()) {698auto SuccIt = BlockEntryStates.find(Successor);699LifetimeLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())700? SuccIt->second701: LifetimeLattice{};702LifetimeLattice NewSuccEntryState =703OldSuccEntryState.join(ExitState, LifetimeFact);704// Enqueue the successor if its entry state has changed.705// TODO(opt): Consider changing 'join' to report a change if !=706// comparison is found expensive.707if (SuccIt == BlockEntryStates.end() ||708NewSuccEntryState != OldSuccEntryState) {709BlockEntryStates[Successor] = NewSuccEntryState;710Worklist.enqueueBlock(Successor);711}712}713}714}715716void dump() const {717llvm::dbgs() << "==========================================\n";718llvm::dbgs() << " Dataflow results:\n";719llvm::dbgs() << "==========================================\n";720const CFGBlock &B = Cfg.getExit();721getExitState(&B).dump(llvm::dbgs());722}723724LifetimeLattice getEntryState(const CFGBlock *B) const {725return BlockEntryStates.lookup(B);726}727728LifetimeLattice getExitState(const CFGBlock *B) const {729return BlockExitStates.lookup(B);730}731};732733// ========================================================================= //734// TODO: Analysing dataflow results and error reporting.735// ========================================================================= //736} // anonymous namespace737738void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,739AnalysisDeclContext &AC) {740llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");741DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),742/*ShowColors=*/true));743FactManager FactMgr;744FactGenerator FactGen(FactMgr, AC);745FactGen.run();746DEBUG_WITH_TYPE("LifetimeFacts", FactMgr.dump(Cfg, AC));747748/// TODO(opt): Consider optimizing individual blocks before running the749/// dataflow analysis.750/// 1. Expression Origins: These are assigned once and read at most once,751/// forming simple chains. These chains can be compressed into a single752/// assignment.753/// 2. Block-Local Loans: Origins of expressions are never read by other754/// blocks; only Decls are visible. Therefore, loans in a block that755/// never reach an Origin associated with a Decl can be safely dropped by756/// the analysis.757LifetimeDataflow Dataflow(Cfg, FactMgr, AC);758Dataflow.run();759DEBUG_WITH_TYPE("LifetimeDataflow", Dataflow.dump());760}761} // namespace clang762763764