@@ -100,6 +100,238 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy,
100100 return phiArg;
101101}
102102
103+ // ===----------------------------------------------------------------------===//
104+ // BorrowedLifetimeExtender
105+ // ===----------------------------------------------------------------------===//
106+
107+ // / Model an extended borrow scope, including transitive reborrows. This applies
108+ // / to "local" borrow scopes (begin_borrow, load_borrow, & phi).
109+ // /
110+ // / Allow extending the lifetime of an owned value that dominates this borrowed
111+ // / value across that extended borrow scope. This handles uses of reborrows that
112+ // / are not dominated by the owned value by generating phis and copying the
113+ // / borrowed values the reach this borrow scope from non-dominated paths.
114+ // /
115+ // / This produces somewhat canonical owned phis, although that isn't a
116+ // / requirement for valid SIL. Given an owned value, a dominated borrowed value,
117+ // / and a reborrow:
118+ // /
119+ // / %ownedValue = ...
120+ // / %borrowedValue = ...
121+ // / %reborrow = phi(%borrowedValue, %otherBorrowedValue)
122+ // /
123+ // / %otherBorrowedValue will always be copied even if %ownedValue also dominates
124+ // / %otherBorrowedValue, as such:
125+ // /
126+ // / %otherCopy = copy_value %borrowedValue
127+ // / %newPhi = phi(%ownedValue, %otherCopy)
128+ // /
129+ // / The immediate effect is to produce an unnecesssary copy, but it avoids
130+ // / extending %ownedValue's liveness to new paths and hopefully simplifies
131+ // / downstream optimization and debugging. Unnecessary copies could be
132+ // / avoided with simple dominance check if it becomes desirable to do so.
133+ struct BorrowedLifetimeExtender {
134+ BorrowedValue borrowedValue;
135+
136+ // Owned value currently being extended over borrowedValue.
137+ SILValue currentOwnedValue;
138+
139+ InstModCallbacks &callbacks;
140+
141+ llvm::SmallVector<PhiValue, 4 > reborrowedPhis;
142+ llvm::SmallDenseMap<PhiValue, PhiValue, 4 > reborrowedToOwnedPhis;
143+
144+ // / Check that all reaching operands are handled. This can be removed once the
145+ // / utility and OSSA representation are stable.
146+ SWIFT_ASSERT_ONLY_DECL (llvm::SmallDenseSet<PhiOperand, 4 > reborrowedOperands);
147+
148+ // / Initially map the reborrowed phi to an invalid value prior to creating the
149+ // / owned phi.
150+ void discoverReborrow (PhiValue reborrowedPhi) {
151+ if (reborrowedToOwnedPhis.try_emplace (reborrowedPhi, PhiValue ()).second ) {
152+ reborrowedPhis.push_back (reborrowedPhi);
153+ }
154+ }
155+
156+ // / Remap the reborrowed phi to an valid owned phi after creating it.
157+ void mapOwnedPhi (PhiValue reborrowedPhi, PhiValue ownedPhi) {
158+ reborrowedToOwnedPhis[reborrowedPhi] = ownedPhi;
159+ }
160+
161+ // / Get the owned value associated with this reborrowed operand, or return an
162+ // / invalid SILValue indicating that the borrowed lifetime does not reach this
163+ // / operand.
164+ SILValue getExtendedOwnedValue (PhiOperand reborrowedOper) {
165+ // If this operand reborrows the original borrow, then the currentOwned phi
166+ // reaches it directly.
167+ SILValue borrowSource = reborrowedOper.getSource ();
168+ if (borrowSource == borrowedValue.value )
169+ return currentOwnedValue;
170+
171+ // Check if the borrowed operand's source is already mapped to an owned phi.
172+ auto reborrowedAndOwnedPhi = reborrowedToOwnedPhis.find (borrowSource);
173+ if (reborrowedAndOwnedPhi != reborrowedToOwnedPhis.end ()) {
174+ // Return the already-mapped owned phi.
175+ assert (reborrowedOperands.erase (reborrowedOper));
176+ return reborrowedAndOwnedPhi->second ;
177+ }
178+ // The owned value does not reach this reborrowed operand.
179+ assert (
180+ !reborrowedOperands.count (reborrowedOper)
181+ && " reachable borrowed phi operand must be mapped to an owned value" );
182+ return SILValue ();
183+ }
184+
185+ public:
186+ // / Precondition: \p borrowedValue must introduce a local borrow scope
187+ // / (begin_borrow, load_borrow, & phi).
188+ BorrowedLifetimeExtender (BorrowedValue borrowedValue,
189+ InstModCallbacks &callbacks)
190+ : borrowedValue(borrowedValue), callbacks(callbacks) {
191+ assert (borrowedValue.isLocalScope () && " expect a valid borrowed value" );
192+ }
193+
194+ // / Extend \p ownedValue over this extended borrow scope.
195+ // /
196+ // / Precondition: \p ownedValue dominates this borrowed value.
197+ void extendOverBorrowScopeAndConsume (SILValue ownedValue);
198+
199+ protected:
200+ void analyzeExtendedScope ();
201+
202+ SILValue createCopyAtEdge (PhiOperand reborrowOper);
203+
204+ void destroyAtScopeEnd (SILValue ownedValue, BorrowedValue pairedBorrow);
205+ };
206+
207+ // Gather all transitive phi-reborrows and check that all the borrowed uses can
208+ // be found with no escapes.
209+ //
210+ // Calls discoverReborrow to populate reborrowedPhis.
211+ void BorrowedLifetimeExtender::analyzeExtendedScope () {
212+ auto visitReborrow = [&](Operand *endScope) {
213+ if (auto borrowingOper = BorrowingOperand (endScope)) {
214+ assert (borrowingOper.isReborrow ());
215+
216+ SWIFT_ASSERT_ONLY (reborrowedOperands.insert (endScope));
217+
218+ // TODO: if non-phi reborrows are added, handle multiple results.
219+ discoverReborrow (borrowingOper.getBorrowIntroducingUserResult ().value );
220+ }
221+ return true ;
222+ };
223+
224+ bool result = borrowedValue.visitLocalScopeEndingUses (visitReborrow);
225+ assert (result && " visitReborrow always succeeds, escapes are irrelevant" );
226+
227+ // Note: Iterate in the same manner as findExtendedTransitiveGuaranteedUses(),
228+ // but using BorrowedLifetimeExtender's own reborrowedPhis.
229+ for (unsigned idx = 0 ; idx < reborrowedPhis.size (); ++idx) {
230+ auto borrowedValue = BorrowedValue (reborrowedPhis[idx]);
231+ result = borrowedValue.visitLocalScopeEndingUses (visitReborrow);
232+ assert (result && " visitReborrow always succeeds, escapes are irrelevant" );
233+ }
234+ }
235+
236+ // Insert a copy on this edge. This might not be necessary if the owned
237+ // value dominates this path, but this avoids forcing the owned value to be
238+ // live across new paths.
239+ //
240+ // TODO: consider copying the base of the borrowed value instead of the
241+ // borrowed value directly. It's likely that the copy is used outside of the
242+ // borrow scope, in which case, canonicalizeOSSA will create a copy outside
243+ // the borrow scope anyway. However, we can't be sure that the base is the
244+ // same type.
245+ //
246+ // TODO: consider reusing copies that dominate multiple reborrowed
247+ // operands. Howeer, this requires copying in an earlier block and inserting
248+ // post-dominating destroys, which may be better handled in an ownership phi
249+ // canonicalization pass.
250+ SILValue BorrowedLifetimeExtender::createCopyAtEdge (PhiOperand reborrowOper) {
251+ auto *branch = reborrowOper.getBranch ();
252+ auto loc = RegularLocation::getAutoGeneratedLocation (branch->getLoc ());
253+ auto *copy = SILBuilderWithScope (branch).createCopyValue (
254+ loc, reborrowOper.getSource ());
255+ callbacks.createdNewInst (copy);
256+ return copy;
257+ }
258+
259+ // Destroy \p ownedValue at \p pairedBorrow's scope-ending uses, excluding
260+ // reborrows.
261+ //
262+ // Precondition: ownedValue takes ownership of its value at the same point as
263+ // pairedBorrow. e.g. an owned and guaranteed pair of phis.
264+ void BorrowedLifetimeExtender::destroyAtScopeEnd (SILValue ownedValue,
265+ BorrowedValue pairedBorrow) {
266+ pairedBorrow.visitLocalScopeEndingUses ([&](Operand *scopeEnd) {
267+ if (scopeEnd->getOperandOwnership () == OperandOwnership::Reborrow)
268+ return true ;
269+
270+ auto *endInst = scopeEnd->getUser ();
271+ assert (!isa<TermInst>(endInst) && " branch must be a reborrow" );
272+ auto *destroyPt = &*std::next (endInst->getIterator ());
273+ auto *destroy = SILBuilderWithScope (destroyPt).createDestroyValue (
274+ destroyPt->getLoc (), ownedValue);
275+ callbacks.createdNewInst (destroy);
276+ return true ;
277+ });
278+ }
279+
280+ // Insert and map an owned phi for each reborrowed phi.
281+ //
282+ // For each reborrowed phi, insert a copy on each edge that does not originate
283+ // from the extended borrowedValue.
284+ //
285+ // TODO: If non-phi reborrows are added, they would also need to be
286+ // mapped to their owned counterpart. This means generating new owned
287+ // struct/destructure instructions.
288+ void BorrowedLifetimeExtender::
289+ extendOverBorrowScopeAndConsume (SILValue ownedValue) {
290+ currentOwnedValue = ownedValue;
291+
292+ // Populate the reborrowedPhis vector.
293+ analyzeExtendedScope ();
294+
295+ InstructionDeleter deleter (callbacks);
296+
297+ // Generate and map the phis with undef operands first, in case of recursion.
298+ auto undef = SILUndef::get (ownedValue->getType (), *ownedValue->getFunction ());
299+ for (PhiValue reborrowedPhi : reborrowedPhis) {
300+ auto *phiBlock = reborrowedPhi.phiBlock ;
301+ auto *ownedPhi = phiBlock->createPhiArgument (ownedValue->getType (),
302+ OwnershipKind::Owned);
303+ for (auto *predBlock : phiBlock->getPredecessorBlocks ()) {
304+ TermInst *ti = predBlock->getTerminator ();
305+ addNewEdgeValueToBranch (ti, phiBlock, undef, deleter);
306+ }
307+ mapOwnedPhi (reborrowedPhi, PhiValue (ownedPhi));
308+ }
309+ // Generate copies and set the phi operands.
310+ for (PhiValue reborrowedPhi : reborrowedPhis) {
311+ PhiValue ownedPhi = reborrowedToOwnedPhis[reborrowedPhi];
312+ reborrowedPhi.getValue ()->visitIncomingPhiOperands (
313+ // For each reborrowed operand, get the owned value for that edge,
314+ // and set the owned phi's operand.
315+ [&](Operand *reborrowedOper) {
316+ SILValue ownedVal = getExtendedOwnedValue (reborrowedOper);
317+ if (!ownedVal) {
318+ ownedVal = createCopyAtEdge (reborrowedOper);
319+ }
320+ BranchInst *branch = PhiOperand (reborrowedOper).getBranch ();
321+ branch->getOperandRef (ownedPhi.argIndex ).set (ownedVal);
322+ return true ;
323+ });
324+ }
325+ assert (reborrowedOperands.empty () && " not all phi operands are handled" );
326+
327+ // Create destroys at the last uses.
328+ destroyAtScopeEnd (ownedValue, borrowedValue);
329+ for (PhiValue reborrowedPhi : reborrowedPhis) {
330+ PhiValue ownedPhi = reborrowedToOwnedPhis[reborrowedPhi];
331+ destroyAtScopeEnd (ownedPhi, BorrowedValue (reborrowedPhi));
332+ }
333+ }
334+
103335// ===----------------------------------------------------------------------===//
104336// Ownership RAUW Helper Functions
105337// ===----------------------------------------------------------------------===//
0 commit comments