Skip to content

Commit 93ad267

Browse files
authored
Merge pull request #80979 from atrick/rdar149397018-optional-return
LifetimeDependenceScopeFixup: handle returning dependent Optional
2 parents 780ddaa + a1aaed9 commit 93ad267

File tree

5 files changed

+97
-3
lines changed

5 files changed

+97
-3
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -762,13 +762,20 @@ extension ExtendableScope {
762762
defer { unusedEnds.deinitialize() }
763763
for end in range.ends {
764764
let location = end.location.autoGenerated
765-
if end is ReturnInst {
765+
switch end {
766+
case is BranchInst:
767+
assert(end.parentBlock.singleSuccessor!.terminator is ReturnInst,
768+
"a phi only ends a use range if it is a returned value")
769+
fallthrough
770+
case is ReturnInst:
766771
// End this inner scope just before the return. The mark_dependence base operand will be redirected to a
767772
// function argument.
768773
let builder = Builder(before: end, location: location, context)
769774
// Insert newEnd so that this scope will be nested in any outer scopes.
770775
range.insert(createEndInstruction(builder, context))
771776
continue
777+
default:
778+
break
772779
}
773780
if unusedEnds.contains(end) {
774781
unusedEnds.erase(end)

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ extension LifetimeDependenceDefUseWalker {
587587
// Override ForwardingDefUseWalker.
588588
mutating func walkDown(operand: Operand) -> WalkResult {
589589
// Initially delegate all uses to OwnershipUseVisitor.
590-
// walkDownDefault will be called for uses that forward ownership.
590+
// forwardingUse() will be called for uses that forward ownership, which then delegates to walkDownDefault().
591591
return classify(operand: operand)
592592
}
593593

@@ -665,6 +665,11 @@ extension LifetimeDependenceDefUseWalker {
665665

666666
mutating func forwardingUse(of operand: Operand, isInnerLifetime: Bool)
667667
-> WalkResult {
668+
// Lifetime dependence is only interested in dominated uses. Treat a returned phi like a dominated use by stopping
669+
// at the phi operand rather than following the forwarded value to the ReturnInst.
670+
if let phi = Phi(using: operand), phi.isReturnValue {
671+
return returnedDependence(result: operand)
672+
}
668673
// Delegate ownership forwarding operations to the ForwardingDefUseWalker.
669674
return walkDownDefault(forwarding: operand)
670675
}

SwiftCompilerSources/Sources/SIL/Argument.swift

+16
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ public struct Phi {
192192
}
193193
}
194194

195+
extension Phi {
196+
/// Return true of this phi is directly returned with no side effects between the phi and the return.
197+
public var isReturnValue: Bool {
198+
if let singleUse = value.uses.singleUse, let ret = singleUse.instruction as? ReturnInst,
199+
ret.parentBlock == successor {
200+
for inst in successor.instructions {
201+
if inst.mayHaveSideEffects {
202+
return false
203+
}
204+
}
205+
return true
206+
}
207+
return false
208+
}
209+
}
210+
195211
extension Operand {
196212
public var forwardingBorrowedFromUser: BorrowedFromInst? {
197213
if let bfi = instruction as? BorrowedFromInst, index == 0 {

test/SILOptimizer/lifetime_dependence/scope_fixup.sil

+42-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ sil @getNEPointerToA : $@convention(thin) (@guaranteed NE) -> UnsafePointer<A>
6464
sil @useA : $@convention(thin) (A) -> ()
6565

6666
sil @getPtr : $@convention(thin) () -> @out UnsafeRawPointer
67-
sil @getSpan : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
67+
68+
sil @getOwnedNE : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @owned NE
6869

6970
sil @useNE : $@convention(thin) (@guaranteed NE) -> ()
7071

@@ -439,3 +440,43 @@ bb0(%0 : @owned $B):
439440
%24 = tuple ()
440441
return %24
441442
}
443+
444+
// =============================================================================
445+
// Return value extension
446+
// =============================================================================
447+
448+
// Sink the end access to the "return" phi and rewrite mark_dependence on the argument.
449+
//
450+
// CHECK-LABEL: sil hidden [ossa] @testReturnPhi : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @owned Optional<NE> {
451+
// CHECK: bb0(%0 : $*Holder):
452+
// CHECK: bb1:
453+
// CHECK: enum $Optional<NE>, #Optional.none!enumelt
454+
// CHECK: bb2:
455+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0
456+
// CHECK: [[MD:%.*]] = mark_dependence [unresolved] {{%.*}} on %0
457+
// CHECK: [[OPT:%.*]] = enum $Optional<NE>, #Optional.some!enumelt, %7
458+
// CHECK: end_access [[ACCESS]]
459+
// CHECK: br bb3([[OPT]])
460+
// CHECK: bb3([[RET:%.*]] : @owned $Optional<NE>):
461+
// CHECK: return [[RET]]
462+
// CHECK-LABEL: } // end sil function 'testReturnPhi'
463+
sil hidden [ossa] @testReturnPhi : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @owned Optional<NE> {
464+
bb0(%0 : $*Holder):
465+
cond_br undef, bb1, bb2
466+
467+
bb1:
468+
%none = enum $Optional<NE>, #Optional.none!enumelt
469+
br bb3(%none)
470+
471+
bb2:
472+
%access = begin_access [modify] [unknown] %0
473+
%f = function_ref @getOwnedNE : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @owned NE
474+
%ne = apply %f(%access) : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @owned NE
475+
%md = mark_dependence [unresolved] %ne on %access
476+
end_access %access
477+
%some = enum $Optional<NE>, #Optional.some!enumelt, %md
478+
br bb3(%some)
479+
480+
bb3(%ret : @owned $Optional<NE>):
481+
return %ret
482+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-frontend %s -emit-sil \
2+
// RUN: -o /dev/null \
3+
// RUN: -verify \
4+
// RUN: -sil-verify-all \
5+
// RUN: -enable-experimental-feature LifetimeDependence
6+
7+
// REQUIRES: swift_in_compiler
8+
// REQUIRES: swift_feature_LifetimeDependence
9+
10+
// Test diagnostic output for interesting corner cases. Similar to semantics.swift, but this tests corner cases in the
11+
// implementation as opposed to basic language rules.
12+
13+
// Test that conditionally returning an Optional succeeds.
14+
//
15+
// See scope_fixup.sil: testReturnPhi.
16+
@available(SwiftStdlib 6.2, *)
17+
extension Array {
18+
@lifetime(&self)
19+
mutating func dumb() -> MutableSpan<Element>? {
20+
if count == 0 {
21+
return nil
22+
}
23+
return mutableSpan
24+
}
25+
}

0 commit comments

Comments
 (0)