|
94 | 94 | #ifndef SWIFT_SILOPTIMIZER_UTILS_PRUNEDLIVENESS_H
|
95 | 95 | #define SWIFT_SILOPTIMIZER_UTILS_PRUNEDLIVENESS_H
|
96 | 96 |
|
| 97 | +#include "swift/AST/TypeExpansionContext.h" |
97 | 98 | #include "swift/SIL/SILBasicBlock.h"
|
| 99 | +#include "swift/SIL/SILFunction.h" |
98 | 100 | #include "llvm/ADT/MapVector.h"
|
99 | 101 | #include "llvm/ADT/PointerIntPair.h"
|
100 | 102 | #include "llvm/ADT/SmallVector.h"
|
@@ -236,6 +238,8 @@ class PrunedLiveBlocks {
|
236 | 238 | assert(!discoveredBlocks || discoveredBlocks->empty());
|
237 | 239 | }
|
238 | 240 |
|
| 241 | + unsigned getNumBitsToTrack() const { return numBitsToTrack; } |
| 242 | + |
239 | 243 | bool empty() const { return liveBlocks.empty(); }
|
240 | 244 |
|
241 | 245 | void clear() {
|
@@ -290,7 +294,6 @@ class PrunedLiveBlocks {
|
290 | 294 | return;
|
291 | 295 | }
|
292 | 296 |
|
293 |
| - assert(liveBlockIter->second.size() == 1); |
294 | 297 | liveBlockIter->second.getLiveness(startBitNo, endBitNo, foundLivenessInfo);
|
295 | 298 | }
|
296 | 299 |
|
@@ -553,6 +556,289 @@ struct PrunedLivenessBoundary {
|
553 | 556 | ArrayRef<SILBasicBlock *> postDomBlocks);
|
554 | 557 | };
|
555 | 558 |
|
| 559 | +/// Given a type T and a descendent field F in T's type tree, then the |
| 560 | +/// sub-element number of F is the first leaf element of the type tree in its |
| 561 | +/// linearized representation. |
| 562 | +struct SubElementNumber { |
| 563 | + unsigned number; |
| 564 | + |
| 565 | + SubElementNumber(unsigned number) : number(number) {} |
| 566 | + |
| 567 | + /// Given an arbitrary projection \p projectionFromRoot from the \p |
| 568 | + /// rootAddress, compute the sub element number for that \p SILValue. The sub |
| 569 | + /// element number of a type T is always the index of its first leaf node |
| 570 | + /// descendent in the type tree. |
| 571 | + /// |
| 572 | + /// DISCUSSION: This works for non-leaf types in the type tree as well as |
| 573 | + /// normal leaf elements. It is the index of the first leaf element that is a |
| 574 | + /// sub element of the root SILType that this projection will effect. The rest |
| 575 | + /// of the elements effected can be found by computing the number of leaf sub |
| 576 | + /// elements of \p projectionFromRoot's type and adding this to the result of |
| 577 | + /// this function. |
| 578 | + /// |
| 579 | + /// \returns None if we didn't know how to compute sub-element for this |
| 580 | + /// projection. |
| 581 | + static Optional<SubElementNumber> compute(SILValue projectionFromRoot, |
| 582 | + SILValue root); |
| 583 | + |
| 584 | + operator unsigned() const { return number; } |
| 585 | +}; |
| 586 | + |
| 587 | +/// Given a type T, this is the number of leaf field types in T's type tree. A |
| 588 | +/// leaf field type is a descendent field of T that does not have any |
| 589 | +/// descendent's itself. |
| 590 | +struct TypeSubElementCount { |
| 591 | + unsigned number; |
| 592 | + |
| 593 | + TypeSubElementCount(unsigned number) : number(number) {} |
| 594 | + |
| 595 | + /// Given a type \p type, compute the total number of leaf sub-elements of \p |
| 596 | + /// type in the type tree. |
| 597 | + /// |
| 598 | + /// Some interesting properties of this computation: |
| 599 | + /// |
| 600 | + /// 1. When applied to the root type, this equals the total number of bits of |
| 601 | + /// liveness that we track. |
| 602 | + /// |
| 603 | + /// 2. When applied to a field type F of the type tree for a type T, |
| 604 | + /// computeNumLeafSubElements(F) when added to F's start sub element number |
| 605 | + /// will go to the next sibling node in the type tree, walking up the tree and |
| 606 | + /// attempting to find siblings if no further siblings exist. |
| 607 | + TypeSubElementCount(SILType type, SILModule &mod, |
| 608 | + TypeExpansionContext context); |
| 609 | + |
| 610 | + TypeSubElementCount(SILValue value) |
| 611 | + : TypeSubElementCount(value->getType(), *value->getModule(), |
| 612 | + TypeExpansionContext(*value->getFunction())) {} |
| 613 | + |
| 614 | + operator unsigned() const { return number; } |
| 615 | +}; |
| 616 | + |
| 617 | +/// A span of leaf elements in the sub-element break down of the linearization |
| 618 | +/// of the type tree of a type T. |
| 619 | +struct TypeTreeLeafTypeRange { |
| 620 | + SubElementNumber startEltOffset; |
| 621 | + SubElementNumber endEltOffset; |
| 622 | + |
| 623 | + /// The leaf type range for the entire type tree. |
| 624 | + TypeTreeLeafTypeRange(SILValue rootAddress) |
| 625 | + : startEltOffset(0), endEltOffset(TypeSubElementCount(rootAddress)) {} |
| 626 | + |
| 627 | + /// The leaf type sub-range of the type tree of \p rootAddress, consisting of |
| 628 | + /// \p projectedAddress and all of \p projectedAddress's descendent fields in |
| 629 | + /// the type tree. |
| 630 | + TypeTreeLeafTypeRange(SILValue projectedAddress, SILValue rootAddress) |
| 631 | + : startEltOffset( |
| 632 | + *SubElementNumber::compute(projectedAddress, rootAddress)), |
| 633 | + endEltOffset(startEltOffset + TypeSubElementCount(projectedAddress)) {} |
| 634 | + |
| 635 | + /// Is the given leaf type specified by \p singleLeafElementNumber apart of |
| 636 | + /// our \p range of leaf type values in the our larger type. |
| 637 | + bool contains(SubElementNumber singleLeafElementNumber) const { |
| 638 | + return startEltOffset <= singleLeafElementNumber && |
| 639 | + singleLeafElementNumber < endEltOffset; |
| 640 | + } |
| 641 | + |
| 642 | + /// Returns true if either of this overlaps at all with the given range. |
| 643 | + bool contains(TypeTreeLeafTypeRange range) const { |
| 644 | + if (startEltOffset <= range.startEltOffset && |
| 645 | + range.startEltOffset < endEltOffset) |
| 646 | + return true; |
| 647 | + |
| 648 | + // If our start and end offsets, our extent is only 1 and we know that our |
| 649 | + // value |
| 650 | + unsigned rangeLastElt = range.endEltOffset - 1; |
| 651 | + if (range.startEltOffset == rangeLastElt) |
| 652 | + return false; |
| 653 | + |
| 654 | + // Othrwise, see if endEltOffset - 1 is within the range. |
| 655 | + return startEltOffset <= rangeLastElt && rangeLastElt < endEltOffset; |
| 656 | + } |
| 657 | +}; |
| 658 | + |
| 659 | +/// This is exactly like pruned liveness except that instead of tracking a |
| 660 | +/// single bit of liveness, it tracks multiple bits of liveness for leaf type |
| 661 | +/// tree nodes of an allocation one is calculating pruned liveness for. |
| 662 | +/// |
| 663 | +/// DISCUSSION: One can view a type T as a tree with recursively each field F of |
| 664 | +/// the type T being a child of T in the tree. We say recursively since the tree |
| 665 | +/// unfolds for F and its children as well. |
| 666 | +class FieldSensitiveAddressPrunedLiveness { |
| 667 | + PrunedLiveBlocks liveBlocks; |
| 668 | + |
| 669 | + struct InterestingUser { |
| 670 | + TypeTreeLeafTypeRange subEltSpan; |
| 671 | + bool isConsuming; |
| 672 | + |
| 673 | + InterestingUser(TypeTreeLeafTypeRange subEltSpan, bool isConsuming) |
| 674 | + : subEltSpan(subEltSpan), isConsuming(isConsuming) {} |
| 675 | + |
| 676 | + InterestingUser &operator&=(bool otherValue) { |
| 677 | + isConsuming &= otherValue; |
| 678 | + return *this; |
| 679 | + } |
| 680 | + }; |
| 681 | + |
| 682 | + /// Map all "interesting" user instructions in this def's live range to a pair |
| 683 | + /// consisting of the SILValue that it uses and a flag indicating whether they |
| 684 | + /// must end the lifetime. |
| 685 | + /// |
| 686 | + /// Lifetime-ending users are always on the boundary so are always |
| 687 | + /// interesting. |
| 688 | + /// |
| 689 | + /// Non-lifetime-ending uses within a LiveWithin block are interesting because |
| 690 | + /// they may be the last use in the block. |
| 691 | + /// |
| 692 | + /// Non-lifetime-ending within a LiveOut block are uninteresting. |
| 693 | + llvm::SmallMapVector<SILInstruction *, InterestingUser, 8> users; |
| 694 | + |
| 695 | + /// The root address of our type tree. |
| 696 | + SILValue rootAddress; |
| 697 | + |
| 698 | +public: |
| 699 | + FieldSensitiveAddressPrunedLiveness( |
| 700 | + SILFunction *fn, SILValue rootValue, |
| 701 | + SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr) |
| 702 | + : liveBlocks(TypeSubElementCount(rootValue), discoveredBlocks), |
| 703 | + rootAddress(rootValue) {} |
| 704 | + |
| 705 | + bool empty() const { |
| 706 | + assert(!liveBlocks.empty() || users.empty()); |
| 707 | + return liveBlocks.empty(); |
| 708 | + } |
| 709 | + |
| 710 | + void clear() { |
| 711 | + liveBlocks.clear(); |
| 712 | + users.clear(); |
| 713 | + } |
| 714 | + |
| 715 | + SILValue getRootAddress() const { return rootAddress; } |
| 716 | + |
| 717 | + unsigned numLiveBlocks() const { return liveBlocks.numLiveBlocks(); } |
| 718 | + |
| 719 | + /// If the constructor was provided with a vector to populate, then this |
| 720 | + /// returns the list of all live blocks with no duplicates. |
| 721 | + ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const { |
| 722 | + return liveBlocks.getDiscoveredBlocks(); |
| 723 | + } |
| 724 | + |
| 725 | + using UserRange = |
| 726 | + iterator_range<const std::pair<SILInstruction *, InterestingUser> *>; |
| 727 | + UserRange getAllUsers() const { |
| 728 | + return llvm::make_range(users.begin(), users.end()); |
| 729 | + } |
| 730 | + |
| 731 | + using UserBlockRange = TransformRange< |
| 732 | + UserRange, function_ref<SILBasicBlock *( |
| 733 | + const std::pair<SILInstruction *, InterestingUser> &)>>; |
| 734 | + UserBlockRange getAllUserBlocks() const { |
| 735 | + function_ref<SILBasicBlock *( |
| 736 | + const std::pair<SILInstruction *, InterestingUser> &)> |
| 737 | + op; |
| 738 | + op = [](const std::pair<SILInstruction *, InterestingUser> &pair) |
| 739 | + -> SILBasicBlock * { return pair.first->getParent(); }; |
| 740 | + return UserBlockRange(getAllUsers(), op); |
| 741 | + } |
| 742 | + |
| 743 | + void initializeDefBlock(SILBasicBlock *defBB, TypeTreeLeafTypeRange span) { |
| 744 | + liveBlocks.initializeDefBlock(defBB, span.startEltOffset, |
| 745 | + span.endEltOffset); |
| 746 | + } |
| 747 | + |
| 748 | + /// For flexibility, \p lifetimeEnding is provided by the |
| 749 | + /// caller. PrunedLiveness makes no assumptions about the def-use |
| 750 | + /// relationships that generate liveness. For example, use->isLifetimeEnding() |
| 751 | + /// cannot distinguish the end of the borrow scope that defines this extended |
| 752 | + /// live range vs. a nested borrow scope within the extended live range. |
| 753 | + /// |
| 754 | + /// Also for flexibility, \p affectedAddress must be a derived projection from |
| 755 | + /// the base that \p user is affecting. |
| 756 | + void updateForUse(SILInstruction *user, TypeTreeLeafTypeRange span, |
| 757 | + bool lifetimeEnding); |
| 758 | + |
| 759 | + void getBlockLiveness( |
| 760 | + SILBasicBlock *bb, TypeTreeLeafTypeRange span, |
| 761 | + SmallVectorImpl<PrunedLiveBlocks::IsLive> &resultingFoundLiveness) const { |
| 762 | + liveBlocks.getBlockLiveness(bb, span.startEltOffset, span.endEltOffset, |
| 763 | + resultingFoundLiveness); |
| 764 | + } |
| 765 | + |
| 766 | + /// Return the liveness for this specific sub-element of our root value. |
| 767 | + PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb, |
| 768 | + unsigned subElementNumber) const { |
| 769 | + SmallVector<PrunedLiveBlocks::IsLive, 1> isLive; |
| 770 | + liveBlocks.getBlockLiveness(bb, subElementNumber, subElementNumber + 1, |
| 771 | + isLive); |
| 772 | + return isLive[0]; |
| 773 | + } |
| 774 | + |
| 775 | + void getBlockLiveness( |
| 776 | + SILBasicBlock *bb, |
| 777 | + SmallVectorImpl<PrunedLiveBlocks::IsLive> &foundLiveness) const { |
| 778 | + liveBlocks.getBlockLiveness(bb, 0, liveBlocks.getNumBitsToTrack(), |
| 779 | + foundLiveness); |
| 780 | + } |
| 781 | + |
| 782 | + enum IsInterestingUser { NonUser, NonLifetimeEndingUse, LifetimeEndingUse }; |
| 783 | + |
| 784 | + /// Return a result indicating whether the given user was identified as an |
| 785 | + /// interesting use of the current def and whether it ends the lifetime. |
| 786 | + std::pair<IsInterestingUser, Optional<TypeTreeLeafTypeRange>> |
| 787 | + isInterestingUser(SILInstruction *user) const { |
| 788 | + auto useIter = users.find(user); |
| 789 | + if (useIter == users.end()) |
| 790 | + return {NonUser, None}; |
| 791 | + auto isInteresting = |
| 792 | + useIter->second.isConsuming ? LifetimeEndingUse : NonLifetimeEndingUse; |
| 793 | + return {isInteresting, useIter->second.subEltSpan}; |
| 794 | + } |
| 795 | + |
| 796 | + unsigned getNumSubElements() const { return liveBlocks.getNumBitsToTrack(); } |
| 797 | + |
| 798 | + /// Return true if \p inst occurs before the liveness boundary. Used when the |
| 799 | + /// client already knows that inst occurs after the start of liveness. |
| 800 | + void isWithinBoundary(SILInstruction *inst, SmallBitVector &outVector) const; |
| 801 | +}; |
| 802 | + |
| 803 | +/// Record the last use points and CFG edges that form the boundary of |
| 804 | +/// FieldSensitiveAddressPrunedLiveness. It does this on a per type tree leaf |
| 805 | +/// node basis. |
| 806 | +struct FieldSensitiveAddressPrunedLivenessBoundary { |
| 807 | + /// The list of last users and an associated SILValue that is the address that |
| 808 | + /// is being used. The address can be used to determine the start sub element |
| 809 | + /// number of the user in the type tree and the end sub element number. |
| 810 | + /// |
| 811 | + /// TODO (MG): If we don't eventually need to store the SILValue here (I am |
| 812 | + /// not sure yet...), just store a tuple with the start/end sub element |
| 813 | + /// number. |
| 814 | + SmallVector<std::tuple<SILInstruction *, TypeTreeLeafTypeRange>, 8> lastUsers; |
| 815 | + |
| 816 | + /// Blocks where the value was live out but had a successor that was dead. |
| 817 | + SmallVector<SILBasicBlock *, 8> boundaryEdges; |
| 818 | + |
| 819 | + void clear() { |
| 820 | + lastUsers.clear(); |
| 821 | + boundaryEdges.clear(); |
| 822 | + } |
| 823 | + |
| 824 | + /// Compute the boundary from the blocks discovered during liveness analysis. |
| 825 | + /// |
| 826 | + /// Precondition: \p liveness.getDiscoveredBlocks() is a valid list of all |
| 827 | + /// live blocks with no duplicates. |
| 828 | + /// |
| 829 | + /// The computed boundary will completely post-dominate, including dead end |
| 830 | + /// paths. The client should query DeadEndBlocks to ignore those dead end |
| 831 | + /// paths. |
| 832 | + void compute(const FieldSensitiveAddressPrunedLiveness &liveness); |
| 833 | + |
| 834 | +private: |
| 835 | + void |
| 836 | + findLastUserInBlock(SILBasicBlock *bb, |
| 837 | + FieldSensitiveAddressPrunedLivenessBoundary &boundary, |
| 838 | + const FieldSensitiveAddressPrunedLiveness &liveness, |
| 839 | + unsigned subElementNumber); |
| 840 | +}; |
| 841 | + |
556 | 842 | } // namespace swift
|
557 | 843 |
|
558 | 844 | #endif
|
0 commit comments