@@ -78,6 +78,11 @@ public enum AccessBase : CustomStringConvertible, Hashable {
7878 /// An address which is derived from a `Builtin.RawPointer`.
7979 case pointer( PointerToAddressInst )
8080
81+ // The result of an `index_addr` with a non-constant index.
82+ // This can only occur in access paths returned by `Value.constantAccessPath`.
83+ // In "regular" access paths such `index_addr` projections are contained in the `projectionPath` (`i*`).
84+ case index( IndexAddrInst )
85+
8186 /// The access base is some SIL pattern which does not fit into any other case.
8287 /// This should be a very rare situation.
8388 case unidentified
@@ -115,6 +120,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
115120 case . yield( let result) : return " yield - \( result) "
116121 case . storeBorrow( let sb) : return " storeBorrow - \( sb) "
117122 case . pointer( let p) : return " pointer - \( p) "
123+ case . index( let ia) : return " index - \( ia) "
118124 }
119125 }
120126
@@ -123,7 +129,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
123129 switch self {
124130 case . class, . tail:
125131 return true
126- case . box, . stack, . global, . argument, . yield, . storeBorrow, . pointer, . unidentified:
132+ case . box, . stack, . global, . argument, . yield, . storeBorrow, . pointer, . index , . unidentified:
127133 return false
128134 }
129135 }
@@ -134,7 +140,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
134140 case . box( let pbi) : return pbi. box
135141 case . class( let rea) : return rea. instance
136142 case . tail( let rta) : return rta. instance
137- case . stack, . global, . argument, . yield, . storeBorrow, . pointer, . unidentified:
143+ case . stack, . global, . argument, . yield, . storeBorrow, . pointer, . index , . unidentified:
138144 return nil
139145 }
140146 }
@@ -162,7 +168,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
162168 switch self {
163169 case . class( let rea) : return rea. fieldIsLet
164170 case . global( let g) : return g. isLet
165- case . box, . stack, . tail, . argument, . yield, . storeBorrow, . pointer, . unidentified:
171+ case . box, . stack, . tail, . argument, . yield, . storeBorrow, . pointer, . index , . unidentified:
166172 return false
167173 }
168174 }
@@ -174,7 +180,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
174180 case . class( let rea) : return rea. instance. referenceRoot is AllocRefInstBase
175181 case . tail( let rta) : return rta. instance. referenceRoot is AllocRefInstBase
176182 case . stack, . storeBorrow: return true
177- case . global, . argument, . yield, . pointer, . unidentified:
183+ case . global, . argument, . yield, . pointer, . index , . unidentified:
178184 return false
179185 }
180186 }
@@ -184,7 +190,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
184190 switch self {
185191 case . box, . class, . tail, . stack, . storeBorrow, . global:
186192 return true
187- case . argument, . yield, . pointer, . unidentified:
193+ case . argument, . yield, . pointer, . index , . unidentified:
188194 return false
189195 }
190196 }
@@ -216,6 +222,8 @@ public enum AccessBase : CustomStringConvertible, Hashable {
216222 return sb1 == sb2
217223 case ( . pointer( let p1) , . pointer( let p2) ) :
218224 return p1 == p2
225+ case ( . index( let ia1) , . index( let ia2) ) :
226+ return ia1 == ia2
219227 default :
220228 return false
221229 }
@@ -258,7 +266,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
258266
259267 switch ( self , other) {
260268
261- // First handle all pairs of the same kind (except `yield` and `pointer `).
269+ // First handle all pairs of the same kind (except `yield`, `pointer` and `index `).
262270 case ( . box( let pb) , . box( let otherPb) ) :
263271 return pb. fieldIndex != otherPb. fieldIndex ||
264272 isDifferentAllocation ( pb. box. referenceRoot, otherPb. box. referenceRoot) ||
@@ -303,7 +311,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
303311
304312/// An `AccessPath` is a pair of a `base: AccessBase` and a `projectionPath: Path`
305313/// which denotes the offset of the access from the base in terms of projections.
306- public struct AccessPath : CustomStringConvertible {
314+ public struct AccessPath : CustomStringConvertible , Hashable {
307315 public let base : AccessBase
308316
309317 /// address projections only
@@ -475,6 +483,11 @@ public enum EnclosingScope {
475483private struct AccessPathWalker : AddressUseDefWalker {
476484 var result = AccessPath . unidentified ( )
477485 var foundBeginAccess : BeginAccessInst ?
486+ let enforceConstantProjectionPath : Bool
487+
488+ init ( enforceConstantProjectionPath: Bool = false ) {
489+ self . enforceConstantProjectionPath = enforceConstantProjectionPath
490+ }
478491
479492 mutating func walk( startAt address: Value , initialPath: SmallProjectionPath = SmallProjectionPath ( ) ) {
480493 if walkUp ( address: address, path: Path ( projectionPath: initialPath) ) == . abortWalk {
@@ -533,9 +546,13 @@ private struct AccessPathWalker : AddressUseDefWalker {
533546 }
534547
535548 mutating func walkUp( address: Value , path: Path ) -> WalkResult {
536- if address is IndexAddrInst {
549+ if let indexAddr = address as? IndexAddrInst {
550+ if !( indexAddr. index is IntegerLiteralInst ) && enforceConstantProjectionPath {
551+ self . result = AccessPath ( base: . index( indexAddr) , projectionPath: path. projectionPath)
552+ return . continueWalk
553+ }
537554 // Track that we crossed an `index_addr` during the walk-up
538- return walkUpDefault ( address: address , path: path. with ( indexAddr: true ) )
555+ return walkUpDefault ( address: indexAddr , path: path. with ( indexAddr: true ) )
539556 } else if path. indexAddr && !canBeOperandOfIndexAddr( address) {
540557 // An `index_addr` instruction cannot be derived from an address
541558 // projection. Bail out
@@ -565,6 +582,25 @@ extension Value {
565582 return walker. result
566583 }
567584
585+ /// Like `accessPath`, but ensures that the projectionPath only contains "constant" elements.
586+ /// This means: if the access contains an `index_addr` projection with a non-constant index,
587+ /// the `projectionPath` does _not_ contain the `index_addr`.
588+ /// Instead, the `base` is an `AccessBase.index` which refers to the `index_addr`.
589+ /// For example:
590+ /// ```
591+ /// %1 = ref_tail_addr %some_reference
592+ /// %2 = index_addr %1, %some_non_const_value
593+ /// %3 = struct_element_addr %2, #field2
594+ /// ```
595+ /// `%3.accessPath` = base: tail(`%1`), projectionPath: `i*.s2`
596+ /// `%3.constantAccessPath` = base: index(`%2`), projectionPath: `s2`
597+ ///
598+ public var constantAccessPath : AccessPath {
599+ var walker = AccessPathWalker ( enforceConstantProjectionPath: true )
600+ walker. walk ( startAt: self )
601+ return walker. result
602+ }
603+
568604 public func getAccessPath( fromInitialPath: SmallProjectionPath ) -> AccessPath {
569605 var walker = AccessPathWalker ( )
570606 walker. walk ( startAt: self , initialPath: fromInitialPath)
@@ -637,7 +673,7 @@ extension ValueUseDefWalker where Path == SmallProjectionPath {
637673 return walkUp ( value: rea. instance, path: path. push ( . classField, index: rea. fieldIndex) ) != . abortWalk
638674 case . tail( let rta) :
639675 return walkUp ( value: rta. instance, path: path. push ( . tailElements, index: 0 ) ) != . abortWalk
640- case . stack, . global, . argument, . yield, . storeBorrow, . pointer, . unidentified:
676+ case . stack, . global, . argument, . yield, . storeBorrow, . pointer, . index , . unidentified:
641677 return false
642678 }
643679 }
0 commit comments