@@ -527,6 +527,12 @@ struct AddressLoweringState {
527
527
// Handle moves from a phi's operand storage to the phi storage.
528
528
std::unique_ptr<PhiRewriter> phiRewriter;
529
529
530
+ // Projections created for uses, recorded in order to be sunk.
531
+ //
532
+ // Not all use projections are recorded in the valueStorageMap. It's not
533
+ // legal to reuse use projections for non-canonical users or for phis.
534
+ SmallVector<SILValue, 16 > useProjections;
535
+
530
536
AddressLoweringState (SILFunction *function, DominanceInfo *domInfo,
531
537
DeadEndBlocks *deBlocks)
532
538
: function(function), loweredFnConv(getLoweredFnConv(function)),
@@ -1159,6 +1165,8 @@ class OpaqueStorageAllocation {
1159
1165
// / jointly postdominate.
1160
1166
void finalizeOpaqueStorage ();
1161
1167
1168
+ void sinkProjections ();
1169
+
1162
1170
protected:
1163
1171
void allocateValue (SILValue value);
1164
1172
bool findProjectionIntoUseImpl (SILValue value,
@@ -1518,6 +1526,54 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
1518
1526
return alloc;
1519
1527
}
1520
1528
1529
+ namespace {
1530
+ enum class SinkResult {
1531
+ NoUsers,
1532
+ Unmoved,
1533
+ Moved,
1534
+ };
1535
+ SinkResult sinkToUses (SingleValueInstruction *svi, DominanceInfo *domInfo) {
1536
+ // Fast paths for 0 and 1 users.
1537
+
1538
+ if (svi->use_begin () == svi->use_end ()) {
1539
+ return SinkResult::NoUsers;
1540
+ }
1541
+
1542
+ if (auto *use = svi->getSingleUse ()) {
1543
+ auto *user = use->getUser ();
1544
+ if (user == svi->getNextInstruction ())
1545
+ return SinkResult::Unmoved;
1546
+ svi->moveBefore (user);
1547
+ return SinkResult::Moved;
1548
+ }
1549
+
1550
+ // Compute the lca and sink the instruction to before its first user or its
1551
+ // end if there are none.
1552
+
1553
+ SILBasicBlock *lca = domInfo->getLeastCommonAncestorOfUses (svi);
1554
+
1555
+ // The lca may contain a user. Look for the user to insert before it.
1556
+
1557
+ InstructionSet userSet (svi->getFunction ());
1558
+ for (auto user : svi->getUsers ()) {
1559
+ userSet.insert (user);
1560
+ }
1561
+
1562
+ for (auto &instruction : *lca) {
1563
+ if (userSet.contains (&instruction)) {
1564
+ if (&instruction == svi->getNextInstruction ())
1565
+ return SinkResult::Unmoved;
1566
+ svi->moveBefore (&instruction);
1567
+ return SinkResult::Moved;
1568
+ }
1569
+ }
1570
+
1571
+ // No user was found in the lca, move to before the end.
1572
+ svi->moveBefore (&lca->back ());
1573
+ return SinkResult::Moved;
1574
+ }
1575
+ } // end anonymous namespace
1576
+
1521
1577
void OpaqueStorageAllocation::finalizeOpaqueStorage () {
1522
1578
SmallVector<SILBasicBlock *, 4 > boundary;
1523
1579
for (auto maybeAlloc : allocs) {
@@ -1548,6 +1604,40 @@ void OpaqueStorageAllocation::finalizeOpaqueStorage() {
1548
1604
}
1549
1605
}
1550
1606
1607
+ void OpaqueStorageAllocation::sinkProjections () {
1608
+ // First, sink use projections to their uses. It's necessary to do this
1609
+ // separately from sinking projections in valueStorageMap because not all use
1610
+ // projections are recorded there (those for non-canonical users and those for
1611
+ // phis).
1612
+ //
1613
+ // Done in reverse order because outer projections are materialized first and
1614
+ // so appear in `useProjections` before inner projections, and inner
1615
+ // projections must be sunk first.
1616
+ for (auto projection : llvm::reverse (pass.useProjections )) {
1617
+ assert (projection);
1618
+ auto *svi = dyn_cast<SingleValueInstruction>(projection);
1619
+ assert (svi);
1620
+ auto sank = sinkToUses (svi, pass.domInfo );
1621
+ if (sank == SinkResult::NoUsers) {
1622
+ pass.deleter .forceDelete (svi);
1623
+ }
1624
+ }
1625
+
1626
+ // Second, sink all storage from the valueStorageMap.
1627
+ for (auto pair : llvm::reverse (pass.valueStorageMap )) {
1628
+ auto addr = pair.storage .getMaterializedAddress ();
1629
+ if (!pair.storage .isProjection ())
1630
+ continue ;
1631
+ auto *inst = dyn_cast<SingleValueInstruction>(addr);
1632
+ if (!inst)
1633
+ continue ;
1634
+ auto sank = sinkToUses (inst, pass.domInfo );
1635
+ if (sank == SinkResult::NoUsers) {
1636
+ pass.deleter .forceDelete (inst);
1637
+ }
1638
+ }
1639
+ }
1640
+
1551
1641
// ===----------------------------------------------------------------------===//
1552
1642
// AddressMaterialization
1553
1643
//
@@ -1607,6 +1697,8 @@ class AddressMaterialization {
1607
1697
SILValue elementValue, unsigned fieldIdx);
1608
1698
1609
1699
SILValue materializeProjectionIntoUse (Operand *operand, bool intoPhiOperand);
1700
+ SILValue materializeProjectionIntoUseImpl (Operand *operand,
1701
+ bool intoPhiOperand);
1610
1702
1611
1703
SILValue materializeComposingUser (SingleValueInstruction *user,
1612
1704
bool intoPhiOperand) {
@@ -1788,12 +1880,20 @@ SILValue AddressMaterialization::materializeTupleExtract(
1788
1880
elementValue->getType ().getAddressType ());
1789
1881
}
1790
1882
1883
+ SILValue
1884
+ AddressMaterialization::materializeProjectionIntoUse (Operand *operand,
1885
+ bool intoPhiOperand) {
1886
+ auto projection = materializeProjectionIntoUseImpl (operand, intoPhiOperand);
1887
+ pass.useProjections .push_back (projection);
1888
+ return projection;
1889
+ }
1890
+
1791
1891
// / Recursively materialize the address of a subobject that is a member of the
1792
1892
// / operand's user. The operand's user must be an aggregate struct, tuple, enum,
1793
1893
// / init_existential_value.
1794
1894
SILValue
1795
- AddressMaterialization::materializeProjectionIntoUse (Operand *operand,
1796
- bool intoPhiOperand) {
1895
+ AddressMaterialization::materializeProjectionIntoUseImpl (Operand *operand,
1896
+ bool intoPhiOperand) {
1797
1897
SILInstruction *user = operand->getUser ();
1798
1898
switch (user->getKind ()) {
1799
1899
default :
@@ -4285,6 +4385,8 @@ void AddressLowering::runOnFunction(SILFunction *function) {
4285
4385
4286
4386
allocator.finalizeOpaqueStorage ();
4287
4387
4388
+ allocator.sinkProjections ();
4389
+
4288
4390
deleteRewrittenInstructions (pass);
4289
4391
4290
4392
StackNesting::fixNesting (function);
0 commit comments