//===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the main LifetimeSafetyAnalysis class, which coordinates // the various components (fact generation, loan propagation, live origins // analysis, and checking) to detect lifetime safety violations in C++ code. // //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h" #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h" #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h" #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TimeProfiler.h" #include namespace clang::lifetimes { namespace internal { #ifndef NDEBUG static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg, FactManager &FactMgr) { std::string Name; if (const Decl *D = AC.getDecl()) { if (const auto *ND = dyn_cast(D)) Name = ND->getQualifiedNameAsString(); }; DEBUG_WITH_TYPE(Name.c_str(), AC.getDecl()->dumpColor()); DEBUG_WITH_TYPE(Name.c_str(), Cfg.dump(AC.getASTContext().getLangOpts(), /*ShowColors=*/true)); DEBUG_WITH_TYPE(Name.c_str(), FactMgr.dump(Cfg, AC)); } #endif LifetimeSafetyAnalysis::LifetimeSafetyAnalysis( AnalysisDeclContext &AC, LifetimeSafetySemaHelper *SemaHelper, const LifetimeSafetyOpts &LSOpts) : AC(AC), SemaHelper(SemaHelper), LSOpts(LSOpts) {} void LifetimeSafetyAnalysis::run() { llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis"); const CFG &Cfg = *AC.getCFG(); if (LSOpts.MaxCFGBlocks > 0 && Cfg.getNumBlockIDs() > LSOpts.MaxCFGBlocks) { DEBUG_WITH_TYPE( "LifetimeSafety", std::string FuncName = ""; if (const Decl *D = AC.getDecl()) if (const auto *ND = dyn_cast(D)) FuncName = ND->getQualifiedNameAsString(); llvm::dbgs() << "LifetimeSafety: Skipping function " << FuncName << "due to large CFG: " << Cfg.getNumBlockIDs() << " blocks (threshold: " << LSOpts.MaxCFGBlocks << ")\n"); return; } FactMgr = std::make_unique(AC, Cfg); FactsGenerator FactGen(*FactMgr, AC); FactGen.run(); /// TODO(opt): Consider optimizing individual blocks before running the /// dataflow analysis. /// 1. Expression Origins: These are assigned once and read at most once, /// forming simple chains. These chains can be compressed into a single /// assignment. /// 2. Block-Local Loans: Origins of expressions are never read by other /// blocks; only Decls are visible. Therefore, loans in a block that /// never reach an Origin associated with a Decl can be safely dropped by /// the analysis. /// 3. Collapse ExpireFacts belonging to same source location into a single /// Fact. LoanPropagation = std::make_unique( Cfg, AC, *FactMgr, Factory.OriginMapFactory, Factory.LoanSetFactory); LiveOrigins = std::make_unique( Cfg, AC, *FactMgr, Factory.LivenessMapFactory); MovedLoans = std::make_unique( Cfg, AC, *FactMgr, *LoanPropagation, *LiveOrigins, FactMgr->getLoanMgr(), Factory.MovedLoansMapFactory); runLifetimeChecker(*LoanPropagation, *MovedLoans, *LiveOrigins, *FactMgr, AC, SemaHelper); DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(), /*ShowColors=*/true)); DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC)); // Debug print facts for a specific function using // -debug-only=EnableFilterByFunctionName,YourFunctionNameFoo DEBUG_WITH_TYPE("EnableFilterByFunctionName", DebugOnlyFunction(AC, Cfg, *FactMgr)); DEBUG_WITH_TYPE("LiveOrigins", LiveOrigins->dump(llvm::dbgs(), FactMgr->getTestPoints())); } void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM, LifetimeSafetyStats &Stats) { Stmt *FunctionBody = AC.getBody(); if (FunctionBody == nullptr) return; OM.collectMissingOrigins(*FunctionBody, Stats); } } // namespace internal void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, LifetimeSafetySemaHelper *SemaHelper, LifetimeSafetyStats &Stats, bool CollectStats) { LifetimeSafetyOpts LSOpts; LSOpts.MaxCFGBlocks = AC.getASTContext().getLangOpts().LifetimeSafetyMaxCFGBlocks; internal::LifetimeSafetyAnalysis Analysis(AC, SemaHelper, LSOpts); Analysis.run(); if (CollectStats) collectLifetimeStats(AC, Analysis.getFactManager().getOriginMgr(), Stats); } } // namespace clang::lifetimes