@@ -78,6 +78,11 @@ public enum AccessBase : CustomStringConvertible, Hashable {
78
78
/// An address which is derived from a `Builtin.RawPointer`.
79
79
case pointer( PointerToAddressInst )
80
80
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
+
81
86
/// The access base is some SIL pattern which does not fit into any other case.
82
87
/// This should be a very rare situation.
83
88
case unidentified
@@ -115,6 +120,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
115
120
case . yield( let result) : return " yield - \( result) "
116
121
case . storeBorrow( let sb) : return " storeBorrow - \( sb) "
117
122
case . pointer( let p) : return " pointer - \( p) "
123
+ case . index( let ia) : return " index - \( ia) "
118
124
}
119
125
}
120
126
@@ -123,7 +129,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
123
129
switch self {
124
130
case . class, . tail:
125
131
return true
126
- case . box, . stack, . global, . argument, . yield, . storeBorrow, . pointer, . unidentified:
132
+ case . box, . stack, . global, . argument, . yield, . storeBorrow, . pointer, . index , . unidentified:
127
133
return false
128
134
}
129
135
}
@@ -134,7 +140,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
134
140
case . box( let pbi) : return pbi. box
135
141
case . class( let rea) : return rea. instance
136
142
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:
138
144
return nil
139
145
}
140
146
}
@@ -162,7 +168,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
162
168
switch self {
163
169
case . class( let rea) : return rea. fieldIsLet
164
170
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:
166
172
return false
167
173
}
168
174
}
@@ -174,7 +180,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
174
180
case . class( let rea) : return rea. instance. referenceRoot is AllocRefInstBase
175
181
case . tail( let rta) : return rta. instance. referenceRoot is AllocRefInstBase
176
182
case . stack, . storeBorrow: return true
177
- case . global, . argument, . yield, . pointer, . unidentified:
183
+ case . global, . argument, . yield, . pointer, . index , . unidentified:
178
184
return false
179
185
}
180
186
}
@@ -184,7 +190,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
184
190
switch self {
185
191
case . box, . class, . tail, . stack, . storeBorrow, . global:
186
192
return true
187
- case . argument, . yield, . pointer, . unidentified:
193
+ case . argument, . yield, . pointer, . index , . unidentified:
188
194
return false
189
195
}
190
196
}
@@ -216,6 +222,8 @@ public enum AccessBase : CustomStringConvertible, Hashable {
216
222
return sb1 == sb2
217
223
case ( . pointer( let p1) , . pointer( let p2) ) :
218
224
return p1 == p2
225
+ case ( . index( let ia1) , . index( let ia2) ) :
226
+ return ia1 == ia2
219
227
default :
220
228
return false
221
229
}
@@ -258,7 +266,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
258
266
259
267
switch ( self , other) {
260
268
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 `).
262
270
case ( . box( let pb) , . box( let otherPb) ) :
263
271
return pb. fieldIndex != otherPb. fieldIndex ||
264
272
isDifferentAllocation ( pb. box. referenceRoot, otherPb. box. referenceRoot) ||
@@ -303,7 +311,7 @@ public enum AccessBase : CustomStringConvertible, Hashable {
303
311
304
312
/// An `AccessPath` is a pair of a `base: AccessBase` and a `projectionPath: Path`
305
313
/// 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 {
307
315
public let base : AccessBase
308
316
309
317
/// address projections only
@@ -475,6 +483,11 @@ public enum EnclosingScope {
475
483
private struct AccessPathWalker : AddressUseDefWalker {
476
484
var result = AccessPath . unidentified ( )
477
485
var foundBeginAccess : BeginAccessInst ?
486
+ let enforceConstantProjectionPath : Bool
487
+
488
+ init ( enforceConstantProjectionPath: Bool = false ) {
489
+ self . enforceConstantProjectionPath = enforceConstantProjectionPath
490
+ }
478
491
479
492
mutating func walk( startAt address: Value , initialPath: SmallProjectionPath = SmallProjectionPath ( ) ) {
480
493
if walkUp ( address: address, path: Path ( projectionPath: initialPath) ) == . abortWalk {
@@ -533,9 +546,13 @@ private struct AccessPathWalker : AddressUseDefWalker {
533
546
}
534
547
535
548
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
+ }
537
554
// 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 ) )
539
556
} else if path. indexAddr && !canBeOperandOfIndexAddr( address) {
540
557
// An `index_addr` instruction cannot be derived from an address
541
558
// projection. Bail out
@@ -565,6 +582,25 @@ extension Value {
565
582
return walker. result
566
583
}
567
584
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
+
568
604
public func getAccessPath( fromInitialPath: SmallProjectionPath ) -> AccessPath {
569
605
var walker = AccessPathWalker ( )
570
606
walker. walk ( startAt: self , initialPath: fromInitialPath)
@@ -637,7 +673,7 @@ extension ValueUseDefWalker where Path == SmallProjectionPath {
637
673
return walkUp ( value: rea. instance, path: path. push ( . classField, index: rea. fieldIndex) ) != . abortWalk
638
674
case . tail( let rta) :
639
675
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:
641
677
return false
642
678
}
643
679
}
0 commit comments