@@ -37,8 +37,8 @@ protocol AddressUseVisitor {
3737 /// end_access, end_apply, abort_apply, end_borrow.
3838 mutating func scopeEndingAddressUse( of operand: Operand ) -> WalkResult
3939
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.
4242 ///
4343 /// StoringInstructions are leaf uses.
4444 mutating func leafAddressUse( of operand: Operand ) -> WalkResult
@@ -186,3 +186,144 @@ extension AddressUseVisitor {
186186 }
187187 }
188188}
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