Skip to content

Commit 625e3a5

Browse files
Merge pull request #73691 from nate-chandler/cherrypick/release/6.0/rdar128077404
6.0: [PrunedLiveness] Fix boundary check for dead-ends.
2 parents c77d271 + b58fed9 commit 625e3a5

File tree

7 files changed

+193
-9
lines changed

7 files changed

+193
-9
lines changed

lib/SIL/Utils/BasicBlockUtils.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SIL/BasicBlockUtils.h"
14-
#include "swift/SIL/BasicBlockDatastructures.h"
1514
#include "swift/Basic/Defer.h"
1615
#include "swift/Basic/STLExtras.h"
16+
#include "swift/SIL/BasicBlockDatastructures.h"
1717
#include "swift/SIL/Dominance.h"
1818
#include "swift/SIL/LoopInfo.h"
1919
#include "swift/SIL/SILArgument.h"
2020
#include "swift/SIL/SILBasicBlock.h"
2121
#include "swift/SIL/SILBuilder.h"
2222
#include "swift/SIL/SILFunction.h"
2323
#include "swift/SIL/TerminatorUtils.h"
24+
#include "swift/SIL/Test.h"
2425
#include "llvm/ADT/STLExtras.h"
2526

2627
using namespace swift;
@@ -413,6 +414,27 @@ bool DeadEndBlocks::triviallyEndsInUnreachable(SILBasicBlock *block) {
413414
return isa<UnreachableInst>(block->getTerminator());
414415
}
415416

417+
namespace swift::test {
418+
// Arguments:
419+
// - none
420+
// Dumps:
421+
// - the function
422+
// - the blocks which are dead-end blocks
423+
static FunctionTest DeadEndBlocksTest("dead_end_blocks", [](auto &function,
424+
auto &arguments,
425+
auto &test) {
426+
std::unique_ptr<DeadEndBlocks> DeadEnds;
427+
DeadEnds.reset(new DeadEndBlocks(&function));
428+
function.print(llvm::outs());
429+
#ifndef NDEBUG
430+
for (auto &block : function) {
431+
if (DeadEnds->isDeadEnd(&block))
432+
block.printID(llvm::outs(), true);
433+
}
434+
#endif
435+
});
436+
} // end namespace swift::test
437+
416438
//===----------------------------------------------------------------------===//
417439
// Post Dominance Set Completion Utilities
418440
//===----------------------------------------------------------------------===//

lib/SIL/Utils/PrunedLiveness.cpp

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

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;
537+
}
538+
492539
template <typename LivenessWithDefs>
493540
bool PrunedLiveRange<LivenessWithDefs>::areUsesWithinBoundary(
494541
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
495542
assert(asImpl().isInitialized());
496543

497-
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
498-
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
499-
};
500544
for (auto *use : uses) {
501545
auto *user = use->getUser();
502-
if (!asImpl().isWithinBoundary(user) && !checkDeadEnd(user))
546+
if (!asImpl().isWithinBoundary(user) &&
547+
!checkDeadEnd(user->getParent(), deadEndBlocks, liveBlocks))
503548
return false;
504549
}
505550
return true;
@@ -510,12 +555,10 @@ bool PrunedLiveRange<LivenessWithDefs>::areUsesOutsideBoundary(
510555
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
511556
assert(asImpl().isInitialized());
512557

513-
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
514-
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
515-
};
516558
for (auto *use : uses) {
517559
auto *user = use->getUser();
518-
if (asImpl().isWithinBoundary(user) || checkDeadEnd(user))
560+
if (asImpl().isWithinBoundary(user) ||
561+
checkDeadEnd(user->getParent(), deadEndBlocks, liveBlocks))
519562
return false;
520563
}
521564
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
/*

test/SILOptimizer/dead_end_blocks.sil

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
2+
3+
// REQUIRES: asserts
4+
5+
// CHECK-LABEL: begin running test {{.*}} on with_trap: dead_end_blocks
6+
// CHECK-LABEL: sil @with_trap : {{.*}} {
7+
// CHECK: cond_br undef, [[DIE:bb[0-9]+]], [[EXIT:bb[0-9]+]]
8+
// CHECK: [[DIE]]:
9+
// CHECK: unreachable
10+
// CHECK-LABEL: } // end sil function 'with_trap'
11+
// CHECK: [[DIE]]
12+
// CHECK-LABEL: end running test {{.*}} on with_trap: dead_end_blocks
13+
sil @with_trap : $@convention(thin) () -> () {
14+
entry:
15+
specify_test "dead_end_blocks"
16+
cond_br undef, die, exit
17+
18+
die:
19+
unreachable
20+
21+
exit:
22+
%retval = tuple ()
23+
return %retval : $()
24+
}
25+
26+
// CHECK-LABEL: begin running test {{.*}} on with_infinite_loop: dead_end_blocks
27+
// CHECK-LABEL: sil @with_infinite_loop : $@convention(thin) () -> () {
28+
// CHECK: cond_br undef, [[EXIT:bb[0-9]+]], [[HEADER:bb[0-9]+]]
29+
// CHECK: [[HEADER]]:
30+
// CHECK: br [[LOOP:bb[0-9]+]]
31+
// CHECK: [[LOOP]]:
32+
// CHECK: br [[LOOP]]
33+
// CHECK-LABEL: } // end sil function 'with_infinite_loop'
34+
// CHECK: [[HEADER]]
35+
// CHECK: [[LOOP]]
36+
// CHECK-LABEL: end running test {{.*}} on with_infinite_loop: dead_end_blocks
37+
sil @with_infinite_loop : $@convention(thin) () -> () {
38+
entry:
39+
specify_test "dead_end_blocks"
40+
cond_br undef, exit, header
41+
header:
42+
br loop
43+
loop:
44+
br loop
45+
exit:
46+
%retval = tuple ()
47+
return %retval : $()
48+
}
49+
50+

test/embedded/lit.local.cfg

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Make a local copy of the substitutions.
2+
config.substitutions = list(config.substitutions)
3+
4+
if config.target_sdk_name == 'macosx':
5+
def do_fixup(key, value):
6+
if isinstance(value, str):
7+
value = value.replace("-apple-macosx10.13", "-apple-macos14")
8+
elif isinstance(value, SubstituteCaptures):
9+
value.substitution = value.substitution.replace("-apple-macosx10.13", "-apple-macos14")
10+
return (key, value)
11+
12+
config.substitutions = [do_fixup(a, b) for (a, b) in config.substitutions]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import sys
2+
import platform
3+
4+
# Load the config at test/embedded/lit.local.cfg
5+
lit_config.load_config(config,
6+
os.path.join(
7+
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
8+
'test', 'embedded', 'lit.local.cfg'))
9+
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)