diff --git a/include/swift/SIL/BasicBlockUtils.h b/include/swift/SIL/BasicBlockUtils.h index d443b1f5abf03..1e15bcb78706d 100644 --- a/include/swift/SIL/BasicBlockUtils.h +++ b/include/swift/SIL/BasicBlockUtils.h @@ -68,6 +68,10 @@ class DeadEndBlocks { const SILFunction *f; bool didComputeValue = false; + /// When non-null, indicates whether dead-end blocks are present + /// in the current function. + std::optional hasAnyDeadEnds = std::nullopt; + void compute(); public: @@ -85,6 +89,17 @@ class DeadEndBlocks { return reachableBlocks.count(block) == 0; } + /// Returns true iff none of the function's blocks is a dead-end. + /// Note: The underlying value is lazily computed & cached. + bool isEmpty() { + if (!hasAnyDeadEnds.has_value()) { + hasAnyDeadEnds = llvm::any_of( + *f, [this](const SILBasicBlock &BB) { return isDeadEnd(&BB); }); + } + + return !hasAnyDeadEnds.value(); + } + /// Return true if this dead end blocks has computed its internal cache yet. /// /// Used to determine if we need to verify a DeadEndBlocks. diff --git a/include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h b/include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h index ca7a6dd9a7f71..f686492a70e06 100644 --- a/include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h +++ b/include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h @@ -475,6 +475,10 @@ class CanonicalizeOSSALifetime final { return !endingLifetimeAtExplicitEnds(); } + bool hasAnyDeadEnds() const { + return !deadEndBlocksAnalysis->get(function)->isEmpty(); + } + bool respectsDeinitBarriers() const { if (!currentDef->isLexical()) return false; diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index c0653ca3e4035..b375c94d31a64 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -446,6 +446,16 @@ static FunctionTest DeadEndBlocksTest("dead_end_blocks", [](auto &function, } #endif }); + +// Arguments: +// - none +// Dumps: +// - message +static FunctionTest HasAnyDeadEndBlocksTest( + "has_any_dead_ends", [](auto &function, auto &arguments, auto &test) { + auto deb = test.getDeadEndBlocks(); + llvm::outs() << (deb->isEmpty() ? "no dead ends\n" : "has dead ends\n"); + }); } // end namespace swift::test //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp index 2a9f209fd5f43..52f85ed36178c 100644 --- a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp +++ b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp @@ -1406,7 +1406,7 @@ bool CanonicalizeOSSALifetime::computeLiveness() { clear(); return false; } - if (respectsDeadEnds()) { + if (respectsDeadEnds() && hasAnyDeadEnds()) { if (respectsDeinitBarriers()) { extendLexicalLivenessToDeadEnds(); } diff --git a/test/SILOptimizer/dead_end_blocks.sil b/test/SILOptimizer/dead_end_blocks.sil index 6bc6e24935956..4332fa66370b7 100644 --- a/test/SILOptimizer/dead_end_blocks.sil +++ b/test/SILOptimizer/dead_end_blocks.sil @@ -47,4 +47,64 @@ exit: return %retval : $() } +// no dead ends - simple return +// CHECK-LABEL: begin running test {{.*}} on simple_function: has_any_dead_ends +// CHECK: no dead ends +// CHECK-LABEL: end running test {{.*}} on simple_function: has_any_dead_ends +sil @simple_function : $@convention(thin) () -> () { +entry: + specify_test "has_any_dead_ends" + %retval = tuple () + return %retval : $() +} + +// dead ends - unreachable blocks +// CHECK-LABEL: begin running test {{.*}} on function_with_dead_ends: has_any_dead_ends +// CHECK: has dead ends +// CHECK-LABEL: end running test {{.*}} on function_with_dead_ends: has_any_dead_ends +sil @function_with_dead_ends : $@convention(thin) () -> () { +entry: + specify_test "has_any_dead_ends" + cond_br undef, die, exit + +die: + unreachable + +exit: + %retval = tuple () + return %retval : $() +} +// dead ends – infinite loop +// CHECK-LABEL: begin running test {{.*}} on function_with_loop: has_any_dead_ends +// CHECK: has dead ends +// CHECK-LABEL: end running test {{.*}} on function_with_loop: has_any_dead_ends +sil @function_with_loop : $@convention(thin) () -> () { +entry: + specify_test "has_any_dead_ends" + cond_br undef, exit, loop + +loop: + br loop + +exit: + %retval = tuple () + return %retval : $() +} + +// no dead ends – conditional branches but all paths return +// CHECK-LABEL: begin running test {{.*}} on branching_no_dead_ends: has_any_dead_ends +// CHECK: no dead ends +// CHECK-LABEL: end running test {{.*}} on branching_no_dead_ends: has_any_dead_ends +sil @branching_no_dead_ends : $@convention(thin) () -> () { +entry: + specify_test "has_any_dead_ends" + cond_br undef, then, else + +then: + br else + +else: + %retval2 = tuple () + return %retval2 : $() +}