45
45
#include " swift/SIL/OwnershipUtils.h"
46
46
#include " swift/SILOptimizer/Utils/CFGOptUtils.h"
47
47
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
48
+ #include " swift/SILOptimizer/Utils/ValueLifetime.h"
48
49
#include " llvm/ADT/Statistic.h"
49
50
50
51
using namespace swift ;
@@ -73,8 +74,19 @@ SILValue CanonicalizeOSSALifetime::getCanonicalCopiedDef(SILValue v) {
73
74
case BorrowedValueKind::Invalid:
74
75
llvm_unreachable (" Using invalid case?!" );
75
76
case BorrowedValueKind::SILFunctionArgument:
76
- case BorrowedValueKind::BeginBorrow:
77
77
return def;
78
+ case BorrowedValueKind::BeginBorrow: {
79
+ bool localScope = true ;
80
+ // TODO: visitLocalScopeEndingUses should have an early exit.
81
+ borrowedVal.visitLocalScopeEndingUses ([&](Operand *endBorrow) {
82
+ if (endBorrow->getUser ()->getParent () != def->getParentBlock ())
83
+ localScope = false ;
84
+ });
85
+ if (localScope) {
86
+ return def;
87
+ }
88
+ break ;
89
+ }
78
90
case BorrowedValueKind::LoadBorrow:
79
91
case BorrowedValueKind::Phi:
80
92
break ;
@@ -87,6 +99,20 @@ SILValue CanonicalizeOSSALifetime::getCanonicalCopiedDef(SILValue v) {
87
99
return v;
88
100
}
89
101
102
+ // / The lifetime extends beyond given consuming use. Copy the value.
103
+ static void copyLiveUse (Operand *use) {
104
+ SILInstruction *user = use->getUser ();
105
+ SILBuilderWithScope B (user->getIterator ());
106
+
107
+ auto loc = RegularLocation::getAutoGeneratedLocation (
108
+ user->getLoc ().getSourceLoc ());
109
+ auto *copy = B.createCopyValue (loc, use->get ());
110
+ use->set (copy);
111
+
112
+ ++NumCopiesGenerated;
113
+ LLVM_DEBUG (llvm::dbgs () << " Copying at last use " << *copy);
114
+ }
115
+
90
116
// ===----------------------------------------------------------------------===//
91
117
// MARK: Rewrite borrow scopes
92
118
// ===----------------------------------------------------------------------===//
@@ -121,12 +147,11 @@ bool CanonicalizeOSSALifetime::computeBorrowLiveness() {
121
147
liveness.updateForUse (use->getUser (), /* lifetimeEnding*/ true );
122
148
});
123
149
124
- // TODO: Remove this check. Canonicalize multi-block borrow scopes only after
125
- // consolidateBorrowScope can handle persistentCopies, otherwise we may end up
126
- // generating more dynamic copies than the non-canonical form.
127
- if (liveness.numLiveBlocks () > 1 ) {
128
- return false ;
129
- }
150
+ // TODO: Fix getCanonicalCopiedDef to allow multi-block borrows and remove
151
+ // this assert. This should only be done once consolidateBorrowScope can
152
+ // handle persistentCopies, otherwise we may end up generating more dynamic
153
+ // copies than the non-canonical form.
154
+ assert (liveness.numLiveBlocks () == 1 );
130
155
return true ;
131
156
}
132
157
@@ -153,15 +178,26 @@ static CopyValueInst *createOuterCopy(BeginBorrowInst *beginBorrow) {
153
178
// TODO: Canonicalize multi-block borrow scopes, load_borrow scope, and phi
154
179
// borrow scopes by adding one copy per block to persistentCopies for
155
180
// each block that dominates an outer use.
156
- void CanonicalizeOSSALifetime::consolidateBorrowScope () {
181
+ bool CanonicalizeOSSALifetime::consolidateBorrowScope () {
157
182
if (isa<SILFunctionArgument>(currentDef)) {
158
- return ;
183
+ return true ;
159
184
}
160
185
// Gather all outer uses before rewriting any to avoid scanning any basic
161
186
// block more than once.
162
187
SmallVector<Operand *, 8 > outerUses;
163
188
llvm::SmallPtrSet<SILInstruction *, 8 > outerUseInsts;
189
+ auto isUserInLiveOutBlock = [&](SILInstruction *user) {
190
+ // TODO: enable isUserInLiveOutBlock once we support multi-block borrows
191
+ // return (liveness.getBlockLiveness(user->getParent())
192
+ // == PrunedLiveBlocks::LiveOut);
193
+ return false ;
194
+ };
164
195
auto recordOuterUse = [&](Operand *use) {
196
+ // If the user's block is LiveOut, then it is definitely within the borrow
197
+ // scope, so there's no need to record it.
198
+ if (isUserInLiveOutBlock (use->getUser ())) {
199
+ return ;
200
+ }
165
201
outerUses.push_back (use);
166
202
outerUseInsts.insert (use->getUser ());
167
203
};
@@ -177,12 +213,10 @@ void CanonicalizeOSSALifetime::consolidateBorrowScope() {
177
213
defUseWorklist.insert (copy);
178
214
continue ;
179
215
}
180
- // debug_value uses are handled like normal uses here. They should be
181
- // stripped later if required when handling outerCopy or persistentCopies.
182
- if (liveness.getBlockLiveness (user->getParent ())
183
- == PrunedLiveBlocks::LiveOut) {
184
- continue ;
185
- }
216
+ // Note: debug_value uses are handled like normal uses here. They should
217
+ // be stripped later if required when handling outerCopy or
218
+ // persistentCopies.
219
+
186
220
switch (use->getOperandOwnership ()) {
187
221
case OperandOwnership::NonUse:
188
222
break ;
@@ -198,41 +232,102 @@ void CanonicalizeOSSALifetime::consolidateBorrowScope() {
198
232
199
233
case OperandOwnership::ForwardingUnowned:
200
234
case OperandOwnership::PointerEscape:
235
+ return false ;
236
+
201
237
case OperandOwnership::InstantaneousUse:
202
238
case OperandOwnership::UnownedInstantaneousUse:
203
239
case OperandOwnership::BitwiseEscape:
204
240
case OperandOwnership::ForwardingConsume:
205
241
case OperandOwnership::DestroyingConsume:
242
+ recordOuterUse (use);
243
+ break ;
206
244
case OperandOwnership::Borrow:
245
+ BorrowingOperand borrowOper (use);
246
+ if (borrowOper.kind == BorrowingOperandKind::Invalid) {
247
+ return false ;
248
+ }
207
249
recordOuterUse (use);
250
+ // For borrows, record the scope-ending instructions in addition to the
251
+ // borrow instruction as an outer use point.
252
+ borrowOper.visitLocalEndScopeUses ([&](Operand *endBorrow) {
253
+ if (!isUserInLiveOutBlock (endBorrow->getUser ())) {
254
+ outerUseInsts.insert (endBorrow->getUser ());
255
+ }
256
+ return true ;
257
+ });
208
258
break ;
209
259
}
210
260
}
211
261
} // end def-use traversal
212
262
213
- // Remove outer uses that occur before the end of the borrow scope.
214
263
auto *beginBorrow = cast<BeginBorrowInst>(currentDef);
215
- BorrowedValue::get (beginBorrow).visitLocalScopeEndingUses ([&](Operand *use) {
216
- // Forward iterate until we find the end of the borrow scope.
217
- auto *endScope = use->getUser ();
218
- for (auto instIter = beginBorrow->getIterator (),
219
- endIter = endScope->getIterator ();
220
- instIter != endIter; ++instIter) {
221
- outerUseInsts.erase (&*instIter);
222
- }
223
- });
264
+ SmallVector<SILInstruction *, 1 > scopeEndingInst;
265
+ BorrowedValue::get (beginBorrow)
266
+ .getLocalScopeEndingInstructions (scopeEndingInst);
267
+ assert (scopeEndingInst.size () == 1 && " expected single-block borrow" );
268
+ // Remove outer uses that occur before the end of the borrow scope by
269
+ // forward iterating from begin_borrow to end_borrow.
270
+ for (auto instIter = beginBorrow->getIterator (),
271
+ endIter = scopeEndingInst[0 ]->getIterator ();
272
+ instIter != endIter; ++instIter) {
273
+ outerUseInsts.erase (&*instIter);
274
+ }
224
275
if (outerUseInsts.empty ()) {
225
- return ;
276
+ return true ;
226
277
}
227
- // Rewrite the outer uses.
278
+ // Rewrite the outer uses and record lifetime-ending uses.
279
+ SmallVector<Operand *, 4 > consumingUses;
280
+ SmallPtrSet<SILInstruction *, 4 > unclaimedConsumingUsers;
228
281
this ->outerCopy = createOuterCopy (beginBorrow);
229
282
for (Operand *use : outerUses) {
230
283
if (!outerUseInsts.count (use->getUser ())) {
231
- continue ;
284
+ // The immediate use is within this borrow scope.
285
+ BorrowingOperand borrowOper (use);
286
+ if (borrowOper.kind == BorrowingOperandKind::Invalid) {
287
+ continue ;
288
+ }
289
+ // For sub-borrows also check that the scope-ending instructions are
290
+ // within the scope.
291
+ if (borrowOper.visitLocalEndScopeUses ([&](Operand *endBorrow) {
292
+ return !outerUseInsts.count (endBorrow->getUser ());
293
+ })) {
294
+ continue ;
295
+ }
232
296
}
233
297
LLVM_DEBUG (llvm::dbgs () << " Use of outer copy " << *use->getUser ());
234
298
use->set (outerCopy);
299
+ if (use->isLifetimeEnding ()) {
300
+ consumingUses.push_back (use);
301
+ unclaimedConsumingUsers.insert (use->getUser ());
302
+ }
303
+ }
304
+ // Insert a destroy on the outer copy's lifetime frontier, or claim an
305
+ // existing consume.
306
+ ValueLifetimeAnalysis lifetimeAnalysis (outerCopy, outerUseInsts);
307
+ ValueLifetimeAnalysis::Frontier frontier;
308
+ bool result = lifetimeAnalysis.computeFrontier (
309
+ frontier, ValueLifetimeAnalysis::DontModifyCFG, deBlocks);
310
+ assert (result);
311
+ while (!frontier.empty ()) {
312
+ auto *insertPt = frontier.pop_back_val ();
313
+ if (unclaimedConsumingUsers.erase (&*std::prev (insertPt->getIterator ()))) {
314
+ continue ;
315
+ }
316
+ SILBuilderWithScope (insertPt).createDestroyValue (insertPt->getLoc (),
317
+ outerCopy);
235
318
}
319
+ // Add copies for consuming users of outerCopy.
320
+ for (auto *use : consumingUses) {
321
+ // If the user is still in the unclaimedConsumingUsers set, then it does not
322
+ // end the outer copy's lifetime and therefore requires a copy. Only one
323
+ // operand can be claimed as ending the lifetime, so return its user to the
324
+ // unclaimedConsumingUsers set after skipping the first copy.
325
+ auto iterAndInserted = unclaimedConsumingUsers.insert (use->getUser ());
326
+ if (!iterAndInserted.second ) {
327
+ copyLiveUse (use);
328
+ }
329
+ }
330
+ return true ;
236
331
}
237
332
238
333
// ===----------------------------------------------------------------------===//
@@ -650,20 +745,6 @@ void CanonicalizeOSSALifetime::findOrInsertDestroys() {
650
745
// MARK: Step 3. Rewrite copies and destroys
651
746
// ===----------------------------------------------------------------------===//
652
747
653
- // / The lifetime extends beyond given consuming use. Copy the value.
654
- static void copyLiveUse (Operand *use) {
655
- SILInstruction *user = use->getUser ();
656
- SILBuilderWithScope B (user->getIterator ());
657
-
658
- auto loc = RegularLocation::getAutoGeneratedLocation (
659
- user->getLoc ().getSourceLoc ());
660
- auto *copy = B.createCopyValue (loc, use->get ());
661
- use->set (copy);
662
-
663
- ++NumCopiesGenerated;
664
- LLVM_DEBUG (llvm::dbgs () << " Copying at last use " << *copy);
665
- }
666
-
667
748
// / Revisit the def-use chain of currentDef. Mark unneeded original
668
749
// / copies and destroys for deletion. Insert new copies for interior uses that
669
750
// / require ownership of the used operand.
@@ -774,6 +855,8 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
774
855
// ===----------------------------------------------------------------------===//
775
856
776
857
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime (SILValue def) {
858
+ LLVM_DEBUG (llvm::dbgs () << " Canonicalizing: " << def);
859
+
777
860
switch (def.getOwnershipKind ()) {
778
861
case OwnershipKind::None:
779
862
case OwnershipKind::Unowned:
@@ -787,7 +870,10 @@ bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
787
870
}
788
871
// Set outerCopy and persistentCopies and rewrite uses
789
872
// outside the scope.
790
- consolidateBorrowScope ();
873
+ if (!consolidateBorrowScope ()) {
874
+ clearLiveness ();
875
+ return false ;
876
+ }
791
877
// Invalidate book-keeping before deleting instructions.
792
878
clearLiveness ();
793
879
// Rewrite copies and delete extra destroys within the scope.
0 commit comments