Skip to content

Commit 3cad5c5

Browse files
authored
Merge pull request #81043 from meg-gupta/fixcow
Insert end_cow_mutation_addr for lifetime dependent values dependent on mutable addresses
2 parents 23a1817 + 5395721 commit 3cad5c5

40 files changed

+276
-17
lines changed

SwiftCompilerSources/Sources/AST/Type.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ extension TypeProperties {
182182
public var hasLocalArchetype: Bool { rawType.bridged.hasLocalArchetype() }
183183
public var isEscapable: Bool { rawType.bridged.isEscapable() }
184184
public var isNoEscape: Bool { rawType.bridged.isNoEscape() }
185+
public var isBuiltinType: Bool { rawType.bridged.isBuiltinType() }
185186
public var archetypeRequiresClass: Bool { rawType.bridged.archetypeRequiresClass() }
186187

187188
public var representationOfMetatype: AST.`Type`.MetatypeRepresentation {

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,102 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
124124
}
125125
let args = scopeExtension.findArgumentDependencies()
126126

127+
// If the scope cannot be extended to the caller, this must be the outermost dependency level.
128+
// Insert end_cow_mutation_addr if needed.
129+
if args.isEmpty {
130+
createEndCOWMutationIfNeeded(lifetimeDep: newLifetimeDep, context)
131+
}
132+
127133
// Redirect the dependence base to the function arguments. This may create additional mark_dependence instructions.
128134
markDep.redirectFunctionReturn(to: args, context)
129135
}
130136
}
131137

138+
private extension Type {
139+
func mayHaveMutableSpan(in function: Function, _ context: FunctionPassContext) -> Bool {
140+
if hasArchetype {
141+
return true
142+
}
143+
if isBuiltinType {
144+
return false
145+
}
146+
// Only result types that are nominal can have a MutableSpan derived from an inout array access.
147+
if nominal == nil {
148+
return false
149+
}
150+
if nominal == context.swiftMutableSpan {
151+
return true
152+
}
153+
if isStruct {
154+
guard let fields = getNominalFields(in: function) else {
155+
return false
156+
}
157+
return fields.contains { $0.mayHaveMutableSpan(in: function, context) }
158+
}
159+
if isTuple {
160+
return tupleElements.contains { $0.mayHaveMutableSpan(in: function, context) }
161+
}
162+
if isEnum {
163+
guard let cases = getEnumCases(in: function) else {
164+
return true
165+
}
166+
return cases.contains { $0.payload?.mayHaveMutableSpan(in: function, context) ?? false }
167+
}
168+
// Classes cannot be ~Escapable, therefore cannot hold a MutableSpan.
169+
if isClass {
170+
return false
171+
}
172+
return false
173+
}
174+
}
175+
176+
/// Insert end_cow_mutation_addr for lifetime dependent values that maybe of type MutableSpan and depend on a mutable address.
177+
private func createEndCOWMutationIfNeeded(lifetimeDep: LifetimeDependence, _ context: FunctionPassContext) {
178+
var scoped : ScopedInstruction
179+
180+
// Handle cases which generate mutable addresses: begin_access [modify] and yield &
181+
switch lifetimeDep.scope {
182+
case let .access(beginAccess):
183+
if beginAccess.accessKind != .modify {
184+
return
185+
}
186+
scoped = beginAccess
187+
case let .yield(value):
188+
let beginApply = value.definingInstruction as! BeginApplyInst
189+
if value == beginApply.token {
190+
return
191+
}
192+
if beginApply.convention(of: value as! MultipleValueInstructionResult) != .indirectInout {
193+
return
194+
}
195+
scoped = beginApply
196+
// None of the below cases can generate a mutable address.
197+
case let .owned:
198+
fallthrough
199+
case let .borrowed:
200+
fallthrough
201+
case let .local:
202+
fallthrough
203+
case let .initialized:
204+
fallthrough
205+
case let .caller:
206+
fallthrough
207+
case let .global:
208+
fallthrough
209+
case let .unknown:
210+
return
211+
}
212+
213+
guard lifetimeDep.dependentValue.type.mayHaveMutableSpan(in: lifetimeDep.dependentValue.parentFunction, context) else {
214+
return
215+
}
216+
217+
for endInstruction in scoped.endInstructions {
218+
let builder = Builder(before: endInstruction, context)
219+
builder.createEndCOWMutationAddr(address: lifetimeDep.parentValue)
220+
}
221+
}
222+
132223
private extension MarkDependenceInstruction {
133224
/// Rewrite the mark_dependence base operand to ignore inner borrow scopes (begin_borrow, load_borrow).
134225
///
@@ -194,7 +285,7 @@ private extension MarkDependenceAddrInst {
194285
}
195286
}
196287

197-
/// A scope extension is a set of nested scopes and their owners. The owner is a value that represents ownerhip of
288+
/// A scope extension is a set of nested scopes and their owners. The owner is a value that represents ownership of
198289
/// the outermost scopes, which cannot be extended; it limits how far the nested scopes can be extended.
199290
private struct ScopeExtension {
200291
let context: FunctionPassContext

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ struct FunctionPassContext : MutatingContext {
326326
_bridged.getSwiftArrayDecl().getAs(NominalTypeDecl.self)
327327
}
328328

329+
var swiftMutableSpan: NominalTypeDecl {
330+
_bridged.getSwiftMutableSpanDecl().getAs(NominalTypeDecl.self)
331+
}
332+
329333
func loadFunction(name: StaticString, loadCalleesRecursively: Bool) -> Function? {
330334
return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer<UInt8>) in
331335
let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count)

SwiftCompilerSources/Sources/Optimizer/TestPasses/MemBehaviorDumper.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ private extension Instruction {
6363
is BeginAccessInst,
6464
is EndAccessInst,
6565
is EndCOWMutationInst,
66+
is EndCOWMutationAddrInst,
6667
is CopyValueInst,
6768
is DestroyValueInst,
6869
is StrongReleaseInst,

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ extension AddressUseVisitor {
131131
is DestroyAddrInst, is DeallocStackInst,
132132
is DeinitExistentialAddrInst,
133133
is IsUniqueInst, is MarkFunctionEscapeInst,
134-
is PackElementSetInst:
134+
is PackElementSetInst, is EndCOWMutationAddrInst:
135135
return leafAddressUse(of: operand)
136136

137137
case is LoadInst, is LoadUnownedInst, is LoadWeakInst, is ValueMetatypeInst, is ExistentialMetatypeInst,

SwiftCompilerSources/Sources/SIL/Builder.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,11 @@ public struct Builder {
575575
return notifyNew(endMutation.getAs(EndCOWMutationInst.self))
576576
}
577577

578+
public func createEndCOWMutationAddr(address: Value) -> EndCOWMutationAddrInst {
579+
let endMutation = bridged.createEndCOWMutationAddr(address.bridged)
580+
return notifyNew(endMutation.getAs(EndCOWMutationAddrInst.self))
581+
}
582+
578583
public func createMarkDependence(value: Value, base: Value, kind: MarkDependenceKind) -> MarkDependenceInst {
579584
let markDependence = bridged.createMarkDependence(value.bridged, base.bridged,
580585
BridgedInstruction.MarkDependenceKind(rawValue: kind.rawValue)!)

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,10 @@ final public class EndCOWMutationInst : SingleValueInstruction, UnaryInstruction
12421242
public var doKeepUnique: Bool { bridged.EndCOWMutationInst_doKeepUnique() }
12431243
}
12441244

1245+
final public class EndCOWMutationAddrInst : Instruction, UnaryInstruction {
1246+
public var address: Value { operand.value }
1247+
}
1248+
12451249
final public
12461250
class ClassifyBridgeObjectInst : SingleValueInstruction, UnaryInstruction {}
12471251

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ public func registerSILClasses() {
215215
register(MoveValueInst.self)
216216
register(DropDeinitInst.self)
217217
register(EndCOWMutationInst.self)
218+
register(EndCOWMutationAddrInst.self)
218219
register(ClassifyBridgeObjectInst.self)
219220
register(PartialApplyInst.self)
220221
register(ApplyInst.self)

docs/SIL/Instructions.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,6 +2146,22 @@ not replace this reference with a not uniquely reference object.
21462146

21472147
For details see [Copy-on-Write Representation](SIL.md#Copy-on-Write-Representation).
21482148

2149+
### end_cow_mutation_addr
2150+
2151+
```
2152+
sil-instruction ::= 'end_cow_mutation_addr' sil-operand
2153+
2154+
end_cow_mutation_addr %0 : $*T
2155+
// %0 must be of an address $*T type
2156+
```
2157+
2158+
This instruction marks the end of mutation of an address. The address could be
2159+
an opaque archetype, a struct, tuple or enum type and the end_cow_mutation_addr
2160+
will apply to all members contained within it.
2161+
It is currently only generated in cases where we maybe deriving a MutableSpan from
2162+
`%0` since it is not possible to schedule an `end_cow_mutation` in the standard
2163+
library automatically for Array.mutableSpan etc.
2164+
21492165
### destroy_not_escaped_closure
21502166

21512167
```

include/swift/AST/ASTBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3106,6 +3106,7 @@ struct BridgedASTType {
31063106
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getBuiltinVectorElementType() const;
31073107
BRIDGED_INLINE bool isBuiltinFixedWidthInteger(SwiftInt width) const;
31083108
BRIDGED_INLINE bool isOptional() const;
3109+
BRIDGED_INLINE bool isBuiltinType() const;
31093110
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getNominalOrBoundGenericNominal() const;
31103111
BRIDGED_INLINE TraitResult canBeClass() const;
31113112
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getAnyNominal() const;

0 commit comments

Comments
 (0)