@@ -100,6 +100,238 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy,
100
100
return phiArg;
101
101
}
102
102
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
+
103
335
// ===----------------------------------------------------------------------===//
104
336
// Ownership RAUW Helper Functions
105
337
// ===----------------------------------------------------------------------===//
0 commit comments