Skip to content

Commit 81910bf

Browse files
committed
[PrunedLiveness] Fix boundary check for dead-ends.
Consider dead, dead-end blocks within the availability boundary to be within the boundary. rdar://126965232
1 parent 187485f commit 81910bf

File tree

3 files changed

+97
-4
lines changed

3 files changed

+97
-4
lines changed

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,51 @@ bool PrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
489489
llvm_unreachable("instruction must be in its parent block");
490490
}
491491

492-
static bool checkDeadEnd(SILInstruction *inst, DeadEndBlocks *deadEndBlocks) {
493-
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
492+
/// Whether \p parent is a dead (reported to be dead by `liveBlocks`), dead-end
493+
/// (such as an infinite loop) block within the availability boundary (where
494+
/// the value has not been consumed).
495+
static bool checkDeadEnd(SILBasicBlock *parent, DeadEndBlocks *deadEndBlocks,
496+
PrunedLiveBlocks const &liveBlocks) {
497+
if (!deadEndBlocks) {
498+
return false;
499+
}
500+
if (!deadEndBlocks->isDeadEnd(parent)) {
501+
return false;
502+
}
503+
if (liveBlocks.getBlockLiveness(parent) != PrunedLiveBlocks::Dead) {
504+
return false;
505+
}
506+
// Check whether the value is available in `parent` (i.e. not consumed on any
507+
// path to it):
508+
//
509+
// Search backward until LiveOut or LiveWithin blocks are reached.
510+
// (1) If ALL the reached blocks are LiveOut, then `parent` IS within the
511+
// availability boundary.
512+
// (2) If ANY reached block is LiveWithin, the value was consumed in that
513+
// reached block, preventing the value from being available at `parent`,
514+
// so `parent` is NOT within the availability boundary.
515+
BasicBlockWorklist worklist(parent->getFunction());
516+
worklist.push(parent);
517+
while (auto *block = worklist.pop()) {
518+
auto isLive = liveBlocks.getBlockLiveness(block);
519+
switch (isLive) {
520+
case PrunedLiveBlocks::Dead: {
521+
// Availability is unchanged; continue the backwards walk.
522+
for (auto *predecessor : block->getPredecessorBlocks()) {
523+
worklist.pushIfNotVisited(predecessor);
524+
}
525+
break;
526+
}
527+
case PrunedLiveBlocks::LiveWithin:
528+
// Availability ended in this block. Some path to `parent` consumed the
529+
// value. Case (2) above.
530+
return false;
531+
case PrunedLiveBlocks::LiveOut:
532+
// Availability continued out of this block. Case (1) above.
533+
continue;
534+
}
535+
}
536+
return true;
494537
}
495538

496539
template <typename LivenessWithDefs>
@@ -500,7 +543,8 @@ bool PrunedLiveRange<LivenessWithDefs>::areUsesWithinBoundary(
500543

501544
for (auto *use : uses) {
502545
auto *user = use->getUser();
503-
if (!asImpl().isWithinBoundary(user) && !checkDeadEnd(user, deadEndBlocks))
546+
if (!asImpl().isWithinBoundary(user) &&
547+
!checkDeadEnd(user->getParent(), deadEndBlocks, liveBlocks))
504548
return false;
505549
}
506550
return true;
@@ -513,7 +557,8 @@ bool PrunedLiveRange<LivenessWithDefs>::areUsesOutsideBoundary(
513557

514558
for (auto *use : uses) {
515559
auto *user = use->getUser();
516-
if (asImpl().isWithinBoundary(user) || checkDeadEnd(user, deadEndBlocks))
560+
if (asImpl().isWithinBoundary(user) ||
561+
checkDeadEnd(user->getParent(), deadEndBlocks, liveBlocks))
517562
return false;
518563
}
519564
return true;

test/SILOptimizer/cse_ossa_nontrivial.sil

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,29 @@ bb0(%0 : @guaranteed $WrapperKlass):
723723
return %res : $()
724724
}
725725

726+
// CHECK-LABEL: sil [ossa] @test_refelementaddr_loop : {{.*}} {
727+
// CHECK: ref_element_addr
728+
// CHECK: ref_element_addr
729+
// CHECK-LABEL: } // end sil function 'test_refelementaddr_loop'
730+
sil [ossa] @test_refelementaddr_loop : $@convention(thin) () -> () {
731+
bb0:
732+
%2 = apply undef() : $@convention(thin) () -> @owned WrapperKlass
733+
%3 = move_value [lexical] [var_decl] %2 : $WrapperKlass
734+
br bb1
735+
736+
bb1:
737+
%func = function_ref @use_object : $@convention(thin) (@inout Builtin.NativeObject) -> ()
738+
%borrow1 = begin_borrow %3 : $WrapperKlass
739+
%ele1 = ref_element_addr %borrow1 : $WrapperKlass, #WrapperKlass.val
740+
apply %func(%ele1) : $@convention(thin) (@inout Builtin.NativeObject) -> ()
741+
end_borrow %borrow1 : $WrapperKlass
742+
%borrow2 = begin_borrow %3 : $WrapperKlass
743+
%ele2 = ref_element_addr %borrow2 : $WrapperKlass, #WrapperKlass.val
744+
apply %func(%ele2) : $@convention(thin) (@inout Builtin.NativeObject) -> ()
745+
end_borrow %borrow2 : $WrapperKlass
746+
br bb1
747+
}
748+
726749
sil [ossa] @use_word2 : $@convention(thin) (@inout Builtin.Word) -> ()
727750

728751
/*
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swiftc_driver \
2+
// RUN: -c \
3+
// RUN: %s \
4+
// RUN: -Xfrontend -sil-verify-all \
5+
// RUN: -enable-experimental-feature Embedded \
6+
// RUN: -wmo \
7+
// RUN: -Osize \
8+
// RUN: -o %t/bin
9+
10+
// REQUIRES: swift_in_compiler
11+
// REQUIRES: optimized_stdlib
12+
// REQUIRES: OS=macosx || OS=linux-gnu
13+
14+
class MyClass {
15+
var x: Int? = nil
16+
var enabled: Bool = true
17+
}
18+
19+
public func app_main() {
20+
let object = MyClass()
21+
while true {
22+
let enabled = object.enabled
23+
object.enabled = !enabled
24+
}
25+
}

0 commit comments

Comments
 (0)