Skip to content

Commit 0554d74

Browse files
authored
Merge pull request #71713 from meg-gupta/readmodifyinference
Add support for lifetime dependence inference for _read/_modify accessors
2 parents 8a9b106 + 01efb6d commit 0554d74

File tree

9 files changed

+369
-97
lines changed

9 files changed

+369
-97
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,10 @@ extension DiagnoseDependenceWalker : LifetimeDependenceDefUseWalker {
375375
return diagnostics.checkFunctionResult(operand: operand)
376376
}
377377

378+
mutating func yieldedDependence(result: Operand) -> WalkResult {
379+
return diagnostics.checkFunctionResult(operand: result)
380+
}
381+
378382
// Override AddressUseVisitor here because LifetimeDependenceDefUseWalker
379383
// returns .abortWalk, and we want a more useful crash report.
380384
mutating func unknownAddressUse(of operand: Operand) -> WalkResult {

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,5 +168,9 @@ private struct LifetimeDependenceScopeFixupWalker : LifetimeDependenceDefUseWalk
168168
using operand: Operand) -> WalkResult {
169169
return .continueWalk
170170
}
171+
172+
mutating func yieldedDependence(result: Operand) -> WalkResult {
173+
return .continueWalk
174+
}
171175
}
172176

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ extension LifetimeDependenceUseDefWalker {
780780
/// escapingDependence(on operand: Operand) -> WalkResult
781781
/// returnedDependence(result: Operand) -> WalkResult
782782
/// returnedDependence(address: FunctionArgument, using: Operand) -> WalkResult
783-
///
783+
/// yieldedDependence(result: Operand) -> WalkResult
784784
/// Start walking:
785785
/// walkDown(root: Value)
786786
protocol LifetimeDependenceDefUseWalker : ForwardingDefUseWalker,
@@ -796,6 +796,8 @@ protocol LifetimeDependenceDefUseWalker : ForwardingDefUseWalker,
796796

797797
mutating func returnedDependence(address: FunctionArgument, using: Operand)
798798
-> WalkResult
799+
800+
mutating func yieldedDependence(result: Operand) -> WalkResult
799801
}
800802

801803
extension LifetimeDependenceDefUseWalker {
@@ -841,6 +843,9 @@ extension LifetimeDependenceDefUseWalker {
841843
if operand.instruction is ReturnInst, !operand.value.type.isEscapable {
842844
return returnedDependence(result: operand)
843845
}
846+
if operand.instruction is YieldInst, !operand.value.type.isEscapable {
847+
return yieldedDependence(result: operand)
848+
}
844849
return escapingDependence(on: operand)
845850
}
846851
}
@@ -1009,6 +1014,9 @@ extension LifetimeDependenceDefUseWalker {
10091014
if operand.instruction is ReturnInst, !operand.value.type.isEscapable {
10101015
return returnedDependence(result: operand)
10111016
}
1017+
if operand.instruction is YieldInst, !operand.value.type.isEscapable {
1018+
return yieldedDependence(result: operand)
1019+
}
10121020
return escapingDependence(on: operand)
10131021
}
10141022

@@ -1208,6 +1216,11 @@ private struct LifetimeDependenceUsePrinter : LifetimeDependenceDefUseWalker {
12081216
print("Returned use: \(operand) in: \(address)")
12091217
return .continueWalk
12101218
}
1219+
1220+
mutating func yieldedDependence(result: Operand) -> WalkResult {
1221+
print("Yielded use: \(result)")
1222+
return .continueWalk
1223+
}
12111224
}
12121225

12131226
let lifetimeDependenceUseTest = FunctionTest("lifetime_dependence_use") {

lib/Sema/LifetimeDependence.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,16 @@ LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd, Type resultType) {
274274
auto returnTypeRepr = afd->getResultTypeRepr();
275275
auto returnLoc = returnTypeRepr ? returnTypeRepr->getLoc() : afd->getLoc();
276276
Type returnTyInContext = afd->mapTypeIntoContext(resultType);
277+
std::optional<Type> yieldTyInContext;
277278

278-
if (returnTyInContext->isEscapable()) {
279+
if (auto *accessor = dyn_cast<AccessorDecl>(afd)) {
280+
if (accessor->isCoroutine()) {
281+
yieldTyInContext = accessor->getStorage()->getValueInterfaceType();
282+
yieldTyInContext = accessor->mapTypeIntoContext(*yieldTyInContext);
283+
}
284+
}
285+
if (returnTyInContext->isEscapable() &&
286+
(!yieldTyInContext.has_value() || (*yieldTyInContext)->isEscapable())) {
279287
return llvm::None;
280288
}
281289
if (afd->getAttrs().hasAttribute<UnsafeNonEscapableResultAttr>()) {
@@ -285,21 +293,25 @@ LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd, Type resultType) {
285293
if (afd->getKind() != DeclKind::Constructor && afd->hasImplicitSelfDecl()) {
286294
ValueOwnership ownership = ValueOwnership::Default;
287295
if (auto *AD = dyn_cast<AccessorDecl>(afd)) {
288-
if (AD->getAccessorKind() == AccessorKind::Get) {
296+
if (AD->getAccessorKind() == AccessorKind::Get ||
297+
AD->getAccessorKind() == AccessorKind::Read) {
289298
// We don't support "borrowing/consuming" ownership modifiers on
290-
// getters, by default they are guaranteed for now.
299+
// getters/_read accessors, they are guaranteed by default for now.
291300
ownership = ValueOwnership::Shared;
292301
}
302+
if (AD->getAccessorKind() == AccessorKind::Modify) {
303+
ownership = ValueOwnership::InOut;
304+
}
293305
} else {
306+
assert(afd->getKind() == DeclKind::Func);
294307
ownership = afd->getImplicitSelfDecl()->getValueOwnership();
295-
}
296-
297-
if (ownership == ValueOwnership::Default) {
298-
diags.diagnose(
299-
returnLoc,
300-
diag::
301-
lifetime_dependence_cannot_infer_wo_ownership_modifier_on_method);
302-
return llvm::None;
308+
if (ownership == ValueOwnership::Default) {
309+
diags.diagnose(
310+
returnLoc,
311+
diag::
312+
lifetime_dependence_cannot_infer_wo_ownership_modifier_on_method);
313+
return llvm::None;
314+
}
303315
}
304316
return LifetimeDependenceInfo::getForParamIndex(afd, /*selfIndex*/ 0,
305317
ownership);

test/SIL/buffer_view_prototype.swift

Lines changed: 123 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,131 @@
44
// RUN: -enable-experimental-feature NoncopyableGenerics \
55
// RUN: -enable-experimental-lifetime-dependence-inference \
66
// RUN: -Xllvm -enable-lifetime-dependence-diagnostics
7+
8+
// REQUIRES: asserts
9+
// REQUIRES: swift_in_compiler
710
// REQUIRES: noncopyable_generics
811

12+
// Incrementally testing https://github.pie.apple.com/guillaume-l/BufferView with lifetime dependence
13+
14+
// TODO: Use real Range
15+
public struct FakeRange<Bound> {
16+
public let lowerBound: Bound
17+
public let upperBound: Bound
18+
}
19+
20+
// TODO: Use real Optional
21+
public enum FakeOptional<T> {
22+
case none
23+
case some(T)
24+
}
25+
26+
public struct BufferViewIndex<Element> {
27+
let _rawValue: UnsafeRawPointer
28+
29+
internal init(rawValue: UnsafeRawPointer) {
30+
_rawValue = rawValue
31+
}
32+
33+
var isAligned: Bool {
34+
(Int(bitPattern: _rawValue) & (MemoryLayout<Element>.alignment-1)) == 0
35+
}
36+
}
37+
38+
extension BufferViewIndex: Equatable {}
39+
40+
extension BufferViewIndex: Hashable {}
41+
42+
extension BufferViewIndex: Strideable {
43+
public typealias Stride = Int
44+
45+
public func distance(to other: BufferViewIndex) -> Int {
46+
let bytes = _rawValue.distance(to: other._rawValue)
47+
let (q, r) = bytes.quotientAndRemainder(dividingBy: MemoryLayout<Element>.stride)
48+
precondition(r == 0)
49+
return q
50+
}
51+
52+
public func advanced(by n: Int) -> BufferViewIndex {
53+
.init(rawValue: _rawValue.advanced(by: n &* MemoryLayout<Element>.stride))
54+
}
55+
}
56+
57+
extension BufferViewIndex: Comparable {
58+
public static func <(lhs: BufferViewIndex, rhs: BufferViewIndex) -> Bool {
59+
lhs._rawValue < rhs._rawValue
60+
}
61+
}
62+
963
public struct BufferView<Element> : ~Escapable {
10-
public typealias Index = Int
11-
public typealias Pointer = UnsafePointer<Element>
12-
public let baseAddress: Pointer
64+
let start: BufferViewIndex<Element>
1365
public let count: Int
14-
15-
// TODO: This should be a failable initializer
16-
// Currently optional is Escapable, so we cant yet write it.
17-
public init<Storage>(unsafeBuffer: UnsafeBufferPointer<Element>,
18-
storage: borrowing Storage)
19-
-> _borrow(storage) Self {
20-
let baseAddress = unsafeBuffer.baseAddress!
21-
self = BufferView<Element>(baseAddress: baseAddress,
22-
count: unsafeBuffer.count)
23-
return self
24-
}
25-
// unsafe private API
66+
private var baseAddress: UnsafeRawPointer { start._rawValue }
67+
68+
public init<Owner>(
69+
baseAddress: UnsafeRawPointer,
70+
count: Int,
71+
dependsOn owner: borrowing Owner
72+
) {
73+
self.init(
74+
start: .init(rawValue: baseAddress), count: count, dependsOn: owner
75+
)
76+
}
77+
init<Owner>(
78+
start index: BufferViewIndex<Element>,
79+
count: Int,
80+
dependsOn owner: borrowing Owner
81+
) {
82+
precondition(count >= 0, "Count must not be negative")
83+
if !_isPOD(Element.self) {
84+
precondition(
85+
index.isAligned,
86+
"baseAddress must be properly aligned for \(Element.self)"
87+
)
88+
}
89+
self.start = index
90+
self.count = count
91+
}
2692
@_unsafeNonescapableResult
27-
init(baseAddress: Pointer, count: Int) {
93+
init(start index: BufferViewIndex<Element>,
94+
count: Int) {
2895
precondition(count >= 0, "Count must not be negative")
29-
self.baseAddress = baseAddress
96+
if !_isPOD(Element.self) {
97+
precondition(
98+
index.isAligned,
99+
"baseAddress must be properly aligned for \(Element.self)"
100+
)
101+
}
102+
self.start = index
30103
self.count = count
31104
}
32-
subscript(_ index: Index) -> Element? {
33-
if (index < 0 || index >= count) {
34-
return nil
105+
}
106+
// TODO: extend Collection, BidirectionalCollection, RandomAccessCollection {
107+
extension BufferView {
108+
public typealias Index = BufferViewIndex<Element>
109+
public typealias SubSequence = Self
110+
111+
public var startIndex: Index { start }
112+
public var endIndex: Index { start.advanced(by: count) }
113+
114+
public subscript(position: Index) -> Element {
115+
get {
116+
if _isPOD(Element.self) {
117+
return position._rawValue.loadUnaligned(as: Element.self)
118+
}
119+
else {
120+
return position._rawValue.load(as: Element.self)
121+
}
122+
}
123+
}
124+
125+
public subscript(bounds: FakeRange<BufferViewIndex<Element>>) -> Self {
126+
get {
127+
BufferView(
128+
start: bounds.lowerBound,
129+
count: bounds.upperBound.distance(to:bounds.lowerBound) / MemoryLayout<Element>.stride
130+
)
35131
}
36-
return baseAddress[index]
37132
}
38133
}
39134

@@ -47,14 +142,17 @@ extension Array {
47142
// rdar://123071321
48143
var view: BufferView<Element> {
49144
var _view : BufferView<Element>?
50-
withUnsafeBufferPointer {
51-
_view = BufferView(unsafeBuffer: $0, storage: self)
145+
withUnsafePointer(to:self) {
146+
_view = BufferView(baseAddress: $0, count: self.count, dependsOn: self)
52147
}
53148
return _view!
54149
}
55150
}
56151

57-
public func array_view_element(a: [Int] , i: Int) -> Int {
58-
a.view[i]!
152+
public func array_view_element(a: [Int] , i: BufferViewIndex<Int>) -> Int {
153+
a.view[i]
59154
}
60155

156+
public func array_view_slice_element(a: [Int] , sliceIdx: FakeRange<BufferViewIndex<Int>>, Idx: BufferViewIndex<Int>) -> Int {
157+
a.view[sliceIdx][Idx]
158+
}

0 commit comments

Comments
 (0)