Skip to content

Commit f7fc36d

Browse files
authored
[LifetimeSafety] Support bidirectional dataflow analysis (#148967)
Generalize the dataflow analysis to support both forward and backward analyses. Some program analyses would be expressed as backward dataflow problems (like liveness analysis). This change enables the framework to support both forward analyses (like the loan propagation analysis) and backward analyses with the same infrastructure.
1 parent 53355ab commit f7fc36d

File tree

1 file changed

+59
-49
lines changed

1 file changed

+59
-49
lines changed

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -499,13 +499,16 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
499499
// ========================================================================= //
500500
// Generic Dataflow Analysis
501501
// ========================================================================= //
502-
/// A generic, policy-based driver for forward dataflow analyses. It combines
502+
503+
enum class Direction { Forward, Backward };
504+
505+
/// A generic, policy-based driver for dataflow analyses. It combines
503506
/// the dataflow runner and the transferer logic into a single class hierarchy.
504507
///
505508
/// The derived class is expected to provide:
506509
/// - A `Lattice` type.
507510
/// - `StringRef getAnalysisName() const`
508-
/// - `Lattice getInitialState();` The initial state at the function entry.
511+
/// - `Lattice getInitialState();` The initial state of the analysis.
509512
/// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths.
510513
/// - `Lattice transfer(Lattice, const FactType&);` Defines how a single
511514
/// lifetime-relevant `Fact` transforms the lattice state. Only overloads
@@ -514,18 +517,23 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
514517
/// \tparam Derived The CRTP derived class that implements the specific
515518
/// analysis.
516519
/// \tparam LatticeType The dataflow lattice used by the analysis.
520+
/// \tparam Dir The direction of the analysis (Forward or Backward).
517521
/// TODO: Maybe use the dataflow framework! The framework might need changes
518522
/// to support the current comparison done at block-entry.
519-
template <typename Derived, typename LatticeType> class DataflowAnalysis {
523+
template <typename Derived, typename LatticeType, Direction Dir>
524+
class DataflowAnalysis {
520525
public:
521526
using Lattice = LatticeType;
527+
using Base = DataflowAnalysis<Derived, LatticeType, Dir>;
522528

523529
private:
524530
const CFG &Cfg;
525531
AnalysisDeclContext &AC;
526532

527-
llvm::DenseMap<const CFGBlock *, Lattice> BlockEntryStates;
528-
llvm::DenseMap<const CFGBlock *, Lattice> BlockExitStates;
533+
llvm::DenseMap<const CFGBlock *, Lattice> InStates;
534+
llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
535+
536+
static constexpr bool isForward() { return Dir == Direction::Forward; }
529537

530538
protected:
531539
FactManager &AllFacts;
@@ -539,75 +547,76 @@ template <typename Derived, typename LatticeType> class DataflowAnalysis {
539547
Derived &D = static_cast<Derived &>(*this);
540548
llvm::TimeTraceScope Time(D.getAnalysisName());
541549

542-
ForwardDataflowWorklist Worklist(Cfg, AC);
543-
const CFGBlock *Entry = &Cfg.getEntry();
544-
BlockEntryStates[Entry] = D.getInitialState();
545-
Worklist.enqueueBlock(Entry);
546-
llvm::SmallBitVector Visited;
547-
Visited.resize(Cfg.getNumBlockIDs() + 1);
548-
549-
while (const CFGBlock *B = Worklist.dequeue()) {
550-
Lattice EntryState = getEntryState(B);
551-
Lattice ExitState = transferBlock(B, EntryState);
552-
BlockExitStates[B] = ExitState;
553-
Visited.set(B->getBlockID());
550+
using Worklist =
551+
std::conditional_t<Dir == Direction::Forward, ForwardDataflowWorklist,
552+
BackwardDataflowWorklist>;
553+
Worklist W(Cfg, AC);
554+
555+
const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit();
556+
InStates[Start] = D.getInitialState();
557+
W.enqueueBlock(Start);
554558

555-
for (const CFGBlock *Successor : B->succs()) {
556-
Lattice OldSuccEntryState = getEntryState(Successor);
557-
Lattice NewSuccEntryState = D.join(OldSuccEntryState, ExitState);
559+
llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1);
558560

559-
// Enqueue the successor if its entry state has changed or if we have
561+
while (const CFGBlock *B = W.dequeue()) {
562+
Lattice StateIn = getInState(B);
563+
Lattice StateOut = transferBlock(B, StateIn);
564+
OutStates[B] = StateOut;
565+
Visited.set(B->getBlockID());
566+
for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) {
567+
Lattice OldInState = getInState(AdjacentB);
568+
Lattice NewInState = D.join(OldInState, StateOut);
569+
// Enqueue the adjacent block if its in-state has changed or if we have
560570
// never visited it.
561-
if (!Visited.test(Successor->getBlockID()) ||
562-
NewSuccEntryState != OldSuccEntryState) {
563-
BlockEntryStates[Successor] = NewSuccEntryState;
564-
Worklist.enqueueBlock(Successor);
571+
if (!Visited.test(AdjacentB->getBlockID()) ||
572+
NewInState != OldInState) {
573+
InStates[AdjacentB] = NewInState;
574+
W.enqueueBlock(AdjacentB);
565575
}
566576
}
567577
}
568578
}
569579

570-
Lattice getEntryState(const CFGBlock *B) const {
571-
return BlockEntryStates.lookup(B);
572-
}
580+
Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
573581

574-
Lattice getExitState(const CFGBlock *B) const {
575-
return BlockExitStates.lookup(B);
576-
}
582+
Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
577583

578584
void dump() const {
579585
const Derived *D = static_cast<const Derived *>(this);
580586
llvm::dbgs() << "==========================================\n";
581587
llvm::dbgs() << D->getAnalysisName() << " results:\n";
582588
llvm::dbgs() << "==========================================\n";
583-
const CFGBlock &B = Cfg.getExit();
584-
getExitState(&B).dump(llvm::dbgs());
589+
const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry();
590+
getOutState(&B).dump(llvm::dbgs());
585591
}
586592

587-
private:
588-
/// Computes the exit state of a block by applying all its facts sequentially
589-
/// to a given entry state.
593+
/// Computes the state at one end of a block by applying all its facts
594+
/// sequentially to a given state from the other end.
590595
/// TODO: We might need to store intermediate states per-fact in the block for
591596
/// later analysis.
592-
Lattice transferBlock(const CFGBlock *Block, Lattice EntryState) {
593-
Lattice BlockState = EntryState;
594-
for (const Fact *F : AllFacts.getFacts(Block)) {
595-
BlockState = transferFact(BlockState, F);
596-
}
597-
return BlockState;
597+
Lattice transferBlock(const CFGBlock *Block, Lattice State) {
598+
auto Facts = AllFacts.getFacts(Block);
599+
if constexpr (isForward())
600+
for (const Fact *F : Facts)
601+
State = transferFact(State, F);
602+
else
603+
for (const Fact *F : llvm::reverse(Facts))
604+
State = transferFact(State, F);
605+
return State;
598606
}
599607

600608
Lattice transferFact(Lattice In, const Fact *F) {
601-
Derived *d = static_cast<Derived *>(this);
609+
assert(F);
610+
Derived *D = static_cast<Derived *>(this);
602611
switch (F->getKind()) {
603612
case Fact::Kind::Issue:
604-
return d->transfer(In, *F->getAs<IssueFact>());
613+
return D->transfer(In, *F->getAs<IssueFact>());
605614
case Fact::Kind::Expire:
606-
return d->transfer(In, *F->getAs<ExpireFact>());
615+
return D->transfer(In, *F->getAs<ExpireFact>());
607616
case Fact::Kind::AssignOrigin:
608-
return d->transfer(In, *F->getAs<AssignOriginFact>());
617+
return D->transfer(In, *F->getAs<AssignOriginFact>());
609618
case Fact::Kind::ReturnOfOrigin:
610-
return d->transfer(In, *F->getAs<ReturnOfOriginFact>());
619+
return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
611620
}
612621
llvm_unreachable("Unknown fact kind");
613622
}
@@ -715,7 +724,8 @@ struct LoanPropagationLattice {
715724

716725
/// The analysis that tracks which loans belong to which origins.
717726
class LoanPropagationAnalysis
718-
: public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice> {
727+
: public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice,
728+
Direction::Forward> {
719729

720730
LifetimeFactory &Factory;
721731

@@ -724,7 +734,7 @@ class LoanPropagationAnalysis
724734
LifetimeFactory &Factory)
725735
: DataflowAnalysis(C, AC, F), Factory(Factory) {}
726736

727-
using DataflowAnalysis<LoanPropagationAnalysis, Lattice>::transfer;
737+
using Base::transfer;
728738

729739
StringRef getAnalysisName() const { return "LoanPropagation"; }
730740

0 commit comments

Comments
 (0)