@@ -89,6 +89,8 @@ class TempRValueOptPass : public SILFunctionTransform {
89
89
bool
90
90
checkTempObjectDestroy (AllocStackInst *tempObj, CopyAddrInst *copyInst);
91
91
92
+ bool extendAccessScopes (CopyAddrInst *copyInst, SILInstruction *lastUseInst);
93
+
92
94
bool tryOptimizeCopyIntoTemp (CopyAddrInst *copyInst);
93
95
std::pair<SILBasicBlock::iterator, bool >
94
96
tryOptimizeStoreIntoTemp (StoreInst *si);
@@ -167,12 +169,10 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy,
167
169
// %addr = begin_access [read]
168
170
// ... // there can be no writes to %addr here
169
171
// end_acess %addr // <- This is where the use actually ends.
170
- for (Operand *accessUse : beginAccess->getUses ()) {
171
- if (auto *endAccess = dyn_cast<EndAccessInst>(accessUse->getUser ())) {
172
- if (endAccess->getParent () != block)
173
- return false ;
174
- loadInsts.insert (endAccess);
175
- }
172
+ for (EndAccessInst *endAccess : beginAccess->getEndAccesses ()) {
173
+ if (endAccess->getParent () != block)
174
+ return false ;
175
+ loadInsts.insert (endAccess);
176
176
}
177
177
return true ;
178
178
}
@@ -347,6 +347,49 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified(
347
347
return nullptr ;
348
348
}
349
349
350
+ // / Tries to move an end_access down to extend the access scope over all uses
351
+ // / of the temporary. For example:
352
+ // /
353
+ // / %a = begin_access %src
354
+ // / copy_addr %a to [initialization] %temp : $*T
355
+ // / end_access %a
356
+ // / use %temp
357
+ // /
358
+ // / We must not replace %temp with %a after the end_access. Instead we try to
359
+ // / move the end_access after "use %temp".
360
+ bool TempRValueOptPass::extendAccessScopes (
361
+ CopyAddrInst *copyInst, SILInstruction *lastUseInst) {
362
+
363
+ SILValue copySrc = copyInst->getSrc ();
364
+ EndAccessInst *endAccessToMove = nullptr ;
365
+ auto begin = std::next (copyInst->getIterator ());
366
+ auto end = std::next (lastUseInst->getIterator ());
367
+
368
+ for (SILInstruction &inst : make_range (begin, end)) {
369
+ if (auto *endAccess = dyn_cast<EndAccessInst>(&inst)) {
370
+ // Is this the end of an access scope of the copy-source?
371
+ if (!aa->isNoAlias (copySrc, endAccess->getSource ())) {
372
+ // To keep things simple, we can just move a single end_access.
373
+ if (endAccessToMove)
374
+ return false ;
375
+ endAccessToMove = endAccess;
376
+ }
377
+ } else if (endAccessToMove) {
378
+ // We cannot move an end_access over a begin_access. This would destroy
379
+ // the proper nesting of accesses.
380
+ if (isa<BeginAccessInst>(&inst))
381
+ return false ;
382
+ // Don't extend a read-access scope over a (potential) write.
383
+ if (aa->mayWriteToMemory (&inst, endAccessToMove->getSource ()))
384
+ return false ;
385
+ }
386
+ }
387
+ if (endAccessToMove)
388
+ endAccessToMove->moveAfter (lastUseInst);
389
+
390
+ return true ;
391
+ }
392
+
350
393
// / Return true if the \p tempObj, which is initialized by \p copyInst, is
351
394
// / destroyed in an orthodox way.
352
395
// /
@@ -419,13 +462,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
419
462
420
463
bool isOSSA = copyInst->getFunction ()->hasOwnership ();
421
464
422
- // The copy's source address must not be a scoped instruction, like
423
- // begin_borrow. When the temporary object is eliminated, it's uses are
424
- // replaced with the copy's source. Therefore, the source address must be
425
- // valid at least until the next instruction that may write to or destroy the
426
- // source. End-of-scope markers, such as end_borrow, do not write to or
427
- // destroy memory, so scoped addresses are not valid replacements.
428
- SILValue copySrc = stripAccessMarkers (copyInst->getSrc ());
465
+ SILValue copySrc = copyInst->getSrc ();
429
466
assert (tempObj != copySrc && " can't initialize temporary with itself" );
430
467
431
468
// If the source of the copyInst is taken, we must insert a compensating
@@ -487,6 +524,9 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
487
524
if (!isOSSA && !checkTempObjectDestroy (tempObj, copyInst))
488
525
return false ;
489
526
527
+ if (!extendAccessScopes (copyInst, lastLoadInst))
528
+ return false ;
529
+
490
530
LLVM_DEBUG (llvm::dbgs () << " Success: replace temp" << *tempObj);
491
531
492
532
if (needToInsertDestroy) {
@@ -529,7 +569,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
529
569
auto *li = cast<LoadInst>(user);
530
570
if (li->getOwnershipQualifier () == LoadOwnershipQualifier::Take)
531
571
li->setOwnershipQualifier (LoadOwnershipQualifier::Copy);
532
- use->set (copyInst-> getSrc () );
572
+ use->set (copySrc );
533
573
break ;
534
574
}
535
575
@@ -702,7 +742,7 @@ void TempRValueOptPass::run() {
702
742
bool changed = false ;
703
743
704
744
// Find all copy_addr instructions.
705
- llvm::SmallVector <CopyAddrInst *, 8 > deadCopies;
745
+ llvm::SmallSetVector <CopyAddrInst *, 8 > deadCopies;
706
746
for (auto &block : *getFunction ()) {
707
747
// Increment the instruction iterator only after calling
708
748
// tryOptimizeCopyIntoTemp because the instruction after CopyInst might be
@@ -716,9 +756,9 @@ void TempRValueOptPass::run() {
716
756
// calling tryOptimizeCopyIntoTemp or was created by an earlier
717
757
// iteration, where another copy_addr copied the temporary back to the
718
758
// source location.
719
- if (stripAccessMarkers ( copyInst->getSrc () ) == copyInst->getDest ()) {
759
+ if (copyInst->getSrc () == copyInst->getDest ()) {
720
760
changed = true ;
721
- deadCopies.push_back (copyInst);
761
+ deadCopies.insert (copyInst);
722
762
}
723
763
++ii;
724
764
continue ;
@@ -735,21 +775,14 @@ void TempRValueOptPass::run() {
735
775
}
736
776
}
737
777
738
- // Delete the copies and any unused address operands.
739
- // The same copy may have been added multiple times.
740
- sortUnique (deadCopies);
741
- InstModCallbacks callbacks{
742
- #ifndef NDEBUG
743
- // With asserts, we include this assert. Otherwise, we use the default
744
- // impl for perf.
745
- [](SILInstruction *instToKill) {
746
- // SimplifyInstruction is not in the business of removing
747
- // copy_addr. If it were, then we would need to update deadCopies.
748
- assert (!isa<CopyAddrInst>(instToKill));
749
- instToKill->eraseFromParent ();
750
- }
751
- #endif
752
- };
778
+ InstModCallbacks callbacks (
779
+ [](SILInstruction *instToKill) {
780
+ // SimplifyInstruction is not in the business of removing
781
+ // copy_addr. If it were, then we would need to update deadCopies.
782
+ assert (!isa<CopyAddrInst>(instToKill));
783
+ instToKill->eraseFromParent ();
784
+ }
785
+ );
753
786
754
787
DeadEndBlocks deBlocks (getFunction ());
755
788
for (auto *deadCopy : deadCopies) {
0 commit comments