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 " );
@@ -100,15 +117,57 @@ void CopyPropagation::run() {
100117 }
101118 }
102119 }
120+ // Push copy_value instructions above their struct_extract operands by
121+ // inserting destructures.
122+ //
123+ // copiedDefs be be modified, but it never shrinks
124+ for (unsigned idx = 0 ; idx < copiedDefs.size (); ++idx) {
125+ SILValue def = copiedDefs[idx];
126+ auto *copy = dyn_cast<CopyValueInst>(def);
127+ if (!copy)
128+ continue ;
129+
130+ auto *extract = dyn_cast<StructExtractInst>(copy->getOperand ());
131+ if (!extract
132+ || SILValue (extract).getOwnershipKind () != OwnershipKind::Guaranteed)
133+ continue ;
134+
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+
141+ if (SILValue destructuredResult = convertExtractToDestructure (extract)) {
142+ // Remove to-be-deleted instructions from copiedDeds. The extract cannot
143+ // be in the copiedDefs set since getCanonicalCopiedDef does not allow a
144+ // guaranteed projection to be a canonical def.
145+ copiedDefs.remove (copy);
146+ --idx; // point back to the current element, which was erased.
147+
148+ // TODO: unfortunately SetVector has no element replacement.
149+ copiedDefs.insert (destructuredResult);
150+
151+ auto *destructure = cast<DestructureStructInst>(
152+ destructuredResult.getDefiningInstruction ());
153+ auto *newCopy = cast<CopyValueInst>(destructure->getOperand ());
154+ copiedDefs.insert (
155+ CanonicalizeOSSALifetime::getCanonicalCopiedDef (newCopy));
156+
157+ LLVM_DEBUG (llvm::dbgs () << " Destructure Conversion:\n "
158+ << *extract << " to " << *destructure);
159+ // Delete both the copy and the extract.
160+ InstructionDeleter ().recursivelyDeleteUsersIfDead (extract);
161+ }
162+ }
103163 // Perform copy propgation for each copied value.
104- CanonicalizeOSSALifetime canonicalizer (pruneDebug, poisonRefs,
105- accessBlockAnalysis,
106- dominanceAnalysis,
107- deBlocksAnalysis->get (f));
164+ CanonicalizeOSSALifetime canonicalizer (pruneDebug, canonicalizeBorrows,
165+ poisonRefs, accessBlockAnalysis,
166+ dominanceAnalysis);
108167 // Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
109168 // copy's source operand is unrecgonized), then the copy is itself treated
110169 // like a def and may be dead after canonicalization.
111- llvm::SmallVector<CopyValueInst *, 4 > deadCopies;
170+ llvm::SmallVector<SILInstruction *, 4 > deadCopies;
112171 for (auto &def : copiedDefs) {
113172 // Canonicalized this def.
114173 canonicalizer.canonicalizeValueLifetime (def);
@@ -118,6 +177,14 @@ void CopyPropagation::run() {
118177 deadCopies.push_back (copy);
119178 }
120179 }
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+ }
121188 // Canonicalize any new outer copy.
122189 if (SILValue outerCopy = canonicalizer.createdOuterCopy ()) {
123190 SILValue outerDef = canonicalizer.getCanonicalCopiedDef (outerCopy);
@@ -128,26 +195,29 @@ void CopyPropagation::run() {
128195 }
129196 if (canonicalizer.hasChanged () || !deadCopies.empty ()) {
130197 InstructionDeleter deleter;
131- for (auto *copy : deadCopies) {
132- deleter.recursivelyDeleteUsersIfDead (copy );
198+ for (auto *copyOrBorrow : deadCopies) {
199+ deleter.recursivelyDeleteUsersIfDead (copyOrBorrow );
133200 }
134201 // Preserves NonLocalAccessBlockAnalysis.
135202 accessBlockAnalysis->lockInvalidation ();
136203 invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
137204 accessBlockAnalysis->unlockInvalidation ();
138205 if (f->getModule ().getOptions ().VerifySILOwnership ) {
206+ auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
139207 f->verifyOwnership (deBlocksAnalysis->get (f));
140208 }
141209 }
142210}
143211
144212SILTransform *swift::createMandatoryCopyPropagation () {
145213 return new CopyPropagation (/* pruneDebug*/ true , /* canonicalizeAll*/ true ,
214+ /* canonicalizeBorrows*/ false ,
146215 /* poisonRefs*/ true );
147216}
148217
149218SILTransform *swift::createCopyPropagation () {
150219 return new CopyPropagation (/* pruneDebug*/ true , /* canonicalizeAll*/ false ,
220+ /* canonicalizeBorrows*/ EnableRewriteBorrows,
151221 /* poisonRefs*/ false );
152222}
153223
0 commit comments