Skip to content

Commit b405c8b

Browse files
committed
[DCE] Keep insts which consume escaping values.
When DCE deletes instructions as dead, if the instruction ends one of its operands lifetimes, it must insert a compensating lifetime end. When the def block of the value and the parent block of the instruction are different, it uses lifetime completion. Lifetime completion relies on complete liveness, which doesn't and can't exist for values with pointer escapes. The result is ending lifetimes too early. Avoid this scenario by marking such instructions live. In the fullness of time, it may be possible to track the deleted instruction's "location" even in the face of deletions of adjacent instructions and parent blocks and to insert the lifetime end at that location. rdar://149007151
1 parent 9fa0888 commit b405c8b

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

Lines changed: 22 additions & 0 deletions
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.

test/SILOptimizer/dead_code_elimination_ossa.sil

Lines changed: 31 additions & 0 deletions
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)