@@ -40,36 +40,73 @@ STATISTIC(NumLoadCopyConvertedToLoadBorrow,
40
40
// /
41
41
// / Semantically this implies that a value is never passed off as +1 to memory
42
42
// / or another function implying it can be used everywhere at +0.
43
- static bool isConsumed (SILValue v,
44
- SmallVectorImpl<DestroyValueInst *> &destroys) {
43
+ static bool isConsumed (
44
+ SILValue v, SmallVectorImpl<DestroyValueInst *> &destroys,
45
+ NullablePtr<SmallVectorImpl<SILInstruction *>> forwardingInsts = nullptr ) {
45
46
assert (v.getOwnershipKind () == ValueOwnershipKind::Owned);
46
- return !all_of (v->getUses (), [&destroys](Operand *op) {
47
- // We know that a copy_value produces an @owned value. Look
48
- // through all of our uses and classify them as either
49
- // invalidating or not invalidating. Make sure that all of the
50
- // invalidating ones are destroy_value since otherwise the
51
- // live_range is not complete.
47
+ SmallVector<Operand *, 32 > worklist (v->use_begin (), v->use_end ());
48
+ while (!worklist.empty ()) {
49
+ auto *op = worklist.pop_back_val ();
50
+
51
+ // Skip type dependent operands.
52
+ if (op->isTypeDependent ())
53
+ continue ;
54
+
55
+ auto *user = op->getUser ();
56
+
57
+ // We know that a copy_value produces an @owned value. Look through all of
58
+ // our uses and classify them as either invalidating or not
59
+ // invalidating. Make sure that all of the invalidating ones are
60
+ // destroy_value since otherwise the live_range is not complete.
52
61
auto map = op->getOwnershipKindMap ();
53
62
auto constraint = map.getLifetimeConstraint (ValueOwnershipKind::Owned);
54
63
switch (constraint) {
55
64
case UseLifetimeConstraint::MustBeInvalidated: {
56
65
// See if we have a destroy value. If we don't we have an
57
66
// unknown consumer. Return false, we need this live range.
58
- auto *dvi = dyn_cast<DestroyValueInst>(op->getUser ());
59
- if (!dvi)
60
- return false ;
67
+ if (auto *dvi = dyn_cast<DestroyValueInst>(user)) {
68
+ destroys.push_back (dvi);
69
+ continue ;
70
+ }
71
+
72
+ // Otherwise, see if we have a forwarding value that has a single
73
+ // non-trivial operand that can accept a guaranteed value. If so, at its
74
+ // users to the worklist and continue.
75
+ //
76
+ // DISCUSSION: For now we do not support forwarding instructions with
77
+ // multiple non-trivial arguments since we would need to optimize all of
78
+ // the non-trivial arguments at the same time.
79
+ //
80
+ // NOTE: Today we do not support TermInsts for simplicity... we /could/
81
+ // support it though if we need to.
82
+ if (forwardingInsts.isNonNull () && !isa<TermInst>(user) &&
83
+ isGuaranteedForwardingInst (user) &&
84
+ 1 == count_if (user->getOperandValues (
85
+ true /* ignore type dependent operands*/ ),
86
+ [&](SILValue v) {
87
+ return v.getOwnershipKind () ==
88
+ ValueOwnershipKind::Owned;
89
+ })) {
90
+ forwardingInsts.get ()->push_back (user);
91
+ for (SILValue v : user->getResults ()) {
92
+ copy (v->getUses (), std::back_inserter (worklist));
93
+ }
94
+ continue ;
95
+ }
61
96
62
- // Otherwise, return true and stash this destroy value.
63
- destroys.push_back (dvi);
97
+ // Otherwise be conservative and assume that we /may consume/ the value.
64
98
return true ;
65
99
}
66
100
case UseLifetimeConstraint::MustBeLive:
67
101
// Ok, this constraint can take something owned as live. Lets
68
102
// see if it can also take something that is guaranteed. If it
69
103
// can not, then we bail.
70
- return map.canAcceptKind (ValueOwnershipKind::Guaranteed);
104
+ map.canAcceptKind (ValueOwnershipKind::Guaranteed);
105
+ continue ;
71
106
}
72
- });
107
+ }
108
+
109
+ return false ;
73
110
}
74
111
75
112
// ===----------------------------------------------------------------------===//
@@ -168,13 +205,14 @@ static bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi) {
168
205
if (!canHandleOperand (cvi->getOperand (), borrowIntroducers))
169
206
return false ;
170
207
171
- // Then go over all of our uses. Find our destroying instructions
172
- // and make sure all of them are destroy_value. For our
173
- // non-destroying instructions, make sure that they accept a
174
- // guaranteed value. After that , make sure that our destroys are
175
- // within the lifetime of our borrowed values.
208
+ // Then go over all of our uses. Find our destroying instructions (ignoring
209
+ // forwarding instructions that can forward both owned and guaranteed) and
210
+ // make sure all of them are destroy_value. For our non-destroying
211
+ // instructions , make sure that they accept a guaranteed value. After that,
212
+ // make sure that our destroys are within the lifetime of our borrowed values.
176
213
SmallVector<DestroyValueInst *, 16 > destroys;
177
- if (isConsumed (cvi, destroys))
214
+ SmallVector<SILInstruction *, 16 > guaranteedForwardingInsts;
215
+ if (isConsumed (cvi, destroys, &guaranteedForwardingInsts))
178
216
return false ;
179
217
180
218
// If we reached this point, then we know that all of our users can
@@ -189,15 +227,56 @@ static bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi) {
189
227
assert (all_of (borrowIntroducers,
190
228
[](SILValue v) { return isa<SILFunctionArgument>(v); }));
191
229
192
- // Otherwise, we know that our copy_value/destroy_values are all
193
- // completely within the guaranteed value scope.
230
+ // Otherwise, we know that our copy_value/destroy_values are all completely
231
+ // within the guaranteed value scope. First delete the destroys/copies .
194
232
while (!destroys.empty ()) {
195
233
auto *dvi = destroys.pop_back_val ();
196
234
dvi->eraseFromParent ();
197
235
++NumEliminatedInsts;
198
236
}
199
237
cvi->replaceAllUsesWith (cvi->getOperand ());
200
238
cvi->eraseFromParent ();
239
+
240
+ // Then change all of our guaranteed forwarding insts to have guaranteed
241
+ // ownership kind instead of what ever they previously had (ignoring trivial
242
+ // results);
243
+ while (!guaranteedForwardingInsts.empty ()) {
244
+ auto *i = guaranteedForwardingInsts.pop_back_val ();
245
+
246
+ assert (i->hasResults ());
247
+
248
+ for (SILValue result : i->getResults ()) {
249
+ if (auto *svi = dyn_cast<OwnershipForwardingSingleValueInst>(result)) {
250
+ if (svi->getOwnershipKind () == ValueOwnershipKind::Owned) {
251
+ svi->setOwnershipKind (ValueOwnershipKind::Guaranteed);
252
+ }
253
+ continue ;
254
+ }
255
+
256
+ if (auto *ofci = dyn_cast<OwnershipForwardingConversionInst>(result)) {
257
+ if (ofci->getOwnershipKind () == ValueOwnershipKind::Owned) {
258
+ ofci->setOwnershipKind (ValueOwnershipKind::Guaranteed);
259
+ }
260
+ continue ;
261
+ }
262
+
263
+ if (auto *sei = dyn_cast<OwnershipForwardingSelectEnumInstBase>(result)) {
264
+ if (sei->getOwnershipKind () == ValueOwnershipKind::Owned) {
265
+ sei->setOwnershipKind (ValueOwnershipKind::Guaranteed);
266
+ }
267
+ continue ;
268
+ }
269
+
270
+ if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(result)) {
271
+ if (mvir->getOwnershipKind () == ValueOwnershipKind::Owned) {
272
+ mvir->setOwnershipKind (ValueOwnershipKind::Guaranteed);
273
+ }
274
+ continue ;
275
+ }
276
+
277
+ llvm_unreachable (" unhandled forwarding instruction?!" );
278
+ }
279
+ }
201
280
++NumEliminatedInsts;
202
281
return true ;
203
282
}
@@ -350,6 +429,11 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
350
429
// load_borrow.
351
430
auto *lbi =
352
431
SILBuilderWithScope (li).createLoadBorrow (li->getLoc (), li->getOperand ());
432
+
433
+ // Since we are looking through forwarding uses that can accept guaranteed
434
+ // parameters, we can have multiple destroy_value along the same path. We need
435
+ // to find the post-dominating block set of these destroy value to ensure that
436
+ // we do not insert multiple end_borrow.
353
437
while (!destroyValues.empty ()) {
354
438
auto *dvi = destroyValues.pop_back_val ();
355
439
SILBuilderWithScope (dvi).createEndBorrow (dvi->getLoc (), lbi);
0 commit comments