Skip to content

Commit babc53f

Browse files
atrickgottesmm
authored andcommitted
destructure conversion
1 parent fe8b3a8 commit babc53f

File tree

5 files changed

+1677
-1238
lines changed

5 files changed

+1677
-1238
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@
102102

103103
namespace swift {
104104

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

190+
// Worklist of pointer-like things that have an invalid default value. Avoid
191+
// revisiting nodes--suitable for DAGs, but pops finished nodes without
192+
// preserving them in the vector.
193+
//
194+
// The primary API has two methods: intialize() and pop(). Others are provided
195+
// for flexibility.
196+
//
197+
// TODO: make this a better utility.
198+
template <typename T, unsigned SmallSize> struct PtrWorklist {
199+
SmallPtrSet<T, SmallSize> ptrVisited;
200+
SmallVector<T, SmallSize> ptrVector;
201+
202+
PtrWorklist() = default;
203+
204+
PtrWorklist(const PtrWorklist &) = delete;
205+
206+
void initialize(T t) {
207+
clear();
208+
insert(t);
209+
}
210+
211+
template <typename R> void initializeRange(R &&range) {
212+
clear();
213+
ptrVisited.insert(range.begin(), range.end());
214+
ptrVector.append(range.begin(), range.end());
215+
}
216+
217+
T pop() { return empty() ? T() : ptrVector.pop_back_val(); }
218+
219+
bool empty() const { return ptrVector.empty(); }
220+
221+
unsigned size() const { return ptrVector.size(); }
222+
223+
void clear() {
224+
ptrVector.clear();
225+
ptrVisited.clear();
226+
}
227+
228+
void insert(T t) {
229+
if (ptrVisited.insert(t).second)
230+
ptrVector.push_back(t);
231+
}
232+
};
233+
178234
/// Canonicalize OSSA lifetimes.
179235
///
180236
/// Allows the allocation of analysis state to be reused across calls to
@@ -221,11 +277,11 @@ class CanonicalizeOSSALifetime {
221277
/// outisde the pruned liveness at the time it is discovered.
222278
llvm::SmallPtrSet<DebugValueInst *, 8> debugValues;
223279

224-
/// Reuse a general worklist for def-use traversal.
225-
SmallSetVector<SILValue, 8> defUseWorklist;
280+
/// Reuse a general visited set for def-use traversal.
281+
PtrWorklist<SILValue, 8> defUseWorklist;
226282

227283
/// Reuse a general worklist for CFG traversal.
228-
SmallSetVector<SILBasicBlock *, 8> blockWorklist;
284+
PtrWorklist<SILBasicBlock *, 8> blockWorklist;
229285

230286
/// Pruned liveness for the extended live range including copies. For this
231287
/// purpose, only consuming instructions are considered "lifetime
@@ -298,6 +354,15 @@ class CanonicalizeOSSALifetime {
298354

299355
bool consolidateBorrowScope();
300356

357+
bool findBorrowScopeUses(llvm::SmallPtrSetImpl<SILInstruction *> &useInsts);
358+
359+
void filterOuterBorrowUseInsts(
360+
llvm::SmallPtrSetImpl<SILInstruction *> &outerUseInsts);
361+
362+
void rewriteOuterBorrowUsesAndFindConsumes(
363+
SILValue incomingValue,
364+
llvm::SmallPtrSetImpl<SILInstruction *> &outerUseInsts);
365+
301366
bool computeCanonicalLiveness();
302367

303368
bool endsAccessOverlappingPrunedBoundary(SILInstruction *inst);

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
#include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h"
3333
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
3434

35+
//!!!
36+
#include "swift/Basic/Defer.h"
37+
3538
using namespace swift;
3639

3740
//===----------------------------------------------------------------------===//
@@ -72,6 +75,13 @@ void CopyPropagation::run() {
7275
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
7376
auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
7477

78+
//!!!
79+
if (f->hasName("$ss18_StringBreadcrumbsCyABSScfc")) {
80+
llvm::DebugFlag = true;
81+
llvm::setCurrentDebugType("copy-propagation");
82+
}
83+
SWIFT_DEFER { llvm::DebugFlag = false; };
84+
7585
// Debug label for unit testing.
7686
LLVM_DEBUG(llvm::dbgs() << "*** CopyPropagation: " << f->getName() << "\n");
7787

@@ -88,12 +98,49 @@ void CopyPropagation::run() {
8898
CanonicalizeOSSALifetime::getCanonicalCopiedDef(copy));
8999
}
90100
}
101+
// Push copy_value instructions above their struct_extract operands by
102+
// inserting destructures.
103+
//
104+
// copiedDefs be be modified, but it never shrinks
105+
for (unsigned idx = 0; idx < copiedDefs.size(); ++idx) {
106+
SILValue def = copiedDefs[idx];
107+
auto *copy = dyn_cast<CopyValueInst>(def);
108+
if (!copy)
109+
continue;
110+
111+
auto *extract = dyn_cast<StructExtractInst>(copy->getOperand());
112+
if (!extract
113+
|| SILValue(extract).getOwnershipKind() != OwnershipKind::Guaranteed)
114+
continue;
115+
116+
if (SILValue destructuredResult = convertExtractToDestructure(extract)) {
117+
// Remove to-be-deleted instructions from copiedDeds. The extract cannot
118+
// be in the copiedDefs set since getCanonicalCopiedDef does not allow a
119+
// guaranteed projection to be a canonical def.
120+
copiedDefs.remove(copy);
121+
--idx; // point back to the current element, which was erased.
122+
123+
// TODO: unfortunately SetVector has no element replacement.
124+
copiedDefs.insert(destructuredResult);
125+
126+
auto *destructure = cast<DestructureStructInst>(
127+
destructuredResult.getDefiningInstruction());
128+
auto *newCopy = cast<CopyValueInst>(destructure->getOperand());
129+
copiedDefs.insert(
130+
CanonicalizeOSSALifetime::getCanonicalCopiedDef(newCopy));
131+
132+
LLVM_DEBUG(llvm::dbgs() << "Destructure Conversion:\n"
133+
<< *extract << " to " << *destructure);
134+
// Delete both the copy and the extract.
135+
InstructionDeleter().recursivelyDeleteUsersIfDead(extract);
136+
}
137+
}
91138
// Perform copy propgation for each copied value.
92139
CanonicalizeOSSALifetime canonicalizer(pruneDebug, accessBlockAnalysis,
93140
dominanceAnalysis,
94141
deBlocksAnalysis->get(f));
95142
// Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
96-
// copy's source operand is unrecgonized), then the copy is itself treated
143+
// copy's source operand is unrecgonized), then thecan copy is itself treated
97144
// like a def and may be dead after canonicalization.
98145
llvm::SmallVector<CopyValueInst *, 4> deadCopies;
99146
for (auto &def : copiedDefs) {

0 commit comments

Comments
 (0)