Skip to content

Commit 01b019d

Browse files
committed
add-liveness-finally
1 parent c29040a commit 01b019d

File tree

3 files changed

+224
-0
lines changed

3 files changed

+224
-0
lines changed

clang/include/clang/Analysis/Analyses/LifetimeSafety.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Fact;
3434
class FactManager;
3535
class LoanPropagationAnalysis;
3636
class ExpiredLoansAnalysis;
37+
class LiveOriginAnalysis;
3738
struct LifetimeFactory;
3839

3940
/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
@@ -97,6 +98,9 @@ class LifetimeSafetyAnalysis {
9798
/// their Path.
9899
std::vector<LoanID> getLoanIDForVar(const VarDecl *VD) const;
99100

101+
/// Returns the set of live origins at a specific program point.
102+
OriginSet getLiveOriginsAtPoint(ProgramPoint PP) const;
103+
100104
/// Retrieves program points that were specially marked in the source code
101105
/// for testing.
102106
///
@@ -114,6 +118,7 @@ class LifetimeSafetyAnalysis {
114118
std::unique_ptr<FactManager> FactMgr;
115119
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
116120
std::unique_ptr<ExpiredLoansAnalysis> ExpiredLoans;
121+
std::unique_ptr<LiveOriginAnalysis> LiveOrigins;
117122
};
118123
} // namespace internal
119124
} // namespace clang::lifetimes

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,12 +727,14 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
727727
// ========================================================================= //
728728

729729
using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
730+
using OriginSet = llvm::ImmutableSet<OriginID>;
730731

731732
/// An object to hold the factories for immutable collections, ensuring
732733
/// that all created states share the same underlying memory management.
733734
struct LifetimeFactory {
734735
OriginLoanMap::Factory OriginMapFactory;
735736
LoanSet::Factory LoanSetFactory;
737+
OriginSet::Factory OriginSetFactory;
736738

737739
/// Creates a singleton set containing only the given loan ID.
738740
LoanSet createLoanSet(LoanID LID) {
@@ -833,6 +835,78 @@ class LoanPropagationAnalysis
833835
}
834836
};
835837

838+
// ========================================================================= //
839+
// Live Origins Analysis
840+
// ========================================================================= //
841+
842+
/// The dataflow lattice for origin liveness analysis.
843+
/// It tracks the set of origins that are live at a given program point.
844+
struct LivenessLattice {
845+
OriginSet LiveOrigins;
846+
847+
LivenessLattice() : LiveOrigins(nullptr) {};
848+
explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {}
849+
850+
bool operator==(const LivenessLattice &Other) const {
851+
return LiveOrigins == Other.LiveOrigins;
852+
}
853+
bool operator!=(const LivenessLattice &Other) const {
854+
return !(*this == Other);
855+
}
856+
857+
void dump(llvm::raw_ostream &OS) const {
858+
OS << "LivenessLattice State:\n";
859+
if (LiveOrigins.isEmpty())
860+
OS << " <empty>\n";
861+
for (const OriginID &OID : LiveOrigins)
862+
OS << " Origin " << OID << " is live\n";
863+
}
864+
};
865+
866+
/// The analysis that tracks which origins are live. This is a backward
867+
/// analysis.
868+
class LiveOriginAnalysis
869+
: public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
870+
Direction::Backward> {
871+
872+
OriginSet::Factory &SetFactory;
873+
874+
public:
875+
LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
876+
OriginSet::Factory &SF)
877+
: DataflowAnalysis(C, AC, F), SetFactory(SF) {}
878+
879+
using DataflowAnalysis<LiveOriginAnalysis, Lattice,
880+
Direction::Backward>::transfer;
881+
882+
StringRef getAnalysisName() const { return "LiveOrigins"; }
883+
884+
Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); }
885+
886+
/// Merges two lattices by taking the union of the live origin sets.
887+
Lattice join(Lattice L1, Lattice L2) const {
888+
return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory));
889+
}
890+
891+
/// An assignment `p = q` kills the liveness of `p` and generates liveness
892+
/// for `q`.
893+
Lattice transfer(Lattice In, const AssignOriginFact &F) {
894+
OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID());
895+
S = SetFactory.add(S, F.getSrcOriginID());
896+
return Lattice(S);
897+
}
898+
899+
/// Issuing a new loan to an origin kills its liveness.
900+
Lattice transfer(Lattice In, const IssueFact &F) {
901+
return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID()));
902+
}
903+
904+
/// A return statement generates liveness for the returned origin.
905+
Lattice transfer(Lattice In, const ReturnOfOriginFact &F) {
906+
return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID()));
907+
}
908+
};
909+
836910
// ========================================================================= //
837911
// Expired Loans Analysis
838912
// ========================================================================= //
@@ -937,6 +1011,10 @@ void LifetimeSafetyAnalysis::run() {
9371011
ExpiredLoans =
9381012
std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
9391013
ExpiredLoans->run();
1014+
1015+
LiveOrigins = std::make_unique<LiveOriginAnalysis>(Cfg, AC, *FactMgr,
1016+
Factory->OriginSetFactory);
1017+
LiveOrigins->run();
9401018
}
9411019

9421020
LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
@@ -969,6 +1047,11 @@ LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const {
9691047
return Result;
9701048
}
9711049

1050+
OriginSet LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const {
1051+
assert(LiveOrigins && "LiveOriginAnalysis has not been run.");
1052+
return LiveOrigins->getState(PP).LiveOrigins;
1053+
}
1054+
9721055
llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
9731056
assert(FactMgr && "FactManager not initialized");
9741057
llvm::StringMap<ProgramPoint> AnnotationToPointMap;

clang/unittests/Analysis/LifetimeSafetyTest.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ class LifetimeTestHelper {
125125
return Analysis.getExpiredLoansAtPoint(PP);
126126
}
127127

128+
std::optional<OriginSet> getLiveOriginsAtPoint(llvm::StringRef Annotation) {
129+
ProgramPoint PP = Runner.getProgramPoint(Annotation);
130+
if (!PP)
131+
return std::nullopt;
132+
return Analysis.getLiveOriginsAtPoint(PP);
133+
}
134+
128135
private:
129136
template <typename DeclT> DeclT *findDecl(llvm::StringRef Name) {
130137
auto &Ctx = Runner.getASTContext();
@@ -162,6 +169,15 @@ class OriginInfo {
162169
LifetimeTestHelper &Helper;
163170
};
164171

172+
// A helper class to represent a set of origins, identified by variable names.
173+
class OriginsInfo {
174+
public:
175+
OriginsInfo(const std::vector<std::string> &Vars, LifetimeTestHelper &H)
176+
: OriginVars(Vars), Helper(H) {}
177+
std::vector<std::string> OriginVars;
178+
LifetimeTestHelper &Helper;
179+
};
180+
165181
/// Matcher to verify the set of loans held by an origin at a specific
166182
/// program point.
167183
///
@@ -232,6 +248,34 @@ MATCHER_P(AreExpiredAt, Annotation, "") {
232248
ActualExpiredLoans, result_listener);
233249
}
234250

251+
/// Matcher to verify the complete set of live origins at a program point.
252+
MATCHER_P(AreLiveAt, Annotation, "") {
253+
const OriginsInfo &Info = arg;
254+
auto &Helper = Info.Helper;
255+
256+
auto ActualLiveSetOpt = Helper.getLiveOriginsAtPoint(Annotation);
257+
if (!ActualLiveSetOpt) {
258+
*result_listener << "could not get a valid live origin set at point '"
259+
<< Annotation << "'";
260+
return false;
261+
}
262+
std::vector<OriginID> ActualLiveOrigins(ActualLiveSetOpt->begin(),
263+
ActualLiveSetOpt->end());
264+
265+
std::vector<OriginID> ExpectedLiveOrigins;
266+
for (const auto &VarName : Info.OriginVars) {
267+
auto OriginIDOpt = Helper.getOriginForDecl(VarName);
268+
if (!OriginIDOpt) {
269+
*result_listener << "could not find an origin for variable '" << VarName
270+
<< "'";
271+
return false;
272+
}
273+
ExpectedLiveOrigins.push_back(*OriginIDOpt);
274+
}
275+
return ExplainMatchResult(UnorderedElementsAreArray(ExpectedLiveOrigins),
276+
ActualLiveOrigins, result_listener);
277+
}
278+
235279
// Base test fixture to manage the runner and helper.
236280
class LifetimeAnalysisTest : public ::testing::Test {
237281
protected:
@@ -244,6 +288,13 @@ class LifetimeAnalysisTest : public ::testing::Test {
244288
return OriginInfo(OriginVar, *Helper);
245289
}
246290

291+
/// Factory function that hides the std::vector creation.
292+
OriginsInfo Origins(std::initializer_list<std::string> OriginVars) {
293+
return OriginsInfo({OriginVars}, *Helper);
294+
}
295+
296+
OriginsInfo NoOrigins() { return Origins({}); }
297+
247298
/// Factory function that hides the std::vector creation.
248299
LoanSetInfo LoansTo(std::initializer_list<std::string> LoanVars) {
249300
return LoanSetInfo({LoanVars}, *Helper);
@@ -706,5 +757,90 @@ TEST_F(LifetimeAnalysisTest, ReassignedPointerThenOriginalExpires) {
706757
EXPECT_THAT(LoansTo({"s1", "s2"}), AreExpiredAt("p_after_s1_expires"));
707758
}
708759

760+
TEST_F(LifetimeAnalysisTest, LivenessDeadPointer) {
761+
SetupTest(R"(
762+
void target() {
763+
POINT(p2);
764+
MyObj s;
765+
MyObj* p = &s;
766+
POINT(p1);
767+
}
768+
)");
769+
EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
770+
EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));
771+
}
772+
773+
TEST_F(LifetimeAnalysisTest, LivenessSimpleReturn) {
774+
SetupTest(R"(
775+
MyObj* target() {
776+
MyObj s;
777+
MyObj* p = &s;
778+
POINT(p1);
779+
return p;
780+
}
781+
)");
782+
EXPECT_THAT(Origins({"p"}), AreLiveAt("p1"));
783+
}
784+
785+
TEST_F(LifetimeAnalysisTest, LivenessKilledByReassignment) {
786+
SetupTest(R"(
787+
MyObj* target() {
788+
MyObj s1, s2;
789+
MyObj* p = &s1;
790+
POINT(p1);
791+
p = &s2;
792+
POINT(p2);
793+
return p;
794+
}
795+
)");
796+
EXPECT_THAT(Origins({"p"}), AreLiveAt("p2"));
797+
EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
798+
}
799+
800+
TEST_F(LifetimeAnalysisTest, LivenessAcrossBranches) {
801+
SetupTest(R"(
802+
MyObj* target(bool c) {
803+
MyObj x, y;
804+
MyObj* p = nullptr;
805+
POINT(p1);
806+
if (c) {
807+
p = &x;
808+
POINT(p2);
809+
} else {
810+
p = &y;
811+
POINT(p3);
812+
}
813+
return p;
814+
}
815+
)");
816+
EXPECT_THAT(Origins({"p"}), AreLiveAt("p2"));
817+
EXPECT_THAT(Origins({"p"}), AreLiveAt("p3"));
818+
// Before the `if`, the value of `p` (`nullptr`) is always overwritten before
819+
EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
820+
}
821+
822+
TEST_F(LifetimeAnalysisTest, LivenessInLoop) {
823+
SetupTest(R"(
824+
MyObj* target(bool c) {
825+
MyObj s1, s2;
826+
MyObj* p = &s1;
827+
MyObj* q = &s2;
828+
POINT(p1);
829+
while(c) {
830+
POINT(p2);
831+
p = q;
832+
POINT(p3);
833+
}
834+
POINT(p4);
835+
return p;
836+
}
837+
)");
838+
839+
EXPECT_THAT(Origins({"p"}), AreLiveAt("p4"));
840+
EXPECT_THAT(Origins({"p","q"}), AreLiveAt("p3"));
841+
EXPECT_THAT(Origins({"q"}), AreLiveAt("p2"));
842+
EXPECT_THAT(Origins({"p", "q"}), AreLiveAt("p1"));
843+
}
844+
709845
} // anonymous namespace
710846
} // namespace clang::lifetimes::internal

0 commit comments

Comments
 (0)