Skip to content

Commit 6e0eeb0

Browse files
committed
Fix LifetimeDependenceDefUseWalker for @inout reassignment
1 parent ff4d053 commit 6e0eeb0

File tree

3 files changed

+170
-70
lines changed

3 files changed

+170
-70
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ extension AddressOwnershipLiveRange {
649649
var reachableUses = Stack<LocalVariableAccess>(context)
650650
defer { reachableUses.deinitialize() }
651651

652-
localReachability.gatherKnownLifetimeUses(from: assignment, in: &reachableUses)
652+
localReachability.gatherKnownLivenessUses(from: assignment, in: &reachableUses)
653653

654654
let assignmentInst = assignment.instruction ?? allocation.parentFunction.entryBlock.instructions.first!
655655
var range = InstructionRange(begin: assignmentInst, context)

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -662,12 +662,38 @@ extension LifetimeDependenceDefUseWalker {
662662
}
663663
let root = dependence.dependentValue
664664
if root.type.isAddress {
665-
// The root address may be an escapable mark_dependence that guards its address uses (unsafeAddress), an
666-
// allocation, an incoming argument, or an outgoing argument. In all these cases, walk down the address uses.
665+
// 'root' may be an incoming ~Escapable argument (where the argument is both the scope and the dependent value).
666+
// If it is @inout, treat it like a local variable initialized on entry and possibly reassigned.
667+
if let arg = root as? FunctionArgument, arg.convention.isInout {
668+
return visitInoutAccess(argument: arg)
669+
}
670+
671+
// Conservatively walk down any other address. This includes:
672+
// An @in argument: assume it is initialized on entry and never reassigned.
673+
// An @out argument: assume the first address use is the one and only assignment on each return path.
674+
// An escapable mark_dependence that guards its address uses (unsafeAddress).
675+
// Any other unknown address producer.
667676
return walkDownAddressUses(of: root)
668677
}
669678
return walkDownUses(of: root, using: nil)
670679
}
680+
681+
// Find all @inout local variable uses reachabile from function entry. If local analysis fails to gather reachable
682+
// uses, fall back to walkDownAddressUse to produce a better diagnostic.
683+
mutating func visitInoutAccess(argument: FunctionArgument) -> WalkResult {
684+
guard let localReachability = localReachabilityCache.reachability(for: argument, walkerContext) else {
685+
return walkDownAddressUses(of: argument)
686+
}
687+
var reachableUses = Stack<LocalVariableAccess>(walkerContext)
688+
defer { reachableUses.deinitialize() }
689+
690+
if !localReachability.gatherAllReachableDependentUsesFromEntry(in: &reachableUses) {
691+
return walkDownAddressUses(of: argument)
692+
}
693+
return reachableUses.walk { localAccess in
694+
visitLocalAccess(allocation: argument, localAccess: localAccess)
695+
}
696+
}
671697
}
672698

673699
// Implement ForwardingDefUseWalker
@@ -1022,7 +1048,7 @@ extension LifetimeDependenceDefUseWalker {
10221048
if case let .access(beginAccess) = storeAddress.enclosingAccessScope {
10231049
storeAccess = beginAccess
10241050
}
1025-
if !localReachability.gatherAllReachableUses(of: storeAccess, in: &accessStack) {
1051+
if !localReachability.gatherAllReachableDependentUses(of: storeAccess, in: &accessStack) {
10261052
return escapingDependence(on: storedOperand)
10271053
}
10281054
return accessStack.walk { localAccess in

0 commit comments

Comments
 (0)