Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift KeyPath Internals

Yuta Saito
October 04, 2022

Swift KeyPath Internals

Yuta Saito

October 04, 2022
Tweet

More Decks by Yuta Saito

Other Decks in Programming

Transcript

  1. KeyPath ศར ཚ๫ʹݴ͏ͱ getter ͱ setter ΛऔΓճ͢දݱ ৭ΜͳॴͰ࢖ΘΕͯΔ • subscript(dynamicMember:

    KeyPath<X, Y>) -> Z • func map<T>(_ keyPath: KeyPath<Self.Output, T>) -> Publishers.MapKeyPath<Self, T> • Key Paths Expressions as Functions 4
  2. KeyPath ͓͍ͦ struct Foo { let bar: Int } let

    foo = Foo(bar: 42) @_optimize(none) func getter() { _ = foo.bar } @_optimize(none) func keyPathGetter() { _ = foo[keyPath: \.bar] } print("Simple Getter =>", ContinuousClock().measure { for _ in 1...10000000 { getter() } }) print("KeyPath Getter =>", ContinuousClock().measure { for _ in 1...10000000 { keyPathGetter() } }) $ swiftc main.swift $ ./main Simple Getter => 0.046666295 seconds KeyPath Getter => 1.590084352 seconds ʢ࠷దԽ͠ͳ͍ͱʣ 34ഒ ͓͍ͦ ! 6
  3. KeyPath ࢖ͬͯΔ…ʁ ࢖ΘΕͯແͦ͞͏ͳϚΠφʔػೳ • \[String: Int].["foo"] • \[Int].last?.littleEndian • class

    KeyPath: Hashable, Equatable • PartialKeyPath.appending(path:) let arrayDescription: PartialKeyPath<Array<Int>> = \.description let stringLength: PartialKeyPath<String> = \.count let arrayDescriptionLength = arrayDescription.appending(path: stringLength) 7
  4. ҉໧ʹ subscript indexΩϟϓνϟͯ͠Δέʔ ε KeyPath͕SendableʹͳΕͳ͍࿩ by iceman @dynamicMemberLookup struct Box<Value>

    { var value: Value subscript<T>(dynamicMember keyPath: KeyPath<Value, T>) -> T { get { value[keyPath: keyPath] } } } struct ContentView: View { @State private var state = Box(value: Dog()) var body: some View { TextField("", text: $state.name) // TextField("", text: _state.projectedValue[dynamicMember: \Box<Dog>.[dynamicMember: \Dog.name]]) } } 8
  5. ͔ͳΓ୯७Խͨ͠KeyPathͷ࣮૷ let kp0 = \Foo.bar // -> let kp0 =

    KeyPath(getter: { $0.bar }) let kp1 = \Foo.[42] // -> let kp1 = KeyPath(getter: { $0[42] }) ࣮ࡍ͸΋ͬͱෳࡶ 10
  6. SIL্ͷදݱ // keyPathGetter() sil hidden [Onone] @$s4main13keyPathGetteryyF : $@convention(thin) ()

    -> () { bb0: // 1. άϩʔόϧม਺ `foo` Λϩʔυ %0 = global_addr @$s4main3fooAA3FooVvp : $*Foo // user: %1 %1 = load %0 : $*Foo // user: %4 // 2. `\Foo.bar` ͷKeyPathΛੜ੒ %2 = keypath $KeyPath<Foo, Int>, (root $Foo; stored_property #Foo.bar : $Int) // users: %10, %7 // 3. `foo` ΛελοΫʹΞϩέʔτ %3 = alloc_stack $Foo // users: %4, %9, %7 store %1 to %3 : $*Foo // id: %4 // 4. `foo` ͔ΒKeyPathܦ༝Ͱ `bar` Λऔಘ %5 = function_ref @swift_getAtKeyPath : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @guaranteed KeyPath<τ_0_0, τ_0_1>) -> @out τ_0_1 // user: %7 %6 = alloc_stack $Int // users: %8, %7 %7 = apply %5<Foo, Int>(%6, %3, %2) : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @guaranteed KeyPath<τ_0_0, τ_0_1>) -> @out τ_0_1 dealloc_stack %6 : $*Int // id: %8 dealloc_stack %3 : $*Foo // id: %9 strong_release %2 : $KeyPath<Foo, Int> // id: %10 %11 = tuple () // user: %12 return %11 : $() // id: %12 } // end sil function '$s4main13keyPathGetteryyF' 16
  7. KeyPathInstͷߏ଄ keypath /* ಘΒΕΔKeyPathܕ */, ( root /* ϧʔτͷܕ */;

    /* ύεͷཁૉ1 ʢKeyPathPatternComponentʣ */ /* ύεͷཁૉ2 ʢKeyPathPatternComponentʣ */ /* ύεͷཁૉN ʢKeyPathPatternComponentʣ */ ) 17
  8. KeyPathPatternComponent ͷछྨ class KeyPathPatternComponent { ... enum class Kind: unsigned

    { StoredProperty, // \Foo.bar (stored) GettableProperty, // \Foo.bar (getter), \Foo.[42] (subscript) SettableProperty, // GettableProperty + setter TupleElement, // \(Int, String).1 OptionalChain, // \Foo.bar?.baz OptionalForce, // \Foo.bar! OptionalWrap, // \Foo.bar?.baz }; }; 18
  9. StoredProperty struct Foo { let bar: Int } // `\Foo.bar`

    => %2 = keypath $KeyPath<Foo, Int>, ( root $Foo; stored_property #Foo.bar : $Int ) 19
  10. StoredProperty struct Foo { var bar: Int } // `\Foo.bar`

    => %2 = keypath $WritableKeyPath<Foo, Int>, ( root $Foo; stored_property #Foo.bar : $Int ) 20
  11. TupleElement typealias Foo = ( bar: Int, baz: String )

    // `\Foo.bar` => %2 = keypath $WritableKeyPath<(bar: Int, baz: String), Int>, ( root $(bar: Int, String); tuple_element #0 : $Int ) 21
  12. GettableProperty struct Foo { var bar: Int { 42 }

    } // `\Foo.bar` => %2 = keypath $KeyPath<Foo, Int>, ( root $Foo; gettable_property $Int, id @$s4main3FooV3barSivg : $@convention(method) (Foo) -> Int, getter @$s4main3FooV3barSivpACTK : $@convention(thin) (@in_guaranteed Foo) -> @out Int ) 22
  13. GettableProperty (Subscript) struct Foo { subscript(x: Int) -> Int {

    0xbadbabe } } // `\Foo.[42]` => %2 = integer_literal $Builtin.Int64, 0xdeadbeef %3 = struct $Int (%2 : $Builtin.Int64) %4 = keypath $KeyPath<Foo, Int>, ( root $Foo; gettable_property $Int, id @$s4main3FooVyS2icig : $@convention(method) (Int, Foo) -> Int, getter @$s4main3FooVyS2icipACTK : $@convention(thin) (@in_guaranteed Foo, UnsafeRawPointer) -> @out Int, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int ) (%3) ஋ʢindicesʣɺൺֱɺϋογϡͷͨΊͷؔ਺ΛΩϟϓνϟ 23
  14. GettableProperty (Subscript) • ϥϯλΠϜ͔Βݺ͹ΕΔ༻ͷαϯΫ • ͢΂ͯͷcomputed subscriptΛ୯ҰͷγάωνϟͰݺͼग़ͨ͢Ίʹඞཁ // key path

    getter for Foo.subscript(_:) : Foo sil shared [thunk] @$s4main3FooVyS2icipACTK : $@convention(thin) (@in_guaranteed Foo, UnsafeRawPointer) -> @out Int { // %0 // user: %8 // %1 // user: %3 // %2 // user: %4 bb0(%0 : $*Int, %1 : $*Foo, %2 : $UnsafeRawPointer): %3 = load %1 : $*Foo // user: %7 %4 = pointer_to_address %2 : $UnsafeRawPointer to $*Int // user: %5 %5 = load %4 : $*Int // user: %7 // function_ref Foo.subscript.getter %6 = function_ref @$s4main3FooVyS2icig : $@convention(method) (Int, Foo) -> Int // user: %7 %7 = apply %6(%5, %3) : $@convention(method) (Int, Foo) -> Int // user: %8 store %7 to %0 : $*Int // id: %8 %9 = tuple () // user: %10 return %9 : $() // id: %10 } // end sil function '$s4main3FooVyS2icipACTK' 24
  15. SettableProperty struct Foo { var bar: Int { get {

    42 } set { } } } // `\Foo.bar` => %2 = keypath $WritableKeyPath<Foo, Int>, ( root $Foo; settable_property $Int, id @$s4main3FooV3barSivg : $@convention(method) (Foo) -> Int, getter @$s4main3FooV3barSivpACTK : $@convention(thin) (@in_guaranteed Foo) -> @out Int, setter @$s4main3FooV3barSivpACTk : $@convention(thin) (@in_guaranteed Int, @inout Foo) -> () ) 25
  16. GettableProperty + GettableProperty struct Foo { struct Bar { var

    baz: Int { 42 } } var bar: Bar { Bar() } } // `\Foo.bar.baz` => %2 = keypath $KeyPath<Foo, Int>, ( root $Foo; gettable_property $Foo.Bar, id @$s4main3FooV3barAC3BarVvg : $@convention(method) (Foo) -> Foo.Bar, getter @$s4main3FooV3barAC3BarVvpACTK : $@convention(thin) (@in_guaranteed Foo) -> @out Foo.Bar; gettable_property $Int, id @$s4main3FooV3BarV3bazSivg : $@convention(method) (Foo.Bar) -> Int, getter @$s4main3FooV3BarV3bazSivpAETK : $@convention(thin) (@in_guaranteed Foo.Bar) -> @out Int ) 26
  17. OptionalChain + OptionalWrap struct Foo { struct Bar { var

    baz: Int { 42 } } var bar: Bar? { Bar() } } // `\Foo.bar.baz` => %2 = keypath $KeyPath<Foo, Optional<Int>>, ( root $Foo; gettable_property $Optional<Foo.Bar>, id @$s4main3FooV3barAC3BarVSgvg : $@convention(method) (Foo) -> Optional<Foo.Bar>, getter @$s4main3FooV3barAC3BarVSgvpACTK : $@convention(thin) (@in_guaranteed Foo) -> @out Optional<Foo.Bar>; optional_chain : $Foo.Bar; gettable_property $Int, id @$s4main3FooV3BarV3bazSivg : $@convention(method) (Foo.Bar) -> Int, getter @$s4main3FooV3BarV3bazSivpAETK : $@convention(thin) (@in_guaranteed Foo.Bar) -> @out Int; optional_wrap : $Optional<Int> ) 27
  18. ͔͜͜Βઌɺ͜ͷίʔυΛର৅ʹ࿩ΛਐΊΔɻ struct Foo { subscript(x: Int) -> Int { 0xbadbabe

    } } let foo = Foo() @_optimize(none) func keyPathGetter() { _ = foo[keyPath: \Foo.[0xdeadbeef]] } 29
  19. LLVM IR্ͷදݱ • KeyPath Object (Not ABI) • KeyPathΠϯελϯε͕อ࣋͢Δ৘ใ •

    ͍ͭ΋Ϣʔβ͕औΓճͯ͠Δͷ͸͜Ε • KeyPath Pattern (ABI) • PropertyͷಛੑͱKeyPathͱͯ͠ͷ஋ͷಛੑΛදݱ • ϥϯλΠϜ͕Pattern͔ΒKeyPath ObjectΛੜ੒͢Δ 30
  20. KeyPath Pattern -> KeyPath Object (ΠϯελϯεԽ) ΠϯελϯεԽʹ͸Ҿ਺ΛऔΔ৔߹͕͋Δ • Ҿ਺: IndicesɺGeneric

    Params • Ҿ਺͕ඞཁͳ͍৔߹͸Πϯελϯε͕Ωϟογϡ͞ΕΔ func withIndices(_ x: Int, y: String) -> AnyKeyPath { return \Foo.[x, y] } func withGenericContext<X: Hashable>(_ x: X.Type) -> AnyKeyPath { return \Blah.[x] } 31
  21. KeyPath Pattern in LLVM IR (Gettable Subscript) \Foo.[0xdeadbeef] @keypath =

    private global <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }> <{ ; oncePtr (0 if not cacheable, otherwise a relative pointer to the cache space) i32 0, ; --- KeyPathComponentHeader --- ; genericEnvironment i32 0, ; rootMetadataRef i32 cast_relptr (i64 symbolic_ref @$s4main3FooVMn), ; nominal type descriptor for main.Foo ; leafMetadataRef i32 cast_relptr (i64 symbolic_ref @$sSi), ; mangled name for Swift.Int ("Si\0") ; kvcString i32 0, ; KeyPathBuffer.Header i32 0b00000000000000000000000000011000, ; (size: 24, hasReferencePrefix: false, trivial: false) ; --- RawKeyPathComponent --- ; RawKeyPathComponent.Header i32 0b00000010000010000000000000000000, ; (hasComputedArguments: true, discriminator: computedTag(2)) ; RawKeyPathComponent.idValue i32 cast_relptr (i64 (i64)* @"$s4main3FooVyS2icig" to i64), ; main.Foo.subscript.getter : (Swift.Int) -> Swift.Int ; RawKeyPathComponent.getter i32 cast_relptr (i64 (i64)* @"$s4main3FooVyS2icipACTK" to i64), ; key path getter for main.Foo.subscript(Swift.Int) -> Swift.Int : main.Foo ; --- KeyPathPatternComputedArguments --- ; KeyPathPatternComputedArguments.getLayout i32 cast_relptr ({ i64, i64 } (i8*)* @keypath_get_arg_layout to i64), ; KeyPathPatternComputedArguments.witnesses i32 cast_relptr ({ i8*, void (i8*, i8*, i64)*, i1 (i8*, i8*)*, i64 (i8*)* }* @keypath_witnesses to i64), ; KeyPathPatternComputedArguments.initializer i32 cast_relptr (void (i8*, i8*)* @keypath_arg_init to i64), }>, section ".rodata", align 8 32
  22. KeyPathੜ੒ in LLVM IR ; 1. Argument Bufferͷ૊Έཱͯ %1 =

    alloca i8, i64 8, align 16 %2 = getelementptr inbounds i8, i8* %1, i64 0 %3 = bitcast i8* %2 to %TSi* %._value = getelementptr inbounds %TSi, %TSi* %3, i32 0, i32 0 store i64 0xdeadbeef, i64* %._value, align 8 ; 2. KeyPath Objectͷੜ੒ %4 = call %swift.refcounted* @swift_getKeyPath(i8* @keypath_pattern, i8* %1) 33
  23. 1. oncePtrͷΩϟογϡνΣοΫ let oncePtrPtr = pattern let oncePtrOffset = oncePtrPtr.load(as:

    Int32.self) let oncePtr: UnsafeRawPointer? if oncePtrOffset != 0 { let theOncePtr = _resolveRelativeAddress(oncePtrPtr, oncePtrOffset) oncePtr = theOncePtr // See whether we already instantiated this key path. // This is a non-atomic load because the instantiated pointer will be // written with a release barrier, and loads of the instantiated key path // ought to carry a dependency through this loaded pointer. let existingInstance = theOncePtr.load(as: UnsafeRawPointer?.self) if let existingInstance = existingInstance { // Return the instantiated object at +1. let object = Unmanaged<AnyKeyPath>.fromOpaque(existingInstance) // TODO: This retain will be unnecessary once we support global objects // with inert refcounting. _ = object.retain() return existingInstance } } else { oncePtr = nil } 37
  24. 2. KeyPathΠϯελϯεͷੜ੒ // Instantiate a new key path object modeled

    on the pattern. // Do a pass to determine the class of the key path we'll be instantiating // and how much space we'll need for it. let (keyPathClass, rootType, size, _) = _getKeyPathClassAndInstanceSizeFromPattern(patternPtr, arguments) // Allocate the instance. let instance = keyPathClass._create(capacityInBytes: size) { instanceData in // Instantiate the pattern into the instance. _instantiateKeyPathBuffer(patternPtr, instanceData, rootType, arguments) } 38
  25. 2. KeyPathΠϯελϯεͷੜ੒ Tail-alloc͞ΕͨόοϑΝ͸KeyPathBufferͱͯ͠࢖ΘΕΔ public class AnyKeyPath: Hashable, _AppendKeyPath { ...

    internal static func _create( capacityInBytes bytes: Int, initializedBy body: (UnsafeMutableRawBufferPointer) -> Void ) -> Self { _internalInvariant(bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes") let result = Builtin.allocWithTailElems_1(self, (bytes/4)._builtinWordValue, Int32.self) result._kvcKeyPathStringPtr = nil let base = UnsafeMutableRawPointer(Builtin.projectTailElems(result, Int32.self)) body(UnsafeMutableRawBufferPointer(start: base, count: bytes)) return result } ... } 39
  26. 3. Ωϟογϡʹอଘ • CAS (Compared-and-Swap)ͰΞτ ϛοΫʹΩϟογϡྖҬʹॻ͖ࠐΈ • ΋͠ଞͷεϨου͕Ωϟογϡॻ͖ ࠐΈʹ੒ޭͨ͠ΒͦΕΛ࠾༻ͯ͠ɺ ࡞ͬͨΦϒδΣΫτΛഇغ

    // Try to replace a null pointer in the cache variable with the instance // pointer. let instancePtr = Unmanaged.passRetained(instance) while true { let (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Word( oncePtr._rawValue, 0._builtinWordValue, UInt(bitPattern: instancePtr.toOpaque())._builtinWordValue) // If the exchange succeeds, then the instance we formed is the canonical // one. if Bool(won) { break } // Otherwise, someone raced with us to instantiate the key path pattern // and won. Their instance should be just as good as ours, so we can take // that one and let ours get deallocated. if let existingInstance = UnsafeRawPointer(bitPattern: Int(oldValue)) { // Return the instantiated object at +1. let object = Unmanaged<AnyKeyPath>.fromOpaque(existingInstance) // TODO: This retain will be unnecessary once we support global objects // with inert refcounting. _ = object.retain() // Release the instance we created. instancePtr.release() return existingInstance } else { // Try the cmpxchg again if it spuriously failed. continue } } 40
  27. • ίϯϙʔωϯτ͝ͱʹ۩ମܕ ΛऔΓग़ͯ͠౤Өʢprojectʣͯ͠ ͍͘ • δΣωϦοΫλΠϓͳͷͰOpaque Pointer @usableFromInline internal final

    func _projectReadOnly(from root: Root) -> Value { // TODO: For perf, we could use a local growable buffer instead of Any var curBase: Any = root return withBuffer { var buffer = $0 if buffer.data.isEmpty { return unsafeBitCast(root, to: Value.self) } while true { let (rawComponent, optNextType) = buffer.next() let valueType = optNextType ?? Value.self let isLast = optNextType == nil func project<CurValue>(_ base: CurValue) -> Value? { func project2<NewValue>(_: NewValue.Type) -> Value? { switch rawComponent._projectReadOnly(base, to: NewValue.self, endingWith: Value.self) { case .continue(let newBase): if isLast { _internalInvariant(NewValue.self == Value.self, "key path does not terminate in correct type") return unsafeBitCast(newBase, to: Value.self) } else { curBase = newBase return nil } case .break(let result): return result } } return _openExistential(valueType, do: project2) } if let result = _openExistential(curBase, do: project) { return result } } } } 42
  28. internal struct RawKeyPathComponent { ... internal func _projectReadOnly<CurValue, NewValue, LeafValue>(

    _ base: CurValue, to: NewValue.Type, endingWith: LeafValue.Type ) -> ProjectionResult<NewValue, LeafValue> { switch value { ... case .get(id: _, accessors: let accessors, argument: let argument), .mutatingGetSet(id: _, accessors: let accessors, argument: let argument), .nonmutatingGetSet(id: _, accessors: let accessors, argument: let argument): return .continue(accessors.getter()( base, argument?.data.baseAddress ?? accessors._value, argument?.data.count ?? 0) ) ... } ... } getterΛݺΜͩΓɺstoredPropertyΛऔΓग़ͨ͠Γ͢Δ 43
  29. getterݺͼग़͠ internal typealias Getter<CurValue, NewValue> = @convention(thin) (CurValue, UnsafeRawPointer, Int)

    -> NewValue accessors.getter()( base, argument?.data.baseAddress ?? accessors._value, argument?.data.count ?? 0 ) ݺͼग़͞ΕΔؔ਺ // key path getter for Foo.subscript(_:) : Foo sil shared [thunk] @$s4main3FooVyS2icipACTK : $@convention(thin) (@in_guaranteed Foo, UnsafeRawPointer) -> @out Int 44
  30. ༨ஊ: getterఆٛʹόϑΝαΠζͷύϥϝʔλ͕଍Γͯͳ͍ • ଍Γͯͳ͍ • αΠζ΍Argument Buffer͕ඞཁͳ ͍έʔεͰ͸লུ͞Εͯ͠·͏ • γάωνϟෆҰக͸WasmͰ͸

    Τϥʔ • ৗʹ3ύϥϝʔλు͘Α͏ʹύον ౰ͯͯΔ ఆٛ sil shared [thunk] @$s4main3FooVyS2icipACTK : $@convention(thin) ( @in_guaranteed Foo, // base UnsafeRawPointer // KeyPath Argument Buffer ) -> @out Int ݺͼग़͠ଆ let ret = accessors.getter()( base, argument?.data.baseAddress ?? accessors._value, argument?.data.count ?? 0 ) 45
  31. KeyPath Argument BufferͷϨΠΞ΢τ ྫ3. subscript<T>(x: T, y: Int) -> Int

    Offset Type 0 T sizeof(T) Int sizeof(T) + 8 %swift.type* T sizeof(T) + 16 witness table for T: Hashable 48
  32. ೋॏαϯΫ໰୊ key path getter͕δΣωϦοΫύϥϝʔλΛ࣋ͭ৔߹ɺLLVM IRͰ͸Ҿ਺ʹϝλλΠ ϓͱWitness Table͕౉͞ΕΔɻ // key path

    getter for Bar.subscript<A>(_:_:) : <A>BarA sil [thunk] @$s4main3BarVySix_SitcluipSHRzlACxTK : $@convention(thin) <T where T : Hashable> ( @in_guaranteed Bar, UnsafeRawPointer ) -> @out Int ↓ define swiftcc void @"$s4main3BarVySix_SitcluipSHRzlACxTK"( %TSi* noalias nocapture sret(%TSi) %0, %T4main3BarV* noalias nocapture dereferenceable(24) %1, i8* %2, %swift.type* %T, i8** %T.Hashable ) 50
  33. ೋॏαϯΫ໰୊ ϥϯλΠϜ͸୯ҰͷγάωνϟͰkey path getterΛݺͼग़͍ͨ͠ͷͰɺ δΣωϦοΫύϥϝʔλΛArgument Buffer͔ΒऔΓग़ͯ͠key path getterΛݺͼग़ ͢αϯΫΛߋʹੜ੒͢Δɻ define

    private swiftcc void @keypath_get(%TSi* noalias nocapture sret(%TSi) %0, %T4main3BarV* noalias nocapture %1, i8* %2, i64 %3) { entry: %4 = sub i64 %3, 16 %5 = getelementptr inbounds i8, i8* %2, i64 %4 %6 = bitcast i8* %5 to %swift.type** %"\CF\84_0_0" = load %swift.type*, %swift.type** %6, align 8 %7 = getelementptr inbounds %swift.type*, %swift.type** %6, i32 1 %8 = bitcast %swift.type** %7 to i8*** %"\CF\84_0_0.Hashable" = load i8**, i8*** %8, align 8 ; key path getter for Bar.subscript<A>(_:_:) : <A>BarA call swiftcc void @"$s4main3BarVySix_SitcluipSHRzlACxTK"( %TSi* noalias nocapture sret(%TSi) %0, ; return value %T4main3BarV* noalias nocapture dereferenceable(24) %1, ; base i8* %2, ; KeyPath Argument Buffer %swift.type* %"\CF\84_0_0", ; generic parameter i8** %"\CF\84_0_0.Hashable" ; Hashable witness table ) ret void } 51
  34. Flame Graph Search ic [[stack]] _.. __sw.. _swi.. $s5benchyyXEfU0_ swift_getAtKeyPath

    __swift_instantiateCanonicalPrespecializedGen.. _swift_getGen.. swift_getAtKeyPath bench _swift_getGe.. swift.. getCache $s5bench13keyPathGetteryyF s.. s.. $s.. $.. main s.. _start _swift_getGenericMetadata s.. getCache $sypSgMf swift::C.. swift_g.. $.. $ss7KeyPathC16_projectReadOnly4fromq_x_tFq_s0aB6BufferVXEfU_ __libc_start_main getC.. swift_getAtK.. __swift_instantia.. $ss19RawKeyPa.. $ss5ClockPsE7measurey8DurationQzyyKXEKF _.. __swift_inst.. swift_.. sw.. _sw.. https://gist.github.com/kateinoigakukun/d540dfdabe1948258d0860c7c6f462ee 54
  35. KeyPath଎͍ͨ͘͠ ʢίϯύΠϥͰͰ͖Δ͜ͱʣ ϥϯλΠϜؔ਺ͷݺͼग़͠Λফ͢ʢSILCombiner in SIL Optimizerʣ %kp = keypath $KeyPath<Foo,

    Int>, ( root $Foo; gettable_property $Int, id @$s4main3FooV3barSivg : $@convention(method) (Foo) -> Int, getter @$s4main3FooV3barSivpACTK : $@convention(thin) (@in_guaranteed Foo) -> @out Int ) %fn = function_ref @swift_getAtKeyPath %ret = alloc_stack $Int apply %fn<Foo, Int>(%ret, %base, %kp) ↓ // function_ref key path getter for Foo.bar : Foo %getter = function_ref @$s4main3FooV3barSivpACTK %ret = alloc_stack $Int apply %getter(%ret, %base) : $@convention(thin) (@in_guaranteed Foo) -> @out Int 55
  36. SILCombiner ͷ੍໿ • Argument BufferΛҾ਺ʹऔΔ৔߹͸࠷దԽͰ͖ͳ͍ɻ • Argument BufferͷϝϞϦϨΠΞ΢τ͸IRGenͰ૊·ΕΔͷͰɺ SIL Optimizerͷஈ֊ͰҾ਺Λ૊ΈཱͯΒΕͳ͍ͨΊɻ

    // key path getter for Bar.subscript<A>(_:_:) : <A>BarA sil [thunk] @$s4main3BarVySix_SitcluipSHRzlACxTK : $@convention(thin) <T where T : Hashable> ( @in_guaranteed Bar, UnsafeRawPointer ) -> @out Int 56