Skip to content

Commit 3c045a8

Browse files
committed
CanonicalOSSALifetime: Add convertExtractToDestructure utility
struct_extract and tuple_extract do not belong in OSSA (except to workaround certain extreme cases). They completely defeat simplification that OSSA provides for optimizing owned lifetimes. Copy propagation uses this utility to canonicalize owned values before canonicalizing their lifetime.
1 parent f171428 commit 3c045a8

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@
103103

104104
namespace swift {
105105

106+
/// Convert this struct_extract into a copy+destructure. Return the destructured
107+
/// result or invalid SILValue. The caller must delete the extract and its
108+
/// now-dead copy use.
109+
///
110+
// If a copied-def is a struct-extract, attempt a destructure conversion
111+
// %extract = struct_extract %... : $TypeWithSingleOwnershipValue
112+
// %copy = copy_value %extract : $OwnershipValue
113+
// To:
114+
// %copy = copy_value %extract : $TypeWithSingleOwnershipValue
115+
// (%extracted,...) = destructure %copy : $TypeWithSingleOwnershipValue
116+
SILValue convertExtractToDestructure(StructExtractInst *extract);
117+
106118
/// Information about consumes on the extended-lifetime boundary. Consuming uses
107119
/// within the lifetime are not included--they will consume a copy after
108120
/// rewriting. For borrowed def values, the consumes do not include the end of

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,50 @@ void CopyPropagation::run() {
100100
}
101101
}
102102
}
103+
// Push copy_value instructions above their struct_extract operands by
104+
// inserting destructures.
105+
//
106+
// copiedDefs be be modified, but it never shrinks
107+
for (unsigned idx = 0; idx < copiedDefs.size(); ++idx) {
108+
SILValue def = copiedDefs[idx];
109+
auto *copy = dyn_cast<CopyValueInst>(def);
110+
if (!copy)
111+
continue;
112+
113+
auto *extract = dyn_cast<StructExtractInst>(copy->getOperand());
114+
if (!extract
115+
|| SILValue(extract).getOwnershipKind() != OwnershipKind::Guaranteed)
116+
continue;
117+
118+
if (SILValue destructuredResult = convertExtractToDestructure(extract)) {
119+
// Remove to-be-deleted instructions from copiedDeds. The extract cannot
120+
// be in the copiedDefs set since getCanonicalCopiedDef does not allow a
121+
// guaranteed projection to be a canonical def.
122+
copiedDefs.remove(copy);
123+
--idx; // point back to the current element, which was erased.
124+
125+
// TODO: unfortunately SetVector has no element replacement.
126+
copiedDefs.insert(destructuredResult);
127+
128+
auto *destructure = cast<DestructureStructInst>(
129+
destructuredResult.getDefiningInstruction());
130+
auto *newCopy = cast<CopyValueInst>(destructure->getOperand());
131+
copiedDefs.insert(
132+
CanonicalizeOSSALifetime::getCanonicalCopiedDef(newCopy));
133+
134+
LLVM_DEBUG(llvm::dbgs() << "Destructure Conversion:\n"
135+
<< *extract << " to " << *destructure);
136+
// Delete both the copy and the extract.
137+
InstructionDeleter().recursivelyDeleteUsersIfDead(extract);
138+
}
139+
}
103140
// Perform copy propgation for each copied value.
104141
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
105142
accessBlockAnalysis,
106143
dominanceAnalysis,
107144
deBlocksAnalysis->get(f));
108145
// Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
109-
// copy's source operand is unrecgonized), then the copy is itself treated
146+
// copy's source operand is unrecgonized), then thecan copy is itself treated
110147
// like a def and may be dead after canonicalization.
111148
llvm::SmallVector<CopyValueInst *, 4> deadCopies;
112149
for (auto &def : copiedDefs) {

lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,32 @@ static void copyLiveUse(Operand *use) {
114114
LLVM_DEBUG(llvm::dbgs() << " Copying at last use " << *copy);
115115
}
116116

117+
// TODO: generalize this to handle multiple nondebug uses of the struct_extract.
118+
SILValue swift::convertExtractToDestructure(StructExtractInst *extract) {
119+
if (!hasOneNonDebugUse(extract))
120+
return nullptr;
121+
122+
if (!extract->isFieldOnlyNonTrivialField())
123+
return nullptr;
124+
125+
auto *extractCopy =
126+
dyn_cast<CopyValueInst>(getNonDebugUses(extract).begin()->getUser());
127+
if (!extractCopy)
128+
return nullptr;
129+
130+
SILBuilderWithScope builder(extract);
131+
auto loc = extract->getLoc();
132+
auto *copy = builder.createCopyValue(loc, extract->getOperand());
133+
auto *destructure = builder.createDestructureStruct(loc, copy);
134+
135+
SILValue nonTrivialResult = destructure->getResult(extract->getFieldIndex());
136+
assert(!nonTrivialResult->getType().isTrivial(*destructure->getFunction())
137+
&& "field idx mismatch");
138+
139+
extractCopy->replaceAllUsesWith(nonTrivialResult);
140+
return nonTrivialResult;
141+
}
142+
117143
//===----------------------------------------------------------------------===//
118144
// MARK: Rewrite borrow scopes
119145
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)