18
18
#include " swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
19
19
#include " swift/SILOptimizer/Utils/CFGOptUtils.h"
20
20
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
21
+ #include " swift/SILOptimizer/Utils/OwnershipOptUtils.h"
21
22
#include " swift/SILOptimizer/Utils/SILInliner.h"
22
23
#include " llvm/ADT/SmallPtrSet.h"
23
24
#include " llvm/ADT/SmallVector.h"
@@ -52,6 +53,11 @@ class CheckedCastBrJumpThreading {
52
53
// Enable non-trivial terminator rewriting in OSSA.
53
54
bool EnableOSSARewriteTerminator;
54
55
56
+ InstModCallbacks callbacks;
57
+
58
+ // Shared data structures across OwnershipRAUWHelper instances.
59
+ OwnershipFixupContext rauwContext;
60
+
55
61
// List of predecessors.
56
62
typedef SmallVector<SILBasicBlock *, 8 > PredList;
57
63
@@ -82,8 +88,10 @@ class CheckedCastBrJumpThreading {
82
88
// Copy of CheckedCastBrJumpThreading::FailurePreds.
83
89
PredList FailurePreds;
84
90
// The argument of the dominating checked_cast_br's successor block.
85
- SILValue SuccessArg;
91
+ SILPhiArgument * SuccessArg;
86
92
93
+ // True if the dominating check is inverted AND all the predecessors are on
94
+ // the dominating check's success path.
87
95
bool InvertSuccess;
88
96
89
97
// True if CheckedCastBrJumpThreading::numUnknownPreds is not 0.
@@ -92,13 +100,16 @@ class CheckedCastBrJumpThreading {
92
100
Edit (SILBasicBlock *CCBBlock, bool InvertSuccess,
93
101
const PredList &SuccessPreds,
94
102
const PredList &FailurePreds,
95
- bool hasUnknownPreds, SILValue SuccessArg) :
103
+ bool hasUnknownPreds, SILPhiArgument * SuccessArg) :
96
104
CCBBlock (CCBBlock), SuccessPreds(SuccessPreds), FailurePreds(FailurePreds),
97
105
SuccessArg (SuccessArg), InvertSuccess(InvertSuccess),
98
106
hasUnknownPreds (hasUnknownPreds) { }
99
107
108
+ bool canRAUW (OwnershipFixupContext &rauwContext);
109
+
100
110
void modifyCFGForFailurePreds (BasicBlockCloner &Cloner);
101
- void modifyCFGForSuccessPreds (BasicBlockCloner &Cloner);
111
+ void modifyCFGForSuccessPreds (BasicBlockCloner &Cloner,
112
+ OwnershipFixupContext &rauwContext);
102
113
};
103
114
104
115
// Contains an entry for each checked_cast_br to be optimized.
@@ -135,6 +146,7 @@ class CheckedCastBrJumpThreading {
135
146
bool EnableOSSARewriteTerminator)
136
147
: Fn(Fn), DT(DT), deBlocks(deBlocks),
137
148
EnableOSSARewriteTerminator (EnableOSSARewriteTerminator),
149
+ rauwContext(callbacks, *deBlocks),
138
150
BlocksForWorklist(BlocksForWorklist), BlocksToEdit(Fn),
139
151
BlocksToClone(Fn) {}
140
152
@@ -238,8 +250,54 @@ SILValue CheckedCastBrJumpThreading::isArgValueEquivalentToCondition(
238
250
}
239
251
}
240
252
241
- // / Create a copy of the BB as a landing BB
242
- // / for all FailurePreds.
253
+ // Return false if an ownership RAUW is necessary but cannot be performed.
254
+ bool CheckedCastBrJumpThreading::Edit::
255
+ canRAUW (OwnershipFixupContext &rauwContext) {
256
+ if (InvertSuccess || SuccessPreds.empty ())
257
+ return true ;
258
+
259
+ auto *ccbi = cast<CheckedCastBranchInst>(CCBBlock->getTerminator ());
260
+ auto *oldSuccessArg = ccbi->getSuccessBB ()->getArgument (0 );
261
+ // Check the ownership validity of the RAUW transformation that will replace
262
+ // oldSuccessArg with SuccessArg. This is valid iff it will be valid to
263
+ // replace the new checked_cast_br. The new checked_cast_br will be in a
264
+ // cloned block reachable from a subset of the original block's predecessors,
265
+ // it will have equivalent operands. Checking the current uses is unnecessary,
266
+ // because after cloning, the only use of the cloned checked_cast_br will be
267
+ // a phi in the successor. It is always valid to replace a phi use, because
268
+ // phi itself already guarantees that lifetime extends over its own uses.
269
+ return OwnershipRAUWHelper::hasValidNonLexicalRAUWOwnership (oldSuccessArg,
270
+ SuccessArg);
271
+ }
272
+
273
+ // Erase the checked_cast_br that terminates this block. The caller must replace
274
+ // and erase the successful cast result.
275
+ //
276
+ // The checked_cast_br failure result's uses are replaced with the cast's
277
+ // operand, and the block argument representing that result is deleted. Since
278
+ // the checked_cast's uses now use its forwarded operand, they are still in
279
+ // valid OSSA form, so this can be done before updateOSSAAfterCloning, which
280
+ // doesn't need to know about the erased checked_cast.
281
+ static void eraseCheckedCastBr (
282
+ CheckedCastBranchInst *checkedCastBr,
283
+ CheckedCastBranchInst::SuccessorPath successorIdx) {
284
+
285
+ SILBuilderWithScope Builder (checkedCastBr);
286
+ Builder.createBranch (checkedCastBr->getLoc (),
287
+ checkedCastBr->getSuccessors ()[successorIdx]);
288
+ auto *successBB = checkedCastBr->getSuccessBB ();
289
+ assert (successBB->getNumArguments () == 1 );
290
+ assert (successBB->getArgument (0 )->use_empty ());
291
+ successBB->eraseArgument (0 );
292
+ if (checkedCastBr->getFunction ()->hasOwnership ()) {
293
+ auto *failureBB = checkedCastBr->getFailureBB ();
294
+ assert (failureBB->getNumArguments () == 1 );
295
+ failureBB->getArgument (0 )->replaceAllUsesWith (checkedCastBr->getOperand ());
296
+ failureBB->eraseArgument (0 );
297
+ }
298
+ checkedCastBr->eraseFromParent ();
299
+ }
300
+
243
301
void CheckedCastBrJumpThreading::Edit::modifyCFGForFailurePreds (
244
302
BasicBlockCloner &Cloner) {
245
303
if (FailurePreds.empty ())
@@ -248,12 +306,13 @@ void CheckedCastBrJumpThreading::Edit::modifyCFGForFailurePreds(
248
306
assert (!Cloner.wasCloned ());
249
307
Cloner.cloneBlock ();
250
308
SILBasicBlock *TargetFailureBB = Cloner.getNewBB ();
309
+ // This cloned block branches to the FailureBB, so just delete the cast and
310
+ // ignore the success target which will keep it's original predecessor.
251
311
auto *clonedCCBI =
252
- cast<CheckedCastBranchInst>(TargetFailureBB->getTerminator ());
253
- SILBuilderWithScope Builder (clonedCCBI);
254
- // This BB copy branches to the FailureBB.
255
- Builder.createBranch (clonedCCBI->getLoc (), clonedCCBI->getFailureBB ());
256
- clonedCCBI->eraseFromParent ();
312
+ cast<CheckedCastBranchInst>(TargetFailureBB->getTerminator ());
313
+ auto *clonedSuccessArg = clonedCCBI->getSuccessBB ()->getArgument (0 );
314
+ clonedSuccessArg->replaceAllUsesWithUndef ();
315
+ eraseCheckedCastBr (clonedCCBI, CheckedCastBranchInst::FailIdx);
257
316
258
317
// Redirect all FailurePreds to the copy of BB.
259
318
for (auto *Pred : FailurePreds) {
@@ -262,62 +321,93 @@ void CheckedCastBrJumpThreading::Edit::modifyCFGForFailurePreds(
262
321
replaceBranchTarget (TI, CCBBlock, TargetFailureBB,
263
322
/* PreserveArgs=*/ true );
264
323
}
324
+ Cloner.updateOSSAAfterCloning ();
265
325
}
266
326
267
- // / Create a copy of the BB or reuse BB as
268
- // / a landing basic block for all FailurePreds.
327
+ // / Create a copy of the BB or reuse BB as a landing basic block for all
328
+ // / FailurePreds.
329
+ // /
330
+ // / Note: must be called after modifyCFGForFailurePreds and
331
+ // / before modifyCFGForUnknownPreds.
269
332
void CheckedCastBrJumpThreading::Edit::modifyCFGForSuccessPreds (
270
- BasicBlockCloner &Cloner) {
333
+ BasicBlockCloner &Cloner, OwnershipFixupContext &rauwContext ) {
271
334
335
+ auto *checkedCastBr = cast<CheckedCastBranchInst>(CCBBlock->getTerminator ());
336
+ auto *oldSuccessArg = checkedCastBr->getSuccessBB ()->getArgument (0 );
272
337
if (InvertSuccess) {
273
- auto *CCBI = cast<CheckedCastBranchInst>(CCBBlock-> getTerminator () );
274
- SILBuilderWithScope (CCBI). createBranch (CCBI-> getLoc (),
275
- CCBI-> getFailureBB () );
276
- CCBI-> eraseFromParent ( );
338
+ assert (!hasUnknownPreds && " is not handled, should have been checked " );
339
+ // This success path is unused, so undef its uses and delete the cast.
340
+ oldSuccessArg-> replaceAllUsesWithUndef ( );
341
+ eraseCheckedCastBr (checkedCastBr, CheckedCastBranchInst::FailIdx );
277
342
return ;
278
343
}
279
- if (hasUnknownPreds) {
280
- if (!SuccessPreds.empty ()) {
281
- // Create a copy of the BB as a landing BB.
282
- // for all SuccessPreds.
283
- assert (!Cloner.wasCloned ());
284
- Cloner.cloneBlock ();
285
- SILBasicBlock *TargetSuccessBB = Cloner.getNewBB ();
286
- auto *clonedCCBI =
287
- cast<CheckedCastBranchInst>(TargetSuccessBB->getTerminator ());
288
- SILBuilderWithScope Builder (clonedCCBI);
289
- // This BB copy branches to SuccessBB.
290
- // Take argument value from the dominating BB.
291
- Builder.createBranch (clonedCCBI->getLoc (), clonedCCBI->getSuccessBB (),
292
- {SuccessArg});
293
- clonedCCBI->eraseFromParent ();
294
-
295
- // Redirect all SuccessPreds to the copy of BB.
296
- for (auto *Pred : SuccessPreds) {
297
- TermInst *TI = Pred->getTerminator ();
298
- // Replace branch to BB by branch to TargetSuccessBB.
299
- replaceBranchTarget (TI, CCBBlock, TargetSuccessBB, /* PreserveArgs=*/ true );
300
- }
301
- }
344
+ if (!hasUnknownPreds) {
345
+ // All predecessors are dominated by a successful cast. So the current BB
346
+ // can be re-used instead as their target.
347
+ //
348
+ // NOTE: Assumes that failure predecessors have already been processed and
349
+ // removed from the current block's predecessors.
350
+
351
+ // Replace uses with SuccessArg from the dominating BB. Do this while it is
352
+ // still a valid terminator result, before erasing the cast.
353
+ OwnershipRAUWHelper rauwTransform (rauwContext, oldSuccessArg, SuccessArg);
354
+ assert (rauwTransform.isValid () && " sufficiently checked by canRAUW" );
355
+ rauwTransform.perform ();
356
+
357
+ eraseCheckedCastBr (checkedCastBr, CheckedCastBranchInst::SuccessIdx);
302
358
return ;
303
359
}
304
- // There are no predecessors where it is not clear
305
- // if they are dominated by a success or failure branch
306
- // of DomBB. Therefore, there is no need to clone
307
- // the BB for SuccessPreds. Current BB can be re-used
308
- // instead as their target.
309
-
310
- // Add an unconditional jump at the end of the block.
311
- // Take argument value from the dominating BB
312
- auto *CCBI = cast<CheckedCastBranchInst>(CCBBlock->getTerminator ());
313
- SILBuilderWithScope (CCBI).createBranch (CCBI->getLoc (), CCBI->getSuccessBB (),
314
- {SuccessArg});
315
- CCBI->eraseFromParent ();
360
+ // Only clone if there are preds on the success path.
361
+ if (SuccessPreds.empty ())
362
+ return ;
363
+
364
+ // Create a copy of the BB as a landing BB.
365
+ // for all SuccessPreds.
366
+ assert (!Cloner.wasCloned ());
367
+ Cloner.cloneBlock ();
368
+ SILBasicBlock *clonedCCBBlock = Cloner.getNewBB ();
369
+ // Redirect all SuccessPreds to the copy of BB.
370
+ for (auto *Pred : SuccessPreds) {
371
+ TermInst *TI = Pred->getTerminator ();
372
+ // Replace branch to BB by branch to TargetSuccessBB.
373
+ replaceBranchTarget (TI, CCBBlock, clonedCCBBlock,
374
+ /* PreserveArgs=*/ true );
375
+ }
376
+ // Remove the unreachable checked_cast_br target.
377
+ auto *clonedCCBI =
378
+ cast<CheckedCastBranchInst>(clonedCCBBlock->getTerminator ());
379
+ auto *successBB = clonedCCBI->getSuccessBB ();
380
+ // This cloned block branches to the successBB.
381
+ // The checked_cast_br uses are replaced with SuccessArg.
382
+ if (!CCBBlock->getParent ()->hasOwnership ()) {
383
+ SILBuilderWithScope Builder (clonedCCBI);
384
+ Builder.createBranch (clonedCCBI->getLoc (), successBB, {SuccessArg});
385
+ clonedCCBI->eraseFromParent ();
386
+ Cloner.updateOSSAAfterCloning ();
387
+ return ;
388
+ }
389
+ // Remove all uses from the failure path so RAUW can erase the
390
+ // terminator after replacing the successor argument.
391
+ auto *failureBB = clonedCCBI->getFailureBB ();
392
+ assert (failureBB->getNumArguments () == 1 && " expecting term result" );
393
+ failureBB->getArgument (0 )->replaceAllUsesWithUndef ();
394
+
395
+ // Create nested borrow scopes for new phis either created for the
396
+ // checked_cast's results or during SSA update. This puts the SIL in
397
+ // valid OSSA form before calling OwnershipRAUWHelper.
398
+ Cloner.updateOSSAAfterCloning ();
399
+
400
+ auto *clonedSuccessArg = successBB->getArgument (0 );
401
+ OwnershipRAUWHelper rauwUtil (rauwContext, clonedSuccessArg, SuccessArg);
402
+ assert (rauwUtil.isValid () && " sufficiently checked by canRAUW" );
403
+ rauwUtil.perform ();
404
+
405
+ eraseCheckedCastBr (clonedCCBI, CheckedCastBranchInst::SuccessIdx);
316
406
}
317
407
318
408
// / Handle a special case, where ArgBB is the entry block.
319
- bool CheckedCastBrJumpThreading::handleArgBBIsEntryBlock (SILBasicBlock *ArgBB,
320
- CheckedCastBranchInst *DomCCBI) {
409
+ bool CheckedCastBrJumpThreading::handleArgBBIsEntryBlock (
410
+ SILBasicBlock *ArgBB, CheckedCastBranchInst *DomCCBI) {
321
411
if (!ArgBB->pred_empty ())
322
412
return false ;
323
413
@@ -644,7 +734,8 @@ bool CheckedCastBrJumpThreading::trySimplify(CheckedCastBranchInst *CCBI) {
644
734
// Record what we want to change.
645
735
Edit *edit = new (EditAllocator.Allocate ())
646
736
Edit (BB, InvertSuccess, SuccessPreds, FailurePreds,
647
- numUnknownPreds != 0 , DomCCBI->getSuccessBB ()->getArgument (0 ));
737
+ numUnknownPreds != 0 ,
738
+ cast<SILPhiArgument>(DomCCBI->getSuccessBB ()->getArgument (0 )));
648
739
Edits.push_back (edit);
649
740
650
741
return true ;
@@ -684,12 +775,15 @@ void CheckedCastBrJumpThreading::optimizeFunction() {
684
775
if (!Cloner.canCloneBlock ())
685
776
continue ;
686
777
778
+ if (Fn->hasOwnership () && !edit->canRAUW (rauwContext))
779
+ continue ;
780
+
687
781
// Create a copy of the BB as a landing BB
688
782
// for all FailurePreds.
689
783
edit->modifyCFGForFailurePreds (Cloner);
690
784
// Create a copy of the BB or reuse BB as
691
785
// a landing basic block for all SuccessPreds.
692
- edit->modifyCFGForSuccessPreds (Cloner);
786
+ edit->modifyCFGForSuccessPreds (Cloner, rauwContext );
693
787
694
788
if (Cloner.wasCloned ()) {
695
789
Cloner.updateOSSAAfterCloning ();
0 commit comments