Skip to content

Commit 17c1fbd

Browse files
Merge pull request #82313 from jamieQ/copy-prop-ossa-dead-ends-build-time-fix
[SILOptimizer]: slow OSSA lifetime canonicalization mitigation
2 parents c3e7286 + 1f3f830 commit 17c1fbd

File tree

5 files changed

+90
-1
lines changed

5 files changed

+90
-1
lines changed

include/swift/SIL/BasicBlockUtils.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class DeadEndBlocks {
6868
const SILFunction *f;
6969
bool didComputeValue = false;
7070

71+
/// When non-null, indicates whether dead-end blocks are present
72+
/// in the current function.
73+
std::optional<bool> hasAnyDeadEnds = std::nullopt;
74+
7175
void compute();
7276

7377
public:
@@ -85,6 +89,17 @@ class DeadEndBlocks {
8589
return reachableBlocks.count(block) == 0;
8690
}
8791

92+
/// Returns true iff none of the function's blocks is a dead-end.
93+
/// Note: The underlying value is lazily computed & cached.
94+
bool isEmpty() {
95+
if (!hasAnyDeadEnds.has_value()) {
96+
hasAnyDeadEnds = llvm::any_of(
97+
*f, [this](const SILBasicBlock &BB) { return isDeadEnd(&BB); });
98+
}
99+
100+
return !hasAnyDeadEnds.value();
101+
}
102+
88103
/// Return true if this dead end blocks has computed its internal cache yet.
89104
///
90105
/// Used to determine if we need to verify a DeadEndBlocks.

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,10 @@ class CanonicalizeOSSALifetime final {
475475
return !endingLifetimeAtExplicitEnds();
476476
}
477477

478+
bool hasAnyDeadEnds() const {
479+
return !deadEndBlocksAnalysis->get(function)->isEmpty();
480+
}
481+
478482
bool respectsDeinitBarriers() const {
479483
if (!currentDef->isLexical())
480484
return false;

lib/SIL/Utils/BasicBlockUtils.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,16 @@ static FunctionTest DeadEndBlocksTest("dead_end_blocks", [](auto &function,
446446
}
447447
#endif
448448
});
449+
450+
// Arguments:
451+
// - none
452+
// Dumps:
453+
// - message
454+
static FunctionTest HasAnyDeadEndBlocksTest(
455+
"has_any_dead_ends", [](auto &function, auto &arguments, auto &test) {
456+
auto deb = test.getDeadEndBlocks();
457+
llvm::outs() << (deb->isEmpty() ? "no dead ends\n" : "has dead ends\n");
458+
});
449459
} // end namespace swift::test
450460

451461
//===----------------------------------------------------------------------===//

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ bool CanonicalizeOSSALifetime::computeLiveness() {
14061406
clear();
14071407
return false;
14081408
}
1409-
if (respectsDeadEnds()) {
1409+
if (respectsDeadEnds() && hasAnyDeadEnds()) {
14101410
if (respectsDeinitBarriers()) {
14111411
extendLexicalLivenessToDeadEnds();
14121412
}

test/SILOptimizer/dead_end_blocks.sil

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,64 @@ exit:
4747
return %retval : $()
4848
}
4949

50+
// no dead ends - simple return
51+
// CHECK-LABEL: begin running test {{.*}} on simple_function: has_any_dead_ends
52+
// CHECK: no dead ends
53+
// CHECK-LABEL: end running test {{.*}} on simple_function: has_any_dead_ends
54+
sil @simple_function : $@convention(thin) () -> () {
55+
entry:
56+
specify_test "has_any_dead_ends"
57+
%retval = tuple ()
58+
return %retval : $()
59+
}
60+
61+
// dead ends - unreachable blocks
62+
// CHECK-LABEL: begin running test {{.*}} on function_with_dead_ends: has_any_dead_ends
63+
// CHECK: has dead ends
64+
// CHECK-LABEL: end running test {{.*}} on function_with_dead_ends: has_any_dead_ends
65+
sil @function_with_dead_ends : $@convention(thin) () -> () {
66+
entry:
67+
specify_test "has_any_dead_ends"
68+
cond_br undef, die, exit
69+
70+
die:
71+
unreachable
72+
73+
exit:
74+
%retval = tuple ()
75+
return %retval : $()
76+
}
5077

78+
// dead ends – infinite loop
79+
// CHECK-LABEL: begin running test {{.*}} on function_with_loop: has_any_dead_ends
80+
// CHECK: has dead ends
81+
// CHECK-LABEL: end running test {{.*}} on function_with_loop: has_any_dead_ends
82+
sil @function_with_loop : $@convention(thin) () -> () {
83+
entry:
84+
specify_test "has_any_dead_ends"
85+
cond_br undef, exit, loop
86+
87+
loop:
88+
br loop
89+
90+
exit:
91+
%retval = tuple ()
92+
return %retval : $()
93+
}
94+
95+
// no dead ends – conditional branches but all paths return
96+
// CHECK-LABEL: begin running test {{.*}} on branching_no_dead_ends: has_any_dead_ends
97+
// CHECK: no dead ends
98+
// CHECK-LABEL: end running test {{.*}} on branching_no_dead_ends: has_any_dead_ends
99+
sil @branching_no_dead_ends : $@convention(thin) () -> () {
100+
entry:
101+
specify_test "has_any_dead_ends"
102+
cond_br undef, then, else
103+
104+
then:
105+
br else
106+
107+
else:
108+
%retval2 = tuple ()
109+
return %retval2 : $()
110+
}

0 commit comments

Comments
 (0)