Skip to content

Commit 30024b3

Browse files
committed
[OSSACanOwned] Fix liveness passed to completion.
To determine where a lifetime ends within dead-end blocks, OSSACanonicalizeOwned uses OSSACompleteLifetime's visitAvailabilityBoundary. This API takes a liveness which it uses to walk forward to the availability boundary. Specifically, the liveness passed from OSSACanonicalizeOwned to OSSACompleteLifetime is a variation of OSSACanonicalizeOwned's own liveness (it has destroys added). There is a mismatch in the characteristics of livenesses created by OSSACanonicalizeOwned and OSSACompleteLifetime: The former deals with not only direct uses of a value but also uses of its copies; that introduces the possibility for consuming uses in the middle of liveness. The latter on the other hand deals only with uses of a single value (nestedly, but at each level of nesting only a single value). Passing a liveness from the former to the latter without handling this mismatch is incorrect: OSSACompleteLifetime understands consuming uses to always end a lifetime, even when they are in the middle of a copy-extended liveness. The result is that OSSACompleteLifetime produces non-sensical results when provided with such a liveness. To address this, fixup the liveness passed from OSSACanonicalizeOwned to OSSACompleteLifetime by demoting consuming uses that appear within (that's to say _not_ on the boundary) of liveness to non-consuming uses. rdar://142846936
1 parent 4135540 commit 30024b3

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,44 @@ void CanonicalizeOSSALifetime::extendLivenessToDeadEnds() {
285285
completeLiveness.updateForUse(destroy, /*lifetimeEnding*/ true);
286286
}
287287

288+
// Demote consuming uses within complete liveness to non-consuming uses.
289+
//
290+
// OSSALifetimeCompletion considers the lifetime of a single value. Such
291+
// lifetimes never continue beyond consumes.
292+
std::optional<llvm::SmallPtrSet<SILInstruction *, 8>> lastUsers;
293+
auto isConsumeOnBoundary = [&](SILInstruction *instruction) -> bool {
294+
if (!lastUsers) {
295+
// Avoid computing lastUsers if possible.
296+
auto *function = getCurrentDef()->getFunction();
297+
auto *deadEnds = deadEndBlocksAnalysis->get(function);
298+
llvm::SmallVector<SILBasicBlock *, 8> completeConsumingBlocks(consumingBlocks.getArrayRef());
299+
for (auto &block : *function) {
300+
if (!deadEnds->isDeadEnd(&block))
301+
continue;
302+
completeConsumingBlocks.push_back(&block);
303+
}
304+
PrunedLivenessBoundary boundary;
305+
liveness->computeBoundary(boundary, completeConsumingBlocks);
306+
307+
lastUsers.emplace();
308+
for (auto *lastUser : boundary.lastUsers) {
309+
lastUsers->insert(lastUser);
310+
}
311+
}
312+
return lastUsers->contains(instruction);
313+
};
314+
for (auto pair : liveness->getAllUsers()) {
315+
if (!pair.second.isEnding())
316+
continue;
317+
auto *instruction = pair.first;
318+
if (isa<TermInst>(instruction))
319+
continue;
320+
if (isConsumeOnBoundary(instruction))
321+
continue;
322+
// Demote instruction's lifetime-ending-ness to non-lifetime-ending.
323+
completeLiveness.updateForUse(pair.first, /*lifetimeEnding=*/false);
324+
}
325+
288326
OSSALifetimeCompletion::visitAvailabilityBoundary(
289327
getCurrentDef(), completeLiveness, [&](auto *unreachable, auto end) {
290328
if (end == OSSALifetimeCompletion::LifetimeEnd::Boundary) {

test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,3 +857,25 @@ exit:
857857
%retval = tuple ()
858858
return %retval : $()
859859
}
860+
861+
sil [ossa] @consume_copy_before_use_in_dead_end : $@convention(thin) (@owned C) -> () {
862+
entry(%c : @owned $C):
863+
cond_br undef, exit, die
864+
865+
exit:
866+
destroy_value %c
867+
%retval = tuple ()
868+
return %retval
869+
870+
die:
871+
%move = move_value [lexical] %c
872+
%copy = copy_value %move
873+
specify_test "canonicalize_ossa_lifetime true false true %move"
874+
apply undef(%move) : $@convention(thin) (@owned C) -> ()
875+
%addr = alloc_stack $C
876+
%token = store_borrow %copy to %addr
877+
apply undef() : $@convention(thin) () -> ()
878+
%reload = load_borrow %token
879+
apply undef(%reload) : $@convention(thin) (@guaranteed C) -> ()
880+
unreachable
881+
}

0 commit comments

Comments
 (0)