Skip to content

Commit 5a35479

Browse files
committed
Move around utilities, move isWriteAllocation outside the class
1 parent 9a6ad6f commit 5a35479

File tree

1 file changed

+183
-186
lines changed

1 file changed

+183
-186
lines changed

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 183 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,189 @@ static void collectLoads(SILInstruction *i,
374374
}
375375
}
376376

377+
/// Returns true if \p I is an address of a LoadInst, skipping struct and
378+
/// tuple address projections. Sets \p singleBlock to null if the load (or
379+
/// it's address is not in \p singleBlock.
380+
/// This function looks for these patterns:
381+
/// 1. (load %ASI)
382+
/// 2. (load (struct_element_addr/tuple_element_addr/unchecked_addr_cast %ASI))
383+
static bool isAddressForLoad(SILInstruction *load, SILBasicBlock *&singleBlock,
384+
bool &involvesUntakableProjection) {
385+
if (auto *li = dyn_cast<LoadInst>(load)) {
386+
// SILMem2Reg is disabled when we find a load [take] of an untakable
387+
// projection. See below for further discussion.
388+
if (involvesUntakableProjection &&
389+
li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
390+
return false;
391+
}
392+
return true;
393+
}
394+
395+
if (isa<LoadBorrowInst>(load)) {
396+
if (involvesUntakableProjection) {
397+
return false;
398+
}
399+
return true;
400+
}
401+
402+
if (!isa<UncheckedAddrCastInst>(load) && !isa<StructElementAddrInst>(load) &&
403+
!isa<TupleElementAddrInst>(load))
404+
return false;
405+
406+
// None of the projections are lowered to owned values:
407+
//
408+
// struct_element_addr and tuple_element_addr instructions are lowered to
409+
// struct_extract and tuple_extract instructions respectively. These both
410+
// have guaranteed ownership (since they forward ownership and can only be
411+
// used on a guaranteed value).
412+
//
413+
// unchecked_addr_cast instructions are lowered to unchecked_bitwise_cast
414+
// instructions. These have unowned ownership.
415+
//
416+
// So in no case can a load [take] be lowered into the new projected value
417+
// (some sequence of struct_extract, tuple_extract, and
418+
// unchecked_bitwise_cast instructions) taking over ownership of the original
419+
// value. Without additional changes.
420+
//
421+
// For example, for a sequence of element_addr projections could be
422+
// transformed into a sequence of destructure instructions, followed by a
423+
// sequence of structure instructions where all the original values are
424+
// kept in place but the taken value is "knocked out" and replaced with
425+
// undef. The running value would then be set to the newly structed
426+
// "knockout" value.
427+
//
428+
// Alternatively, a new copy of the running value could be created and a new
429+
// set of destroys placed after its last uses.
430+
involvesUntakableProjection = true;
431+
432+
// Recursively search for other (non-)loads in the instruction's uses.
433+
auto *svi = cast<SingleValueInstruction>(load);
434+
for (auto *use : svi->getUses()) {
435+
SILInstruction *user = use->getUser();
436+
if (user->getParent() != singleBlock)
437+
singleBlock = nullptr;
438+
439+
if (!isAddressForLoad(user, singleBlock, involvesUntakableProjection))
440+
return false;
441+
}
442+
return true;
443+
}
444+
445+
/// Returns true if \p I is a dead struct_element_addr or tuple_element_addr.
446+
static bool isDeadAddrProjection(SILInstruction *inst) {
447+
if (!isa<UncheckedAddrCastInst>(inst) && !isa<StructElementAddrInst>(inst) &&
448+
!isa<TupleElementAddrInst>(inst))
449+
return false;
450+
451+
// Recursively search for uses which are dead themselves.
452+
for (auto UI : cast<SingleValueInstruction>(inst)->getUses()) {
453+
SILInstruction *II = UI->getUser();
454+
if (!isDeadAddrProjection(II))
455+
return false;
456+
}
457+
return true;
458+
}
459+
460+
/// Returns true if this \p def is captured.
461+
/// Sets \p inSingleBlock to true if all uses of \p def are in a single block.
462+
static bool isCaptured(SILValue def, bool *inSingleBlock) {
463+
SILBasicBlock *singleBlock = def->getParentBlock();
464+
465+
// For all users of the def
466+
for (auto *use : def->getUses()) {
467+
SILInstruction *user = use->getUser();
468+
469+
if (user->getParent() != singleBlock)
470+
singleBlock = nullptr;
471+
472+
// Loads are okay.
473+
bool involvesUntakableProjection = false;
474+
if (isAddressForLoad(user, singleBlock, involvesUntakableProjection))
475+
continue;
476+
477+
// We can store into an AllocStack (but not the pointer).
478+
if (auto *si = dyn_cast<StoreInst>(user))
479+
if (si->getDest() == def)
480+
continue;
481+
482+
if (auto *sbi = dyn_cast<StoreBorrowInst>(user)) {
483+
if (sbi->getDest() == def) {
484+
if (isCaptured(sbi, inSingleBlock)) {
485+
return true;
486+
}
487+
continue;
488+
}
489+
}
490+
491+
// Deallocation is also okay, as are DebugValue w/ address value. We will
492+
// promote the latter into normal DebugValue.
493+
if (isa<DeallocStackInst>(user) || DebugValueInst::hasAddrVal(user))
494+
continue;
495+
496+
if (isa<EndBorrowInst>(user))
497+
continue;
498+
499+
// Destroys of loadable types can be rewritten as releases, so
500+
// they are fine.
501+
if (auto *dai = dyn_cast<DestroyAddrInst>(user))
502+
if (dai->getOperand()->getType().isLoadable(*dai->getFunction()))
503+
continue;
504+
505+
// Other instructions are assumed to capture the AllocStack.
506+
LLVM_DEBUG(llvm::dbgs() << "*** AllocStack is captured by: " << *user);
507+
return true;
508+
}
509+
510+
// None of the users capture the AllocStack.
511+
*inSingleBlock = (singleBlock != nullptr);
512+
return false;
513+
}
514+
515+
/// Returns true if the \p def is only stored into.
516+
static bool isWriteOnlyAllocation(SILValue def) {
517+
assert(isa<AllocStackInst>(def) || isa<StoreBorrowInst>(def));
518+
519+
// For all users of the def:
520+
for (auto *use : def->getUses()) {
521+
SILInstruction *user = use->getUser();
522+
523+
// It is okay to store into the AllocStack.
524+
if (auto *si = dyn_cast<StoreInst>(user))
525+
if (!isa<AllocStackInst>(si->getSrc()))
526+
continue;
527+
528+
if (auto *sbi = dyn_cast<StoreBorrowInst>(user)) {
529+
// Since all uses of the alloc_stack will be via store_borrow, check if
530+
// there are any non-writes from the store_borrow location.
531+
if (!isWriteOnlyAllocation(sbi)) {
532+
return false;
533+
}
534+
continue;
535+
}
536+
537+
// Deallocation is also okay.
538+
if (isa<DeallocStackInst>(user))
539+
continue;
540+
541+
if (isa<EndBorrowInst>(user))
542+
continue;
543+
544+
// If we haven't already promoted the AllocStack, we may see
545+
// DebugValue uses.
546+
if (DebugValueInst::hasAddrVal(user))
547+
continue;
548+
549+
if (isDeadAddrProjection(user))
550+
continue;
551+
552+
// Can't do anything else with it.
553+
LLVM_DEBUG(llvm::dbgs() << "*** AllocStack has non-write use: " << *user);
554+
return false;
555+
}
556+
557+
return true;
558+
}
559+
377560
static void
378561
replaceLoad(SILInstruction *inst, SILValue newValue, AllocStackInst *asi,
379562
SILBuilderContext &ctx, InstructionDeleter &deleter,
@@ -1664,9 +1847,6 @@ class MemoryToRegisters {
16641847
return *domTreeLevels;
16651848
}
16661849

1667-
/// Check if \p def is a write-only allocation.
1668-
bool isWriteOnlyAllocation(SILValue def);
1669-
16701850
/// Promote all of the AllocStacks in a single basic block in one
16711851
/// linear scan. Note: This function deletes all of the users of the
16721852
/// AllocStackInst, including the DeallocStackInst but it does not remove the
@@ -1689,189 +1869,6 @@ class MemoryToRegisters {
16891869

16901870
} // end anonymous namespace
16911871

1692-
/// Returns true if \p I is an address of a LoadInst, skipping struct and
1693-
/// tuple address projections. Sets \p singleBlock to null if the load (or
1694-
/// it's address is not in \p singleBlock.
1695-
/// This function looks for these patterns:
1696-
/// 1. (load %ASI)
1697-
/// 2. (load (struct_element_addr/tuple_element_addr/unchecked_addr_cast %ASI))
1698-
static bool isAddressForLoad(SILInstruction *load, SILBasicBlock *&singleBlock,
1699-
bool &involvesUntakableProjection) {
1700-
if (auto *li = dyn_cast<LoadInst>(load)) {
1701-
// SILMem2Reg is disabled when we find a load [take] of an untakable
1702-
// projection. See below for further discussion.
1703-
if (involvesUntakableProjection &&
1704-
li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
1705-
return false;
1706-
}
1707-
return true;
1708-
}
1709-
1710-
if (isa<LoadBorrowInst>(load)) {
1711-
if (involvesUntakableProjection) {
1712-
return false;
1713-
}
1714-
return true;
1715-
}
1716-
1717-
if (!isa<UncheckedAddrCastInst>(load) && !isa<StructElementAddrInst>(load) &&
1718-
!isa<TupleElementAddrInst>(load))
1719-
return false;
1720-
1721-
// None of the projections are lowered to owned values:
1722-
//
1723-
// struct_element_addr and tuple_element_addr instructions are lowered to
1724-
// struct_extract and tuple_extract instructions respectively. These both
1725-
// have guaranteed ownership (since they forward ownership and can only be
1726-
// used on a guaranteed value).
1727-
//
1728-
// unchecked_addr_cast instructions are lowered to unchecked_bitwise_cast
1729-
// instructions. These have unowned ownership.
1730-
//
1731-
// So in no case can a load [take] be lowered into the new projected value
1732-
// (some sequence of struct_extract, tuple_extract, and
1733-
// unchecked_bitwise_cast instructions) taking over ownership of the original
1734-
// value. Without additional changes.
1735-
//
1736-
// For example, for a sequence of element_addr projections could be
1737-
// transformed into a sequence of destructure instructions, followed by a
1738-
// sequence of structure instructions where all the original values are
1739-
// kept in place but the taken value is "knocked out" and replaced with
1740-
// undef. The running value would then be set to the newly structed
1741-
// "knockout" value.
1742-
//
1743-
// Alternatively, a new copy of the running value could be created and a new
1744-
// set of destroys placed after its last uses.
1745-
involvesUntakableProjection = true;
1746-
1747-
// Recursively search for other (non-)loads in the instruction's uses.
1748-
auto *svi = cast<SingleValueInstruction>(load);
1749-
for (auto *use : svi->getUses()) {
1750-
SILInstruction *user = use->getUser();
1751-
if (user->getParent() != singleBlock)
1752-
singleBlock = nullptr;
1753-
1754-
if (!isAddressForLoad(user, singleBlock, involvesUntakableProjection))
1755-
return false;
1756-
}
1757-
return true;
1758-
}
1759-
1760-
/// Returns true if \p I is a dead struct_element_addr or tuple_element_addr.
1761-
static bool isDeadAddrProjection(SILInstruction *inst) {
1762-
if (!isa<UncheckedAddrCastInst>(inst) && !isa<StructElementAddrInst>(inst) &&
1763-
!isa<TupleElementAddrInst>(inst))
1764-
return false;
1765-
1766-
// Recursively search for uses which are dead themselves.
1767-
for (auto UI : cast<SingleValueInstruction>(inst)->getUses()) {
1768-
SILInstruction *II = UI->getUser();
1769-
if (!isDeadAddrProjection(II))
1770-
return false;
1771-
}
1772-
return true;
1773-
}
1774-
1775-
/// Returns true if this \p def is captured.
1776-
/// Sets \p inSingleBlock to true if all uses of \p def are in a single block.
1777-
static bool isCaptured(SILValue def, bool *inSingleBlock) {
1778-
SILBasicBlock *singleBlock = def->getParentBlock();
1779-
1780-
// For all users of the def
1781-
for (auto *use : def->getUses()) {
1782-
SILInstruction *user = use->getUser();
1783-
1784-
if (user->getParent() != singleBlock)
1785-
singleBlock = nullptr;
1786-
1787-
// Loads are okay.
1788-
bool involvesUntakableProjection = false;
1789-
if (isAddressForLoad(user, singleBlock, involvesUntakableProjection))
1790-
continue;
1791-
1792-
// We can store into an AllocStack (but not the pointer).
1793-
if (auto *si = dyn_cast<StoreInst>(user))
1794-
if (si->getDest() == def)
1795-
continue;
1796-
1797-
if (auto *sbi = dyn_cast<StoreBorrowInst>(user)) {
1798-
if (sbi->getDest() == def) {
1799-
if (isCaptured(sbi, inSingleBlock)) {
1800-
return true;
1801-
}
1802-
continue;
1803-
}
1804-
}
1805-
1806-
// Deallocation is also okay, as are DebugValue w/ address value. We will
1807-
// promote the latter into normal DebugValue.
1808-
if (isa<DeallocStackInst>(user) || DebugValueInst::hasAddrVal(user))
1809-
continue;
1810-
1811-
if (isa<EndBorrowInst>(user))
1812-
continue;
1813-
1814-
// Destroys of loadable types can be rewritten as releases, so
1815-
// they are fine.
1816-
if (auto *dai = dyn_cast<DestroyAddrInst>(user))
1817-
if (dai->getOperand()->getType().isLoadable(*dai->getFunction()))
1818-
continue;
1819-
1820-
// Other instructions are assumed to capture the AllocStack.
1821-
LLVM_DEBUG(llvm::dbgs() << "*** AllocStack is captured by: " << *user);
1822-
return true;
1823-
}
1824-
1825-
// None of the users capture the AllocStack.
1826-
*inSingleBlock = (singleBlock != nullptr);
1827-
return false;
1828-
}
1829-
1830-
/// Returns true if the \p def is only stored into.
1831-
bool MemoryToRegisters::isWriteOnlyAllocation(SILValue def) {
1832-
assert(isa<AllocStackInst>(def) || isa<StoreBorrowInst>(def));
1833-
1834-
// For all users of the def:
1835-
for (auto *use : def->getUses()) {
1836-
SILInstruction *user = use->getUser();
1837-
1838-
// It is okay to store into the AllocStack.
1839-
if (auto *si = dyn_cast<StoreInst>(user))
1840-
if (!isa<AllocStackInst>(si->getSrc()))
1841-
continue;
1842-
1843-
if (auto *sbi = dyn_cast<StoreBorrowInst>(user)) {
1844-
// Since all uses of the alloc_stack will be via store_borrow, check if
1845-
// there are any non-writes from the store_borrow location.
1846-
if (!isWriteOnlyAllocation(sbi)) {
1847-
return false;
1848-
}
1849-
continue;
1850-
}
1851-
1852-
// Deallocation is also okay.
1853-
if (isa<DeallocStackInst>(user))
1854-
continue;
1855-
1856-
if (isa<EndBorrowInst>(user))
1857-
continue;
1858-
1859-
// If we haven't already promoted the AllocStack, we may see
1860-
// DebugValue uses.
1861-
if (DebugValueInst::hasAddrVal(user))
1862-
continue;
1863-
1864-
if (isDeadAddrProjection(user))
1865-
continue;
1866-
1867-
// Can't do anything else with it.
1868-
LLVM_DEBUG(llvm::dbgs() << "*** AllocStack has non-write use: " << *user);
1869-
return false;
1870-
}
1871-
1872-
return true;
1873-
}
1874-
18751872
void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) {
18761873
LLVM_DEBUG(llvm::dbgs() << "*** Promoting in-block: " << *asi);
18771874

0 commit comments

Comments
 (0)