Open10

Swiftでのポインタ、アドレスについて

kabeyakabeya

Swiftのポインタというかアドレスについて調べる

普通のローカル変数のアドレス

func printAddress(_ message: String, _ ptr: UnsafePointer<Int>) {
    var addr = String.init(format:"%016x", ptr)
    print(message, addr)
}
func printAddress(_ message: String, _ ptr: UnsafeBufferPointer<Int>) {
    var addr = String.init(format:"%016x", ptr.baseAddress!)
    print(message, addr)
}

var value1 : Int = 3
printAddress("value1: ", &value1)
var value2 = value1
printAddress("value2: ", &value2)
出力結果
value1:  000000000473c7b0
value2:  000000000473c7b8

value1value2は8バイト違うアドレスですね。

Arrayのアドレス

var array1 = Array<Int>(repeating: 0, count: 1)
printAddress("array1: ", &array1)
var array2 = array1
printAddress("array2: ", &array2)

array1.withUnsafeBufferPointer( { ptr in
    printAddress("buffer: ", ptr)
})
出力結果
array1:  00000000011f0350
array2:  00000000011f0350
buffer:  00000000011f0350

array1array2は同じアドレスです。普通の変数とは違い、別の変数でもアドレスが同じです。copy-on-writeと呼ばれる最適化手法が使われているということなんですね。つまりこの場合、array1array2が更新されない限りarray1array2は同じメモリ領域を共有します。
ちなみに、&で取れるポインタと、withUnsafeBufferPointerで実行されるクロージャに渡ってくるポインタは同じなんですね。

array1[0] = 3
printAddress("array1: ", &array1)
printAddress("array2: ", &array2)
出力結果
array1:  0000000001368aa0
array2:  00000000011f0350

array1の中身を変更すると、array1のアドレスが変わります。array2はそのままなので、元々のarray1のメモリ領域の所有権がarray2に移ったかのような格好です(実際、そんな所有権管理のようなことはしてないと思いますが)。

print("array1.capacity: ", array1.capacity)
出力結果
array1.capacity:  2

キャパシティは2なのでそれを超えるように、array1に要素を足します。

printAddress("array1: ", &array1)
printAddress("array2: ", &array2)
var additional = [Int](repeating: 0, count: 10000)
array1.append(contentsOf: additional)

printAddress("array1: ", &array1)
printAddress("array2: ", &array2)
出力結果
array1:  0000000001368aa0
array2:  00000000011f0350
array1:  0000000039e60020
array2:  00000000011f0350

要素を足す前と足した後で、アドレスが変わります。
この辺りは、C言語の感覚からすると違和感がありますが、C++のstd::vectorとかを考えるとまあそうなのかなという感じですね。

kabeyakabeya
// ↓この書き方はエラーになる
//var ptr: UnsafePointer<Int> = &array

&はアドレスを取得する演算子ではなくて、「inout式」と呼ばれるもの、ということですね。関数の引数にしか使えません。関数側は引数にinoutをつけるか、またはUnsafePointer系引数を使う必要があります。
ローカル変数のアドレスを簡単に取得できないようにしておかないと、array1の再配置のようなことがあったときに容易にバグにつながるから、ということでしょうね。
また、

var ptr = UnsafePointer<Int>(array1)

のような文を書くとInitialization of 'UnsafePointer<Int>' results in a dangling pointerという警告が出ます。この文のあとにarray1が変更された結果、アドレスが変わったとしてもptrは古いアドレスを指し続けるよ、ということですね。

kabeyakabeya

生のポインタのようなものを用意する

var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: MemoryLayout<Int16>.stride * 1024, alignment: MemoryLayout<Int16>.stride)
defer {
    buffer.deallocate()
}

C言語で言うところのmallocのようなものですね。
このポインタを使ってmemsetとかできます。

// C/Objective-C/C++などのコードを呼び出す
memset(buffer.baseAddress!, 0, MemoryLayout<Int16>.stride * 1024)
// 4バイト目〜5バイト目にInt16型で30をセットする。
buffer.storeBytes(of: 30, toByteOffset: 4, as: Int16.self)

bufferの中身はバイト列として直に見ていっても良いのですが、bindMemoryで、特定の型にキャストするようなことができます。

var arrayPtr = buffer.bindMemory(to: Int16.self)
var receivedData = [Int16](arrayPtr) // ここでポインタだけ渡して、要素数を指定してないことに注目
for i in (0 ..< 8) {
    print(i, " ", receivedData[i])
}

特筆すべきは、[Int16](arrayPtr)の部分ですね。ポインタだけ渡して、要素数を渡してないのに、ちゃんと配列を初期化できています。つまりUnsafeMutableRawBufferPointerは、Unsafeを名乗るわりには自分の境界を把握しています。

ポインタの配列みたいなこともできるわりに、こういう境界を把握できてたりもするので、C/C++のメモリ配置を念頭においてプログラミングするとはまりそうな予感があります。

kabeyakabeya

この辺りを書籍(Zenn Book)にまとめ直しています。
上記の話も一部、当時誤解していた部分(大きく外れてはないが厳密には合っていない)があります。

まだ執筆途中ですが、読めます。
https://zenn.dev/kabeya/books/swift-pointer-guide

kabeyakabeya

書籍に書いている途中で、色々調べ直しています。

func printAddress(_ index: Int, _ ptr: UnsafePointer<Int>) {
    let addr = String(format:"%016x", ptr)
    print("\(index): \(addr)")
}
func printAddress(_ index: Int, _ ptr: UnsafeBufferPointer<Int>) {
    let addr = String(format:"%016x", ptr.baseAddress!)
    print("\(index): \(addr)")
}

func testFunc() {
    var array1 = Array<Int>(repeating: 0, count: 1)
    printAddress(1, &array1)
    var array2 = array1
    printAddress(2, &array2)

    array1.withUnsafeBufferPointer( { ptr in
        printAddress(3, ptr)
    })
}

testFunc()
出力結果
1: 00000000026cc020
2: 00000000026cc020
3: 00000000026cc020

array1array2がCoWでバッファを共有しているというのは分かります。
ですが、array1array2は別々の変数なのだから、スタック上には2つの領域が確保されていて、そこにポインタが入っていて、それが2変数とも同じ共有バッファをポイントしている、ということになっているはず、というのが推測されるのです。
実際にはそうなっておらず&array1&array2で同じアドレスがprintされます。

具体的にどうなっているのか、SILとアセンブラで確認します。

kabeyakabeya

その前に、CoWのない通常のstructでどのようになるか見てみます。

import Foundation

struct TestStruct {
    var value: Int = 0
}

func printAddress(_ index: Int, _ ptr: UnsafePointer<TestStruct>) {
    let addr = String(format:"%016x", ptr)
    print("\(index): \(addr)")
}

func testFunc1() {
    var struct1 = TestStruct(value: 1)
    printAddress(1, &struct1)
}
001: // testFunc1()
002: sil hidden [stack_protection] @$s6Test289testFunc1yyF : $@convention(thin) () -> () {
003: bb0:
004:   %0 = alloc_stack $TestStruct, var, name "struct1" // users: %6, %15, %9
005:   %1 = metatype $@thin TestStruct.Type            // user: %5
006:   %2 = integer_literal $Builtin.Int64, 1          // user: %3
007:   %3 = struct $Int (%2 : $Builtin.Int64)          // user: %5
008:   // function_ref TestStruct.init(value:)
009:   %4 = function_ref @$s6Test2810TestStructV5valueACSi_tcfC : $@convention(method) (Int, @thin TestStruct.Type) -> TestStruct // user: %5
010:   %5 = apply %4(%3, %1) : $@convention(method) (Int, @thin TestStruct.Type) -> TestStruct // user: %6
011:   store %5 to %0 : $*TestStruct                   // id: %6
012:   %7 = integer_literal $Builtin.Int64, 1          // user: %8
013:   %8 = struct $Int (%7 : $Builtin.Int64)          // user: %13
014:   %9 = begin_access [read] [static] %0 : $*TestStruct // users: %14, %10
015:   %10 = address_to_pointer [stack_protection] %9 : $*TestStruct to $Builtin.RawPointer // user: %11
016:   %11 = struct $UnsafePointer<TestStruct> (%10 : $Builtin.RawPointer) // user: %13
017:   // function_ref printAddress(_:_:)
018:   %12 = function_ref @$s6Test2812printAddressyySi_SPyAA10TestStructVGtF : $@convention(thin) (Int, UnsafePointer<TestStruct>) -> () // user: %13
019:   %13 = apply %12(%8, %11) : $@convention(thin) (Int, UnsafePointer<TestStruct>) -> ()
020:   end_access %9 : $*TestStruct                    // id: %14
021:   dealloc_stack %0 : $*TestStruct                 // id: %15
022:   %16 = tuple ()                                  // user: %17
023:   return %16 : $()                                // id: %17
024: } // end sil function '$s6Test289testFunc1yyF'

かいつまんで見ていきます。
004: struct1のメモリ領域をスタックに確保します。
010: TestStruct.init(value:)を呼びます。
011: 確保したメモリ領域に、initの結果を入れます。
015: 確保したメモリ領域のアドレスから、Builtin.RawPointer型の値を生成します。
016: Builtin.RawPointerからUnsafePointer<TestStruct>型の構造体を生成します。
018: printAddressに018で生成したUnsafePointer<TestStruct>型の構造体を渡します。
021: 004で確保したメモリ領域を破棄します。

特に変わった処理はありません。

kabeyakabeya

Arrayの場合を見てみます。

import Foundation

func printAddress(_ index: Int, _ ptr: UnsafePointer<Int>) {
    let addr = String(format:"%016x", ptr)
    print("\(index): \(addr)")
}

func testFunc2() {
    var array1 = Array<Int>(repeating: 0, count: 1)
    printAddress(1, &array1)
}
SIL-testFunc2()
0001: // testFunc2()
0002: sil hidden @$s6Test299testFunc2yyF : $@convention(thin) () -> () {
0003: bb0:
0004:   %0 = alloc_stack $Array<Int>, var, name "array1" // users: %12, %67, %66
0005:   %1 = metatype $@thin Array<Int>.Type            // user: %9
0006:   %2 = integer_literal $Builtin.Int64, 0          // user: %3
0007:   %3 = struct $Int (%2 : $Builtin.Int64)          // user: %5
0008:   %4 = alloc_stack $Int                           // users: %5, %10, %9
0009:   store %3 to %4 : $*Int                          // id: %5
0010:   %6 = integer_literal $Builtin.Int64, 1          // user: %7
0011:   %7 = struct $Int (%6 : $Builtin.Int64)          // user: %9
0012:   // function_ref Array.init(repeating:count:)
0013:   %8 = function_ref @$sSa9repeating5countSayxGx_SitcfC : $@convention(method) <τ_0_0> (@in τ_0_0, Int, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0> // user: %9
0014:   %9 = apply %8<Int>(%4, %7, %1) : $@convention(method) <τ_0_0> (@in τ_0_0, Int, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0> // users: %65, %22, %20, %16, %33, %98, %12, %11
0015:   dealloc_stack %4 : $*Int                        // id: %10
0016:   retain_value %9 : $Array<Int>                   // id: %11
0017:   store %9 to %0 : $*Array<Int>                   // id: %12
0018:   %13 = integer_literal $Builtin.Int64, 1         // user: %14
0019:   %14 = struct $Int (%13 : $Builtin.Int64)        // user: %63
0020:   // function_ref Array._baseAddressIfContiguous.getter
0021:   %15 = function_ref @$sSa24_baseAddressIfContiguousSpyxGSgvg : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> Optional<UnsafeMutablePointer<τ_0_0>> // user: %16
0022:   %16 = apply %15<Int>(%9) : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> Optional<UnsafeMutablePointer<τ_0_0>> // users: %34, %17
0023:   switch_enum %16 : $Optional<UnsafeMutablePointer<Int>>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 // id: %17
0024: 
0025: bb1(%18 : $UnsafeMutablePointer<Int>):            // Preds: bb0
0026:   br bb3                                          // id: %19
0027: 
0028: bb2:                                              // Preds: bb0
0029:   retain_value %9 : $Array<Int>                   // id: %20
0030:   %21 = alloc_stack $Array<Int>                   // users: %25, %22, %27, %24
0031:   store %9 to %21 : $*Array<Int>                  // id: %22
0032:   // function_ref Collection.isEmpty.getter
0033:   %23 = function_ref @$sSlsE7isEmptySbvg : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0) -> Bool // user: %24
0034:   %24 = apply %23<Array<Int>>(%21) : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0) -> Bool // user: %28
0035:   %25 = load %21 : $*Array<Int>                   // user: %26
0036:   release_value %25 : $Array<Int>                 // id: %26
0037:   dealloc_stack %21 : $*Array<Int>                // id: %27
0038:   %28 = struct_extract %24 : $Bool, #Bool._value  // user: %30
0039:   %29 = integer_literal $Builtin.Int1, -1         // user: %30
0040:   %30 = builtin "int_expect_Int1"(%28 : $Builtin.Int1, %29 : $Builtin.Int1) : $Builtin.Int1 // user: %31
0041:   cond_br %30, bb12, bb13                         // id: %31
0042: 
0043: bb3:                                              // Preds: bb12 bb1
0044:   // function_ref Array._owner.getter
0045:   %32 = function_ref @$sSa6_owneryXlSgvg : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> @owned Optional<AnyObject> // user: %33
0046:   %33 = apply %32<Int>(%9) : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> @owned Optional<AnyObject> // users: %41, %39
0047:   switch_enum %16 : $Optional<UnsafeMutablePointer<Int>>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5 // id: %34
0048: 
0049: // %35                                            // user: %36
0050: bb4(%35 : $UnsafeMutablePointer<Int>):            // Preds: bb3
0051:   %36 = struct_extract %35 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue // user: %37
0052:   %37 = struct $UnsafeRawPointer (%36 : $Builtin.RawPointer) // user: %38
0053:   %38 = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, %37 : $UnsafeRawPointer // user: %39
0054:   br bb6(%33 : $Optional<AnyObject>, %38 : $Optional<UnsafeRawPointer>) // id: %39
0055: 
0056: bb5:                                              // Preds: bb3
0057:   %40 = enum $Optional<UnsafeRawPointer>, #Optional.none!enumelt // user: %41
0058:   br bb6(%33 : $Optional<AnyObject>, %40 : $Optional<UnsafeRawPointer>) // id: %41
0059: 
0060: // %42                                            // users: %64, %61
0061: // %43                                            // user: %45
0062: bb6(%42 : $Optional<AnyObject>, %43 : $Optional<UnsafeRawPointer>): // Preds: bb13 bb5 bb4
0063:   %44 = alloc_stack $UnsafePointer<Int>           // users: %74, %59, %49, %60
0064:   switch_enum %43 : $Optional<UnsafeRawPointer>, case #Optional.some!enumelt: bb7, case #Optional.none!enumelt: bb8 // id: %45
0065: 
0066: // %46                                            // user: %47
0067: bb7(%46 : $UnsafeRawPointer):                     // Preds: bb6
0068:   %47 = struct_extract %46 : $UnsafeRawPointer, #UnsafeRawPointer._rawValue // user: %48
0069:   %48 = struct $UnsafePointer<Int> (%47 : $Builtin.RawPointer) // user: %49
0070:   store %48 to %44 : $*UnsafePointer<Int>         // id: %49
0071:   br bb9                                          // id: %50
0072: 
0073: bb8:                                              // Preds: bb6
0074:   %51 = alloc_stack $Optional<UnsafeRawPointer>   // users: %57, %56, %71
0075:   %52 = integer_literal $Builtin.Word, -8         // user: %53
0076:   %53 = builtin "inttoptr_Word"(%52 : $Builtin.Word) : $Builtin.RawPointer // user: %54
0077:   %54 = struct $UnsafeRawPointer (%53 : $Builtin.RawPointer) // user: %55
0078:   %55 = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, %54 : $UnsafeRawPointer // user: %56
0079:   store %55 to %51 : $*Optional<UnsafeRawPointer> // id: %56
0080:   %57 = load %51 : $*Optional<UnsafeRawPointer>   // user: %58
0081:   switch_enum %57 : $Optional<UnsafeRawPointer>, case #Optional.some!enumelt: bb10, case #Optional.none!enumelt: bb11 // id: %58
0082: 
0083: bb9:                                              // Preds: bb10 bb7
0084:   %59 = load %44 : $*UnsafePointer<Int>           // user: %61
0085:   dealloc_stack %44 : $*UnsafePointer<Int>        // id: %60
0086:   %61 = mark_dependence %59 : $UnsafePointer<Int> on %42 : $Optional<AnyObject> // user: %63
0087:   // function_ref printAddress(_:_:)
0088:   %62 = function_ref @$s6Test2912printAddressyySi_SPySiGtF : $@convention(thin) (Int, UnsafePointer<Int>) -> () // user: %63
0089:   %63 = apply %62(%14, %61) : $@convention(thin) (Int, UnsafePointer<Int>) -> ()
0090:   release_value %42 : $Optional<AnyObject>        // id: %64
0091:   release_value %9 : $Array<Int>                  // id: %65
0092:   destroy_addr %0 : $*Array<Int>                  // id: %66
0093:   dealloc_stack %0 : $*Array<Int>                 // id: %67
0094:   %68 = tuple ()                                  // user: %69
0095:   return %68 : $()                                // id: %69
0096: 
0097: // %70                                            // user: %72
0098: bb10(%70 : $UnsafeRawPointer):                    // Preds: bb8
0099:   dealloc_stack %51 : $*Optional<UnsafeRawPointer> // id: %71
0100:   %72 = struct_extract %70 : $UnsafeRawPointer, #UnsafeRawPointer._rawValue // user: %73
0101:   %73 = struct $UnsafePointer<Int> (%72 : $Builtin.RawPointer) // user: %74
0102:   store %73 to %44 : $*UnsafePointer<Int>         // id: %74
0103:   br bb9                                          // id: %75
0104: 
0105: bb11:                                             // Preds: bb8
0106:   %76 = string_literal utf8 "Swift/arm64e-apple-macos.swiftinterface" // user: %83
0107:   %77 = integer_literal $Builtin.Word, 39         // user: %84
0108:   %78 = string_literal utf8 "Unexpectedly found nil while unwrapping an Optional value" // user: %80
0109:   %79 = integer_literal $Builtin.Word, 57         // user: %82
0110:   %80 = builtin "ptrtoint_Word"(%78 : $Builtin.RawPointer) : $Builtin.Word // user: %82
0111:   %81 = integer_literal $Builtin.Int8, 2          // users: %90, %84, %82
0112:   %82 = struct $StaticString (%80 : $Builtin.Word, %79 : $Builtin.Word, %81 : $Builtin.Int8) // user: %94
0113:   %83 = builtin "ptrtoint_Word"(%76 : $Builtin.RawPointer) : $Builtin.Word // user: %84
0114:   %84 = struct $StaticString (%83 : $Builtin.Word, %77 : $Builtin.Word, %81 : $Builtin.Int8) // user: %94
0115:   %85 = integer_literal $Builtin.Int64, 15150     // user: %86
0116:   %86 = struct $UInt (%85 : $Builtin.Int64)       // user: %94
0117:   %87 = string_literal utf8 "Fatal error"         // user: %89
0118:   %88 = integer_literal $Builtin.Word, 11         // user: %90
0119:   %89 = builtin "ptrtoint_Word"(%87 : $Builtin.RawPointer) : $Builtin.Word // user: %90
0120:   %90 = struct $StaticString (%89 : $Builtin.Word, %88 : $Builtin.Word, %81 : $Builtin.Int8) // user: %94
0121:   %91 = integer_literal $Builtin.Int32, 1         // user: %92
0122:   %92 = struct $UInt32 (%91 : $Builtin.Int32)     // user: %94
0123:   // function_ref _assertionFailure(_:_:file:line:flags:)
0124:   %93 = function_ref @$ss17_assertionFailure__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtF : $@convention(thin) (StaticString, StaticString, StaticString, UInt, UInt32) -> Never // user: %94
0125:   %94 = apply %93(%90, %82, %84, %86, %92) : $@convention(thin) (StaticString, StaticString, StaticString, UInt, UInt32) -> Never
0126:   unreachable                                     // id: %95
0127: 
0128: bb12:                                             // Preds: bb2
0129:   br bb3                                          // id: %96
0130: 
0131: bb13:                                             // Preds: bb2
0132:   %97 = metatype $@thin ContiguousArray<Int>.Type // user: %103
0133:   %98 = struct_extract %9 : $Array<Int>, #Array._buffer // users: %101, %99
0134:   retain_value %98 : $_ArrayBuffer<Int>           // id: %99
0135:   %100 = alloc_stack $_ArrayBuffer<Int>           // users: %101, %104, %103
0136:   store %98 to %100 : $*_ArrayBuffer<Int>         // id: %101
0137:   // function_ref ContiguousArray.init<A>(_:)
0138:   %102 = function_ref @$ss15ContiguousArrayVyAByxGqd__c7ElementQyd__RszSTRd__lufC : $@convention(method) <τ_0_0><τ_1_0 where τ_0_0 == τ_1_0.Element, τ_1_0 : Sequence> (@in τ_1_0, @thin ContiguousArray<τ_0_0>.Type) -> @owned ContiguousArray<τ_0_0> // user: %103
0139:   %103 = apply %102<Int, _ArrayBuffer<Int>>(%100, %97) : $@convention(method) <τ_0_0><τ_1_0 where τ_0_0 == τ_1_0.Element, τ_1_0 : Sequence> (@in τ_1_0, @thin ContiguousArray<τ_0_0>.Type) -> @owned ContiguousArray<τ_0_0> // users: %107, %105
0140:   dealloc_stack %100 : $*_ArrayBuffer<Int>        // id: %104
0141:   %105 = struct_extract %103 : $ContiguousArray<Int>, #ContiguousArray._buffer // users: %113, %109, %112, %106
0142:   retain_value %105 : $_ContiguousArrayBuffer<Int> // id: %106
0143:   release_value %103 : $ContiguousArray<Int>      // id: %107
0144:   // function_ref _ContiguousArrayBuffer.owner.getter
0145:   %108 = function_ref @$ss22_ContiguousArrayBufferV5owneryXlvg : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> @owned AnyObject // user: %109
0146:   %109 = apply %108<Int>(%105) : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> @owned AnyObject // user: %110
0147:   %110 = enum $Optional<AnyObject>, #Optional.some!enumelt, %109 : $AnyObject // user: %117
0148:   // function_ref _ContiguousArrayBuffer.firstElementAddress.getter
0149:   %111 = function_ref @$ss22_ContiguousArrayBufferV19firstElementAddressSpyxGvg : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> UnsafeMutablePointer<τ_0_0> // user: %112
0150:   %112 = apply %111<Int>(%105) : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> UnsafeMutablePointer<τ_0_0> // user: %114
0151:   release_value %105 : $_ContiguousArrayBuffer<Int> // id: %113
0152:   %114 = struct_extract %112 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue // user: %115
0153:   %115 = struct $UnsafeRawPointer (%114 : $Builtin.RawPointer) // user: %116
0154:   %116 = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, %115 : $UnsafeRawPointer // user: %117
0155:   br bb6(%110 : $Optional<AnyObject>, %116 : $Optional<UnsafeRawPointer>) // id: %117
0156: } // end sil function '$s6Test299testFunc2yyF'

予想に反して超絶長いSILコードが出力されました。

ある程度詳細な内容

0004:array1のメモリ領域をスタックに確保します。
0014:Array.init(repeating:count:)を呼びます。
0016:initで得られたArrayretain_valueしています。
0017:スタックに確保したメモリ領域にArrayを入れます。
0022:Arrayのプライベートメソッドである_baseAddressIfContiguousを呼びます。
0023:_baseAddressIfContiguousの結果により分岐します。non-nilの場合ラベルbb1へ。nilの場合ラベルbb2へ。
0025:0023の分岐がnon-nilの場合のbb1です。引数は非OptionalのUnsafeMutablePointer<Int>です。
0026:そのままラベルbb3にジャンプします。
0028: 0023の分岐がnilの場合のbb2です。
0029:0014で初期化したArrayを再度、retain_valueします。
0030:再度、スタックにArrayのメモリ領域を確保します。
0031:確保したメモリ領域に0014で初期化したArrayを入れます。
0034:ArrayisEmptyを呼びます。
0036:0029でretain_valueした分をrelease_valueします。
0037:0039で確保したメモリ領域を破棄します。
0041:isEmptyの結果により分岐します。isEmptytrueならラベルbb12falseならbb13にジャンプします。
0043:0023の分岐がnon-nilの場合のbb1から、およびbb12からのbb3です。
0046:Array_ownerを呼びます。
0047:_baseAddressIfContiguousの結果により分岐します。non-nilの場合ラベルbb4へ。nilの場合ラベルbb5へ。
0050:0047の分岐がnon-nilの場合のbb4です。引数はUnsafeMutablePointer<Int>です。
0052:UnsafeMutablePointer<Int>からUnsafeRawPointerを生成します。
0053:UnsafeRawPointerからOptinal<UnsafeRawPointer>を生成します。
0054:ラベルbb6にジャンプします。
0056:0047の分岐がnilの場合のbb5です。
0057:nil値でOptinal<UnsafeRawPointer>を生成します。
0058:ラベルbb6にジャンプします。
0062:0054,0058,0155から合流するbb6です。引数は_ownerOptional<UnsafeRawPointer>です。
0063:UnsafePointer<Int>のメモリ領域をスタックに確保します。
0064:引数のOptional<UnsafeRawPointer>がnilかどうかで分岐します。non-nilの場合ラベルbb7、nilの場合ラベルbb8にジャンプします。
0067:Optional<UnsafeRawPointer>がnon-nilの場合のbb7です。引数はUnsafeRawPointerです。
0070:UnsafeRawPointerからUnsafePointer<Int>を作って0063で確保したメモリ領域に入れます。
0071:ラベルbb9にジャンプします。
0073:Optional<UnsafeRawPointer>がnilの場合のbb8です。
0074:Optional<UnsafeRawPointer>のメモリ領域をスタックに確保します。
0076:Builtin.RawPointerを整数値-8(=0xFFFF_FFFF_FFFF_FFF8)から生成します。
0077:Builtin.RawPointerからUnsafeRawPointerを生成します。
0078:UnsafeRawPointerからOptional<UnsafeRawPointer>を生成します。
0079:Optional<UnsafeRawPointer>を確保したメモリ領域に入れます。
0081:Optional<UnsafeRawPointer>がnilかどうかで分岐します。non-nilの場合ラベルbb10、nilの場合ラベルbb11にジャンプします。
0083:0071、0103のジャンプ先bb9です。
0089:0063で確保したUnsafePointer<Int>を渡してprintAddressを呼びます。
0091:array1release_valueします。
0093:array1destroy_addrします。これにより、内部に保持している参照があればrelease_valueされます。
0093:array1のスタック上のメモリ領域を破壊します。
0095:testFunc2()を終了し、returnします。
0098:整数から生成したUnsafeRawPointerがnon-nilの場合のbb10です。引数は整数から生成したUnsafeRawPointerです。
0102:整数から生成したUnsafeRawPointerUnsafePointer<Int>に変換して0063で確保した領域に入れます。
0103:ラベルbb9にジャンプします。
0105:整数から生成したUnsafeRawPointerがnilの場合のbb11です。
0125:assertionFailureを呼び出し、プログラムを終了します。
0128:isEmptytrueの場合のbb12です。
0129:そのままラベルbb3にジャンプします。
0131:isEmptyfalseの場合のbb13です。
0133:array1_bufferを取得します。
0135:_ArrayBuffer<Int>のメモリ領域をスタックに確保します。
0136:array1_bufferをメモリ領域に入れます。
0139:ContiguousArray.init<Int>(_:)を呼びます。array1_bufferがコピーされます。
0140:確保した_ArrayBuffer<Int>のメモリ領域を破棄します。
0141:ContiguousArray_bufferを取得します。
0142:ContiguousArray_bufferretain_valueします。
0143:ContiguousArray自体をrelease_valueします。ガワは破棄されて、中身だけが残るようなイメージです。
0146:ContiguousArray._buffer.ownerを呼び出します。
0147:ContiguousArray._buffer.ownerからOptional<AnyObject>を生成します。
0150:ContiguousArray._buffer.firstElementAddressを呼び出します。
0151:ContiguousArray_bufferrelease_valueします。
0153:ContiguousArray._buffer.firstElementAddressからUnsafeRawPointerを生成します。
0154:Optional<UnsafeRawPointer>を生成します。
0155:ラベルbb6にジャンプします。

ジャンプしまくりでややこしいのでグラフにしてみます。

グラフ

ちなみに処理中に出てくるArray._baseAddressIfContiguousの実装は以下です。

https://github.com/swiftlang/swift/blob/swift-DEVELOPMENT-SNAPSHOT-2024-07-15-a/stdlib/public/core/Array.swift#L494-L500

これらを総合してざっくり言うと、以下のようになっています。

  1. Array内部のバッファに連続したメモリ領域が割り当たっているのであれば、バッファの先頭アドレスをそのまま返す。スタック上のArrayのアドレスではなく、内部のバッファ(_buffer)の先頭アドレス
  2. Array内部に連続したメモリ領域が割り当たっていない場合
    1. Arrayが空の場合、Fatal Errorになる
    2. Arrayが空でない場合、新たにContiguousArrayを生成し、そこにArrayの内容をコピーしたうえで、そこのバッファの先頭アドレスをそのまま返す

この結果を見ると、inout式の&Arrayのアドレスを取得してUnsafePointerで受け取る場合、場合によっては元のArrayとは何の関係もないポインタが返ってくることが分かります。

ContiguousArrayを生成したあと、どうにかして元のArray_bufferを差し替えるのかなと思っていましたが、差し替えないままになっていて驚きました。
UnsafePointerはimmutableなので、差し替えなくてもよいという判断でしょうか。UnsafeMutablePointerで受けるバージョンを作ると違う動きになるのかも知れません。

kabeyakabeya

UnsafeMutablePointerで受けるバージョンを見てみます。

import Foundation

func printAddress(_ index: Int, _ ptr: UnsafeMutablePointer<Int>) {
    let addr = String(format:"%016x", ptr)
    print("\(index): \(addr)")
}

func testFunc2() {
    var array1 = Array<Int>(repeating: 0, count: 1)
    printAddress(1, &array1)
}
SIL-testFunc2
0001: // testFunc2()
0002: sil hidden @$s6Test309testFunc2yyF : $@convention(thin) () -> () {
0003: bb0:
0004:   %0 = alloc_stack $Array<Int>, var, name "array1" // users: %11, %102, %101, %14
0005:   %1 = metatype $@thin Array<Int>.Type            // user: %9
0006:   %2 = integer_literal $Builtin.Int64, 0          // user: %3
0007:   %3 = struct $Int (%2 : $Builtin.Int64)          // user: %5
0008:   %4 = alloc_stack $Int                           // users: %5, %10, %9
0009:   store %3 to %4 : $*Int                          // id: %5
0010:   %6 = integer_literal $Builtin.Int64, 1          // user: %7
0011:   %7 = struct $Int (%6 : $Builtin.Int64)          // user: %9
0012:   // function_ref Array.init(repeating:count:)
0013:   %8 = function_ref @$sSa9repeating5countSayxGx_SitcfC : $@convention(method) <τ_0_0> (@in τ_0_0, Int, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0> // user: %9
0014:   %9 = apply %8<Int>(%4, %7, %1) : $@convention(method) <τ_0_0> (@in τ_0_0, Int, @thin Array<τ_0_0>.Type) -> @owned Array<τ_0_0> // user: %11
0015:   dealloc_stack %4 : $*Int                        // id: %10
0016:   store %9 to %0 : $*Array<Int>                   // id: %11
0017:   %12 = integer_literal $Builtin.Int64, 1         // user: %13
0018:   %13 = struct $Int (%12 : $Builtin.Int64)        // user: %98

0019:   %14 = begin_access [modify] [static] %0 : $*Array<Int> // users: %49, %36, %30, %18, %99
0020:   %15 = integer_literal $Builtin.Int64, 0         // user: %16
0021:   %16 = struct $Int (%15 : $Builtin.Int64)        // user: %18
0022:   // function_ref Array.reserveCapacity(_:)
0023:   %17 = function_ref @$sSa15reserveCapacityyySiF : $@convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> () // user: %18
0024:   %18 = apply %17<Int>(%16, %14) : $@convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> ()

0025:   %19 = string_literal utf8 "Swift/arm64e-apple-macos.swiftinterface" // user: %21
0026:   %20 = integer_literal $Builtin.Word, 39         // user: %23
0027:   %21 = builtin "ptrtoint_Word"(%19 : $Builtin.RawPointer) : $Builtin.Word // user: %23
0028:   %22 = integer_literal $Builtin.Int8, 2          // users: %152, %120, %114, %29, %23
0029:   %23 = struct $StaticString (%21 : $Builtin.Word, %20 : $Builtin.Word, %22 : $Builtin.Int8) // users: %156, %124
0030:   %24 = integer_literal $Builtin.Int64, 15161     // user: %25
0031:   %25 = struct $UInt (%24 : $Builtin.Int64)       // user: %156
0032:   %26 = string_literal utf8 ""                    // user: %28
0033:   %27 = integer_literal $Builtin.Word, 0          // user: %29
0034:   %28 = builtin "ptrtoint_Word"(%26 : $Builtin.RawPointer) : $Builtin.Word // user: %29
0035:   %29 = struct $StaticString (%28 : $Builtin.Word, %27 : $Builtin.Word, %22 : $Builtin.Int8) // user: %156

0036:   %30 = load %14 : $*Array<Int>                   // user: %32

0037:   // function_ref Array._baseAddressIfContiguous.getter
0038:   %31 = function_ref @$sSa24_baseAddressIfContiguousSpyxGSgvg : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> Optional<UnsafeMutablePointer<τ_0_0>> // user: %32
0039:   %32 = apply %31<Int>(%30) : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> Optional<UnsafeMutablePointer<τ_0_0>> // user: %33
0040:   switch_enum %32 : $Optional<UnsafeMutablePointer<Int>>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 // id: %33
0041: 
0042: bb1(%34 : $UnsafeMutablePointer<Int>):            // Preds: bb0
0043:   br bb3                                          // id: %35
0044: 
0045: bb2:                                              // Preds: bb0
0046:   %36 = load %14 : $*Array<Int>                   // users: %39, %37
0047:   retain_value %36 : $Array<Int>                  // id: %37
0048:   %38 = alloc_stack $Array<Int>                   // users: %42, %39, %44, %41
0049:   store %36 to %38 : $*Array<Int>                 // id: %39
0050:   // function_ref Collection.isEmpty.getter
0051:   %40 = function_ref @$sSlsE7isEmptySbvg : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0) -> Bool // user: %41
0052:   %41 = apply %40<Array<Int>>(%38) : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0) -> Bool // user: %45
0053:   %42 = load %38 : $*Array<Int>                   // user: %43
0054:   release_value %42 : $Array<Int>                 // id: %43
0055:   dealloc_stack %38 : $*Array<Int>                // id: %44
0056:   %45 = struct_extract %41 : $Bool, #Bool._value  // user: %47
0057:   %46 = integer_literal $Builtin.Int1, -1         // user: %47
0058:   %47 = builtin "int_expect_Int1"(%45 : $Builtin.Int1, %46 : $Builtin.Int1) : $Builtin.Int1 // user: %48
0059:   cond_br %47, bb17, bb18                         // id: %48
0060: 
0061: bb3:                                              // Preds: bb17 bb1
0062:   %49 = load %14 : $*Array<Int>                   // users: %57, %55, %51, %68, %128
0063:   // function_ref Array._baseAddressIfContiguous.getter
0064:   %50 = function_ref @$sSa24_baseAddressIfContiguousSpyxGSgvg : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> Optional<UnsafeMutablePointer<τ_0_0>> // user: %51
0065:   %51 = apply %50<Int>(%49) : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> Optional<UnsafeMutablePointer<τ_0_0>> // users: %69, %52
0066:   switch_enum %51 : $Optional<UnsafeMutablePointer<Int>>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5 // id: %52
0067: 
0068: bb4(%53 : $UnsafeMutablePointer<Int>):            // Preds: bb3
0069:   br bb6                                          // id: %54
0070: 
0071: bb5:                                              // Preds: bb3
0072:   retain_value %49 : $Array<Int>                  // id: %55
0073:   %56 = alloc_stack $Array<Int>                   // users: %60, %57, %62, %59
0074:   store %49 to %56 : $*Array<Int>                 // id: %57
0075:   // function_ref Collection.isEmpty.getter
0076:   %58 = function_ref @$sSlsE7isEmptySbvg : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0) -> Bool // user: %59
0077:   %59 = apply %58<Array<Int>>(%56) : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in_guaranteed τ_0_0) -> Bool // user: %63
0078:   %60 = load %56 : $*Array<Int>                   // user: %61
0079:   release_value %60 : $Array<Int>                 // id: %61
0080:   dealloc_stack %56 : $*Array<Int>                // id: %62
0081:   %63 = struct_extract %59 : $Bool, #Bool._value  // user: %65
0082:   %64 = integer_literal $Builtin.Int1, -1         // user: %65
0083:   %65 = builtin "int_expect_Int1"(%63 : $Builtin.Int1, %64 : $Builtin.Int1) : $Builtin.Int1 // user: %66
0084:   cond_br %65, bb15, bb16                         // id: %66
0085: 
0086: bb6:                                              // Preds: bb15 bb4
0087:   // function_ref Array._owner.getter
0088:   %67 = function_ref @$sSa6_owneryXlSgvg : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> @owned Optional<AnyObject> // user: %68
0089:   %68 = apply %67<Int>(%49) : $@convention(method) <τ_0_0> (@guaranteed Array<τ_0_0>) -> @owned Optional<AnyObject> // users: %76, %74
0090:   switch_enum %51 : $Optional<UnsafeMutablePointer<Int>>, case #Optional.some!enumelt: bb7, case #Optional.none!enumelt: bb8 // id: %69
0091: 
0092: // %70                                            // user: %71
0093: bb7(%70 : $UnsafeMutablePointer<Int>):            // Preds: bb6
0094:   %71 = struct_extract %70 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue // user: %72
0095:   %72 = struct $UnsafeRawPointer (%71 : $Builtin.RawPointer) // user: %73
0096:   %73 = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, %72 : $UnsafeRawPointer // user: %74
0097:   br bb9(%68 : $Optional<AnyObject>, %73 : $Optional<UnsafeRawPointer>) // id: %74
0098: 
0099: bb8:                                              // Preds: bb6
0100:   %75 = enum $Optional<UnsafeRawPointer>, #Optional.none!enumelt // user: %76
0101:   br bb9(%68 : $Optional<AnyObject>, %75 : $Optional<UnsafeRawPointer>) // id: %76
0102: 
0103: // %77                                            // users: %100, %96
0104: // %78                                            // user: %80
0105: bb9(%77 : $Optional<AnyObject>, %78 : $Optional<UnsafeRawPointer>): // Preds: bb16 bb8 bb7
0106:   %79 = alloc_stack $UnsafeMutablePointer<Int>    // users: %109, %94, %84, %95
0107:   switch_enum %78 : $Optional<UnsafeRawPointer>, case #Optional.some!enumelt: bb10, case #Optional.none!enumelt: bb11 // id: %80
0108: 
0109: // %81                                            // user: %82
0110: bb10(%81 : $UnsafeRawPointer):                    // Preds: bb9
0111:   %82 = struct_extract %81 : $UnsafeRawPointer, #UnsafeRawPointer._rawValue // user: %83
0112:   %83 = struct $UnsafeMutablePointer<Int> (%82 : $Builtin.RawPointer) // user: %84
0113:   store %83 to %79 : $*UnsafeMutablePointer<Int>  // id: %84
0114:   br bb12                                         // id: %85
0115: 
0116: bb11:                                             // Preds: bb9
0117:   %86 = alloc_stack $Optional<UnsafeRawPointer>   // users: %92, %91, %106
0118:   %87 = integer_literal $Builtin.Word, -8         // user: %88
0119:   %88 = builtin "inttoptr_Word"(%87 : $Builtin.Word) : $Builtin.RawPointer // user: %89
0120:   %89 = struct $UnsafeRawPointer (%88 : $Builtin.RawPointer) // user: %90
0121:   %90 = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, %89 : $UnsafeRawPointer // user: %91
0122:   store %90 to %86 : $*Optional<UnsafeRawPointer> // id: %91
0123:   %92 = load %86 : $*Optional<UnsafeRawPointer>   // user: %93
0124:   switch_enum %92 : $Optional<UnsafeRawPointer>, case #Optional.some!enumelt: bb13, case #Optional.none!enumelt: bb14 // id: %93
0125: 
0126: bb12:                                             // Preds: bb13 bb10
0127:   %94 = load %79 : $*UnsafeMutablePointer<Int>    // user: %96
0128:   dealloc_stack %79 : $*UnsafeMutablePointer<Int> // id: %95
0129:   %96 = mark_dependence %94 : $UnsafeMutablePointer<Int> on %77 : $Optional<AnyObject> // user: %98
0130:   // function_ref printAddress(_:_:)
0131:   %97 = function_ref @$s6Test3012printAddressyySi_SpySiGtF : $@convention(thin) (Int, UnsafeMutablePointer<Int>) -> () // user: %98
0132:   %98 = apply %97(%13, %96) : $@convention(thin) (Int, UnsafeMutablePointer<Int>) -> ()
0133:   end_access %14 : $*Array<Int>                   // id: %99
0134:   release_value %77 : $Optional<AnyObject>        // id: %100
0135:   destroy_addr %0 : $*Array<Int>                  // id: %101
0136:   dealloc_stack %0 : $*Array<Int>                 // id: %102
0137:   %103 = tuple ()                                 // user: %104
0138:   return %103 : $()                               // id: %104
0139: 
0140: // %105                                           // user: %107
0141: bb13(%105 : $UnsafeRawPointer):                   // Preds: bb11
0142:   dealloc_stack %86 : $*Optional<UnsafeRawPointer> // id: %106
0143:   %107 = struct_extract %105 : $UnsafeRawPointer, #UnsafeRawPointer._rawValue // user: %108
0144:   %108 = struct $UnsafeMutablePointer<Int> (%107 : $Builtin.RawPointer) // user: %109
0145:   store %108 to %79 : $*UnsafeMutablePointer<Int> // id: %109
0146:   br bb12                                         // id: %110
0147: 
0148: bb14:                                             // Preds: bb11
0149:   %111 = string_literal utf8 "Unexpectedly found nil while unwrapping an Optional value" // user: %113
0150:   %112 = integer_literal $Builtin.Word, 57        // user: %114
0151:   %113 = builtin "ptrtoint_Word"(%111 : $Builtin.RawPointer) : $Builtin.Word // user: %114
0152:   %114 = struct $StaticString (%113 : $Builtin.Word, %112 : $Builtin.Word, %22 : $Builtin.Int8) // user: %124
0153:   %115 = integer_literal $Builtin.Int64, 15150    // user: %116
0154:   %116 = struct $UInt (%115 : $Builtin.Int64)     // user: %124
0155:   %117 = string_literal utf8 "Fatal error"        // user: %119
0156:   %118 = integer_literal $Builtin.Word, 11        // user: %120
0157:   %119 = builtin "ptrtoint_Word"(%117 : $Builtin.RawPointer) : $Builtin.Word // user: %120
0158:   %120 = struct $StaticString (%119 : $Builtin.Word, %118 : $Builtin.Word, %22 : $Builtin.Int8) // user: %124
0159:   %121 = integer_literal $Builtin.Int32, 1        // user: %122
0160:   %122 = struct $UInt32 (%121 : $Builtin.Int32)   // user: %124
0161:   // function_ref _assertionFailure(_:_:file:line:flags:)
0162:   %123 = function_ref @$ss17_assertionFailure__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtF : $@convention(thin) (StaticString, StaticString, StaticString, UInt, UInt32) -> Never // user: %124
0163:   %124 = apply %123(%120, %114, %23, %116, %122) : $@convention(thin) (StaticString, StaticString, StaticString, UInt, UInt32) -> Never
0164:   unreachable                                     // id: %125
0165: 
0166: bb15:                                             // Preds: bb5
0167:   br bb6                                          // id: %126
0168: 

0169: bb16:                                             // Preds: bb5
0170:   %127 = metatype $@thin ContiguousArray<Int>.Type // user: %133
0171:   %128 = struct_extract %49 : $Array<Int>, #Array._buffer // users: %131, %129
0172:   retain_value %128 : $_ArrayBuffer<Int>          // id: %129
0173:   %130 = alloc_stack $_ArrayBuffer<Int>           // users: %131, %134, %133
0174:   store %128 to %130 : $*_ArrayBuffer<Int>        // id: %131
0175:   // function_ref ContiguousArray.init<A>(_:)
0176:   %132 = function_ref @$ss15ContiguousArrayVyAByxGqd__c7ElementQyd__RszSTRd__lufC : $@convention(method) <τ_0_0><τ_1_0 where τ_0_0 == τ_1_0.Element, τ_1_0 : Sequence> (@in τ_1_0, @thin ContiguousArray<τ_0_0>.Type) -> @owned ContiguousArray<τ_0_0> // user: %133
0177:   %133 = apply %132<Int, _ArrayBuffer<Int>>(%130, %127) : $@convention(method) <τ_0_0><τ_1_0 where τ_0_0 == τ_1_0.Element, τ_1_0 : Sequence> (@in τ_1_0, @thin ContiguousArray<τ_0_0>.Type) -> @owned ContiguousArray<τ_0_0> // users: %137, %135
0178:   dealloc_stack %130 : $*_ArrayBuffer<Int>        // id: %134
0179:   %135 = struct_extract %133 : $ContiguousArray<Int>, #ContiguousArray._buffer // users: %143, %139, %142, %136
0180:   retain_value %135 : $_ContiguousArrayBuffer<Int> // id: %136
0181:   release_value %133 : $ContiguousArray<Int>      // id: %137
0182:   // function_ref _ContiguousArrayBuffer.owner.getter
0183:   %138 = function_ref @$ss22_ContiguousArrayBufferV5owneryXlvg : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> @owned AnyObject // user: %139
0184:   %139 = apply %138<Int>(%135) : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> @owned AnyObject // user: %140
0185:   %140 = enum $Optional<AnyObject>, #Optional.some!enumelt, %139 : $AnyObject // user: %147
0186:   // function_ref _ContiguousArrayBuffer.firstElementAddress.getter
0187:   %141 = function_ref @$ss22_ContiguousArrayBufferV19firstElementAddressSpyxGvg : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> UnsafeMutablePointer<τ_0_0> // user: %142
0188:   %142 = apply %141<Int>(%135) : $@convention(method) <τ_0_0> (@guaranteed _ContiguousArrayBuffer<τ_0_0>) -> UnsafeMutablePointer<τ_0_0> // user: %144
0189:   release_value %135 : $_ContiguousArrayBuffer<Int> // id: %143
0190:   %144 = struct_extract %142 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue // user: %145
0191:   %145 = struct $UnsafeRawPointer (%144 : $Builtin.RawPointer) // user: %146
0192:   %146 = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt, %145 : $UnsafeRawPointer // user: %147
0193:   br bb9(%140 : $Optional<AnyObject>, %146 : $Optional<UnsafeRawPointer>) // id: %147

0194: 
0195: bb17:                                             // Preds: bb2
0196:   br bb3                                          // id: %148
0197: 
0198: bb18:                                             // Preds: bb2
0199:   %149 = string_literal utf8 "Fatal error"        // user: %151
0200:   %150 = integer_literal $Builtin.Word, 11        // user: %152
0201:   %151 = builtin "ptrtoint_Word"(%149 : $Builtin.RawPointer) : $Builtin.Word // user: %152
0202:   %152 = struct $StaticString (%151 : $Builtin.Word, %150 : $Builtin.Word, %22 : $Builtin.Int8) // user: %156
0203:   %153 = integer_literal $Builtin.Int32, 1        // user: %154
0204:   %154 = struct $UInt32 (%153 : $Builtin.Int32)   // user: %156
0205:   // function_ref _fatalErrorMessage(_:_:file:line:flags:)
0206:   %155 = function_ref @$ss18_fatalErrorMessage__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtF : $@convention(thin) (StaticString, StaticString, StaticString, UInt, UInt32) -> Never // user: %156
0207:   %156 = apply %155(%152, %29, %23, %25, %154) : $@convention(thin) (StaticString, StaticString, StaticString, UInt, UInt32) -> Never
0208:   unreachable                                     // id: %157
0209: } // end sil function '$s6Test309testFunc2yyF'

20000文字制限に引っかかったので、次のコメントに続きます。

kabeyakabeya
ある程度詳細な内容

0004:array1のメモリ領域をスタックに確保します。
0014:Array.init(repeating:count:)を呼びます。
0016:スタックに確保したメモリ領域にArrayを入れます。
0024:Array.reserveCapacity(_:)をキャパシティ=0で呼び出します。
0029:%23にUTF8文字列"Swift/arm64e-apple-macos.swiftinterface"を入れます。この文字列は、エラーメッセージで使われます。
0031:%25に整数値15161(0x3B39)を入れます。
0035:%29にUTF8文字列""(空文字列)を入れます。この文字列は、エラーメッセージで使われます。
0039:Arrayのプライベートメソッドである_baseAddressIfContiguousを呼びます。
0040:_baseAddressIfContiguousの結果により分岐します。non-nilの場合ラベルbb1へ。nilの場合ラベルbb2へ。
0042:0040の分岐がnon-nilの場合のbb1です。引数は非OptionalのUnsafeMutablePointer<Int>です。
0043:そのままラベルbb3にジャンプします。
0045: 0040の分岐がnilの場合のbb2です。
0047:0014で初期化したArrayretain_valueします。
0048:再度、スタックにArrayのメモリ領域を確保します。
0049:確保したメモリ領域に0014で初期化したArrayを入れます。
0052:ArrayisEmptyを呼びます。
0054:0047でretain_valueした分をrelease_valueします。
0055:0048で確保したメモリ領域を破棄します。
0059:isEmptyの結果により分岐します。isEmptytrueならラベルbb17falseならbb18にジャンプします。
0061:0040の分岐がnon-nilの場合のbb1から、およびbb17からのbb3です。
0065:Arrayのプライベートメソッドである_baseAddressIfContiguousを再び呼びます。
0066:_baseAddressIfContiguousの結果により分岐します。non-nilの場合ラベルbb4へ。nilの場合ラベルbb5へ。
0068:0066の分岐がnon-nilの場合のbb4です。引数は非OptionalのUnsafeMutablePointer<Int>です。
0069:そのままラベルbb6にジャンプします。
0071:0066の分岐がnilの場合のbb5です。
0072:0014で初期化したArrayretain_valueします。
0073:再度、スタックにArrayのメモリ領域を確保します。
0074:確保したメモリ領域に0014で初期化したArrayを入れます。
0077:ArrayisEmptyを呼びます。
0079:0072でretain_valueした分をrelease_valueします。
0080:0073で確保したメモリ領域を破棄します。
0084:isEmptyの結果により分岐します。isEmptytrueならラベルbb15falseならbb16にジャンプします。
0086:0069から合流するbb6です。
0089:Array_ownerを呼びます。
0090:_baseAddressIfContiguousの結果により分岐します。non-nilの場合ラベルbb7へ。nilの場合ラベルbb8へ。
0093:0090の分岐がnon-nilの場合のbb7です。引数はUnsafeMutablePointer<Int>です。
0095:UnsafeMutablePointer<Int>からUnsafeRawPointerを生成します。
0096:UnsafeRawPointerからOptinal<UnsafeRawPointer>を生成します。
0097:ラベルbb9にジャンプします。
0099:0090の分岐がnilの場合のbb8です。
0100:nil値でOptinal<UnsafeRawPointer>を生成します。
0101:ラベルbb9にジャンプします。
0105:0097,0101,0193から合流するbb9です。引数は_ownerOptional<UnsafeRawPointer>です。
0106:UnsafeMutablePointer<Int>のメモリ領域をスタックに確保します。
0107:引数のOptional<UnsafeRawPointer>がnilかどうかで分岐します。non-nilの場合ラベルbb10、nilの場合ラベルbb11にジャンプします。
0110:Optional<UnsafeRawPointer>がnon-nilの場合のbb10です。引数はUnsafeRawPointerです。
0113:UnsafeRawPointerからUnsafeMutablePointer<Int>を作って0106で確保したメモリ領域に入れます。
0114:ラベルbb12にジャンプします。
0116:Optional<UnsafeRawPointer>がnilの場合のbb11です。
0117:Optional<UnsafeRawPointer>のメモリ領域をスタックに確保します。
0118:Builtin.RawPointerを整数値-8(=0xFFFF_FFFF_FFFF_FFF8)から生成します。
0120:Builtin.RawPointerからUnsafeRawPointerを生成します。
0121:UnsafeRawPointerからOptional<UnsafeRawPointer>を生成します。
0122:Optional<UnsafeRawPointer>を確保したメモリ領域に入れます。
0124:Optional<UnsafeRawPointer>がnilかどうかで分岐します。non-nilの場合ラベルbb13、nilの場合ラベルbb14にジャンプします。
0126:0114、0146のジャンプ先bb12です。
0132:0106で確保したUnsafeMutablePointer<Int>を渡してprintAddressを呼びます。
0134:array1._ownerrelease_valueします。
0135:array1destroy_addrします。これにより、内部に保持している参照があればrelease_valueされます。
0136:array1のスタック上のメモリ領域を破壊します。
0138:testFunc2()を終了し、returnします。
0141:整数から生成したUnsafeRawPointerがnon-nilの場合のbb13です。引数は整数から生成したUnsafeRawPointerです。
0145:整数から生成したUnsafeRawPointerUnsafeMutablePointer<Int>に変換して0106で確保した領域に入れます。
0146:ラベルbb12にジャンプします。
0148:整数から生成したUnsafeRawPointerがnilの場合のbb14です。
0163:assertionFailureを呼び出し、プログラムを終了します。
0166:isEmptytrueの場合のbb15です。
0167:そのままラベルbb6にジャンプします。
0169:isEmptyfalseの場合のbb16です。
0171:array1_bufferを取得します。
0173:_ArrayBuffer<Int>のメモリ領域をスタックに確保します。
0174:array1_bufferを確保したメモリ領域に入れます。
0177:ContiguousArray.init<Int>(_:)を呼びます。array1_bufferがコピーされます。
0178:確保した_ArrayBuffer<Int>のメモリ領域を破棄します。
0179:ContiguousArray_bufferを取得します。
0180:ContiguousArray_bufferretain_valueします。
0181:ContiguousArray自体をrelease_valueします。ガワは破棄されて、中身だけが残るようなイメージです。
0184:ContiguousArray._buffer.ownerを呼び出します。
0185:ContiguousArray._buffer.ownerからOptional<AnyObject>を生成します。
0188:ContiguousArray._buffer.firstElementAddressを呼び出します。
0189:ContiguousArray_bufferrelease_valueします。
0191:ContiguousArray._buffer.firstElementAddressからUnsafeRawPointerを生成します。
0192:Optional<UnsafeRawPointer>を生成します。
0193:ラベルbb9にジャンプします。

0195:bb17です。
0196:そのままラベルbb3にジャンプします。

0198:bb18です。
0207:_fatalErrorMessage(_:_:file:line:flags:)を呼んで終了します。

こちらもグラフにします。

グラフ

多少冗長になっていますが、処理内容はUnsafePointerのものとほとんど同じに見えます。
1点、処理の冒頭でArray.reserveCapacity(0)が呼ばれているのが異なる点です。

ContiguousArrayを生成したあと、どうにかして元のArray_bufferを差し替えるのかなと思っていましたが、差し替えないままになっていて驚きました。
UnsafePointerはimmutableなので、差し替えなくてもよいという判断でしょうか。UnsafeMutablePointerで受けるバージョンを作ると違う動きになるのかも知れません。

ContiguousArrayが生成されると、そのままそのバッファのアドレスが返ってきます。ここについてはUnsafePointerと同じです。書き換えた内容を元のArrayに反映する処理はありません。
ただしもしArray.reserveCapacity(0)がContiguousなバッファを確保するのであれば、ContiguousArrayの生成処理は(コードはあるものの)実行されないため、特に問題はありません。

Appleのドキュメントを見てみます。
Array.reserveCapacity(_:)の説明には、以下のような記述があります。

This method ensures that the array has unique, mutable, contiguous storage, with space allocated for at least the requested number of elements.

これによれば、つまりContiguousなバッファが確保される、ということになります。

kabeyakabeya

なぜ、Arrayは他のstructと違うコードが出力されるのでしょうか。

AST(Abstract Syntax Tree)をstructの場合とArrayの場合の両方で出力してみます。
swiftc -dump-ast <ソースファイル名.swift> > <ソースファイル名.astのようにすればASTを出力できます。

まずstructの場合から。

import Foundation

struct TestStruct {
    var value: Int = 0
}

func printAddress(_ index: Int, _ ptr: UnsafePointer<TestStruct>) {
    let addr = String(format:"%016x", ptr)
    print("\(index): \(addr)")
}

func testFunc1() {
    var struct1 = TestStruct(value: 1)
    printAddress(1, &struct1)
}

この場合、testFunc1printAddress呼び出し周辺は以下のようになります。

      (var_decl range=[Test28.swift:13:9 - line:13:9] "struct1" interface type='TestStruct' access=private readImpl=stored writeImpl=stored readWriteImpl=stored)

      (call_expr type='()' location=Test28.swift:14:5 range=[Test28.swift:14:5 - line:14:29] nothrow isolationCrossing=none
        (declref_expr type='(Int, UnsafePointer<TestStruct>) -> ()' location=Test28.swift:14:5 range=[Test28.swift:14:5 - line:14:5] decl=Test28.(file).printAddress@Test28.swift:7:6 function_ref=single)
        (argument_list
          (argument
            (integer_literal_expr type='Int' location=Test28.swift:14:18 range=[Test28.swift:14:18 - line:14:18] value=1 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**))
          (argument
            (inout_to_pointer implicit type='UnsafePointer<TestStruct>' location=Test28.swift:14:21 range=[Test28.swift:14:21 - line:14:22]
              (inout_expr type='inout TestStruct' location=Test28.swift:14:21 range=[Test28.swift:14:21 - line:14:22]
                (declref_expr type='@lvalue TestStruct' location=Test28.swift:14:22 range=[Test28.swift:14:22 - line:14:22] decl=Test28.(file).testFunc1().struct1@Test28.swift:13:9 function_ref=unapplied))))))

UnsafePointerを作る部分はinout_to_pointerという表現になっています。

もう一方のArrayの場合も見てみます。

import Foundation

func printAddress(_ index: Int, _ ptr: UnsafePointer<Int>) {
    let addr = String(format:"%016x", ptr)
    print("\(index): \(addr)")
}

func testFunc2() {
    var array1 = Array<Int>(repeating: 0, count: 1)
    printAddress(1, &array1)
}
(var_decl range=[Test29.swift:9:9 - line:9:9] "array1" interface type='[Int]' access=private readImpl=stored writeImpl=stored readWriteImpl=stored)

      (call_expr type='()' location=Test29.swift:10:5 range=[Test29.swift:10:5 - line:10:28] nothrow isolationCrossing=none
        (declref_expr type='(Int, UnsafePointer<Int>) -> ()' location=Test29.swift:10:5 range=[Test29.swift:10:5 - line:10:5] decl=Test29.(file).printAddress@Test29.swift:3:6 function_ref=single)
        (argument_list
          (argument
            (integer_literal_expr type='Int' location=Test29.swift:10:18 range=[Test29.swift:10:18 - line:10:18] value=1 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**))
          (argument
            (array_to_pointer implicit type='UnsafePointer<Int>' location=Test29.swift:10:21 range=[Test29.swift:10:21 - line:10:22]
              (inout_expr type='inout [Int]' location=Test29.swift:10:21 range=[Test29.swift:10:21 - line:10:22]
                (declref_expr type='@lvalue [Int]' location=Test29.swift:10:22 range=[Test29.swift:10:22 - line:10:22] decl=Test29.(file).testFunc2().array1@Test29.swift:9:9 function_ref=unapplied))))))

こちらはarray_to_pointerという表現になっています。
ASTの段階で、Arrayは特別扱いされています。