@@ -37,8 +37,8 @@ protocol AddressUseVisitor {
37
37
/// end_access, end_apply, abort_apply, end_borrow.
38
38
mutating func scopeEndingAddressUse( of operand: Operand ) -> WalkResult
39
39
40
- /// A address leaf use cannot propagate the address bits beyond the
41
- /// instruction.
40
+ /// An address leaf use propagates neither the address bits, nor the
41
+ /// in-memory value beyond the instruction.
42
42
///
43
43
/// StoringInstructions are leaf uses.
44
44
mutating func leafAddressUse( of operand: Operand ) -> WalkResult
@@ -186,3 +186,144 @@ extension AddressUseVisitor {
186
186
}
187
187
}
188
188
}
189
+
190
+ extension AccessBase {
191
+ /// If this access base has a single initializer, return it, along
192
+ /// with the initialized address. This does not guarantee that all
193
+ /// uses of that address are dominated by the store or even that the
194
+ /// store is a direct use of `address`.
195
+ func findSingleInitializer( _ context: some Context )
196
+ -> ( initialAddress: Value , initializingStore: Instruction ) ? {
197
+ let baseAddr : Value
198
+ switch self {
199
+ case let . stack( allocStack) :
200
+ baseAddr = allocStack
201
+ case let . argument( arg) :
202
+ baseAddr = arg
203
+ default :
204
+ return nil
205
+ }
206
+ var walker = AddressInitializationWalker ( context: context)
207
+ if walker. walkDownUses ( ofAddress: baseAddr, path: SmallProjectionPath ( ) )
208
+ == . abortWalk {
209
+ return nil
210
+ }
211
+ return ( initialAddress: baseAddr, initializingStore: walker. initializer!)
212
+ }
213
+ }
214
+
215
+ // Walk the address def-use paths to find a single initialization.
216
+ //
217
+ // Implements AddressUseVisitor to guarantee that we can't miss any
218
+ // stores. This separates escapingAddressUse from leafAddressUse.
219
+ //
220
+ // TODO: Make AddressDefUseWalker always conform to AddressUseVisitor once we're
221
+ // ready to debug changes to escape analysis etc...
222
+ //
223
+ // Future:
224
+ // AddressUseVisitor
225
+ // (how to transitively follow uses, complete classification)
226
+ // -> AddressPathDefUseWalker
227
+ // (follow projections and track Path,
228
+ // client handles all other uses, such as access scopes)
229
+ // -> AddressProjectionDefUseWalker
230
+ // (follow projections, track Path, ignore access scopes,
231
+ // merge all other callbacks into only two:
232
+ // instantaneousAddressUse vs. escapingAddressUse)
233
+ //
234
+ // FIXME: This currently assumes that isAddressInitialization catches
235
+ // writes to the memory address. We need a complete abstraction that
236
+ // distinguishes between `mayWriteToMemory` for dependence vs. actual
237
+ // modification of memory.
238
+ struct AddressInitializationWalker : AddressDefUseWalker , AddressUseVisitor {
239
+ let context : any Context
240
+
241
+ var walkDownCache = WalkerCache < SmallProjectionPath > ( )
242
+
243
+ var isProjected = false
244
+ var initializer : Instruction ?
245
+
246
+ private mutating func setInitializer( instruction: Instruction ) -> WalkResult {
247
+ // An initializer must be unique and store the full value.
248
+ if initializer != nil || isProjected {
249
+ initializer = nil
250
+ return . abortWalk
251
+ }
252
+ initializer = instruction
253
+ return . continueWalk
254
+ }
255
+ }
256
+
257
+ // Implement AddressDefUseWalker
258
+ extension AddressInitializationWalker {
259
+ mutating func leafUse( address: Operand , path: SmallProjectionPath )
260
+ -> WalkResult {
261
+ isProjected = !path. isEmpty
262
+ return classifyAddress ( operand: address)
263
+ }
264
+ }
265
+
266
+ // Implement AddresUseVisitor
267
+ extension AddressInitializationWalker {
268
+ /// An address projection produces a single address result and does not
269
+ /// escape its address operand in any other way.
270
+ mutating func projectedAddressUse( of operand: Operand , into value: Value )
271
+ -> WalkResult {
272
+ // AddressDefUseWalker should catch most of these.
273
+ return . abortWalk
274
+ }
275
+
276
+ mutating func scopedAddressUse( of operand: Operand ) -> WalkResult {
277
+ // AddressDefUseWalker currently skips most of these.
278
+ return . abortWalk
279
+ }
280
+
281
+ mutating func scopeEndingAddressUse( of operand: Operand ) -> WalkResult {
282
+ // AddressDefUseWalker currently skips most of these.
283
+ return . continueWalk
284
+ }
285
+
286
+ mutating func leafAddressUse( of operand: Operand ) -> WalkResult {
287
+ if operand. isAddressInitialization {
288
+ return setInitializer ( instruction: operand. instruction)
289
+ }
290
+ // FIXME: check mayWriteToMemory but ignore non-stores. Currently,
291
+ // stores should all be checked my isAddressInitialization, but
292
+ // this is not robust.
293
+ return . continueWalk
294
+ }
295
+
296
+ mutating func appliedAddressUse( of operand: Operand , by apply: FullApplySite )
297
+ -> WalkResult {
298
+ if operand. isAddressInitialization {
299
+ return setInitializer ( instruction: operand. instruction)
300
+ }
301
+ guard let convention = apply. convention ( of: operand) else {
302
+ return . continueWalk
303
+ }
304
+ return convention. isIndirectIn ? . continueWalk : . abortWalk
305
+ }
306
+
307
+ mutating func loadedAddressUse( of operand: Operand , into value: Value )
308
+ -> WalkResult {
309
+ return . continueWalk
310
+ }
311
+
312
+ mutating func loadedAddressUse( of operand: Operand , into address: Operand )
313
+ -> WalkResult {
314
+ return . continueWalk
315
+ }
316
+
317
+ mutating func dependentAddressUse( of operand: Operand , into value: Value )
318
+ -> WalkResult {
319
+ return . continueWalk
320
+ }
321
+
322
+ mutating func escapingAddressUse( of operand: Operand ) -> WalkResult {
323
+ return . abortWalk
324
+ }
325
+
326
+ mutating func unknownAddressUse( of operand: Operand ) -> WalkResult {
327
+ return . abortWalk
328
+ }
329
+ }
0 commit comments