Skip to content

Commit 24370b7

Browse files
authored
Merge pull request #32130 from gottesmm/release/5.3-rdar63188362
[5.3] [semantic-arc-opts] Use all consuming uses instead of just destroying uses when validating if a LiveRange is alive in a scope.
2 parents 38b2066 + 2f10202 commit 24370b7

File tree

2 files changed

+95
-25
lines changed

2 files changed

+95
-25
lines changed

lib/SILOptimizer/Transforms/SemanticARCOpts.cpp

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,36 @@ class LiveRange {
5252
/// introducer and not to be forwarding.
5353
OwnedValueIntroducer introducer;
5454

55+
/// A vector that we store all of our uses into.
56+
///
57+
/// Some properties of this array are:
58+
///
59+
/// 1. It is only mutated in the constructor of LiveRange.
60+
///
61+
/// 2. destroyingUses, ownershipForwardingUses, and unknownConsumingUses are
62+
/// views into this array. We store the respective uses in the aforementioned
63+
/// order. This is why it is important not to mutate consumingUses after we
64+
/// construct the LiveRange since going from small -> large could invalidate
65+
/// the uses.
66+
SmallVector<Operand *, 6> consumingUses;
67+
5568
/// A list of destroy_values of the live range.
56-
SmallVector<Operand *, 2> destroyingUses;
69+
///
70+
/// This is just a view into consuming uses.
71+
ArrayRef<Operand *> destroyingUses;
5772

5873
/// A list of forwarding instructions that forward owned ownership, but that
5974
/// are also able to be converted to guaranteed ownership. If we are able to
6075
/// eliminate this LiveRange due to it being from a guaranteed value, we must
6176
/// flip the ownership of all of these instructions to guaranteed from owned.
6277
///
6378
/// Corresponds to isOwnershipForwardingInst(...).
64-
SmallVector<Operand *, 2> ownershipForwardingUses;
79+
ArrayRef<Operand *> ownershipForwardingUses;
6580

6681
/// Consuming uses that we were not able to understand as a forwarding
6782
/// instruction or a destroy_value. These must be passed a strongly control
6883
/// equivalent +1 value.
69-
SmallVector<Operand *, 2> unknownConsumingUses;
84+
ArrayRef<Operand *> unknownConsumingUses;
7085

7186
public:
7287
LiveRange(SILValue value);
@@ -87,6 +102,11 @@ class LiveRange {
87102
HasConsumingUse_t
88103
hasUnknownConsumingUse(bool assumingFixedPoint = false) const;
89104

105+
/// Return an array ref to /all/ consuming uses. Will include all 3 sorts of
106+
/// consuming uses: destroying uses, forwarding consuming uses, and unknown
107+
/// forwarding instruction.
108+
ArrayRef<Operand *> getAllConsumingUses() const { return consumingUses; }
109+
90110
ArrayRef<Operand *> getDestroyingUses() const { return destroyingUses; }
91111

92112
private:
@@ -121,7 +141,7 @@ class LiveRange {
121141
return ownershipForwardingUses;
122142
}
123143

124-
void convertOwnedGeneralForwardingUsesToGuaranteed();
144+
void convertOwnedGeneralForwardingUsesToGuaranteed() &&;
125145

126146
/// A consuming operation that:
127147
///
@@ -192,6 +212,10 @@ LiveRange::LiveRange(SILValue value)
192212
ownershipForwardingUses(), unknownConsumingUses() {
193213
assert(introducer.value.getOwnershipKind() == ValueOwnershipKind::Owned);
194214

215+
SmallVector<Operand *, 32> tmpDestroyingUses;
216+
SmallVector<Operand *, 32> tmpForwardingConsumingUses;
217+
SmallVector<Operand *, 32> tmpUnknownConsumingUses;
218+
195219
// We know that our silvalue produces an @owned value. Look through all of our
196220
// uses and classify them as either consuming or not.
197221
SmallVector<Operand *, 32> worklist(introducer.value->getUses());
@@ -220,7 +244,7 @@ LiveRange::LiveRange(SILValue value)
220244
// Ok, we know now that we have a consuming use. See if we have a destroy
221245
// value, quickly up front. If we do have one, stash it and continue.
222246
if (isa<DestroyValueInst>(user)) {
223-
destroyingUses.push_back(op);
247+
tmpDestroyingUses.push_back(op);
224248
continue;
225249
}
226250

@@ -244,13 +268,13 @@ LiveRange::LiveRange(SILValue value)
244268
return v.getOwnershipKind() ==
245269
ValueOwnershipKind::Owned;
246270
})) {
247-
unknownConsumingUses.push_back(op);
271+
tmpUnknownConsumingUses.push_back(op);
248272
continue;
249273
}
250274

251275
// Ok, this is a forwarding instruction whose ownership we can flip from
252276
// owned -> guaranteed.
253-
ownershipForwardingUses.push_back(op);
277+
tmpForwardingConsumingUses.push_back(op);
254278

255279
// If we have a non-terminator, just visit its users recursively to see if
256280
// the the users force the live range to be alive.
@@ -284,6 +308,20 @@ LiveRange::LiveRange(SILValue value)
284308
}
285309
}
286310
}
311+
312+
// The order in which we append these to consumingUses matters since we assume
313+
// their order as an invariant. This is done to ensure that we can pass off
314+
// all of our uses or individual sub-arrays of our users without needing to
315+
// move around memory.
316+
llvm::copy(tmpDestroyingUses, std::back_inserter(consumingUses));
317+
llvm::copy(tmpForwardingConsumingUses, std::back_inserter(consumingUses));
318+
llvm::copy(tmpUnknownConsumingUses, std::back_inserter(consumingUses));
319+
320+
auto cUseArrayRef = llvm::makeArrayRef(consumingUses);
321+
destroyingUses = cUseArrayRef.take_front(tmpDestroyingUses.size());
322+
ownershipForwardingUses = cUseArrayRef.slice(
323+
tmpDestroyingUses.size(), tmpForwardingConsumingUses.size());
324+
unknownConsumingUses = cUseArrayRef.take_back(tmpUnknownConsumingUses.size());
287325
}
288326

289327
void LiveRange::insertEndBorrowsAtDestroys(
@@ -339,9 +377,10 @@ void LiveRange::insertEndBorrowsAtDestroys(
339377
}
340378
}
341379

342-
void LiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() {
380+
void LiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() && {
343381
while (!ownershipForwardingUses.empty()) {
344-
auto *i = ownershipForwardingUses.pop_back_val()->getUser();
382+
auto *i = ownershipForwardingUses.back()->getUser();
383+
ownershipForwardingUses = ownershipForwardingUses.drop_back();
345384

346385
// If this is a term inst, just convert all of its incoming values that are
347386
// owned to be guaranteed.
@@ -402,7 +441,8 @@ void LiveRange::convertToGuaranteedAndRAUW(SILValue newGuaranteedValue,
402441
InstModCallbacks callbacks) && {
403442
auto *value = cast<SingleValueInstruction>(introducer.value);
404443
while (!destroyingUses.empty()) {
405-
auto *d = destroyingUses.pop_back_val();
444+
auto *d = destroyingUses.back();
445+
destroyingUses = destroyingUses.drop_back();
406446
callbacks.deleteInst(d->getUser());
407447
++NumEliminatedInsts;
408448
}
@@ -412,7 +452,7 @@ void LiveRange::convertToGuaranteedAndRAUW(SILValue newGuaranteedValue,
412452
// Then change all of our guaranteed forwarding insts to have guaranteed
413453
// ownership kind instead of what ever they previously had (ignoring trivial
414454
// results);
415-
convertOwnedGeneralForwardingUsesToGuaranteed();
455+
std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed();
416456
}
417457

418458
void LiveRange::convertArgToGuaranteed(DeadEndBlocks &deadEndBlocks,
@@ -429,15 +469,16 @@ void LiveRange::convertArgToGuaranteed(DeadEndBlocks &deadEndBlocks,
429469

430470
// Then eliminate all of the destroys...
431471
while (!destroyingUses.empty()) {
432-
auto *d = destroyingUses.pop_back_val();
472+
auto *d = destroyingUses.back();
473+
destroyingUses = destroyingUses.drop_back();
433474
callbacks.deleteInst(d->getUser());
434475
++NumEliminatedInsts;
435476
}
436477

437478
// and change all of our guaranteed forwarding insts to have guaranteed
438479
// ownership kind instead of what ever they previously had (ignoring trivial
439480
// results);
440-
convertOwnedGeneralForwardingUsesToGuaranteed();
481+
std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed();
441482
}
442483

443484
LiveRange::HasConsumingUse_t
@@ -1297,8 +1338,9 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
12971338
SmallVector<Operand *, 8> scratchSpace;
12981339
SmallPtrSet<SILBasicBlock *, 4> visitedBlocks;
12991340
if (llvm::any_of(borrowScopeIntroducers, [&](BorrowedValue borrowScope) {
1300-
return !borrowScope.areUsesWithinScope(
1301-
destroys, scratchSpace, visitedBlocks, getDeadEndBlocks());
1341+
return !borrowScope.areUsesWithinScope(lr.getAllConsumingUses(),
1342+
scratchSpace, visitedBlocks,
1343+
getDeadEndBlocks());
13021344
})) {
13031345
return false;
13041346
}
@@ -1330,12 +1372,11 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
13301372
return false;
13311373
}
13321374

1333-
if (llvm::any_of(borrowScopeIntroducers,
1334-
[&](BorrowedValue borrowScope) {
1335-
return !borrowScope.areUsesWithinScope(
1336-
phiArgLR.getDestroyingUses(), scratchSpace,
1337-
visitedBlocks, getDeadEndBlocks());
1338-
})) {
1375+
if (llvm::any_of(borrowScopeIntroducers, [&](BorrowedValue borrowScope) {
1376+
return !borrowScope.areUsesWithinScope(
1377+
phiArgLR.getAllConsumingUses(), scratchSpace, visitedBlocks,
1378+
getDeadEndBlocks());
1379+
})) {
13391380
return false;
13401381
}
13411382
}
@@ -1641,7 +1682,7 @@ class StorageGuaranteesLoadVisitor
16411682
SmallPtrSet<SILBasicBlock *, 4> visitedBlocks;
16421683
LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks());
16431684
if (!checker.validateLifetime(access, endScopeUses,
1644-
liveRange.getDestroyingUses())) {
1685+
liveRange.getAllConsumingUses())) {
16451686
// If we fail the linear lifetime check, then just recur:
16461687
return next(access->getOperand());
16471688
}
@@ -1738,8 +1779,8 @@ class StorageGuaranteesLoadVisitor
17381779
LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks());
17391780

17401781
// Returns true on success. So we invert.
1741-
bool foundError = !checker.validateLifetime(baseObject, endScopeInsts,
1742-
liveRange.getDestroyingUses());
1782+
bool foundError = !checker.validateLifetime(
1783+
baseObject, endScopeInsts, liveRange.getAllConsumingUses());
17431784
return answer(foundError);
17441785
}
17451786

@@ -1777,7 +1818,7 @@ class StorageGuaranteesLoadVisitor
17771818
// Returns true on success. So we invert.
17781819
bool foundError = !checker.validateLifetime(
17791820
stack, destroyAddrOperands /*consuming users*/,
1780-
liveRange.getDestroyingUses() /*non consuming users*/);
1821+
liveRange.getAllConsumingUses() /*non consuming users*/);
17811822
return answer(foundError);
17821823
}
17831824

test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class ClassLet {
5959
@_hasStorage let aLet: Klass
6060
@_hasStorage var aVar: Klass
6161
@_hasStorage let aLetTuple: (Klass, Klass)
62+
@_hasStorage let anOptionalLet: FakeOptional<Klass>
6263

6364
@_hasStorage let anotherLet: ClassLet
6465
}
@@ -2204,3 +2205,31 @@ bb6:
22042205
%9999 = tuple()
22052206
return %9999 : $()
22062207
}
2208+
2209+
// Make sure that we do not promote the load [copy] to a load_borrow since it
2210+
// has a use outside of the access scope.
2211+
//
2212+
// CHECK-LABEL: sil [ossa] @deadEndBlockDoNotPromote : $@convention(method) (@guaranteed ClassLet) -> () {
2213+
// CHECK: load_borrow
2214+
// CHECK: load [copy]
2215+
// CHECK: } // end sil function 'deadEndBlockDoNotPromote'
2216+
sil [ossa] @deadEndBlockDoNotPromote : $@convention(method) (@guaranteed ClassLet) -> () {
2217+
bb0(%0 : @guaranteed $ClassLet):
2218+
%4 = ref_element_addr %0 : $ClassLet, #ClassLet.anotherLet
2219+
%5 = load [copy] %4 : $*ClassLet
2220+
%6 = begin_borrow %5 : $ClassLet
2221+
%7 = ref_element_addr %6 : $ClassLet, #ClassLet.anOptionalLet
2222+
%8 = begin_access [read] [dynamic] %7 : $*FakeOptional<Klass>
2223+
%9 = load [copy] %8 : $*FakeOptional<Klass>
2224+
end_access %8 : $*FakeOptional<Klass>
2225+
end_borrow %6 : $ClassLet
2226+
destroy_value %5 : $ClassLet
2227+
switch_enum %9 : $FakeOptional<Klass>, case #FakeOptional.none!enumelt: bb1, case #FakeOptional.some!enumelt: bb2
2228+
2229+
bb1:
2230+
%107 = tuple ()
2231+
return %107 : $()
2232+
2233+
bb2(%39 : @owned $Klass):
2234+
unreachable
2235+
}

0 commit comments

Comments
 (0)