2626#define DEBUG_TYPE " copy-propagation"
2727
2828#include " swift/SIL/BasicBlockUtils.h"
29+ #include " swift/SIL/SILUndef.h"
2930#include " swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
3031#include " swift/SILOptimizer/PassManager/Passes.h"
3132#include " swift/SILOptimizer/PassManager/Transforms.h"
3435
3536using namespace swift ;
3637
38+ // This only applies to -O copy-propagation.
39+ llvm::cl::opt<bool >
40+ EnableRewriteBorrows (" canonical-ossa-rewrite-borrows" ,
41+ llvm::cl::init (false ),
42+ llvm::cl::desc(" Enable rewriting borrow scopes" ));
43+
3744// ===----------------------------------------------------------------------===//
3845// CopyPropagation: Top-Level Function Transform.
3946// ===----------------------------------------------------------------------===//
@@ -44,13 +51,17 @@ class CopyPropagation : public SILFunctionTransform {
4451 bool pruneDebug;
4552 // / True of all values should be canonicalized.
4653 bool canonicalizeAll;
54+ // / If true, then borrow scopes will be canonicalized, allowing copies of
55+ // / guaranteed values to be optimized. Does *not* shrink the borrow scope.
56+ bool canonicalizeBorrows;
4757 // / If true, then new destroy_value instructions will be poison.
4858 bool poisonRefs;
4959
5060public:
51- CopyPropagation (bool pruneDebug, bool canonicalizeAll, bool poisonRefs)
52- : pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
53- poisonRefs (poisonRefs) {}
61+ CopyPropagation (bool pruneDebug, bool canonicalizeAll,
62+ bool canonicalizeBorrows, bool poisonRefs)
63+ : pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
64+ canonicalizeBorrows (canonicalizeBorrows), poisonRefs(poisonRefs) {}
5465
5566 // / The entry point to this function transformation.
5667 void run () override ;
@@ -71,12 +82,18 @@ static bool isCopyDead(CopyValueInst *copy, bool pruneDebug) {
7182 return true ;
7283}
7384
85+ static bool isBorrowDead (BeginBorrowInst *borrow) {
86+ return llvm::all_of (borrow->getUses (), [](Operand *use) {
87+ SILInstruction *user = use->getUser ();
88+ return isa<EndBorrowInst>(user) || user->isDebugInstruction ();
89+ });
90+ }
91+
7492// / Top-level pass driver.
7593void CopyPropagation::run () {
7694 auto *f = getFunction ();
7795 auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
7896 auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
79- auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
8097
8198 // Debug label for unit testing.
8299 LLVM_DEBUG (llvm::dbgs () << " *** CopyPropagation: " << f->getName () << " \n " );
@@ -115,6 +132,12 @@ void CopyPropagation::run() {
115132 || SILValue (extract).getOwnershipKind () != OwnershipKind::Guaranteed)
116133 continue ;
117134
135+ // Bail-out if we won't rewrite borrows because that currently regresses
136+ // Breadcrumbs.MutatedUTF16ToIdx.Mixed/Breadcrumbs.MutatedIdxToUTF16.Mixed.
137+ // Also, mandatory copyprop does not need to rewrite destructures.
138+ if (!canonicalizeBorrows)
139+ continue ;
140+
118141 if (SILValue destructuredResult = convertExtractToDestructure (extract)) {
119142 // Remove to-be-deleted instructions from copiedDeds. The extract cannot
120143 // be in the copiedDefs set since getCanonicalCopiedDef does not allow a
@@ -138,14 +161,13 @@ void CopyPropagation::run() {
138161 }
139162 }
140163 // Perform copy propgation for each copied value.
141- CanonicalizeOSSALifetime canonicalizer (pruneDebug, poisonRefs,
142- accessBlockAnalysis,
143- dominanceAnalysis,
144- deBlocksAnalysis->get (f));
164+ CanonicalizeOSSALifetime canonicalizer (pruneDebug, canonicalizeBorrows,
165+ poisonRefs, accessBlockAnalysis,
166+ dominanceAnalysis);
145167 // Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
146- // copy's source operand is unrecgonized), then thecan copy is itself treated
168+ // copy's source operand is unrecgonized), then the copy is itself treated
147169 // like a def and may be dead after canonicalization.
148- llvm::SmallVector<CopyValueInst *, 4 > deadCopies;
170+ llvm::SmallVector<SILInstruction *, 4 > deadCopies;
149171 for (auto &def : copiedDefs) {
150172 // Canonicalized this def.
151173 canonicalizer.canonicalizeValueLifetime (def);
@@ -155,6 +177,14 @@ void CopyPropagation::run() {
155177 deadCopies.push_back (copy);
156178 }
157179 }
180+ // Dead borrow scopes must be removed as uses before canonicalizing the
181+ // outer copy.
182+ if (auto *borrow = dyn_cast<BeginBorrowInst>(def)) {
183+ if (isBorrowDead (borrow)) {
184+ borrow->setOperand (SILUndef::get (borrow->getType (), *f));
185+ deadCopies.push_back (borrow);
186+ }
187+ }
158188 // Canonicalize any new outer copy.
159189 if (SILValue outerCopy = canonicalizer.createdOuterCopy ()) {
160190 SILValue outerDef = canonicalizer.getCanonicalCopiedDef (outerCopy);
@@ -165,26 +195,29 @@ void CopyPropagation::run() {
165195 }
166196 if (canonicalizer.hasChanged () || !deadCopies.empty ()) {
167197 InstructionDeleter deleter;
168- for (auto *copy : deadCopies) {
169- deleter.recursivelyDeleteUsersIfDead (copy );
198+ for (auto *copyOrBorrow : deadCopies) {
199+ deleter.recursivelyDeleteUsersIfDead (copyOrBorrow );
170200 }
171201 // Preserves NonLocalAccessBlockAnalysis.
172202 accessBlockAnalysis->lockInvalidation ();
173203 invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
174204 accessBlockAnalysis->unlockInvalidation ();
175205 if (f->getModule ().getOptions ().VerifySILOwnership ) {
206+ auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
176207 f->verifyOwnership (deBlocksAnalysis->get (f));
177208 }
178209 }
179210}
180211
181212SILTransform *swift::createMandatoryCopyPropagation () {
182213 return new CopyPropagation (/* pruneDebug*/ true , /* canonicalizeAll*/ true ,
214+ /* canonicalizeBorrows*/ false ,
183215 /* poisonRefs*/ true );
184216}
185217
186218SILTransform *swift::createCopyPropagation () {
187219 return new CopyPropagation (/* pruneDebug*/ true , /* canonicalizeAll*/ false ,
220+ /* canonicalizeBorrows*/ EnableRewriteBorrows,
188221 /* poisonRefs*/ false );
189222}
190223
0 commit comments