1//===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the main LifetimeSafetyAnalysis class, which coordinates
10// the various components (fact generation, loan propagation, live origins
11// analysis, and checking) to detect lifetime safety violations in C++ code.
12//
13//===----------------------------------------------------------------------===//
14#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/Type.h"
18#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
19#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
20#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
21#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
22#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
23#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
24#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
25#include "clang/Analysis/AnalysisDeclContext.h"
26#include "clang/Analysis/CFG.h"
27#include "llvm/ADT/FoldingSet.h"
28#include "llvm/Support/Debug.h"
29#include "llvm/Support/ErrorHandling.h"
30#include "llvm/Support/TimeProfiler.h"
31#include <memory>
32
33namespace clang::lifetimes {
34namespace internal {
35
36#ifndef NDEBUG
37static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg,
38 FactManager &FactMgr) {
39 std::string Name;
40 if (const Decl *D = AC.getDecl()) {
41 if (const auto *ND = dyn_cast<NamedDecl>(D))
42 Name = ND->getQualifiedNameAsString();
43 };
44 DEBUG_WITH_TYPE(Name.c_str(), AC.getDecl()->dumpColor());
45 DEBUG_WITH_TYPE(Name.c_str(), Cfg.dump(AC.getASTContext().getLangOpts(),
46 /*ShowColors=*/true));
47 DEBUG_WITH_TYPE(Name.c_str(), FactMgr.dump(Cfg, AC));
48}
49#endif
50
51LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(
52 AnalysisDeclContext &AC, LifetimeSafetySemaHelper *SemaHelper,
53 const LifetimeSafetyOpts &LSOpts)
54 : AC(AC), SemaHelper(SemaHelper), LSOpts(LSOpts) {}
55
56void LifetimeSafetyAnalysis::run() {
57 llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
58
59 const CFG &Cfg = *AC.getCFG();
60 if (LSOpts.MaxCFGBlocks > 0 && Cfg.getNumBlockIDs() > LSOpts.MaxCFGBlocks) {
61 DEBUG_WITH_TYPE(
62 "LifetimeSafety", std::string FuncName = "<unknown>";
63 if (const Decl *D = AC.getDecl()) if (const auto *ND =
64 dyn_cast<NamedDecl>(D))
65 FuncName = ND->getQualifiedNameAsString();
66 llvm::dbgs() << "LifetimeSafety: Skipping function " << FuncName
67 << "due to large CFG: " << Cfg.getNumBlockIDs()
68 << " blocks (threshold: " << LSOpts.MaxCFGBlocks << ")\n");
69 return;
70 }
71
72 FactMgr = std::make_unique<FactManager>(args&: AC, args: Cfg);
73
74 FactsGenerator FactGen(*FactMgr, AC);
75 FactGen.run();
76
77 /// TODO(opt): Consider optimizing individual blocks before running the
78 /// dataflow analysis.
79 /// 1. Expression Origins: These are assigned once and read at most once,
80 /// forming simple chains. These chains can be compressed into a single
81 /// assignment.
82 /// 2. Block-Local Loans: Origins of expressions are never read by other
83 /// blocks; only Decls are visible. Therefore, loans in a block that
84 /// never reach an Origin associated with a Decl can be safely dropped by
85 /// the analysis.
86 /// 3. Collapse ExpireFacts belonging to same source location into a single
87 /// Fact.
88 LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
89 args: Cfg, args&: AC, args&: *FactMgr, args&: Factory.OriginMapFactory, args&: Factory.LoanSetFactory);
90
91 LiveOrigins = std::make_unique<LiveOriginsAnalysis>(
92 args: Cfg, args&: AC, args&: *FactMgr, args&: Factory.LivenessMapFactory);
93
94 MovedLoans = std::make_unique<MovedLoansAnalysis>(
95 args: Cfg, args&: AC, args&: *FactMgr, args&: *LoanPropagation, args&: *LiveOrigins, args&: FactMgr->getLoanMgr(),
96 args&: Factory.MovedLoansMapFactory);
97
98 runLifetimeChecker(LoanPropagation: *LoanPropagation, MovedLoans: *MovedLoans, LiveOrigins: *LiveOrigins, FactMgr&: *FactMgr, ADC&: AC,
99 SemaHelper);
100
101 DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
102 /*ShowColors=*/true));
103
104 DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));
105
106 // Debug print facts for a specific function using
107 // -debug-only=EnableFilterByFunctionName,YourFunctionNameFoo
108 DEBUG_WITH_TYPE("EnableFilterByFunctionName",
109 DebugOnlyFunction(AC, Cfg, *FactMgr));
110 DEBUG_WITH_TYPE("LiveOrigins",
111 LiveOrigins->dump(llvm::dbgs(), FactMgr->getTestPoints()));
112}
113
114void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM,
115 LifetimeSafetyStats &Stats) {
116 Stmt *FunctionBody = AC.getBody();
117 if (FunctionBody == nullptr)
118 return;
119 OM.collectMissingOrigins(FunctionBody&: *FunctionBody, LSStats&: Stats);
120}
121} // namespace internal
122
123void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
124 LifetimeSafetySemaHelper *SemaHelper,
125 LifetimeSafetyStats &Stats, bool CollectStats) {
126 LifetimeSafetyOpts LSOpts;
127 LSOpts.MaxCFGBlocks =
128 AC.getASTContext().getLangOpts().LifetimeSafetyMaxCFGBlocks;
129
130 internal::LifetimeSafetyAnalysis Analysis(AC, SemaHelper, LSOpts);
131 Analysis.run();
132 if (CollectStats)
133 collectLifetimeStats(AC, OM&: Analysis.getFactManager().getOriginMgr(), Stats);
134}
135} // namespace clang::lifetimes
136