Skip to content

Commit 9cc1947

Browse files
Merge pull request swiftlang#80766 from nate-chandler/rdar149007151
[DCE] Don't delete instructions which consume escaping values.
2 parents 8310c8a + 3ec9b26 commit 9cc1947

File tree

4 files changed

+85
-21
lines changed

4 files changed

+85
-21
lines changed

Diff for: include/swift/SIL/OSSALifetimeCompletion.h

+26-18
Original file line numberDiff line numberDiff line change
@@ -43,34 +43,42 @@ class OSSALifetimeCompletion {
4343
enum HandleTrivialVariable_t { IgnoreTrivialVariable, ExtendTrivialVariable };
4444

4545
private:
46-
// If domInfo is nullptr, then InteriorLiveness never assumes dominance. As a
47-
// result it may report extra unenclosedPhis. In that case, any attempt to
48-
// create a new phi would result in an immediately redundant phi.
46+
/// If domInfo is nullptr, then InteriorLiveness never assumes dominance. As a
47+
/// result it may report extra unenclosedPhis. In that case, any attempt to
48+
/// create a new phi would result in an immediately redundant phi.
4949
const DominanceInfo *domInfo = nullptr;
5050

5151
DeadEndBlocks &deadEndBlocks;
5252

53-
// Cache intructions already handled by the recursive algorithm to avoid
54-
// recomputing their lifetimes.
53+
/// Cache intructions already handled by the recursive algorithm to avoid
54+
/// recomputing their lifetimes.
5555
ValueSet completedValues;
5656

57-
// Extend trivial variables for lifetime diagnostics (only in SILGenCleanup).
57+
/// Extend trivial variables for lifetime diagnostics (only in SILGenCleanup).
5858
HandleTrivialVariable_t handleTrivialVariable;
5959

60+
/// Whether verification of the computed liveness should be run even when the
61+
/// global setting is off.
62+
/// TODO: Remove this option.
63+
bool ForceLivenessVerification;
64+
6065
public:
61-
OSSALifetimeCompletion(SILFunction *function, const DominanceInfo *domInfo,
62-
DeadEndBlocks &deadEndBlocks,
63-
HandleTrivialVariable_t handleTrivialVariable = IgnoreTrivialVariable)
66+
OSSALifetimeCompletion(
67+
SILFunction *function, const DominanceInfo *domInfo,
68+
DeadEndBlocks &deadEndBlocks,
69+
HandleTrivialVariable_t handleTrivialVariable = IgnoreTrivialVariable,
70+
bool forceLivenessVerification = false)
6471
: domInfo(domInfo), deadEndBlocks(deadEndBlocks),
65-
completedValues(function), handleTrivialVariable(handleTrivialVariable) {}
66-
67-
// The kind of boundary at which to complete the lifetime.
68-
//
69-
// Liveness: "As early as possible." Consume the value after the last
70-
// non-consuming uses.
71-
// Availability: "As late as possible." Consume the value in the last blocks
72-
// beyond the non-consuming uses in which the value has been
73-
// consumed on no incoming paths.
72+
completedValues(function), handleTrivialVariable(handleTrivialVariable),
73+
ForceLivenessVerification(forceLivenessVerification) {}
74+
75+
/// The kind of boundary at which to complete the lifetime.
76+
///
77+
/// Liveness: "As early as possible." Consume the value after the last
78+
/// non-consuming uses.
79+
/// Availability: "As late as possible." Consume the value in the last blocks
80+
/// beyond the non-consuming uses in which the value has been
81+
/// consumed on no incoming paths.
7482
struct Boundary {
7583
enum Value : uint8_t {
7684
Liveness,

Diff for: lib/SIL/Utils/OSSALifetimeCompletion.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,9 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
501501
};
502502
Walker walker(*this, scopedAddress, boundary, liveness);
503503
AddressUseKind result = walker.walk(scopedAddress.value);
504-
if (VerifyLifetimeCompletion && boundary != Boundary::Availability
505-
&& result != AddressUseKind::NonEscaping) {
504+
if ((VerifyLifetimeCompletion || ForceLivenessVerification) &&
505+
boundary != Boundary::Availability &&
506+
result != AddressUseKind::NonEscaping) {
506507
llvm::errs() << "Incomplete liveness for:\n" << scopedAddress.value;
507508
if (auto *escapingUse = walker.getEscapingUse()) {
508509
llvm::errs() << " escapes at:\n";

Diff for: lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

+25-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,28 @@ static bool seemsUseful(SILInstruction *I) {
7777
return true;
7878
}
7979

80+
// Instructions which end the lifetimes of values which escape can only be
81+
// deleted if compensating lifetime ends are added. Compensating lifetime
82+
// ends are added by OSSACompleteLifetime when the def block of the value
83+
// is different from the parent block of the instruction. But
84+
// OSSACompleteLifetime requires that liveness be complete--that there are no
85+
// pointer escapes. So we can't delete instructions which end the lifetime
86+
// of values which escape to a pointer and whose parent blocks are different.
87+
if (llvm::any_of(I->getAllOperands(), [I](Operand &operand) {
88+
if (!operand.isLifetimeEnding())
89+
return false;
90+
auto value = operand.get();
91+
if (isa<SILUndef>(value))
92+
return false;
93+
auto *insertionPoint = value->getDefiningInsertionPoint();
94+
ASSERT(insertionPoint);
95+
if (insertionPoint->getParent() == I->getParent())
96+
return false;
97+
return findPointerEscape(value);
98+
})) {
99+
return true;
100+
}
101+
80102
if (auto *BI = dyn_cast<BuiltinInst>(I)) {
81103
// Although the onFastPath builtin has no side-effects we don't want to
82104
// remove it.
@@ -813,7 +835,9 @@ bool DCE::removeDead() {
813835
}
814836
}
815837

816-
OSSALifetimeCompletion completion(F, DT, *deadEndBlocks);
838+
OSSALifetimeCompletion completion(
839+
F, DT, *deadEndBlocks, OSSALifetimeCompletion::IgnoreTrivialVariable,
840+
/*forceLivenessVerification=*/true);
817841
for (auto value : valuesToComplete) {
818842
if (!value.has_value())
819843
continue;

Diff for: test/SILOptimizer/dead_code_elimination_ossa.sil

+31
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ class C {}
1515

1616
sil @getC : $@convention(thin) () -> @owned C
1717
sil @barrier : $@convention(thin) () -> ()
18+
sil @initC : $@convention(thin) (Builtin.Word) -> (@owned C, Builtin.RawPointer)
19+
sil [readnone] @finalC : $@convention(thin) (@owned C) -> @owned C
1820

1921
struct CAndBit {
2022
var c: C
2123
var bit: Int1
2224
}
2325

26+
struct Bool {
27+
var _value: Builtin.Int1
28+
}
29+
2430
struct MO: ~Copyable {
2531
var x: Int
2632
deinit
@@ -507,3 +513,28 @@ bb0(%0 : @owned $Outer):
507513
return %6 : $()
508514
}
509515

516+
// CHECK-LABEL: sil [ossa] @dont_remove_lifetime_end_of_escaping_value
517+
// CHECK: [[FINALIZE:%[^,]+]] = function_ref @finalC
518+
// CHECK: apply [[FINALIZE]]
519+
// CHECK-LABEL: } // end sil function 'dont_remove_lifetime_end_of_escaping_value'
520+
sil [ossa] @dont_remove_lifetime_end_of_escaping_value : $@convention(thin) (Bool) -> () {
521+
bb0(%0 : $Bool):
522+
%1 = integer_literal $Builtin.Word, 1
523+
%2 = function_ref @initC : $@convention(thin) (Builtin.Word) -> (@owned C, Builtin.RawPointer)
524+
%3 = apply %2(%1) : $@convention(thin) (Builtin.Word) -> (@owned C, Builtin.RawPointer)
525+
(%4, %5) = destructure_tuple %3
526+
%6 = mark_dependence %5 on %4
527+
%7 = pointer_to_address %6 to [strict] $*Bool
528+
store %0 to [trivial] %7
529+
br bb1
530+
531+
bb1:
532+
%13 = function_ref @finalC : $@convention(thin) (@owned C) -> @owned C
533+
%14 = apply %13(%4) : $@convention(thin) (@owned C) -> @owned C
534+
destroy_value %14
535+
br bb2
536+
537+
bb2:
538+
%17 = tuple ()
539+
return %17
540+
}

0 commit comments

Comments
 (0)