Skip to content

Commit f9cdf26

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 21e28cb commit f9cdf26

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,43 @@ 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(
299+
consumingBlocks.getArrayRef());
300+
for (auto &block : *function) {
301+
if (!deadEnds->isDeadEnd(&block))
302+
continue;
303+
completeConsumingBlocks.push_back(&block);
304+
}
305+
PrunedLivenessBoundary boundary;
306+
liveness->computeBoundary(boundary, completeConsumingBlocks);
307+
308+
lastUsers.emplace();
309+
for (auto *lastUser : boundary.lastUsers) {
310+
lastUsers->insert(lastUser);
311+
}
312+
}
313+
return lastUsers->contains(instruction);
314+
};
315+
for (auto pair : liveness->getAllUsers()) {
316+
if (!pair.second.isEnding())
317+
continue;
318+
auto *instruction = pair.first;
319+
if (isConsumeOnBoundary(instruction))
320+
continue;
321+
// Demote instruction's lifetime-ending-ness to non-lifetime-ending.
322+
completeLiveness.updateForUse(pair.first, /*lifetimeEnding=*/false);
323+
}
324+
288325
OSSALifetimeCompletion::visitAvailabilityBoundary(
289326
getCurrentDef(), completeLiveness, [&](auto *unreachable, auto end) {
290327
if (end == OSSALifetimeCompletion::LifetimeEnd::Boundary) {

test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,3 +857,30 @@ exit:
857857
%retval = tuple ()
858858
return %retval : $()
859859
}
860+
861+
// CHECK-LABEL: begin running test {{.*}} on consume_copy_before_use_in_dead_end
862+
// CHECK-LABEL: sil [ossa] @consume_copy_before_use_in_dead_end : {{.*}} {
863+
// CHECK-NOT: destroy_value [dead_end]
864+
// CHECK-LABEL: } // end sil function 'consume_copy_before_use_in_dead_end'
865+
// CHECK-LABEL: end running test {{.*}} on consume_copy_before_use_in_dead_end
866+
sil [ossa] @consume_copy_before_use_in_dead_end : $@convention(thin) (@owned C) -> () {
867+
entry(%c : @owned $C):
868+
cond_br undef, exit, die
869+
870+
exit:
871+
destroy_value %c
872+
%retval = tuple ()
873+
return %retval
874+
875+
die:
876+
%move = move_value [lexical] %c
877+
%copy = copy_value %move
878+
specify_test "canonicalize_ossa_lifetime true false true %move"
879+
apply undef(%move) : $@convention(thin) (@owned C) -> ()
880+
%addr = alloc_stack $C
881+
%token = store_borrow %copy to %addr
882+
apply undef() : $@convention(thin) () -> ()
883+
%reload = load_borrow %token
884+
apply undef(%reload) : $@convention(thin) (@guaranteed C) -> ()
885+
unreachable
886+
}

0 commit comments

Comments
 (0)