Swiftでのポインタ、アドレスについて
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
value1
とvalue2
は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
array1
とarray2
は同じアドレスです。普通の変数とは違い、別の変数でもアドレスが同じです。copy-on-writeと呼ばれる最適化手法が使われているということなんですね。つまりこの場合、array1
かarray2
が更新されない限りarray1
とarray2
は同じメモリ領域を共有します。
ちなみに、&
で取れるポインタと、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
とかを考えるとまあそうなのかなという感じですね。
// ↓この書き方はエラーになる
//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
は古いアドレスを指し続けるよ、ということですね。
生のポインタのようなものを用意する
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++のメモリ配置を念頭においてプログラミングするとはまりそうな予感があります。
この辺りを書籍(Zenn Book)にまとめ直しています。
上記の話も一部、当時誤解していた部分(大きく外れてはないが厳密には合っていない)があります。
まだ執筆途中ですが、読めます。
書籍に書いている途中で、色々調べ直しています。
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
array1
とarray2
がCoWでバッファを共有しているというのは分かります。
ですが、array1
とarray2
は別々の変数なのだから、スタック上には2つの領域が確保されていて、そこにポインタが入っていて、それが2変数とも同じ共有バッファをポイントしている、ということになっているはず、というのが推測されるのです。
実際にはそうなっておらず&array1
と&array2
で同じアドレスがprint
されます。
具体的にどうなっているのか、SILとアセンブラで確認します。
その前に、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で確保したメモリ領域を破棄します。
特に変わった処理はありません。
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
で得られたArray
をretain_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:Array
のisEmpty
を呼びます。
0036:0029でretain_value
した分をrelease_value
します。
0037:0039で確保したメモリ領域を破棄します。
0041:isEmpty
の結果により分岐します。isEmpty
がtrue
ならラベルbb12
、false
なら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
です。引数は_owner
とOptional<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:array1
をrelease_value
します。
0093:array1
をdestroy_addr
します。これにより、内部に保持している参照があればrelease_value
されます。
0093:array1
のスタック上のメモリ領域を破壊します。
0095:testFunc2()
を終了し、return
します。
0098:整数から生成したUnsafeRawPointer
がnon-nilの場合のbb10
です。引数は整数から生成したUnsafeRawPointer
です。
0102:整数から生成したUnsafeRawPointer
をUnsafePointer<Int>
に変換して0063で確保した領域に入れます。
0103:ラベルbb9
にジャンプします。
0105:整数から生成したUnsafeRawPointer
がnilの場合のbb11
です。
0125:assertionFailure
を呼び出し、プログラムを終了します。
0128:isEmpty
がtrue
の場合のbb12
です。
0129:そのままラベルbb3
にジャンプします。
0131:isEmpty
がfalse
の場合のbb13
です。
0133:array1
の_buffer
を取得します。
0135:_ArrayBuffer<Int>
のメモリ領域をスタックに確保します。
0136:array1
の_buffer
をメモリ領域に入れます。
0139:ContiguousArray.init<Int>(_:)
を呼びます。array1
の_buffer
がコピーされます。
0140:確保した_ArrayBuffer<Int>
のメモリ領域を破棄します。
0141:ContiguousArray
の_buffer
を取得します。
0142:ContiguousArray
の_buffer
をretain_value
します。
0143:ContiguousArray
自体をrelease_value
します。ガワは破棄されて、中身だけが残るようなイメージです。
0146:ContiguousArray._buffer.owner
を呼び出します。
0147:ContiguousArray._buffer.owner
からOptional<AnyObject>
を生成します。
0150:ContiguousArray._buffer.firstElementAddress
を呼び出します。
0151:ContiguousArray
の_buffer
をrelease_value
します。
0153:ContiguousArray._buffer.firstElementAddress
からUnsafeRawPointer
を生成します。
0154:Optional<UnsafeRawPointer>
を生成します。
0155:ラベルbb6
にジャンプします。
ジャンプしまくりでややこしいのでグラフにしてみます。
グラフ
ちなみに処理中に出てくるArray._baseAddressIfContiguous
の実装は以下です。
これらを総合してざっくり言うと、以下のようになっています。
-
Array
内部のバッファに連続したメモリ領域が割り当たっているのであれば、バッファの先頭アドレスをそのまま返す。スタック上のArray
のアドレスではなく、内部のバッファ(_buffer
)の先頭アドレス -
Array
内部に連続したメモリ領域が割り当たっていない場合-
Array
が空の場合、Fatal Errorになる -
Array
が空でない場合、新たにContiguousArray
を生成し、そこにArray
の内容をコピーしたうえで、そこのバッファの先頭アドレスをそのまま返す
-
この結果を見ると、inout式の&
でArray
のアドレスを取得してUnsafePointer
で受け取る場合、場合によっては元のArray
とは何の関係もないポインタが返ってくることが分かります。
ContiguousArray
を生成したあと、どうにかして元のArray
の_buffer
を差し替えるのかなと思っていましたが、差し替えないままになっていて驚きました。
UnsafePointer
はimmutableなので、差し替えなくてもよいという判断でしょうか。UnsafeMutablePointer
で受けるバージョンを作ると違う動きになるのかも知れません。
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文字制限に引っかかったので、次のコメントに続きます。
ある程度詳細な内容
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で初期化したArray
をretain_value
します。
0048:再度、スタックにArray
のメモリ領域を確保します。
0049:確保したメモリ領域に0014で初期化したArray
を入れます。
0052:Array
のisEmpty
を呼びます。
0054:0047でretain_value
した分をrelease_value
します。
0055:0048で確保したメモリ領域を破棄します。
0059:isEmpty
の結果により分岐します。isEmpty
がtrue
ならラベルbb17
、false
なら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で初期化したArray
をretain_value
します。
0073:再度、スタックにArray
のメモリ領域を確保します。
0074:確保したメモリ領域に0014で初期化したArray
を入れます。
0077:Array
のisEmpty
を呼びます。
0079:0072でretain_value
した分をrelease_value
します。
0080:0073で確保したメモリ領域を破棄します。
0084:isEmpty
の結果により分岐します。isEmpty
がtrue
ならラベルbb15
、false
なら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
です。引数は_owner
とOptional<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._owner
をrelease_value
します。
0135:array1
をdestroy_addr
します。これにより、内部に保持している参照があればrelease_value
されます。
0136:array1
のスタック上のメモリ領域を破壊します。
0138:testFunc2()
を終了し、return
します。
0141:整数から生成したUnsafeRawPointer
がnon-nilの場合のbb13
です。引数は整数から生成したUnsafeRawPointer
です。
0145:整数から生成したUnsafeRawPointer
をUnsafeMutablePointer<Int>
に変換して0106で確保した領域に入れます。
0146:ラベルbb12
にジャンプします。
0148:整数から生成したUnsafeRawPointer
がnilの場合のbb14
です。
0163:assertionFailure
を呼び出し、プログラムを終了します。
0166:isEmpty
がtrue
の場合のbb15
です。
0167:そのままラベルbb6
にジャンプします。
0169:isEmpty
がfalse
の場合のbb16
です。
0171:array1
の_buffer
を取得します。
0173:_ArrayBuffer<Int>
のメモリ領域をスタックに確保します。
0174:array1
の_buffer
を確保したメモリ領域に入れます。
0177:ContiguousArray.init<Int>(_:)
を呼びます。array1
の_buffer
がコピーされます。
0178:確保した_ArrayBuffer<Int>
のメモリ領域を破棄します。
0179:ContiguousArray
の_buffer
を取得します。
0180:ContiguousArray
の_buffer
をretain_value
します。
0181:ContiguousArray
自体をrelease_value
します。ガワは破棄されて、中身だけが残るようなイメージです。
0184:ContiguousArray._buffer.owner
を呼び出します。
0185:ContiguousArray._buffer.owner
からOptional<AnyObject>
を生成します。
0188:ContiguousArray._buffer.firstElementAddress
を呼び出します。
0189:ContiguousArray
の_buffer
をrelease_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なバッファが確保される、ということになります。
なぜ、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)
}
この場合、testFunc1
のprintAddress
呼び出し周辺は以下のようになります。
(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
は特別扱いされています。