@@ -491,247 +491,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
491
491
};
492
492
493
493
// ========================================================================= //
494
- // The Dataflow Lattice
495
- // ========================================================================= //
496
-
497
- // Using LLVM's immutable collections is efficient for dataflow analysis
498
- // as it avoids deep copies during state transitions.
499
- // TODO(opt): Consider using a bitset to represent the set of loans.
500
- using LoanSet = llvm::ImmutableSet<LoanID>;
501
- using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
502
-
503
- // / An object to hold the factories for immutable collections, ensuring
504
- // / that all created states share the same underlying memory management.
505
- struct LifetimeFactory {
506
- OriginLoanMap::Factory OriginMapFact;
507
- LoanSet::Factory LoanSetFact;
508
-
509
- LoanSet createLoanSet (LoanID LID) {
510
- return LoanSetFact.add (LoanSetFact.getEmptySet (), LID);
511
- }
512
- };
513
-
514
- // / LifetimeLattice represents the state of our analysis at a given program
515
- // / point. It is an immutable object, and all operations produce a new
516
- // / instance rather than modifying the existing one.
517
- struct LifetimeLattice {
518
- // / The map from an origin to the set of loans it contains.
519
- // / TODO(opt): To reduce the lattice size, propagate origins of declarations,
520
- // / not expressions, because expressions are not visible across blocks.
521
- OriginLoanMap Origins = OriginLoanMap(nullptr );
522
-
523
- explicit LifetimeLattice (const OriginLoanMap &S) : Origins(S) {}
524
- LifetimeLattice () = default ;
525
-
526
- bool operator ==(const LifetimeLattice &Other) const {
527
- return Origins == Other.Origins ;
528
- }
529
- bool operator !=(const LifetimeLattice &Other) const {
530
- return !(*this == Other);
531
- }
532
-
533
- LoanSet getLoans (OriginID OID, LifetimeFactory &Factory) const {
534
- if (auto *Loans = Origins.lookup (OID))
535
- return *Loans;
536
- return Factory.LoanSetFact .getEmptySet ();
537
- }
538
-
539
- // / Computes the union of two lattices by performing a key-wise join of
540
- // / their OriginLoanMaps.
541
- // TODO(opt): This key-wise join is a performance bottleneck. A more
542
- // efficient merge could be implemented using a Patricia Trie or HAMT
543
- // instead of the current AVL-tree-based ImmutableMap.
544
- LifetimeLattice join (const LifetimeLattice &Other,
545
- LifetimeFactory &Factory) const {
546
- // / Merge the smaller map into the larger one ensuring we iterate over the
547
- // / smaller map.
548
- if (Origins.getHeight () < Other.Origins .getHeight ())
549
- return Other.join (*this , Factory);
550
-
551
- OriginLoanMap JoinedState = Origins;
552
- // For each origin in the other map, union its loan set with ours.
553
- for (const auto &Entry : Other.Origins ) {
554
- OriginID OID = Entry.first ;
555
- LoanSet OtherLoanSet = Entry.second ;
556
- JoinedState = Factory.OriginMapFact .add (
557
- JoinedState, OID,
558
- join (getLoans (OID, Factory), OtherLoanSet, Factory));
559
- }
560
- return LifetimeLattice (JoinedState);
561
- }
562
-
563
- LoanSet join (LoanSet a, LoanSet b, LifetimeFactory &Factory) const {
564
- // / Merge the smaller set into the larger one ensuring we iterate over the
565
- // / smaller set.
566
- if (a.getHeight () < b.getHeight ())
567
- std::swap (a, b);
568
- LoanSet Result = a;
569
- for (LoanID LID : b) {
570
- // / TODO(opt): Profiling shows that this loop is a major performance
571
- // / bottleneck. Investigate using a BitVector to represent the set of
572
- // / loans for improved join performance.
573
- Result = Factory.LoanSetFact .add (Result, LID);
574
- }
575
- return Result;
576
- }
577
-
578
- void dump (llvm::raw_ostream &OS) const {
579
- OS << " LifetimeLattice State:\n " ;
580
- if (Origins.isEmpty ())
581
- OS << " <empty>\n " ;
582
- for (const auto &Entry : Origins) {
583
- if (Entry.second .isEmpty ())
584
- OS << " Origin " << Entry.first << " contains no loans\n " ;
585
- for (const LoanID &LID : Entry.second )
586
- OS << " Origin " << Entry.first << " contains Loan " << LID << " \n " ;
587
- }
588
- }
589
- };
590
-
591
- // ========================================================================= //
592
- // The Transfer Function
593
- // ========================================================================= //
594
- class Transferer {
595
- FactManager &AllFacts;
596
- LifetimeFactory &Factory;
597
-
598
- public:
599
- explicit Transferer (FactManager &F, LifetimeFactory &Factory)
600
- : AllFacts(F), Factory(Factory) {}
601
-
602
- // / Computes the exit state of a block by applying all its facts sequentially
603
- // / to a given entry state.
604
- // / TODO: We might need to store intermediate states per-fact in the block for
605
- // / later analysis.
606
- LifetimeLattice transferBlock (const CFGBlock *Block,
607
- LifetimeLattice EntryState) {
608
- LifetimeLattice BlockState = EntryState;
609
- llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts (Block);
610
-
611
- for (const Fact *F : Facts) {
612
- BlockState = transferFact (BlockState, F);
613
- }
614
- return BlockState;
615
- }
616
-
617
- private:
618
- LifetimeLattice transferFact (LifetimeLattice In, const Fact *F) {
619
- switch (F->getKind ()) {
620
- case Fact::Kind::Issue:
621
- return transfer (In, *F->getAs <IssueFact>());
622
- case Fact::Kind::AssignOrigin:
623
- return transfer (In, *F->getAs <AssignOriginFact>());
624
- // Expire and ReturnOfOrigin facts don't modify the Origins and the State.
625
- case Fact::Kind::Expire:
626
- case Fact::Kind::ReturnOfOrigin:
627
- return In;
628
- }
629
- llvm_unreachable (" Unknown fact kind" );
630
- }
631
-
632
- // / A new loan is issued to the origin. Old loans are erased.
633
- LifetimeLattice transfer (LifetimeLattice In, const IssueFact &F) {
634
- OriginID OID = F.getOriginID ();
635
- LoanID LID = F.getLoanID ();
636
- return LifetimeLattice (
637
- Factory.OriginMapFact .add (In.Origins , OID, Factory.createLoanSet (LID)));
638
- }
639
-
640
- // / The destination origin's loan set is replaced by the source's.
641
- // / This implicitly "resets" the old loans of the destination.
642
- LifetimeLattice transfer (LifetimeLattice InState, const AssignOriginFact &F) {
643
- OriginID DestOID = F.getDestOriginID ();
644
- OriginID SrcOID = F.getSrcOriginID ();
645
- LoanSet SrcLoans = InState.getLoans (SrcOID, Factory);
646
- return LifetimeLattice (
647
- Factory.OriginMapFact .add (InState.Origins , DestOID, SrcLoans));
648
- }
649
- };
650
- // ========================================================================= //
651
- // Dataflow analysis
652
- // ========================================================================= //
653
-
654
- // / Drives the intra-procedural dataflow analysis.
655
- // /
656
- // / Orchestrates the analysis by iterating over the CFG using a worklist
657
- // / algorithm. It computes a fixed point by propagating the LifetimeLattice
658
- // / state through each block until the state no longer changes.
659
- // / TODO: Maybe use the dataflow framework! The framework might need changes
660
- // / to support the current comparison done at block-entry.
661
- class LifetimeDataflow {
662
- const CFG &Cfg;
663
- AnalysisDeclContext &AC;
664
- LifetimeFactory LifetimeFact;
665
-
666
- Transferer Xfer;
667
-
668
- // / Stores the merged analysis state at the entry of each CFG block.
669
- llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockEntryStates;
670
- // / Stores the analysis state at the exit of each CFG block, after the
671
- // / transfer function has been applied.
672
- llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockExitStates;
673
-
674
- public:
675
- LifetimeDataflow (const CFG &C, FactManager &FS, AnalysisDeclContext &AC)
676
- : Cfg(C), AC(AC), Xfer(FS, LifetimeFact) {}
677
-
678
- void run () {
679
- llvm::TimeTraceScope TimeProfile (" Lifetime Dataflow" );
680
- ForwardDataflowWorklist Worklist (Cfg, AC);
681
- const CFGBlock *Entry = &Cfg.getEntry ();
682
- BlockEntryStates[Entry] = LifetimeLattice{};
683
- Worklist.enqueueBlock (Entry);
684
- while (const CFGBlock *B = Worklist.dequeue ()) {
685
- LifetimeLattice EntryState = getEntryState (B);
686
- LifetimeLattice ExitState = Xfer.transferBlock (B, EntryState);
687
- BlockExitStates[B] = ExitState;
688
-
689
- for (const CFGBlock *Successor : B->succs ()) {
690
- auto SuccIt = BlockEntryStates.find (Successor);
691
- LifetimeLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end ())
692
- ? SuccIt->second
693
- : LifetimeLattice{};
694
- LifetimeLattice NewSuccEntryState =
695
- OldSuccEntryState.join (ExitState, LifetimeFact);
696
- // Enqueue the successor if its entry state has changed.
697
- // TODO(opt): Consider changing 'join' to report a change if !=
698
- // comparison is found expensive.
699
- if (SuccIt == BlockEntryStates.end () ||
700
- NewSuccEntryState != OldSuccEntryState) {
701
- BlockEntryStates[Successor] = NewSuccEntryState;
702
- Worklist.enqueueBlock (Successor);
703
- }
704
- }
705
- }
706
- }
707
-
708
- void dump () const {
709
- llvm::dbgs () << " ==========================================\n " ;
710
- llvm::dbgs () << " Dataflow results:\n " ;
711
- llvm::dbgs () << " ==========================================\n " ;
712
- const CFGBlock &B = Cfg.getExit ();
713
- getExitState (&B).dump (llvm::dbgs ());
714
- }
715
-
716
- LifetimeLattice getEntryState (const CFGBlock *B) const {
717
- auto It = BlockEntryStates.find (B);
718
- if (It != BlockEntryStates.end ()) {
719
- return It->second ;
720
- }
721
- return LifetimeLattice{};
722
- }
723
-
724
- LifetimeLattice getExitState (const CFGBlock *B) const {
725
- auto It = BlockExitStates.find (B);
726
- if (It != BlockExitStates.end ()) {
727
- return It->second ;
728
- }
729
- return LifetimeLattice{};
730
- }
731
- };
732
-
733
- // ========================================================================= //
734
- // TODO: Analysing dataflow results and error reporting.
494
+ // TODO: Run dataflow analysis to propagate loans, analyse and error reporting.
735
495
// ========================================================================= //
736
496
} // anonymous namespace
737
497
@@ -744,18 +504,5 @@ void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
744
504
FactGenerator FactGen (FactMgr, AC);
745
505
FactGen.run ();
746
506
DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr.dump (Cfg, AC));
747
-
748
- // / TODO(opt): Consider optimizing individual blocks before running the
749
- // / 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 single
752
- // / assignment.
753
- // / 2. Block-Local Loans: Origins of expressions are never read by other
754
- // / blocks; only Decls are visible. Therefore, loans in a block that
755
- // / never reach an Origin associated with a Decl can be safely dropped by
756
- // / the analysis.
757
- LifetimeDataflow Dataflow (Cfg, FactMgr, AC);
758
- Dataflow.run ();
759
- DEBUG_WITH_TYPE (" LifetimeDataflow" , Dataflow.dump ());
760
507
}
761
508
} // namespace clang
0 commit comments