Skip to content

Commit f0144ab

Browse files
authored
Merge pull request #78830 from atrick/fix-lifedep-trivial-deadend
SILGenCleanup: extend to handle trivial local var scopes
2 parents 5770d59 + 1b72c7b commit f0144ab

File tree

7 files changed

+127
-13
lines changed

7 files changed

+127
-13
lines changed

include/swift/SIL/OSSALifetimeCompletion.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class DeadEndBlocks;
3939
enum class LifetimeCompletion { NoLifetime, AlreadyComplete, WasCompleted };
4040

4141
class OSSALifetimeCompletion {
42+
public:
43+
enum HandleTrivialVariable_t { IgnoreTrivialVariable, ExtendTrivialVariable };
44+
45+
private:
4246
// If domInfo is nullptr, then InteriorLiveness never assumes dominance. As a
4347
// result it may report extra unenclosedPhis. In that case, any attempt to
4448
// create a new phi would result in an immediately redundant phi.
@@ -50,11 +54,15 @@ class OSSALifetimeCompletion {
5054
// recomputing their lifetimes.
5155
ValueSet completedValues;
5256

57+
// Extend trivial variables for lifetime diagnostics (only in SILGenCleanup).
58+
HandleTrivialVariable_t handleTrivialVariable;
59+
5360
public:
5461
OSSALifetimeCompletion(SILFunction *function, const DominanceInfo *domInfo,
55-
DeadEndBlocks &deadEndBlocks)
62+
DeadEndBlocks &deadEndBlocks,
63+
HandleTrivialVariable_t handleTrivialVariable = IgnoreTrivialVariable)
5664
: domInfo(domInfo), deadEndBlocks(deadEndBlocks),
57-
completedValues(function) {}
65+
completedValues(function), handleTrivialVariable(handleTrivialVariable) {}
5866

5967
// The kind of boundary at which to complete the lifetime.
6068
//
@@ -93,10 +101,15 @@ class OSSALifetimeCompletion {
93101
LifetimeCompletion completeOSSALifetime(SILValue value, Boundary boundary) {
94102
switch (value->getOwnershipKind()) {
95103
case OwnershipKind::None: {
96-
auto scopedAddress = ScopedAddressValue(value);
97-
if (!scopedAddress)
98-
return LifetimeCompletion::NoLifetime;
99-
break;
104+
if (auto scopedAddress = ScopedAddressValue(value)) {
105+
break;
106+
}
107+
// During SILGenCleanup, extend move_value [var_decl].
108+
if (handleTrivialVariable == ExtendTrivialVariable
109+
&& value->isFromVarDecl()) {
110+
break;
111+
}
112+
return LifetimeCompletion::NoLifetime;
100113
}
101114
case OwnershipKind::Owned:
102115
break;

include/swift/SIL/OwnershipUseVisitor.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ class OwnershipUseVisitor : OwnershipUseVisitorBase<Impl> {
165165
protected:
166166
bool visitConsumes(SILValue ssaDef);
167167

168+
bool visitExtends(SILValue ssaDef);
169+
168170
bool visitOuterBorrow(SILValue borrowBegin);
169171

170172
bool visitOuterBorrowScopeEnd(Operand *borrowEnd);
@@ -202,8 +204,12 @@ bool OwnershipUseVisitor<Impl>::visitLifetimeEndingUses(SILValue ssaDef) {
202204
case OwnershipKind::Guaranteed:
203205
return visitOuterBorrow(ssaDef);
204206

205-
case OwnershipKind::Any:
206207
case OwnershipKind::None:
208+
if (ssaDef->isFromVarDecl()) {
209+
return visitExtends(ssaDef);
210+
}
211+
LLVM_FALLTHROUGH;
212+
case OwnershipKind::Any:
207213
case OwnershipKind::Unowned:
208214
llvm_unreachable("requires an owned or guaranteed orignalDef");
209215
}
@@ -231,6 +237,18 @@ bool OwnershipUseVisitor<Impl>::visitConsumes(SILValue ssaDef) {
231237
return true;
232238
}
233239

240+
template <typename Impl>
241+
bool OwnershipUseVisitor<Impl>::visitExtends(SILValue ssaDef) {
242+
for (Operand *use : ssaDef->getUses()) {
243+
if (isa<ExtendLifetimeInst>(use->getUser())) {
244+
if (!handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding))
245+
return false;
246+
continue;
247+
}
248+
}
249+
return true;
250+
}
251+
234252
template <typename Impl>
235253
bool OwnershipUseVisitor<Impl>::visitOuterBorrow(SILValue borrowBegin) {
236254
BorrowedValue borrow(borrowBegin);

lib/SIL/Utils/OSSALifetimeCompletion.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ static SILInstruction *endOSSALifetime(SILValue value,
8080
if (auto scopedAddress = ScopedAddressValue(value)) {
8181
return scopedAddress.createScopeEnd(builder.getInsertionPoint(), loc);
8282
}
83+
if (value->getOwnershipKind() == OwnershipKind::None) {
84+
return builder.createExtendLifetime(loc, value);
85+
}
8386
return builder.createEndBorrow(loc, lookThroughBorrowedFromUser(value));
8487
}
8588

@@ -297,9 +300,16 @@ void AvailabilityBoundaryVisitor::computeRegion(
297300
regionWorklist.push(block);
298301
};
299302

300-
for (auto *endBlock : boundary.endBlocks) {
301-
if (!consumingBlocks.contains(endBlock)) {
302-
collect(endBlock);
303+
// Trivial values that correspond to local variables (as opposed to
304+
// ScopedAddresses) are available only up to their last extend_lifetime on
305+
// non-dead-end paths. They cannot be consumed, but are only "available" up to
306+
// the end of their scope.
307+
if (value->getOwnershipKind() != OwnershipKind::None
308+
|| ScopedAddressValue(value)) {
309+
for (auto *endBlock : boundary.endBlocks) {
310+
if (!consumingBlocks.contains(endBlock)) {
311+
collect(endBlock);
312+
}
303313
}
304314
}
305315
for (SILBasicBlock *edge : boundary.boundaryEdges) {
@@ -497,12 +507,21 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value,
497507
if (auto scopedAddress = ScopedAddressValue(value)) {
498508
return analyzeAndUpdateLifetime(scopedAddress, boundary);
499509
}
500-
501510
// Called for inner borrows, inner adjacent reborrows, inner reborrows, and
502511
// scoped addresses.
503512
auto handleInnerScope = [this, boundary](SILValue innerBorrowedValue) {
504513
completeOSSALifetime(innerBorrowedValue, boundary);
505514
};
515+
if (value->getOwnershipKind() == OwnershipKind::None) {
516+
// Trivial variable lifetimes are only relevant up to the extend_lifetime
517+
// instructions emitted by SILGen. Their other uses have no meaning with
518+
// respect to lifetime. The only purpose of "completing" their lifetime is
519+
// to insert extend_lifetime on dead-end blocks.
520+
LinearLiveness liveness(value);
521+
liveness.compute();
522+
return endLifetimeAtBoundary(value, liveness.getLiveness(), boundary,
523+
deadEndBlocks);
524+
}
506525
InteriorLiveness liveness(value);
507526
liveness.compute(domInfo, handleInnerScope);
508527
// TODO: Rebuild outer adjacent phis on demand (SILGen does not currently

lib/SIL/Utils/OwnershipLiveness.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,21 @@ struct LinearLivenessVisitor :
7979
LinearLiveness::LinearLiveness(SILValue def,
8080
IncludeExtensions_t includeExtensions)
8181
: OSSALiveness(def), includeExtensions(includeExtensions) {
82-
if (def->getOwnershipKind() != OwnershipKind::Owned) {
82+
switch (def->getOwnershipKind()) {
83+
case OwnershipKind::Owned:
84+
break;
85+
case OwnershipKind::Guaranteed: {
8386
BorrowedValue borrowedValue(def);
8487
assert(borrowedValue && borrowedValue.isLocalScope());
8588
(void)borrowedValue;
89+
break;
90+
}
91+
case OwnershipKind::None:
92+
assert(def->isFromVarDecl());
93+
break;
94+
case OwnershipKind::Unowned:
95+
case OwnershipKind::Any:
96+
llvm_unreachable("bad ownership for LinearLiveness");
8697
}
8798
}
8899

lib/SILOptimizer/Mandatory/SILGenCleanup.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ bool SILGenCleanup::completeOSSALifetimes(SILFunction *function) {
267267
}
268268

269269
bool changed = false;
270-
OSSALifetimeCompletion completion(function, /*DomInfo*/ nullptr, *deba);
270+
OSSALifetimeCompletion completion(
271+
function, /*DomInfo*/ nullptr, *deba,
272+
OSSALifetimeCompletion::ExtendTrivialVariable);
271273
BasicBlockSet completed(function);
272274
for (auto *root : roots) {
273275
if (root == function->getEntryBlock()) {

test/SILOptimizer/lifetime_dependence/semantics.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,19 @@ func testTrivialScope<T>(a: Array<T>) -> Span<T> {
228228
// expected-note @-3{{this use causes the lifetime-dependent value to escape}}
229229
}
230230

231+
extension Span {
232+
public func withThrowingClosure<E: Error>(_ body: () throws(E) -> ()) throws(E) -> () {
233+
try body()
234+
}
235+
}
236+
237+
// Test dependence on an local variable that needs to be extended into the dead-end block of a never-throwing apply.
238+
public func test(p: UnsafePointer<Int>) {
239+
let pointer = p
240+
let span = Span(base: pointer, count: 1)
241+
span.withThrowingClosure {}
242+
}
243+
231244
// =============================================================================
232245
// Scoped dependence on property access
233246
// =============================================================================

test/SILOptimizer/silgen_cleanup_complete_ossa.sil

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ sil_stage raw
66

77
typealias AnyObject = Builtin.AnyObject
88

9+
protocol Error {}
10+
911
class Klass {
1012
var property: Builtin.Int64
1113
}
@@ -28,6 +30,12 @@ struct UInt8 {
2830

2931
protocol P : AnyObject {}
3032

33+
struct Err: Error {
34+
var i: Int
35+
}
36+
37+
sil @throwing : $@convention(thin) (UInt8) -> @error_indirect Err
38+
3139
// =============================================================================
3240
// Test complete OSSA lifetimes
3341
// =============================================================================
@@ -367,3 +375,33 @@ left:
367375
right:
368376
unreachable
369377
}
378+
379+
// CHECK-LABEL: sil [ossa] @testExtendTrivialToDeadEnd : $@convention(thin) (UInt8) -> () {
380+
// CHECK: bb0(%0 : $UInt8):
381+
// CHECK: [[MV:%.*]] = move_value [var_decl] %0 : $UInt8
382+
// CHECK: try_apply %{{.*}}(%{{.*}}, [[MV]]) : $@convention(thin) (UInt8) -> @error_indirect Err, normal bb1, error bb2
383+
// CHECK: bb1(
384+
// CHECK: extend_lifetime [[MV]] : $UInt8
385+
// CHECK: return
386+
// CHECK: bb2:
387+
// CHECK: dealloc_stack
388+
// CHECK: extend_lifetime [[MV]] : $UInt8
389+
// CHECK: unreachable
390+
// CHECK-LABEL: } // end sil function 'testExtendTrivialToDeadEnd'
391+
sil [ossa] @testExtendTrivialToDeadEnd : $@convention(thin) (UInt8) -> () {
392+
bb0(%0 : $UInt8):
393+
%mv = move_value [var_decl] %0
394+
%e = alloc_stack $Err
395+
%f = function_ref @throwing : $@convention(thin) (UInt8) -> @error_indirect Err
396+
try_apply %f(%e, %mv) : $@convention(thin) (UInt8) -> @error_indirect Err, normal bb1, error bb2
397+
398+
bb1(%ret : $()):
399+
extend_lifetime %mv
400+
dealloc_stack %e : $*Err
401+
%99 = tuple ()
402+
return %99
403+
404+
bb2:
405+
dealloc_stack %e : $*Err
406+
unreachable
407+
}

0 commit comments

Comments
 (0)