Skip to content

Commit a27b390

Browse files
committed
[AddressLowering] Sink projections to uses.
Visit the tree of projections in post-order, sinking each to the lowest position in the least common ancestor of all uses.
1 parent 604150c commit a27b390

File tree

5 files changed

+189
-81
lines changed

5 files changed

+189
-81
lines changed

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,12 @@ struct AddressLoweringState {
527527
// Handle moves from a phi's operand storage to the phi storage.
528528
std::unique_ptr<PhiRewriter> phiRewriter;
529529

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+
530536
AddressLoweringState(SILFunction *function, DominanceInfo *domInfo,
531537
DeadEndBlocks *deBlocks)
532538
: function(function), loweredFnConv(getLoweredFnConv(function)),
@@ -1159,6 +1165,8 @@ class OpaqueStorageAllocation {
11591165
/// jointly postdominate.
11601166
void finalizeOpaqueStorage();
11611167

1168+
void sinkProjections();
1169+
11621170
protected:
11631171
void allocateValue(SILValue value);
11641172
bool findProjectionIntoUseImpl(SILValue value,
@@ -1524,6 +1532,54 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
15241532
return alloc;
15251533
}
15261534

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+
15271583
void OpaqueStorageAllocation::finalizeOpaqueStorage() {
15281584
SmallVector<SILBasicBlock *, 4> boundary;
15291585
for (auto maybeAlloc : allocs) {
@@ -1554,6 +1610,40 @@ void OpaqueStorageAllocation::finalizeOpaqueStorage() {
15541610
}
15551611
}
15561612

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+
15571647
//===----------------------------------------------------------------------===//
15581648
// AddressMaterialization
15591649
//
@@ -1613,6 +1703,8 @@ class AddressMaterialization {
16131703
SILValue elementValue, unsigned fieldIdx);
16141704

16151705
SILValue materializeProjectionIntoUse(Operand *operand, bool intoPhiOperand);
1706+
SILValue materializeProjectionIntoUseImpl(Operand *operand,
1707+
bool intoPhiOperand);
16161708

16171709
SILValue materializeComposingUser(SingleValueInstruction *user,
16181710
bool intoPhiOperand) {
@@ -1794,12 +1886,20 @@ SILValue AddressMaterialization::materializeTupleExtract(
17941886
elementValue->getType().getAddressType());
17951887
}
17961888

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+
17971897
/// Recursively materialize the address of a subobject that is a member of the
17981898
/// operand's user. The operand's user must be an aggregate struct, tuple, enum,
17991899
/// init_existential_value.
18001900
SILValue
1801-
AddressMaterialization::materializeProjectionIntoUse(Operand *operand,
1802-
bool intoPhiOperand) {
1901+
AddressMaterialization::materializeProjectionIntoUseImpl(Operand *operand,
1902+
bool intoPhiOperand) {
18031903
SILInstruction *user = operand->getUser();
18041904
switch (user->getKind()) {
18051905
default:
@@ -4291,6 +4391,8 @@ void AddressLowering::runOnFunction(SILFunction *function) {
42914391

42924392
allocator.finalizeOpaqueStorage();
42934393

4394+
allocator.sinkProjections();
4395+
42944396
deleteRewrittenInstructions(pass);
42954397

42964398
StackNesting::fixNesting(function);

lib/SILOptimizer/Mandatory/AddressLowering.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,12 @@ class ValueStorageMap {
317317
return hashIter->second;
318318
}
319319

320+
ValueStoragePair &operator[](uint32_t index) { return valueVector[index]; }
321+
322+
ValueStoragePair const &operator[](uint32_t index) const {
323+
return valueVector[index];
324+
}
325+
320326
ValueStorage &getStorage(SILValue value) {
321327
return valueVector[getOrdinal(value)].storage;
322328
}

0 commit comments

Comments
 (0)