Skip to content

Commit eae2f06

Browse files
committed
Extend and improve AccessBase.findSingleInitializer
Return an Initializer rather than a tuple. This makes it easier and more robust for clients to track dependencies on initialized memory.
1 parent 8b39b6d commit eae2f06

File tree

1 file changed

+51
-17
lines changed

1 file changed

+51
-17
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -201,25 +201,49 @@ extension AddressUseVisitor {
201201
}
202202

203203
extension AccessBase {
204-
/// If this access base has a single initializer, return it, along
205-
/// with the initialized address. This does not guarantee that all
206-
/// uses of that address are dominated by the store or even that the
207-
/// store is a direct use of `address`.
208-
func findSingleInitializer(_ context: some Context) -> (initialAddress: Value, initializingStore: Instruction)? {
204+
enum Initializer {
205+
// An @in or @inout argument that is never modified inside the function. Handling an unmodified @inout like an @in
206+
// allows clients to ignore access scopes for the purpose of reaching definitions and lifetimes.
207+
case argument(FunctionArgument)
208+
// A yielded @in argument.
209+
case yield(MultipleValueInstructionResult)
210+
// A local variable or @out argument that is assigned once.
211+
// 'initialAddress' is the first point at which address may be used. Typically the allocation.
212+
case store(initializingStore: Instruction, initialAddress: Value)
213+
214+
var initialAddress: Value {
215+
let address: Value
216+
switch self {
217+
case let .argument(arg):
218+
address = arg
219+
case let .yield(result):
220+
address = result
221+
case let .store(_, initialAddress):
222+
address = initialAddress
223+
}
224+
assert(address.type.isAddress)
225+
return address
226+
}
227+
}
228+
229+
/// If this access base has a single initializer, return it, along with the initialized address. This does not
230+
/// guarantee that all uses of that address are dominated by the store or even that the store is a direct use of
231+
/// `address`.
232+
func findSingleInitializer(_ context: some Context) -> Initializer? {
209233
let baseAddr: Value
210234
switch self {
211235
case let .stack(allocStack):
212236
baseAddr = allocStack
213237
case let .argument(arg):
214-
guard arg.convention.isIndirectOut else {
215-
return nil
238+
if arg.convention.isIndirectIn {
239+
return .argument(arg)
216240
}
217241
baseAddr = arg
218242
case let .storeBorrow(sbi):
219243
guard case let .stack(allocStack) = sbi.destinationOperand.value.accessBase else {
220244
return nil
221245
}
222-
return (initialAddress: allocStack, initializingStore: sbi)
246+
return .store(initializingStore: sbi, initialAddress: allocStack)
223247
default:
224248
return nil
225249
}
@@ -254,33 +278,43 @@ extension AccessBase {
254278
// distinguishes between `mayWriteToMemory` for dependence vs. actual
255279
// modification of memory.
256280
struct AddressInitializationWalker: AddressDefUseWalker, AddressUseVisitor {
281+
let baseAddress: Value
257282
let context: any Context
258283

259284
var walkDownCache = WalkerCache<SmallProjectionPath>()
260285

261286
var isProjected = false
262-
var initializingStore: Instruction?
287+
var initializer: AccessBase.Initializer?
263288

264289
static func findSingleInitializer(ofAddress baseAddr: Value, context: some Context)
265-
-> (initialAddress: Value, initializingStore: Instruction)? {
290+
-> AccessBase.Initializer? {
266291

267-
var walker = AddressInitializationWalker(context: context)
292+
var walker = AddressInitializationWalker(baseAddress: baseAddr, context)
268293
if walker.walkDownUses(ofAddress: baseAddr, path: SmallProjectionPath()) == .abortWalk {
269294
return nil
270295
}
271-
guard let initializingStore = walker.initializingStore else {
272-
return nil
296+
return walker.initializer
297+
}
298+
299+
private init(baseAddress: Value, _ context: some Context) {
300+
self.baseAddress = baseAddress
301+
self.context = context
302+
if let arg = baseAddress as? FunctionArgument {
303+
assert(!arg.convention.isIndirectIn, "@in arguments cannot be initialized")
304+
if arg.convention.isInout {
305+
initializer = .argument(arg)
306+
}
307+
// @out arguments are initialized normally via stores.
273308
}
274-
return (initialAddress: baseAddr, initializingStore: initializingStore)
275309
}
276310

277311
private mutating func setInitializer(instruction: Instruction) -> WalkResult {
278312
// An initializer must be unique and store the full value.
279-
if initializingStore != nil || isProjected {
280-
initializingStore = nil
313+
if initializer != nil || isProjected {
314+
initializer = nil
281315
return .abortWalk
282316
}
283-
initializingStore = instruction
317+
initializer = .store(initializingStore: instruction, initialAddress: baseAddress)
284318
return .continueWalk
285319
}
286320
}

0 commit comments

Comments
 (0)