Skip to content

Commit 305d751

Browse files
committed
LocalVariableUtils: precisely handle function live-out
Only record an outgoingArgument access when the current allocation is an outgoing argument and the function exiting instruction is ReturnInst. This avoids invalid SIL where we attempt to extend end_access up to an `unwind` but fail to actually materialize that end_access.
1 parent 62b04ca commit 305d751

File tree

1 file changed

+24
-13
lines changed

1 file changed

+24
-13
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,8 @@ struct LocalVariableAccessMap: Collection, CustomStringConvertible {
281281
let context: Context
282282
let allocation: Value
283283

284-
let liveInAccess: LocalVariableAccess?
284+
let isLiveIn: Bool
285+
let isLiveOut: Bool
285286

286287
// All mapped accesses have a valid instruction.
287288
//
@@ -306,12 +307,19 @@ struct LocalVariableAccessMap: Collection, CustomStringConvertible {
306307
init?(allocation: Value, _ context: Context) {
307308
switch allocation {
308309
case is AllocBoxInst, is AllocStackInst:
309-
self.liveInAccess = nil
310-
break
310+
self.isLiveIn = false
311+
self.isLiveOut = false
311312
case let arg as FunctionArgument:
312313
switch arg.convention {
313-
case .indirectIn, .indirectInout, .indirectInoutAliasable:
314-
self.liveInAccess = LocalVariableAccess(.incomingArgument, nil)
314+
case .indirectIn:
315+
self.isLiveIn = true
316+
self.isLiveOut = false
317+
case .indirectInout, .indirectInoutAliasable:
318+
self.isLiveIn = true
319+
self.isLiveOut = true
320+
case .indirectOut:
321+
self.isLiveIn = false
322+
self.isLiveOut = true
315323
default:
316324
return nil
317325
}
@@ -779,7 +787,8 @@ extension LocalVariableReachableAccess {
779787
if block != accessMap.allocation.parentBlock {
780788
for predecessor in block.predecessors { blockList.pushIfNotVisited(predecessor) }
781789
} else if block == accessMap.function.entryBlock {
782-
accessStack.push(accessMap.liveInAccess!)
790+
assert(accessMap.isLiveIn)
791+
accessStack.push(LocalVariableAccess(.incomingArgument, nil))
783792
}
784793
case .assignValue:
785794
break
@@ -829,7 +838,7 @@ extension LocalVariableReachableAccess {
829838
/// This performs a forward CFG walk to find known reachable uses from the function entry that guarantee liveness and
830839
/// may safely enclose dependent uses.
831840
private func gatherKnownLivenessUsesFromEntry(in accessStack: inout Stack<LocalVariableAccess>) {
832-
assert(accessMap.liveInAccess!.kind == .incomingArgument, "only an argument access is live in to the function")
841+
assert(accessMap.isLiveIn, "only an argument access is live in to the function")
833842
let firstInst = accessMap.function.entryBlock.instructions.first!
834843
_ = gatherReachableUses(onOrAfter: firstInst, in: &accessStack, mode: .livenessUses)
835844
}
@@ -906,7 +915,7 @@ extension LocalVariableReachableAccess {
906915
return false
907916
}
908917
forwardPropagateEffect(in: initialBlock, blockInfo: blockMap[initialBlock], effect: initialEffect,
909-
blockList: &blockList, accessStack: &accessStack)
918+
blockList: &blockList, accessStack: &accessStack, mode: mode)
910919
while let block = blockList.pop() {
911920
let blockInfo = blockMap[block]
912921
var currentEffect: ForwardDataFlowEffect?
@@ -933,7 +942,7 @@ extension LocalVariableReachableAccess {
933942
return false
934943
}
935944
forwardPropagateEffect(in: block, blockInfo: blockInfo, effect: currentEffect, blockList: &blockList,
936-
accessStack: &accessStack)
945+
accessStack: &accessStack, mode: mode)
937946
}
938947
log("\n\(accessMap)")
939948
log(prefix: false, "Reachable access:\n\(accessStack.map({ String(describing: $0)}).joined(separator: "\n"))")
@@ -945,16 +954,18 @@ extension LocalVariableReachableAccess {
945954

946955
private func forwardPropagateEffect(in block: BasicBlock, blockInfo: BlockInfo?, effect: ForwardDataFlowEffect?,
947956
blockList: inout BasicBlockWorklist,
948-
accessStack: inout Stack<LocalVariableAccess>) {
957+
accessStack: inout Stack<LocalVariableAccess>,
958+
mode: DataFlowMode) {
949959
switch effect {
950960
case .none, .read, .modify, .escape:
951961
if let blockInfo, blockInfo.hasDealloc {
952962
break
953963
}
954-
if block.terminator.isFunctionExiting {
955-
// Record any reachable function exit as .outgoingArgument.
964+
// Assume that only a ReturnInst can return a live-out value.
965+
// All other function exits are considered dead-ends.
966+
if block.terminator is ReturnInstruction, accessMap.isLiveOut {
956967
accessStack.push(LocalVariableAccess(.outgoingArgument, block.terminator))
957-
} else if block.successors.isEmpty {
968+
} else if block.successors.isEmpty, mode == .livenessUses {
958969
accessStack.push(LocalVariableAccess(.deadEnd, block.terminator))
959970
} else {
960971
for successor in block.successors { blockList.pushIfNotVisited(successor) }

0 commit comments

Comments
 (0)