@@ -1586,6 +1586,223 @@ void swift::findTransitiveReborrowBaseValuePairs(
1586
1586
}
1587
1587
}
1588
1588
1589
+ // / Visit the phis in the same block as \p phi which are reborrows of a borrow
1590
+ // / of one of the values reaching \p phi.
1591
+ // /
1592
+ // / If the visitor returns false, stops visiting and returns false. Otherwise,
1593
+ // / returns true.
1594
+ // /
1595
+ // /
1596
+ // / When an owned value is passed as a phi argument, it is consumed. So any
1597
+ // / open scope borrowing that owned value must be ended no later than in that
1598
+ // / branch instruction. Either such a borrow scope is ended beforehand
1599
+ // / %lifetime = begin_borrow %value
1600
+ // / ...
1601
+ // / end_borrow %lifetime <-- borrow scope ended here
1602
+ // / br block(%value) <-- before consume
1603
+ // / or the borrow scope is ended in the same instruction as the owned value is
1604
+ // / consumed
1605
+ // / %lifetime = begin_borrow %value
1606
+ // / ...
1607
+ // / end_borrow %lifetime
1608
+ // / br block(%value, %lifetime) <-- borrow scope ended here
1609
+ // / <-- in same instruction as consume
1610
+ // / In particular, the following is invalid
1611
+ // / %lifetime = begin_borrow %value
1612
+ // / ...
1613
+ // / br block(%value)
1614
+ // / block(%value_2 : @owned):
1615
+ // / end_borrow %lifetime
1616
+ // / destroy_value %value_2
1617
+ // / because %lifetime was guaranteed by %value but value is consumed at
1618
+ // / `br two`.
1619
+ // /
1620
+ // / Similarly, when a guaranteed value is passed as a phi argument, its borrow
1621
+ // / scope ends and a new borrow scope is begun. And so any open nested borrow
1622
+ // / of the original outer borrow must be ended no later than in that branch
1623
+ // / instruction.
1624
+ // /
1625
+ // /
1626
+ // / Given an phi argument
1627
+ // / block(..., %value : @owned, ...)
1628
+ // / this function finds the adjacent reborrow phis
1629
+ // / block(..., %lifetime : @guaranteed, ..., %value : @owned, ...)
1630
+ // / ^^^^^^^^^^^^^^^^^^^^^^^
1631
+ // / one of whose reaching values is a borrow of a reaching value of %value.
1632
+ // /
1633
+ // / Finding these is more complicated than merely looking for guaranteed
1634
+ // / operands adjacent to the incoming operands to phi and which are borrows of
1635
+ // / the value consumed there. The reason is that they might not be borrows of
1636
+ // / that incoming value _directly_ but rather reborrows of some other reborrow
1637
+ // / if the incoming value is itself a phi argument:
1638
+ // / %lifetime = begin_borrow %value
1639
+ // / br one(%value, %lifetime)
1640
+ // / one(%value_1 : @owned, %lifetime_1 : @guaranteed)
1641
+ // / br two(%value_1, %lifetime_1)
1642
+ // / two(%value_2 : @owned, %lifetime_2 : @guaranteed)
1643
+ // / end_borrow %lifetime_2
1644
+ // / destroy_value %value_2
1645
+ // /
1646
+ // / When called with %value_2, \p visitor is invoked with both:
1647
+ // / two(%value_2 : @owned, %lifetime_2 : @guaranteed)
1648
+ // / ^^^^^^^^^^^^^^^^^^^^^^^^^
1649
+ bool swift::visitAdjacentReborrowsOfPhi (
1650
+ SILPhiArgument *phi, function_ref<bool (SILPhiArgument *)> visitor) {
1651
+ assert (phi->isPhi ());
1652
+
1653
+ // First, collect all the values that reach \p phi, that is:
1654
+ // - operands to the phi
1655
+ // - operands to phis which are operands to the phi
1656
+ // - and so forth.
1657
+ SmallPtrSet<SILValue, 8 > reachingValues;
1658
+ // At the same time, record all the phis in \p phi's phi web: the phis which
1659
+ // are transitively operands to \p phi. This is the subset of \p
1660
+ // reachingValues that are phis.
1661
+ SmallVector<SILPhiArgument *, 4 > phis;
1662
+ phi->visitTransitiveIncomingPhiOperands (
1663
+ [&](auto *phi, auto *operand) -> bool {
1664
+ phis.push_back (phi);
1665
+ reachingValues.insert (phi);
1666
+ reachingValues.insert (operand->get ());
1667
+ return true ;
1668
+ });
1669
+
1670
+ // Second, find all the guaranteed phis one of whose operands _could_ (by
1671
+ // dint of being adjacent to a phi in the phi web with the appropriate
1672
+ // ownership and type) be a reborrow of a reaching value of \p phi.
1673
+ SmallVector<SILPhiArgument *, 4 > candidates;
1674
+ for (auto *phi : phis) {
1675
+ SILBasicBlock *block = phi->getParentBlock ();
1676
+ for (auto *uncastAdjacent : block->getArguments ()) {
1677
+ auto *adjacent = cast<SILPhiArgument>(uncastAdjacent);
1678
+ if (adjacent == phi)
1679
+ continue ;
1680
+ if (adjacent->getType () != phi->getType ())
1681
+ continue ;
1682
+ if (adjacent->getOwnershipKind () != OwnershipKind::Guaranteed)
1683
+ continue ;
1684
+ candidates.push_back (adjacent);
1685
+ }
1686
+ }
1687
+
1688
+ // Finally, look through \p candidates to find those one of whose incoming
1689
+ // operands either
1690
+ // (1) borrow one of reaching values of \p phi
1691
+ // or (2) is itself a guaranteed phi which does so.
1692
+ // Because we may discover a reborrow R1 of type (1) after visiting another
1693
+ // R2 of type (2) which reborrows R1, we need to iterate to a fixed point.
1694
+ //
1695
+ // Record all the phis that we see which are borrows or reborrows of a
1696
+ // reaching value \p so that we can check for case (2) above.
1697
+ //
1698
+ // Visit those phis which are both reborrows of a reaching value AND are in
1699
+ // the same block as \phi.
1700
+ //
1701
+ // For example, given
1702
+ //
1703
+ // %lifetime = begin_borrow %value
1704
+ // br one(%value, %lifetime)
1705
+ // one(%value_1 : @owned, %lifetime_1 : @guaranteed)
1706
+ // br two(%value_1, %lifetime_1)
1707
+ // two(%value_2 : @owned, %lifetime_2 : @guaranteed)
1708
+ // end_borrow %lifetime_2
1709
+ // destroy_value %value_2
1710
+ //
1711
+ // when visiting the reborrow phis adjacent to %value_2, The following steps
1712
+ // would be taken:
1713
+ //
1714
+ // (1) Look at the first candidate:
1715
+ // two(%value_2 : @owned, %lifetime_2 : @guaranteed)
1716
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^
1717
+ // but see that its one incoming value
1718
+ // br two(%value_1, %lifetime_1)
1719
+ // ^^^^^^^^^^^
1720
+ // although a phi argument itself, is not known (yet!) to be a reborrow phi.
1721
+ // So the first candidate is NOT (yet!) added to reborrowPhis.
1722
+ //
1723
+ // (2) Look at the second candidate:
1724
+ // one(%value_1 : @owned, %lifetime_1 : @guaranteed)
1725
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^
1726
+ // and see that one of its incoming values
1727
+ // br one(%value, %lifetime)
1728
+ // ^^^^^^^^^
1729
+ // is a borrow
1730
+ // %lifetime = begin_borrow %value
1731
+ // of %value, one of the values reaching %value_2.
1732
+ // So the second candidate IS added to reborrowPhis.
1733
+ // AND changed is set to true, so we will repeat the outer loop.
1734
+ // But this candidate is not adjacent to our phi %value_2, so it is not
1735
+ // visited.
1736
+ //
1737
+ // (4.5) Changed is true: repeat the outer loop. Set changed to false.
1738
+ //
1739
+ // (3) Look at the first candidate:
1740
+ // two(%value_2 : @owned, %lifetime_2 : @guaranteed)
1741
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^
1742
+ // and see that one of its incoming values
1743
+ // br two(%value_1, %lifetime_1)
1744
+ // ^^^^^^^^^^^
1745
+ // is itself a phi
1746
+ // one(%value_1 : @owned, %lifetime_1 : @guaranteed)
1747
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^
1748
+ // which was added to reborrowPhis in (2).
1749
+ // So the first candidate IS added to reborrowPhis.
1750
+ // AND changed is set to true.
1751
+ // ALSO, see that the first candidate IS adjacent to our phi %value_2, so our
1752
+ // visitor is invoked with the first candidate.
1753
+ //
1754
+ // (4) Look at the second candidate.
1755
+ // See that it is already a member of reborrowPhis.
1756
+ //
1757
+ // (4.5) Changed is true: repeat the outer loop. Set changed to false.
1758
+ //
1759
+ // (5) Look at the first candidate.
1760
+ // See that it is already a member of reborrowPhis.
1761
+ //
1762
+ // (6) Look at the second candidate.
1763
+ // See that it is already a member of reborrowPhis.
1764
+ //
1765
+ // (6.5) Changed is false: exit the outer loop.
1766
+ bool changed = false ;
1767
+ SmallSetVector<SILPhiArgument *, 4 > reborrowPhis;
1768
+ do {
1769
+ changed = false ;
1770
+ for (auto *candidate : candidates) {
1771
+ if (reborrowPhis.contains (candidate))
1772
+ continue ;
1773
+ auto success = candidate->visitIncomingPhiOperands ([&](auto *operand) {
1774
+ // If the value being reborrowed is itself a reborrow of a value
1775
+ // reaching \p phi, then visit it.
1776
+ SILPhiArgument *forwarded;
1777
+ if ((forwarded = dyn_cast<SILPhiArgument>(operand->get ()))) {
1778
+ if (!reborrowPhis.contains (forwarded))
1779
+ return true ;
1780
+ changed = true ;
1781
+ reborrowPhis.insert (candidate);
1782
+ if (candidate->getParentBlock () == phi->getParentBlock ())
1783
+ return visitor (candidate);
1784
+ return true ;
1785
+ }
1786
+ BeginBorrowInst *bbi;
1787
+ if (!(bbi = dyn_cast<BeginBorrowInst>(operand->get ())))
1788
+ return true ;
1789
+ auto borrowee = bbi->getOperand ();
1790
+ if (!reachingValues.contains (borrowee))
1791
+ return true ;
1792
+ changed = true ;
1793
+ reborrowPhis.insert (candidate);
1794
+ if (candidate->getParentBlock () == phi->getParentBlock ())
1795
+ return visitor (candidate);
1796
+ return true ;
1797
+ });
1798
+ if (!success)
1799
+ return false ;
1800
+ }
1801
+ } while (changed);
1802
+
1803
+ return true ;
1804
+ }
1805
+
1589
1806
void swift::visitTransitiveEndBorrows (
1590
1807
SILValue value,
1591
1808
function_ref<void (EndBorrowInst *)> visitEndBorrow) {
0 commit comments