@@ -175,6 +175,8 @@ bool SILCombiner::trySinkOwnedForwardingInst(SingleValueInstruction *svi) {
175
175
[](Operand *use) { return !use->isLifetimeEnding (); }))
176
176
return false ;
177
177
178
+ LLVM_DEBUG (llvm::dbgs () << " Sink forwarding: " << *svi << ' \n ' );
179
+
178
180
// Otherwise, delete all of the debug uses so we don't have to sink them as
179
181
// well and then return true so we process svi in its new position.
180
182
deleteAllDebugUses (svi, getInstModCallbacks ());
@@ -209,6 +211,10 @@ bool SILCombiner::trySinkOwnedForwardingInst(SingleValueInstruction *svi) {
209
211
}
210
212
211
213
auto *newSVI = svi->clone (sviUser);
214
+
215
+ LLVM_DEBUG (llvm::dbgs ()
216
+ << " Sink forwarding: " << *svi << " to " << *newSVI << ' \n ' );
217
+
212
218
Worklist.add (newSVI);
213
219
sviUse->set (newSVI);
214
220
}
@@ -220,51 +226,70 @@ bool SILCombiner::trySinkOwnedForwardingInst(SingleValueInstruction *svi) {
220
226
221
227
// / Canonicalize each extended OSSA lifetime that contains an instruction newly
222
228
// / created during this SILCombine iteration.
223
- void SILCombiner::canonicalizeOSSALifetimes () {
229
+ // /
230
+ // / \p currentInst is null if the current instruction was deleted during its
231
+ // / SILCombine.
232
+ // /
233
+ // / Avoid endless worklist iteration as follows:
234
+ // /
235
+ // / - Canonicalization only runs on the canonical definition of the visited
236
+ // / instruction if it was itself a copy or any new copies were inserted
237
+ // / as a result of optimization.
238
+ // /
239
+ // / - Instructions are only added back to the SILCombine worklist when
240
+ // / canonicalization deletes an instruction. Only the canonical def being
241
+ // / processed and its uses are added rather than arbitrary operands of the
242
+ // / deleted instruction. This ensures that an instruction is only added back
243
+ // / to the worklist after SILCombine either directly optimized it or created a
244
+ // / new copy_value for which it is the canonical def or its use.
245
+ void SILCombiner::canonicalizeOSSALifetimes (SILInstruction *currentInst) {
224
246
if (!enableCopyPropagation || !Builder.hasOwnership ())
225
247
return ;
226
248
227
249
SmallSetVector<SILValue, 16 > defsToCanonicalize;
228
- for (auto *trackedInst : *Builder.getTrackingList ()) {
229
- if (trackedInst->isDeleted ())
230
- continue ;
231
250
232
- if ( auto *cvi = dyn_cast<CopyValueInst>(trackedInst)) {
233
- SILValue def = CanonicalizeOSSALifetime::getCanonicalCopiedDef (cvi);
234
-
235
- // getCanonicalCopiedDef returns a copy whenever that the copy's source is
236
- // guaranteed. In that case, find the root of the borrowed lifetime. If it
237
- // is a function argument, then a simple guaranteed canonicalization can
238
- // be performed. Canonicalizing other borrow scopes is not handled by
239
- // SILCombine because it's not a single-lifetime canonicalization.
240
- // Instead, SILCombine treats a copy that uses a borrowed value as a
241
- // separate owned live range. Handling the compensation code across the
242
- // borrow scope boundary requires post processing in a particular order.
243
- // The copy propagation pass knows how to handle that. To avoid complexity
244
- // and ensure fast convergence, rewriting borrow scopes should not be
245
- // combined with other unrelated transformations.
246
- if ( auto *copy = dyn_cast<CopyValueInst>(def)) {
247
- if (SILValue borrowDef =
248
- CanonicalizeBorrowScope::getCanonicalBorrowedDef (
249
- copy-> getOperand ())) {
250
- if (isa<SILFunctionArgument>(borrowDef )) {
251
- def = borrowDef;
252
- }
251
+ // copyInst was either optimized by a SILCombine visitor or is a copy_value
252
+ // produced by the visitor. Find the canonical def.
253
+ auto recordCopiedDef = [&defsToCanonicalize](CopyValueInst *copyInst) {
254
+ SILValue def = CanonicalizeOSSALifetime:: getCanonicalCopiedDef(copyInst);
255
+
256
+ // getCanonicalCopiedDef returns a copy whenever that the copy's source is
257
+ // guaranteed. In that case, find the root of the borrowed lifetime. If it
258
+ // is a function argument, then a simple guaranteed canonicalization can be
259
+ // performed. Canonicalizing other borrow scopes is not handled by
260
+ // SILCombine because it's not a single-lifetime canonicalization. Instead,
261
+ // SILCombine treats a copy that uses a borrowed value as a separate owned
262
+ // live range. Handling the compensation code across the borrow scope
263
+ // boundary requires post processing in a particular order. The copy
264
+ // propagation pass knows how to handle that. To avoid complexity and ensure
265
+ // fast convergence, rewriting borrow scopes should not be combined with
266
+ // other unrelated transformations.
267
+ if ( auto *copyDef = dyn_cast<CopyValueInst>(def)) {
268
+ if (SILValue borrowDef = CanonicalizeBorrowScope::getCanonicalBorrowedDef (
269
+ copyDef-> getOperand () )) {
270
+ if (isa<SILFunctionArgument>( borrowDef)) {
271
+ def = borrowDef;
253
272
}
254
273
}
255
- defsToCanonicalize.insert (def);
256
274
}
275
+ defsToCanonicalize.insert (def);
276
+ };
277
+
278
+ if (auto *copyInst = dyn_cast_or_null<CopyValueInst>(currentInst))
279
+ recordCopiedDef (copyInst);
280
+
281
+ for (auto *trackedInst : *Builder.getTrackingList ()) {
282
+ if (trackedInst->isDeleted ())
283
+ continue ;
284
+ if (auto *copyInst = dyn_cast<CopyValueInst>(trackedInst))
285
+ recordCopiedDef (copyInst);
257
286
}
258
287
if (defsToCanonicalize.empty ())
259
288
return ;
260
289
261
290
// Remove instructions deleted during canonicalization from SILCombine's
262
291
// worklist. CanonicalizeOSSALifetime invalidates operands before invoking
263
292
// the deletion callback.
264
- //
265
- // Note: a simpler approach would be to drain the Worklist before
266
- // canonicalizing OSSA, then callbacks could be completely removed from
267
- // CanonicalizeOSSALifetime.
268
293
auto canonicalizeCallbacks =
269
294
InstModCallbacks ().onDelete ([this ](SILInstruction *instToDelete) {
270
295
eraseInstFromFunction (*instToDelete,
@@ -279,23 +304,30 @@ void SILCombiner::canonicalizeOSSALifetimes() {
279
304
280
305
while (!defsToCanonicalize.empty ()) {
281
306
SILValue def = defsToCanonicalize.pop_back_val ();
282
- if (auto functionArg = dyn_cast<SILFunctionArgument>(def)) {
283
- if (!borrowCanonicalizer.canonicalizeFunctionArgument (functionArg))
284
- continue ;
285
- } else if (!canonicalizer.canonicalizeValueLifetime (def)) {
286
- continue ;
287
- }
288
- MadeChange = true ;
289
307
290
- // Canonicalization may rewrite many copies and destroys within a single
291
- // extended lifetime, but the extended lifetime of each canonical def is
292
- // non-overlapping. If def was successfully canonicalized, simply add it
293
- // and its users to the SILCombine worklist.
294
- if (auto *inst = def->getDefiningInstruction ()) {
295
- Worklist.add (inst);
308
+ deleter.getCallbacks ().resetHadCallbackInvocation ();
309
+
310
+ auto canonicalized = [&]() {
311
+ if (!deleter.getCallbacks ().hadCallbackInvocation ())
312
+ return ;
313
+
314
+ if (auto *inst = def->getDefiningInstruction ()) {
315
+ Worklist.add (inst);
316
+ }
317
+ for (auto *use : def->getUses ()) {
318
+ Worklist.add (use->getUser ());
319
+ }
320
+ };
321
+
322
+ if (def->getOwnershipKind () == OwnershipKind::Guaranteed) {
323
+ if (auto functionArg = dyn_cast<SILFunctionArgument>(def)) {
324
+ if (borrowCanonicalizer.canonicalizeFunctionArgument (functionArg))
325
+ canonicalized ();
326
+ }
327
+ continue ;
296
328
}
297
- for ( auto *use : def-> getUses ( )) {
298
- Worklist. add (use-> getUser () );
329
+ if (canonicalizer. canonicalizeValueLifetime (def )) {
330
+ canonicalized ( );
299
331
}
300
332
}
301
333
}
@@ -367,27 +399,29 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) {
367
399
Builder.setInsertionPoint (I);
368
400
369
401
#ifndef NDEBUG
370
- std::string OrigI ;
402
+ std::string OrigIStr ;
371
403
#endif
372
- LLVM_DEBUG (llvm::raw_string_ostream SS (OrigI ); I->print (SS);
373
- OrigI = SS.str (););
374
- LLVM_DEBUG (llvm::dbgs () << " SC: Visiting: " << OrigI << ' \n ' );
404
+ LLVM_DEBUG (llvm::raw_string_ostream SS (OrigIStr ); I->print (SS);
405
+ OrigIStr = SS.str (););
406
+ LLVM_DEBUG (llvm::dbgs () << " SC: Visiting: " << OrigIStr << ' \n ' );
375
407
408
+ SILInstruction *currentInst = I;
376
409
if (SILInstruction *Result = visit (I)) {
377
410
++NumCombined;
378
411
// Should we replace the old instruction with a new one?
379
412
Worklist.replaceInstructionWithInstruction (I, Result
380
413
#ifndef NDEBUG
381
- ,
382
- OrigI
414
+ ,
415
+ OrigIStr
383
416
#endif
384
417
);
418
+ currentInst = Result;
385
419
MadeChange = true ;
386
420
}
387
421
388
422
// Eliminate copies created that this SILCombine iteration may have
389
423
// introduced during OSSA-RAUW.
390
- canonicalizeOSSALifetimes ();
424
+ canonicalizeOSSALifetimes (currentInst-> isDeleted () ? nullptr : currentInst );
391
425
392
426
// Builder's tracking list has been accumulating instructions created by the
393
427
// during this SILCombine iteration. To finish this iteration, go through
0 commit comments