I think you would pattern match at a higher level using semantics function. Lets anchor our conversation to some examples. Consider the following slightly modified swift (I just returned the value since it makes the SIL more compact):
func test() -> Int {
let a = [1, 2, 3]
return a[3]
}
This gives me the following SIL at -Onone:
// test()
sil hidden @$s4main4testSiyF : $@convention(thin) () -> Int {
bb0:
%0 = metatype $@thin Array<Int>.Type // user: %16
%1 = integer_literal $Builtin.Word, 2 // user: %3
// function_ref _allocateUninitializedArray<A>(_:)
%2 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %3
%3 = apply %2<Int>(%1) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %5, %4
%4 = tuple_extract %3 : $(Array<Int>, Builtin.RawPointer), 0 // user: %16
%5 = tuple_extract %3 : $(Array<Int>, Builtin.RawPointer), 1 // user: %6
%6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*Int // users: %9, %11
%7 = integer_literal $Builtin.Int64, 1 // user: %8
%8 = struct $Int (%7 : $Builtin.Int64) // user: %9
store %8 to %6 : $*Int // id: %9
%10 = integer_literal $Builtin.Word, 1 // user: %11
%11 = index_addr %6 : $*Int, %10 : $Builtin.Word // user: %14
%12 = integer_literal $Builtin.Int64, 2 // user: %13
%13 = struct $Int (%12 : $Builtin.Int64) // user: %14
store %13 to %11 : $*Int // id: %14
// function_ref specialized Array.init(arrayLiteral:)
%15 = function_ref @$sSa12arrayLiteralSayxGxd_tcfCSi_Tg5 : $@convention(method) (@owned Array<Int>, @thin Array<Int>.Type) -> @owned Array<Int> // user: %16
%16 = apply %15(%4, %0) : $@convention(method) (@owned Array<Int>, @thin Array<Int>.Type) -> @owned Array<Int> // users: %22, %17, %26
debug_value %16 : $Array<Int>, let, name "a" // id: %17
%18 = integer_literal $Builtin.Int64, 2 // user: %19
%19 = struct $Int (%18 : $Builtin.Int64) // user: %22
%20 = alloc_stack $Int // users: %23, %24, %25
// function_ref specialized Array.subscript.getter
%21 = function_ref @$sSayxSicigSi_Tg5 : $@convention(method) (Int, @guaranteed Array<Int>) -> Int // user: %22
%22 = apply %21(%19, %16) : $@convention(method) (Int, @guaranteed Array<Int>) -> Int // user: %23
store %22 to %20 : $*Int // id: %23
%24 = load %20 : $*Int // user: %27
dealloc_stack %20 : $*Int // id: %25
release_value %16 : $Array<Int> // id: %26
return %24 : $Int // id: %27
} // end sil function '$s4main4testSiyF'
I think this is most likely how you are going to see these sorts of things. We should pattern match on semantic functions. Here is how I would do this with this example:
- If constant propagation manages to constant fold the array access down to a constant, it will call constant fold instruction on Array.subscript.getter. That is I think your entrance point. (In this example, it will process %18 and then %22 for sure).
- Once I found that, I would track back up the guaranteed argument to see if it comes from an Array.init(arrayLiteral:). Then I would look for the allocateUninitializedArray from that which should give me the Builtin.Word, 2. Then I would see if I had a constant for the other parameter and compare that against the size. Then if it is within the size, I would maybe try to find what the actual value was and constant propagate that.
All of this can I think be done with the PatternMatch infrastructure like this:
IntegerLiteralInst *capacity;
if (!match(I,
m_Apply(m_SemanticsCall("array.init.literal"),
m_TupleExtract(
m_SemanticsCall("array.allocateUninitializedArray",
m_IntegerLiteral(capacity)),
1)))) {
return ... // fail
}
NOTE: I am not sure if we have semantics for these specific calls now, but I imagine it wouldn't be hard to add. Also I forgot if I added the semantics matcher. But if I didn't I can show you how to do it.