@@ -89,6 +89,8 @@ class TempRValueOptPass : public SILFunctionTransform {
8989 bool
9090 checkTempObjectDestroy (AllocStackInst *tempObj, CopyAddrInst *copyInst);
9191
92+ bool extendAccessScopes (CopyAddrInst *copyInst, SILInstruction *lastUseInst);
93+
9294 bool tryOptimizeCopyIntoTemp (CopyAddrInst *copyInst);
9395 std::pair<SILBasicBlock::iterator, bool >
9496 tryOptimizeStoreIntoTemp (StoreInst *si);
@@ -167,12 +169,10 @@ collectLoads(Operand *addressUse, CopyAddrInst *originalCopy,
167169 // %addr = begin_access [read]
168170 // ... // there can be no writes to %addr here
169171 // 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);
176176 }
177177 return true ;
178178 }
@@ -347,6 +347,49 @@ SILInstruction *TempRValueOptPass::getLastUseWhileSourceIsNotModified(
347347 return nullptr ;
348348}
349349
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+
350393// / Return true if the \p tempObj, which is initialized by \p copyInst, is
351394// / destroyed in an orthodox way.
352395// /
@@ -419,13 +462,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
419462
420463 bool isOSSA = copyInst->getFunction ()->hasOwnership ();
421464
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 ();
429466 assert (tempObj != copySrc && " can't initialize temporary with itself" );
430467
431468 // If the source of the copyInst is taken, we must insert a compensating
@@ -487,6 +524,9 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
487524 if (!isOSSA && !checkTempObjectDestroy (tempObj, copyInst))
488525 return false ;
489526
527+ if (!extendAccessScopes (copyInst, lastLoadInst))
528+ return false ;
529+
490530 LLVM_DEBUG (llvm::dbgs () << " Success: replace temp" << *tempObj);
491531
492532 if (needToInsertDestroy) {
@@ -529,7 +569,7 @@ bool TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
529569 auto *li = cast<LoadInst>(user);
530570 if (li->getOwnershipQualifier () == LoadOwnershipQualifier::Take)
531571 li->setOwnershipQualifier (LoadOwnershipQualifier::Copy);
532- use->set (copyInst-> getSrc () );
572+ use->set (copySrc );
533573 break ;
534574 }
535575
@@ -702,7 +742,7 @@ void TempRValueOptPass::run() {
702742 bool changed = false ;
703743
704744 // Find all copy_addr instructions.
705- llvm::SmallVector <CopyAddrInst *, 8 > deadCopies;
745+ llvm::SmallSetVector <CopyAddrInst *, 8 > deadCopies;
706746 for (auto &block : *getFunction ()) {
707747 // Increment the instruction iterator only after calling
708748 // tryOptimizeCopyIntoTemp because the instruction after CopyInst might be
@@ -716,9 +756,9 @@ void TempRValueOptPass::run() {
716756 // calling tryOptimizeCopyIntoTemp or was created by an earlier
717757 // iteration, where another copy_addr copied the temporary back to the
718758 // source location.
719- if (stripAccessMarkers ( copyInst->getSrc () ) == copyInst->getDest ()) {
759+ if (copyInst->getSrc () == copyInst->getDest ()) {
720760 changed = true ;
721- deadCopies.push_back (copyInst);
761+ deadCopies.insert (copyInst);
722762 }
723763 ++ii;
724764 continue ;
@@ -735,21 +775,14 @@ void TempRValueOptPass::run() {
735775 }
736776 }
737777
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+ );
753786
754787 DeadEndBlocks deBlocks (getFunction ());
755788 for (auto *deadCopy : deadCopies) {
0 commit comments