26
26
#define DEBUG_TYPE " copy-propagation"
27
27
28
28
#include " swift/SIL/BasicBlockUtils.h"
29
+ #include " swift/SIL/SILUndef.h"
29
30
#include " swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
30
31
#include " swift/SILOptimizer/PassManager/Passes.h"
31
32
#include " swift/SILOptimizer/PassManager/Transforms.h"
34
35
35
36
using namespace swift ;
36
37
38
+ // This only applies to -O copy-propagation.
39
+ llvm::cl::opt<bool >
40
+ EnableRewriteBorrows (" canonical-ossa-rewrite-borrows" ,
41
+ llvm::cl::init (false ),
42
+ llvm::cl::desc(" Enable rewriting borrow scopes" ));
43
+
37
44
// ===----------------------------------------------------------------------===//
38
45
// CopyPropagation: Top-Level Function Transform.
39
46
// ===----------------------------------------------------------------------===//
@@ -44,13 +51,17 @@ class CopyPropagation : public SILFunctionTransform {
44
51
bool pruneDebug;
45
52
// / True of all values should be canonicalized.
46
53
bool canonicalizeAll;
54
+ // / If true, then borrow scopes will be canonicalized, allowing copies of
55
+ // / guaranteed values to be optimized. Does *not* shrink the borrow scope.
56
+ bool canonicalizeBorrows;
47
57
// / If true, then new destroy_value instructions will be poison.
48
58
bool poisonRefs;
49
59
50
60
public:
51
- CopyPropagation (bool pruneDebug, bool canonicalizeAll, bool poisonRefs)
52
- : pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
53
- poisonRefs (poisonRefs) {}
61
+ CopyPropagation (bool pruneDebug, bool canonicalizeAll,
62
+ bool canonicalizeBorrows, bool poisonRefs)
63
+ : pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
64
+ canonicalizeBorrows (canonicalizeBorrows), poisonRefs(poisonRefs) {}
54
65
55
66
// / The entry point to this function transformation.
56
67
void run () override ;
@@ -71,12 +82,18 @@ static bool isCopyDead(CopyValueInst *copy, bool pruneDebug) {
71
82
return true ;
72
83
}
73
84
85
+ static bool isBorrowDead (BeginBorrowInst *borrow) {
86
+ return llvm::all_of (borrow->getUses (), [](Operand *use) {
87
+ SILInstruction *user = use->getUser ();
88
+ return isa<EndBorrowInst>(user) || user->isDebugInstruction ();
89
+ });
90
+ }
91
+
74
92
// / Top-level pass driver.
75
93
void CopyPropagation::run () {
76
94
auto *f = getFunction ();
77
95
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
78
96
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
79
- auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
80
97
81
98
// Debug label for unit testing.
82
99
LLVM_DEBUG (llvm::dbgs () << " *** CopyPropagation: " << f->getName () << " \n " );
@@ -100,15 +117,57 @@ void CopyPropagation::run() {
100
117
}
101
118
}
102
119
}
120
+ // Push copy_value instructions above their struct_extract operands by
121
+ // inserting destructures.
122
+ //
123
+ // copiedDefs be be modified, but it never shrinks
124
+ for (unsigned idx = 0 ; idx < copiedDefs.size (); ++idx) {
125
+ SILValue def = copiedDefs[idx];
126
+ auto *copy = dyn_cast<CopyValueInst>(def);
127
+ if (!copy)
128
+ continue ;
129
+
130
+ auto *extract = dyn_cast<StructExtractInst>(copy->getOperand ());
131
+ if (!extract
132
+ || SILValue (extract).getOwnershipKind () != OwnershipKind::Guaranteed)
133
+ continue ;
134
+
135
+ // Bail-out if we won't rewrite borrows because that currently regresses
136
+ // Breadcrumbs.MutatedUTF16ToIdx.Mixed/Breadcrumbs.MutatedIdxToUTF16.Mixed.
137
+ // Also, mandatory copyprop does not need to rewrite destructures.
138
+ if (!canonicalizeBorrows)
139
+ continue ;
140
+
141
+ if (SILValue destructuredResult = convertExtractToDestructure (extract)) {
142
+ // Remove to-be-deleted instructions from copiedDeds. The extract cannot
143
+ // be in the copiedDefs set since getCanonicalCopiedDef does not allow a
144
+ // guaranteed projection to be a canonical def.
145
+ copiedDefs.remove (copy);
146
+ --idx; // point back to the current element, which was erased.
147
+
148
+ // TODO: unfortunately SetVector has no element replacement.
149
+ copiedDefs.insert (destructuredResult);
150
+
151
+ auto *destructure = cast<DestructureStructInst>(
152
+ destructuredResult.getDefiningInstruction ());
153
+ auto *newCopy = cast<CopyValueInst>(destructure->getOperand ());
154
+ copiedDefs.insert (
155
+ CanonicalizeOSSALifetime::getCanonicalCopiedDef (newCopy));
156
+
157
+ LLVM_DEBUG (llvm::dbgs () << " Destructure Conversion:\n "
158
+ << *extract << " to " << *destructure);
159
+ // Delete both the copy and the extract.
160
+ InstructionDeleter ().recursivelyDeleteUsersIfDead (extract);
161
+ }
162
+ }
103
163
// Perform copy propgation for each copied value.
104
- CanonicalizeOSSALifetime canonicalizer (pruneDebug, poisonRefs,
105
- accessBlockAnalysis,
106
- dominanceAnalysis,
107
- deBlocksAnalysis->get (f));
164
+ CanonicalizeOSSALifetime canonicalizer (pruneDebug, canonicalizeBorrows,
165
+ poisonRefs, accessBlockAnalysis,
166
+ dominanceAnalysis);
108
167
// Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
109
168
// copy's source operand is unrecgonized), then the copy is itself treated
110
169
// like a def and may be dead after canonicalization.
111
- llvm::SmallVector<CopyValueInst *, 4 > deadCopies;
170
+ llvm::SmallVector<SILInstruction *, 4 > deadCopies;
112
171
for (auto &def : copiedDefs) {
113
172
// Canonicalized this def.
114
173
canonicalizer.canonicalizeValueLifetime (def);
@@ -118,6 +177,14 @@ void CopyPropagation::run() {
118
177
deadCopies.push_back (copy);
119
178
}
120
179
}
180
+ // Dead borrow scopes must be removed as uses before canonicalizing the
181
+ // outer copy.
182
+ if (auto *borrow = dyn_cast<BeginBorrowInst>(def)) {
183
+ if (isBorrowDead (borrow)) {
184
+ borrow->setOperand (SILUndef::get (borrow->getType (), *f));
185
+ deadCopies.push_back (borrow);
186
+ }
187
+ }
121
188
// Canonicalize any new outer copy.
122
189
if (SILValue outerCopy = canonicalizer.createdOuterCopy ()) {
123
190
SILValue outerDef = canonicalizer.getCanonicalCopiedDef (outerCopy);
@@ -128,26 +195,29 @@ void CopyPropagation::run() {
128
195
}
129
196
if (canonicalizer.hasChanged () || !deadCopies.empty ()) {
130
197
InstructionDeleter deleter;
131
- for (auto *copy : deadCopies) {
132
- deleter.recursivelyDeleteUsersIfDead (copy );
198
+ for (auto *copyOrBorrow : deadCopies) {
199
+ deleter.recursivelyDeleteUsersIfDead (copyOrBorrow );
133
200
}
134
201
// Preserves NonLocalAccessBlockAnalysis.
135
202
accessBlockAnalysis->lockInvalidation ();
136
203
invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
137
204
accessBlockAnalysis->unlockInvalidation ();
138
205
if (f->getModule ().getOptions ().VerifySILOwnership ) {
206
+ auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
139
207
f->verifyOwnership (deBlocksAnalysis->get (f));
140
208
}
141
209
}
142
210
}
143
211
144
212
SILTransform *swift::createMandatoryCopyPropagation () {
145
213
return new CopyPropagation (/* pruneDebug*/ true , /* canonicalizeAll*/ true ,
214
+ /* canonicalizeBorrows*/ false ,
146
215
/* poisonRefs*/ true );
147
216
}
148
217
149
218
SILTransform *swift::createCopyPropagation () {
150
219
return new CopyPropagation (/* pruneDebug*/ true , /* canonicalizeAll*/ false ,
220
+ /* canonicalizeBorrows*/ EnableRewriteBorrows,
151
221
/* poisonRefs*/ false );
152
222
}
153
223
0 commit comments