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

+1
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

+92-1
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

+4
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

+1
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

+1-1
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

+5
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

+4
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

+1
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

+16
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

+1
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;

include/swift/AST/ASTBridgingImpl.h

+4
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,10 @@ bool BridgedASTType::isUnownedStorageType() const {
556556
return unbridged()->is<swift::UnownedStorageType>();
557557
}
558558

559+
bool BridgedASTType::isBuiltinType() const {
560+
return unbridged()->isBuiltinType();
561+
}
562+
559563
OptionalBridgedDeclObj BridgedASTType::getNominalOrBoundGenericNominal() const {
560564
return {unbridged()->getNominalOrBoundGenericNominal()};
561565
}

include/swift/AST/KnownStdlibTypes.def

+2
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,6 @@ KNOWN_STDLIB_TYPE_DECL(InlineArray, NominalTypeDecl, 2)
103103

104104
KNOWN_STDLIB_TYPE_DECL(SwiftSetting, NominalTypeDecl, 0)
105105

106+
KNOWN_STDLIB_TYPE_DECL(MutableSpan, NominalTypeDecl, 1)
107+
106108
#undef KNOWN_STDLIB_TYPE_DECL

include/swift/AST/Types.h

+7
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,9 @@ class alignas(1 << TypeAlignInBits) TypeBase
10541054
bool is##NAME();
10551055
#include "swift/AST/KnownStdlibTypes.def"
10561056

1057+
/// Check if this type is from the Builtin module.
1058+
bool isBuiltinType();
1059+
10571060
/// Check if this type is equal to Builtin.IntN.
10581061
bool isBuiltinIntegerType(unsigned bitWidth);
10591062

@@ -8199,6 +8202,10 @@ inline GenericTypeDecl *TypeBase::getAnyGeneric() {
81998202
return getCanonicalType().getAnyGeneric();
82008203
}
82018204

8205+
inline bool TypeBase::isBuiltinType() {
8206+
return isa<BuiltinType>(getCanonicalType());
8207+
}
8208+
82028209
inline bool TypeBase::isBuiltinIntegerType(unsigned n) {
82038210
if (auto intTy = dyn_cast<BuiltinIntegerType>(getCanonicalType()))
82048211
return intTy->getWidth().isFixedWidth()

include/swift/SIL/AddressWalker.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) {
229229
isa<PackElementSetInst>(user) || isa<PackElementGetInst>(user) ||
230230
isa<DeinitExistentialAddrInst>(user) || isa<LoadBorrowInst>(user) ||
231231
isa<TupleAddrConstructorInst>(user) || isa<DeallocPackInst>(user) ||
232-
isa<MergeIsolationRegionInst>(user)) {
232+
isa<MergeIsolationRegionInst>(user) || isa<EndCOWMutationAddrInst>(user)) {
233233
callVisitUse(op);
234234
continue;
235235
}

include/swift/SIL/SILBridging.h

+1
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,7 @@ struct BridgedBuilder{
12831283
BridgedASTType::MetatypeRepresentation representation) const;
12841284
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEndCOWMutation(BridgedValue instance,
12851285
bool keepUnique) const;
1286+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEndCOWMutationAddr(BridgedValue instance) const;
12861287
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createMarkDependence(
12871288
BridgedValue value, BridgedValue base, BridgedInstruction::MarkDependenceKind dependenceKind) const;
12881289

include/swift/SIL/SILBridgingImpl.h

+6
Original file line numberDiff line numberDiff line change
@@ -2516,6 +2516,12 @@ BridgedInstruction BridgedBuilder::createEndCOWMutation(BridgedValue instance, b
25162516
keepUnique)};
25172517
}
25182518

2519+
BridgedInstruction
2520+
BridgedBuilder::createEndCOWMutationAddr(BridgedValue instance) const {
2521+
return {unbridged().createEndCOWMutationAddr(regularLoc(),
2522+
instance.getSILValue())};
2523+
}
2524+
25192525
BridgedInstruction BridgedBuilder::createMarkDependence(BridgedValue value, BridgedValue base, BridgedInstruction::MarkDependenceKind kind) const {
25202526
return {unbridged().createMarkDependence(regularLoc(), value.getSILValue(), base.getSILValue(), swift::MarkDependenceKind(kind))};
25212527
}

include/swift/SIL/SILBuilder.h

+5
Original file line numberDiff line numberDiff line change
@@ -2383,6 +2383,11 @@ class SILBuilder {
23832383
return insert(new (getModule()) EndCOWMutationInst(getSILDebugLocation(Loc),
23842384
operand, keepUnique));
23852385
}
2386+
EndCOWMutationAddrInst *createEndCOWMutationAddr(SILLocation Loc,
2387+
SILValue operand) {
2388+
return insert(new (getModule()) EndCOWMutationAddrInst(
2389+
getSILDebugLocation(Loc), operand));
2390+
}
23862391
DestroyNotEscapedClosureInst *createDestroyNotEscapedClosure(SILLocation Loc,
23872392
SILValue operand,
23882393
unsigned VerificationType) {

include/swift/SIL/SILCloner.h

+8
Original file line numberDiff line numberDiff line change
@@ -3170,6 +3170,14 @@ void SILCloner<ImplClass>::visitEndCOWMutationInst(EndCOWMutationInst *Inst) {
31703170
getOpValue(Inst->getOperand()), Inst->doKeepUnique()));
31713171
}
31723172
template <typename ImplClass>
3173+
void SILCloner<ImplClass>::visitEndCOWMutationAddrInst(
3174+
EndCOWMutationAddrInst *Inst) {
3175+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
3176+
recordClonedInstruction(
3177+
Inst, getBuilder().createEndCOWMutationAddr(
3178+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
3179+
}
3180+
template <typename ImplClass>
31733181
void SILCloner<ImplClass>::visitDestroyNotEscapedClosureInst(
31743182
DestroyNotEscapedClosureInst *Inst) {
31753183
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));

include/swift/SIL/SILInstruction.h

+8
Original file line numberDiff line numberDiff line change
@@ -9580,6 +9580,14 @@ class EndCOWMutationInst
95809580
sharedUInt8().EndCOWMutationInst.keepUnique = keepUnique;
95819581
}
95829582
};
9583+
class EndCOWMutationAddrInst
9584+
: public UnaryInstructionBase<SILInstructionKind::EndCOWMutationAddrInst,
9585+
NonValueInstruction> {
9586+
friend SILBuilder;
9587+
9588+
EndCOWMutationAddrInst(SILDebugLocation debugLoc, SILValue address)
9589+
: UnaryInstructionBase(debugLoc, address) {}
9590+
};
95839591

95849592
/// Given an escaping closure return true iff it has a non-nil context and the
95859593
/// context has a strong reference count greater than 1.

include/swift/SIL/SILNodes.def

+3-2
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
528528

529529
SINGLE_VALUE_INST(EndCOWMutationInst, end_cow_mutation,
530530
SingleValueInstruction, None, DoesNotRelease)
531-
532531
SINGLE_VALUE_INST(DestroyNotEscapedClosureInst, destroy_not_escaped_closure,
533532
SingleValueInstruction, MayHaveSideEffects, MayRelease)
534533

@@ -908,7 +907,9 @@ NON_VALUE_INST(IncrementProfilerCounterInst, increment_profiler_counter,
908907
NON_VALUE_INST(MarkDependenceAddrInst, mark_dependence_addr,
909908
SILInstruction, None, DoesNotRelease)
910909

911-
NODE_RANGE(NonValueInstruction, UnreachableInst, MarkDependenceAddrInst)
910+
NON_VALUE_INST(EndCOWMutationAddrInst, end_cow_mutation_addr,
911+
SILInstruction, MayHaveSideEffects, DoesNotRelease)
912+
NODE_RANGE(NonValueInstruction, UnreachableInst, EndCOWMutationAddrInst)
912913

913914
ABSTRACT_INST(MultipleValueInstruction, SILInstruction)
914915
MULTIPLE_VALUE_INST(BeginApplyInst, begin_apply,

include/swift/SILOptimizer/OptimizerBridging.h

+1
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ struct BridgedPassContext {
216216
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDomTree getDomTree() const;
217217
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedPostDomTree getPostDomTree() const;
218218
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj getSwiftArrayDecl() const;
219+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj getSwiftMutableSpanDecl() const;
219220

220221
// AST
221222

include/swift/SILOptimizer/OptimizerBridgingImpl.h

+5
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ BridgedDeclObj BridgedPassContext::getSwiftArrayDecl() const {
219219
return {mod->getASTContext().getArrayDecl()};
220220
}
221221

222+
BridgedDeclObj BridgedPassContext::getSwiftMutableSpanDecl() const {
223+
swift::SILModule *mod = invocation->getPassManager()->getModule();
224+
return {mod->getASTContext().getMutableSpanDecl()};
225+
}
226+
222227
// AST
223228

224229
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE

lib/IRGen/IRGenSIL.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,7 @@ class IRGenSILFunction :
14381438
void visitIsUniqueInst(IsUniqueInst *i);
14391439
void visitBeginCOWMutationInst(BeginCOWMutationInst *i);
14401440
void visitEndCOWMutationInst(EndCOWMutationInst *i);
1441+
void visitEndCOWMutationAddrInst(EndCOWMutationAddrInst *i);
14411442
void visitDestroyNotEscapedClosureInst(DestroyNotEscapedClosureInst *i);
14421443
void visitDeallocStackInst(DeallocStackInst *i);
14431444
void visitDeallocStackRefInst(DeallocStackRefInst *i);
@@ -6366,6 +6367,10 @@ void IRGenSILFunction::visitEndCOWMutationInst(EndCOWMutationInst *i) {
63666367
setLoweredExplosion(i, v);
63676368
}
63686369

6370+
void IRGenSILFunction::visitEndCOWMutationAddrInst(EndCOWMutationAddrInst *i) {
6371+
// end_cow_mutation_addr is purely for SIL.
6372+
}
6373+
63696374
void IRGenSILFunction::visitDestroyNotEscapedClosureInst(
63706375
swift::DestroyNotEscapedClosureInst *i) {
63716376
// The closure operand is allowed to be an optional closure.

lib/SIL/IR/OperandOwnership.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ OPERAND_OWNERSHIP(DestroyingConsume, DestroyNotEscapedClosure)
301301
OPERAND_OWNERSHIP(DestroyingConsume, EndLifetime)
302302
OPERAND_OWNERSHIP(DestroyingConsume, BeginCOWMutation)
303303
OPERAND_OWNERSHIP(DestroyingConsume, EndCOWMutation)
304+
OPERAND_OWNERSHIP(TrivialUse, EndCOWMutationAddr)
304305
OPERAND_OWNERSHIP(DestroyingConsume, EndInitLetRef)
305306
// The move_value instruction creates a distinct lifetime.
306307
OPERAND_OWNERSHIP(DestroyingConsume, MoveValue)

lib/SIL/IR/SILPrinter.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2763,6 +2763,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
27632763
*this << "[keep_unique] ";
27642764
*this << getIDAndType(ECMI->getOperand());
27652765
}
2766+
void visitEndCOWMutationAddrInst(EndCOWMutationAddrInst *ECMI) {
2767+
*this << getIDAndType(ECMI->getOperand());
2768+
}
27662769
void visitEndInitLetRefInst(EndInitLetRefInst *I) {
27672770
*this << getIDAndType(I->getOperand());
27682771
}

0 commit comments

Comments
 (0)