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

Swift KeyPath Internals

Avatar for Yuta Saito Yuta Saito
October 04, 2022

Swift KeyPath Internals

Avatar for Yuta Saito

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