@@ -522,6 +522,12 @@ struct AddressLoweringState {
522
522
// Handle moves from a phi's operand storage to the phi storage.
523
523
std::unique_ptr<PhiRewriter> phiRewriter;
524
524
525
+ // Projections created for uses, recorded in order to be sunk.
526
+ //
527
+ // Not all use projections are recorded in the valueStorageMap. It's not
528
+ // legal to reuse use projections for non-canonical users or for phis.
529
+ SmallVector<SILValue, 16 > useProjections;
530
+
525
531
AddressLoweringState (SILFunction *function, DominanceInfo *domInfo,
526
532
DeadEndBlocks *deBlocks)
527
533
: function(function), loweredFnConv(getLoweredFnConv(function)),
@@ -1147,6 +1153,8 @@ class OpaqueStorageAllocation {
1147
1153
// / jointly postdominate.
1148
1154
void finalizeOpaqueStorage ();
1149
1155
1156
+ void sinkProjections ();
1157
+
1150
1158
protected:
1151
1159
void allocateValue (SILValue value);
1152
1160
bool findProjectionIntoUseImpl (SILValue value,
@@ -1501,6 +1509,54 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
1501
1509
return alloc;
1502
1510
}
1503
1511
1512
+ namespace {
1513
+ enum class SinkResult {
1514
+ NoUsers,
1515
+ Unmoved,
1516
+ Moved,
1517
+ };
1518
+ SinkResult sinkToUses (SingleValueInstruction *svi, DominanceInfo *domInfo) {
1519
+ // Fast paths for 0 and 1 users.
1520
+
1521
+ if (svi->use_begin () == svi->use_end ()) {
1522
+ return SinkResult::NoUsers;
1523
+ }
1524
+
1525
+ if (auto *use = svi->getSingleUse ()) {
1526
+ auto *user = use->getUser ();
1527
+ if (user == svi->getNextInstruction ())
1528
+ return SinkResult::Unmoved;
1529
+ svi->moveBefore (user);
1530
+ return SinkResult::Moved;
1531
+ }
1532
+
1533
+ // Compute the lca and sink the instruction to before its first user or its
1534
+ // end if there are none.
1535
+
1536
+ SILBasicBlock *lca = domInfo->getLeastCommonAncestorOfUses (svi);
1537
+
1538
+ // The lca may contain a user. Look for the user to insert before it.
1539
+
1540
+ InstructionSet userSet (svi->getFunction ());
1541
+ for (auto user : svi->getUsers ()) {
1542
+ userSet.insert (user);
1543
+ }
1544
+
1545
+ for (auto &instruction : *lca) {
1546
+ if (userSet.contains (&instruction)) {
1547
+ if (&instruction == svi->getNextInstruction ())
1548
+ return SinkResult::Unmoved;
1549
+ svi->moveBefore (&instruction);
1550
+ return SinkResult::Moved;
1551
+ }
1552
+ }
1553
+
1554
+ // No user was found in the lca, move to before the end.
1555
+ svi->moveBefore (&lca->back ());
1556
+ return SinkResult::Moved;
1557
+ }
1558
+ } // end anonymous namespace
1559
+
1504
1560
void OpaqueStorageAllocation::finalizeOpaqueStorage () {
1505
1561
SmallVector<SILBasicBlock *, 4 > boundary;
1506
1562
for (auto maybeAlloc : allocs) {
@@ -1531,6 +1587,40 @@ void OpaqueStorageAllocation::finalizeOpaqueStorage() {
1531
1587
}
1532
1588
}
1533
1589
1590
+ void OpaqueStorageAllocation::sinkProjections () {
1591
+ // First, sink use projections to their uses. It's necessary to do this
1592
+ // separately from sinking projections in valueStorageMap because not all use
1593
+ // projections are recorded there (those for non-canonical users and those for
1594
+ // phis).
1595
+ //
1596
+ // Done in reverse order because outer projections are materialized first and
1597
+ // so appear in `useProjections` before inner projections, and inner
1598
+ // projections must be sunk first.
1599
+ for (auto projection : llvm::reverse (pass.useProjections )) {
1600
+ assert (projection);
1601
+ auto *svi = dyn_cast<SingleValueInstruction>(projection);
1602
+ assert (svi);
1603
+ auto sank = sinkToUses (svi, pass.domInfo );
1604
+ if (sank == SinkResult::NoUsers) {
1605
+ pass.deleter .forceDelete (svi);
1606
+ }
1607
+ }
1608
+
1609
+ // Second, sink all storage from the valueStorageMap.
1610
+ for (auto pair : llvm::reverse (pass.valueStorageMap )) {
1611
+ auto addr = pair.storage .getMaterializedAddress ();
1612
+ if (!pair.storage .isProjection ())
1613
+ continue ;
1614
+ auto *inst = dyn_cast<SingleValueInstruction>(addr);
1615
+ if (!inst)
1616
+ continue ;
1617
+ auto sank = sinkToUses (inst, pass.domInfo );
1618
+ if (sank == SinkResult::NoUsers) {
1619
+ pass.deleter .forceDelete (inst);
1620
+ }
1621
+ }
1622
+ }
1623
+
1534
1624
// ===----------------------------------------------------------------------===//
1535
1625
// AddressMaterialization
1536
1626
//
@@ -1590,6 +1680,8 @@ class AddressMaterialization {
1590
1680
SILValue elementValue, unsigned fieldIdx);
1591
1681
1592
1682
SILValue materializeProjectionIntoUse (Operand *operand, bool intoPhiOperand);
1683
+ SILValue materializeProjectionIntoUseImpl (Operand *operand,
1684
+ bool intoPhiOperand);
1593
1685
1594
1686
SILValue materializeComposingUser (SingleValueInstruction *user,
1595
1687
bool intoPhiOperand) {
@@ -1771,12 +1863,20 @@ SILValue AddressMaterialization::materializeTupleExtract(
1771
1863
elementValue->getType ().getAddressType ());
1772
1864
}
1773
1865
1866
+ SILValue
1867
+ AddressMaterialization::materializeProjectionIntoUse (Operand *operand,
1868
+ bool intoPhiOperand) {
1869
+ auto projection = materializeProjectionIntoUseImpl (operand, intoPhiOperand);
1870
+ pass.useProjections .push_back (projection);
1871
+ return projection;
1872
+ }
1873
+
1774
1874
// / Recursively materialize the address of a subobject that is a member of the
1775
1875
// / operand's user. The operand's user must be an aggregate struct, tuple, enum,
1776
1876
// / init_existential_value.
1777
1877
SILValue
1778
- AddressMaterialization::materializeProjectionIntoUse (Operand *operand,
1779
- bool intoPhiOperand) {
1878
+ AddressMaterialization::materializeProjectionIntoUseImpl (Operand *operand,
1879
+ bool intoPhiOperand) {
1780
1880
SILInstruction *user = operand->getUser ();
1781
1881
switch (user->getKind ()) {
1782
1882
default :
@@ -4183,6 +4283,8 @@ void AddressLowering::runOnFunction(SILFunction *function) {
4183
4283
4184
4284
allocator.finalizeOpaqueStorage ();
4185
4285
4286
+ allocator.sinkProjections ();
4287
+
4186
4288
deleteRewrittenInstructions (pass);
4187
4289
4188
4290
StackNesting::fixNesting (function);
0 commit comments