Skip to content

Commit 69b6b1d

Browse files
committed
LifetimeDependenceDefUseWalker: handle addressable dependencies
1 parent d9dd935 commit 69b6b1d

File tree

4 files changed

+120
-49
lines changed

4 files changed

+120
-49
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ protocol AddressUseVisitor {
5656
-> WalkResult
5757

5858
/// A loaded address use propagates the value at the address.
59-
mutating func loadedAddressUse(of operand: Operand, into value: Value)
59+
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value)
6060
-> WalkResult
6161

6262
/// A loaded address use propagates the value at the address to the
6363
/// destination address operand.
64-
mutating func loadedAddressUse(of operand: Operand, into address: Operand)
64+
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand)
6565
-> WalkResult
6666

6767
/// Yielding an address may modify the value at the yield, but not past the yield. The yielded value may escape, but
@@ -137,14 +137,14 @@ extension AddressUseVisitor {
137137
case is LoadInst, is LoadUnownedInst, is LoadWeakInst, is ValueMetatypeInst, is ExistentialMetatypeInst,
138138
is PackElementGetInst:
139139
let svi = operand.instruction as! SingleValueInstruction
140-
return loadedAddressUse(of: operand, into: svi)
140+
return loadedAddressUse(of: operand, intoValue: svi)
141141

142142
case is YieldInst:
143143
return yieldedAddressUse(of: operand)
144144

145145
case let sdai as SourceDestAddrInstruction
146146
where sdai.sourceOperand == operand:
147-
return loadedAddressUse(of: operand, into: sdai.destinationOperand)
147+
return loadedAddressUse(of: operand, intoAddress: sdai.destinationOperand)
148148

149149
case let sdai as SourceDestAddrInstruction
150150
where sdai.destinationOperand == operand:
@@ -401,12 +401,12 @@ extension AddressInitializationWalker {
401401
return convention.isIndirectIn ? .continueWalk : .abortWalk
402402
}
403403

404-
mutating func loadedAddressUse(of operand: Operand, into value: Value)
404+
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value)
405405
-> WalkResult {
406406
return .continueWalk
407407
}
408408

409-
mutating func loadedAddressUse(of operand: Operand, into address: Operand)
409+
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand)
410410
-> WalkResult {
411411
return .continueWalk
412412
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

+41-23
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ extension LifetimeDependenceDefUseWalker {
584584

585585
// Override ForwardingDefUseWalker.
586586
mutating func walkDown(operand: Operand) -> WalkResult {
587-
// Initially delegate all usess to OwnershipUseVisitor.
587+
// Initially delegate all uses to OwnershipUseVisitor.
588588
// walkDownDefault will be called for uses that forward ownership.
589589
return classify(operand: operand)
590590
}
@@ -631,7 +631,7 @@ extension LifetimeDependenceDefUseWalker {
631631
// like [nonescaping] even though they are not considered OSSA
632632
// borrows until after resolution.
633633
assert(operand == mdi.baseOperand)
634-
return dependentUse(of: operand, into: mdi)
634+
return dependentUse(of: operand, dependentValue: mdi)
635635

636636
case is ExistentialMetatypeInst, is FixLifetimeInst, is WitnessMethodInst,
637637
is DynamicMethodBranchInst, is ValueMetatypeInst,
@@ -676,11 +676,24 @@ extension LifetimeDependenceDefUseWalker {
676676
return escapingDependence(on: operand)
677677
}
678678

679-
mutating func dependentUse(of operand: Operand, into value: Value)
679+
// Handle address or non-address operands.
680+
mutating func dependentUse(of operand: Operand, dependentValue value: Value)
680681
-> WalkResult {
681682
return walkDownUses(of: value, using: operand)
682683
}
683684

685+
// Handle address or non-address operands.
686+
mutating func dependentUse(of operand: Operand, dependentAddress address: Value)
687+
-> WalkResult {
688+
// Consider this a leaf use in addition to the dependent address uses, which might all occur earlier.
689+
if leafUse(of: operand) == .abortWalk {
690+
return .abortWalk
691+
}
692+
// The lifetime dependence is effectively "copied into" the dependent address. Find all uses of the dependent
693+
// address as if this were a stored use.
694+
return visitStoredUses(of: operand, into: address)
695+
}
696+
684697
mutating func borrowingUse(of operand: Operand,
685698
by borrowInst: BorrowingInstruction)
686699
-> WalkResult {
@@ -736,7 +749,7 @@ extension LifetimeDependenceDefUseWalker {
736749
return visitAppliedUse(of: operand, by: apply)
737750
}
738751

739-
mutating func loadedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
752+
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value) -> WalkResult {
740753
// Record the load itself, in case the loaded value is Escapable.
741754
if leafUse(of: operand) == .abortWalk {
742755
return .abortWalk
@@ -745,7 +758,7 @@ extension LifetimeDependenceDefUseWalker {
745758
}
746759

747760
// copy_addr
748-
mutating func loadedAddressUse(of operand: Operand, into address: Operand) -> WalkResult {
761+
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand) -> WalkResult {
749762
if leafUse(of: operand) == .abortWalk {
750763
return .abortWalk
751764
}
@@ -761,18 +774,12 @@ extension LifetimeDependenceDefUseWalker {
761774
}
762775

763776
mutating func dependentAddressUse(of operand: Operand, dependentValue value: Value) -> WalkResult {
764-
walkDownUses(of: value, using: operand)
777+
dependentUse(of: operand, dependentValue: value)
765778
}
766779

767780
// mark_dependence_addr
768781
mutating func dependentAddressUse(of operand: Operand, dependentAddress address: Value) -> WalkResult {
769-
// Consider this a leaf use in addition to the dependent address uses, which might all occur earlier.
770-
if leafUse(of: operand) == .abortWalk {
771-
return .abortWalk
772-
}
773-
// The lifetime dependence is effectively "copied into" the dependent address. Find all uses of the dependent
774-
// address as if this were a stored use.
775-
return visitStoredUses(of: operand, into: address)
782+
dependentUse(of: operand, dependentAddress: address)
776783
}
777784

778785
mutating func escapingAddressUse(of operand: Operand) -> WalkResult {
@@ -898,19 +905,30 @@ extension LifetimeDependenceDefUseWalker {
898905
case .load:
899906
switch localAccess.instruction! {
900907
case let load as LoadInst:
901-
return loadedAddressUse(of: localAccess.operand!, into: load)
908+
return loadedAddressUse(of: localAccess.operand!, intoValue: load)
902909
case let load as LoadBorrowInst:
903-
return loadedAddressUse(of: localAccess.operand!, into: load)
910+
return loadedAddressUse(of: localAccess.operand!, intoValue: load)
904911
case let copyAddr as SourceDestAddrInstruction:
905-
return loadedAddressUse(of: localAccess.operand!, into: copyAddr.destinationOperand)
912+
return loadedAddressUse(of: localAccess.operand!, intoAddress: copyAddr.destinationOperand)
913+
default:
914+
return .abortWalk
915+
}
916+
case .dependenceSource:
917+
switch localAccess.instruction! {
918+
case let md as MarkDependenceInst:
919+
if md.type.isAddress {
920+
return loadedAddressUse(of: localAccess.operand!, intoAddress: md.valueOperand)
921+
}
922+
return loadedAddressUse(of: localAccess.operand!, intoValue: md)
923+
case let md as MarkDependenceAddrInst:
924+
return loadedAddressUse(of: localAccess.operand!, intoAddress: md.addressOperand)
906925
default:
907926
return .abortWalk
908927
}
909-
case .dependence:
910-
// An address-forwarding mark_dependence is simply a marker that indicates the start of an in-memory
911-
// dependent value. Typically, it has no uses. If it does have uses, then they are visited earlier by
912-
// LocalVariableAccessWalker to record any other local accesses.
913-
return .continueWalk
928+
case .dependenceDest:
929+
// Simply a marker that indicates the start of an in-memory dependent value. If this was a mark_dependence, uses
930+
// of its forwarded address has were visited by LocalVariableAccessWalker and recorded as separate local accesses.
931+
return .continueWalk
914932
case .store:
915933
let si = localAccess.operand!.instruction as! StoringInstruction
916934
assert(si.sourceOperand == initialValue, "the only reachable store should be the current assignment")
@@ -948,13 +966,13 @@ extension LifetimeDependenceDefUseWalker {
948966
// because a mark_dependence [nonescaping] represents the
949967
// dependence.
950968
if let result = apply.singleDirectResult, !result.isEscapable {
951-
if dependentUse(of: operand, into: result) == .abortWalk {
969+
if dependentUse(of: operand, dependentValue: result) == .abortWalk {
952970
return .abortWalk
953971
}
954972
}
955973
for resultAddr in apply.indirectResultOperands
956974
where !resultAddr.value.isEscapable {
957-
if visitStoredUses(of: operand, into: resultAddr.value) == .abortWalk {
975+
if dependentUse(of: operand, dependentAddress: resultAddr.value) == .abortWalk {
958976
return .abortWalk
959977
}
960978
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

+44-12
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,38 @@ private func log(_ message: @autoclosure () -> String) {
3636
// Local variables are accessed in one of these ways.
3737
//
3838
// Note: @in is only immutable up to when it is destroyed, so still requires a local live range.
39+
//
40+
// A .dependenceSource access creates a new dependent value that keeps this local alive.
41+
//
42+
// %local = alloc_stack // this local
43+
// %md = mark_dependence %val on %local
44+
// mark_dependence_addr %adr on %local
45+
//
46+
// The effect of .dependenceSource on reachability is like a load of this local. The dependent value depends on any
47+
// value in this local that reaches this point.
48+
//
49+
// A .dependenceDest access is the point where another value becomes dependent on this local:
50+
//
51+
// %local = alloc_stack // this local
52+
// %md = mark_dependence %local on %val
53+
// mark_dependence_addr %local on %val
54+
//
55+
// The effect of .dependenceDest on reachability is like a store of this local. All uses of this local reachable from
56+
// this point are uses of the dependence base.
57+
//
58+
// Note that the same mark_dependence_addr often involves two locals:
59+
//
60+
// mark_dependence_addr %localDest on %localSource
61+
//
3962
struct LocalVariableAccess: CustomStringConvertible {
4063
enum Kind {
4164
case incomingArgument // @in, @inout, @inout_aliasable
4265
case outgoingArgument // @inout, @inout_aliasable
4366
case inoutYield // indirect yield from this accessor
4467
case beginAccess // Reading or reassigning a 'var'
4568
case load // Reading a 'let'. Returning 'var' from an initializer.
46-
case dependence // A mark_dependence after an apply with an indirect result. No effect.
69+
case dependenceSource // A value/address depends on this local here (like a load)
70+
case dependenceDest // This local depends on another value/address here (like a store)
4771
case store // 'var' initialization and destruction
4872
case apply // indirect arguments
4973
case escape // alloc_box captures
@@ -77,7 +101,7 @@ struct LocalVariableAccess: CustomStringConvertible {
77101
case .`init`, .modify:
78102
return true
79103
}
80-
case .load, .dependence:
104+
case .load, .dependenceSource, .dependenceDest:
81105
return false
82106
case .incomingArgument, .outgoingArgument, .store, .inoutYield:
83107
return true
@@ -116,8 +140,10 @@ struct LocalVariableAccess: CustomStringConvertible {
116140
str += "beginAccess"
117141
case .load:
118142
str += "load"
119-
case .dependence:
120-
str += "dependence"
143+
case .dependenceSource:
144+
str += "dependenceSource"
145+
case .dependenceDest:
146+
str += "dependenceDest"
121147
case .store:
122148
str += "store"
123149
case .apply:
@@ -152,7 +178,7 @@ class LocalVariableAccessInfo: CustomStringConvertible {
152178
case .`init`, .modify:
153179
break // lazily compute full assignment
154180
}
155-
case .load, .dependence:
181+
case .load, .dependenceSource, .dependenceDest:
156182
self._isFullyAssigned = false
157183
case .store:
158184
if let store = localAccess.instruction as? StoringInstruction {
@@ -365,7 +391,7 @@ extension LocalVariableAccessWalker : ForwardingDefUseWalker {
365391
break
366392
case let markDep as MarkDependenceInst:
367393
assert(markDep.baseOperand == operand)
368-
visit(LocalVariableAccess(.dependence, operand))
394+
visit(LocalVariableAccess(.dependenceSource, operand))
369395
default:
370396
visit(LocalVariableAccess(.escape, operand))
371397
}
@@ -381,10 +407,6 @@ extension LocalVariableAccessWalker : ForwardingDefUseWalker {
381407
extension LocalVariableAccessWalker: AddressUseVisitor {
382408
private mutating func walkDownAddressUses(address: Value) -> WalkResult {
383409
for operand in address.uses.ignoreTypeDependence {
384-
if let md = operand.instruction as? MarkDependenceInst, operand == md.valueOperand {
385-
// Record the forwarding mark_dependence as a fake access before continuing to walk down.
386-
visit(LocalVariableAccess(.dependence, operand))
387-
}
388410
if classifyAddress(operand: operand) == .abortWalk {
389411
return .abortWalk
390412
}
@@ -399,6 +421,13 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
399421
// temporaries do not have access scopes, so we need to walk down any projection that may be used to initialize the
400422
// temporary.
401423
mutating func projectedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
424+
// Intercept mark_dependence destination to record an access point which can be used like a store when finding all
425+
// uses that affect the base after the point that the dependence was marked.
426+
if let md = value as? MarkDependenceInst {
427+
assert(operand == md.valueOperand)
428+
visit(LocalVariableAccess(.dependenceDest, operand))
429+
// walk down the forwarded address as usual...
430+
}
402431
return walkDownAddressUses(address: value)
403432
}
404433

@@ -432,6 +461,9 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
432461
is PackElementSetInst:
433462
// Handle instructions that initialize both temporaries and local variables.
434463
visit(LocalVariableAccess(.store, operand))
464+
case let md as MarkDependenceAddrInst:
465+
assert(operand == md.addressOperand)
466+
visit(LocalVariableAccess(.dependenceDest, operand))
435467
case is DeallocStackInst:
436468
break
437469
default:
@@ -473,12 +505,12 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
473505
return .continueWalk
474506
}
475507

476-
mutating func loadedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
508+
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value) -> WalkResult {
477509
visit(LocalVariableAccess(.load, operand))
478510
return .continueWalk
479511
}
480512

481-
mutating func loadedAddressUse(of operand: Operand, into address: Operand) -> WalkResult {
513+
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand) -> WalkResult {
482514
visit(LocalVariableAccess(.load, operand))
483515
return .continueWalk
484516
}

0 commit comments

Comments
 (0)