@@ -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,
@@ -1524,6 +1532,54 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
1524
1532
return alloc;
1525
1533
}
1526
1534
1535
+ namespace {
1536
+ enum class SinkResult {
1537
+ NoUsers,
1538
+ Unmoved,
1539
+ Moved,
1540
+ };
1541
+ SinkResult sinkToUses (SingleValueInstruction *svi, DominanceInfo *domInfo) {
1542
+ // Fast paths for 0 and 1 users.
1543
+
1544
+ if (svi->use_begin () == svi->use_end ()) {
1545
+ return SinkResult::NoUsers;
1546
+ }
1547
+
1548
+ if (auto *use = svi->getSingleUse ()) {
1549
+ auto *user = use->getUser ();
1550
+ if (user == svi->getNextInstruction ())
1551
+ return SinkResult::Unmoved;
1552
+ svi->moveBefore (user);
1553
+ return SinkResult::Moved;
1554
+ }
1555
+
1556
+ // Compute the lca and sink the instruction to before its first user or its
1557
+ // end if there are none.
1558
+
1559
+ SILBasicBlock *lca = domInfo->getLeastCommonAncestorOfUses (svi);
1560
+
1561
+ // The lca may contain a user. Look for the user to insert before it.
1562
+
1563
+ InstructionSet userSet (svi->getFunction ());
1564
+ for (auto user : svi->getUsers ()) {
1565
+ userSet.insert (user);
1566
+ }
1567
+
1568
+ for (auto &instruction : *lca) {
1569
+ if (userSet.contains (&instruction)) {
1570
+ if (&instruction == svi->getNextInstruction ())
1571
+ return SinkResult::Unmoved;
1572
+ svi->moveBefore (&instruction);
1573
+ return SinkResult::Moved;
1574
+ }
1575
+ }
1576
+
1577
+ // No user was found in the lca, move to before the end.
1578
+ svi->moveBefore (&lca->back ());
1579
+ return SinkResult::Moved;
1580
+ }
1581
+ } // end anonymous namespace
1582
+
1527
1583
void OpaqueStorageAllocation::finalizeOpaqueStorage () {
1528
1584
SmallVector<SILBasicBlock *, 4 > boundary;
1529
1585
for (auto maybeAlloc : allocs) {
@@ -1554,6 +1610,40 @@ void OpaqueStorageAllocation::finalizeOpaqueStorage() {
1554
1610
}
1555
1611
}
1556
1612
1613
+ void OpaqueStorageAllocation::sinkProjections () {
1614
+ // First, sink use projections to their uses. It's necessary to do this
1615
+ // separately from sinking projections in valueStorageMap because not all use
1616
+ // projections are recorded there (those for non-canonical users and those for
1617
+ // phis).
1618
+ //
1619
+ // Done in reverse order because outer projections are materialized first and
1620
+ // so appear in `useProjections` before inner projections, and inner
1621
+ // projections must be sunk first.
1622
+ for (auto projection : llvm::reverse (pass.useProjections )) {
1623
+ assert (projection);
1624
+ auto *svi = dyn_cast<SingleValueInstruction>(projection);
1625
+ assert (svi);
1626
+ auto sank = sinkToUses (svi, pass.domInfo );
1627
+ if (sank == SinkResult::NoUsers) {
1628
+ pass.deleter .forceDelete (svi);
1629
+ }
1630
+ }
1631
+
1632
+ // Second, sink all storage from the valueStorageMap.
1633
+ for (auto pair : llvm::reverse (pass.valueStorageMap )) {
1634
+ auto addr = pair.storage .getMaterializedAddress ();
1635
+ if (!pair.storage .isProjection ())
1636
+ continue ;
1637
+ auto *inst = dyn_cast<SingleValueInstruction>(addr);
1638
+ if (!inst)
1639
+ continue ;
1640
+ auto sank = sinkToUses (inst, pass.domInfo );
1641
+ if (sank == SinkResult::NoUsers) {
1642
+ pass.deleter .forceDelete (inst);
1643
+ }
1644
+ }
1645
+ }
1646
+
1557
1647
// ===----------------------------------------------------------------------===//
1558
1648
// AddressMaterialization
1559
1649
//
@@ -1613,6 +1703,8 @@ class AddressMaterialization {
1613
1703
SILValue elementValue, unsigned fieldIdx);
1614
1704
1615
1705
SILValue materializeProjectionIntoUse (Operand *operand, bool intoPhiOperand);
1706
+ SILValue materializeProjectionIntoUseImpl (Operand *operand,
1707
+ bool intoPhiOperand);
1616
1708
1617
1709
SILValue materializeComposingUser (SingleValueInstruction *user,
1618
1710
bool intoPhiOperand) {
@@ -1794,12 +1886,20 @@ SILValue AddressMaterialization::materializeTupleExtract(
1794
1886
elementValue->getType ().getAddressType ());
1795
1887
}
1796
1888
1889
+ SILValue
1890
+ AddressMaterialization::materializeProjectionIntoUse (Operand *operand,
1891
+ bool intoPhiOperand) {
1892
+ auto projection = materializeProjectionIntoUseImpl (operand, intoPhiOperand);
1893
+ pass.useProjections .push_back (projection);
1894
+ return projection;
1895
+ }
1896
+
1797
1897
// / Recursively materialize the address of a subobject that is a member of the
1798
1898
// / operand's user. The operand's user must be an aggregate struct, tuple, enum,
1799
1899
// / init_existential_value.
1800
1900
SILValue
1801
- AddressMaterialization::materializeProjectionIntoUse (Operand *operand,
1802
- bool intoPhiOperand) {
1901
+ AddressMaterialization::materializeProjectionIntoUseImpl (Operand *operand,
1902
+ bool intoPhiOperand) {
1803
1903
SILInstruction *user = operand->getUser ();
1804
1904
switch (user->getKind ()) {
1805
1905
default :
@@ -4291,6 +4391,8 @@ void AddressLowering::runOnFunction(SILFunction *function) {
4291
4391
4292
4392
allocator.finalizeOpaqueStorage ();
4293
4393
4394
+ allocator.sinkProjections ();
4395
+
4294
4396
deleteRewrittenInstructions (pass);
4295
4397
4296
4398
StackNesting::fixNesting (function);
0 commit comments