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

Deep Dive into "any" and "some"

Deep Dive into "any" and "some"

At SwiftHeroes 2023

freddi(Yuki Aki)

May 04, 2023
Tweet

More Decks by freddi(Yuki Aki)

Other Decks in Technology

Transcript

  1. Deep Dive into “any” and “some”
    Yuki ‘freddi’ Aki, LINE
    SwiftHeroes 2023
    Secrets from deep side of Swift

    View Slide

  2. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  3. ‘any’ and ‘some’ evolution
    Optimization of ‘any’ and ‘some’
    ‘any’ Mechanism with Existential Container

    View Slide

  4. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  5. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  6. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  7. some
    any

    View Slide

  8. any some
    Existential Type Opaque Type

    View Slide

  9. any some
    Existential Type Opaque Type

    View Slide

  10. Example of Existential Type

    View Slide

  11. protocol Shape {


    .
    .
    .


    }
    Example of Existential Type

    View Slide

  12. protocol Shape {


    .
    .
    .


    }
    struct Rectangle: Shape {}


    struct Triangle: Shape {}
    Example of Existential Type

    View Slide

  13. protocol Shape {


    .
    .
    .


    }
    = /* make assignable any object conform to Shape */
    Example of Existential Type
    var shape

    View Slide

  14. protocol Shape {


    .
    .
    .


    }
    : Shape = Rectangle()
    Example of Existential Type
    var shape

    View Slide

  15. protocol Shape {


    .
    .
    .


    }
    var shape: Shape = Rectangle()


    shape = Triangle()
    Example of Existential Type

    View Slide

  16. protocol Shape {


    .
    .
    .


    }
    var shape:


    shape = Triangle()
    ‘Shape’ is Existential Type
    Example of Existential Type
    Shape = Rectangle()
    Shape

    View Slide

  17. protocol Shape {


    .
    .
    .


    }
    SE-0335 Introduce existential ‘any’
    ‘any Shape’ is Existential Type
    // SE-0355, any is forced from Swift 6
    var shape:


    shape = Triangle()
    Shape = Rectangle()
    any
    any

    View Slide

  18. var shape:


    shape = Triangle()
    Existential Type’s cost
    Existential Container
    any Shape
    Shape = Rectangle()
    any

    View Slide

  19. Existential Type’s cost
    Existential Container
    any Shape
    Read value
    Call Functions
    Performance Cost
    var shape:


    shape = Triangle()
    Shape = Rectangle()
    any

    View Slide

  20. var shape: Shape
    Existential Type? Class? Structure?

    View Slide

  21. Existential Type? Class? Structure?
    // Does Shape have existential container’s cost?
    var shape: Shape

    View Slide

  22. any
    Existential Type? Class? Structure?
    // It is more clear that ‘Shape’ is existential type
    var shape: Shape

    View Slide

  23. SE-0309 Unlock existentials for all protocols
    protocol P {


    associatedtype A


    }

    View Slide

  24. let x:
    SE-0309 Unlock existentials for all protocols
    protocol P {


    associatedtype A


    }
    ~ Swift 5.6
    P = /* ... */

    View Slide

  25. SE-0309 Unlock existentials for all protocols
    protocol P {


    associatedtype A


    }
    Swift 5.7 ~
    let x: P = /* ... */
    any

    View Slide

  26. protocol P {


    associatedtype A


    }

    View Slide

  27. protocol P {


    associatedtype A


    }


    P
    Introduced by SE-0346
    “Lightweight same-type requirements for primary associated types”

    View Slide

  28. public protocol Clock: Sendable


    public protocol BidirectionalCollection: Collection


    public protocol Collection: Sequence


    public protocol RawRepresentable


    public protocol Identifiable


    public protocol InstantProtocol


    public protocol MutableCollection


    public protocol RandomAccessCollection


    public protocol RangeExpression


    public protocol RangeReplaceableCollection


    public protocol SIMD


    public protocol IteratorProtocol


    public protocol Sequence


    public protocol SetAlgebra


    public protocol Strideable
    Introduced by SE-0358
    “Primary Associated Types in the Standard Library”
    Collection

    View Slide

  29. func (_ collection: C) where C.Element == Int {


    .
    .
    .


    }


    Collection
    apply

    View Slide

  30. func apply(_ collection: AnyCollection) {


    .
    .
    .


    }


    View Slide

  31. func apply(_ collection: any Collection) {


    .
    .
    .


    }


    Introduced by SE-0353
    “Constrained Existential Types”

    View Slide

  32. protocol P {


    associatedtype A


    }


    func method(_ p: any P ) {


    .
    .
    .


    }


    extension P {


    .
    .
    .


    }
    P
    P
    Don’t need to write “P where A == Int”
    P

    View Slide

  33. protocol P {


    associatedtype A


    }


    func method(_ p: any P ) {


    .
    .
    .


    }


    extension P {


    .
    .
    .


    }


    let p: any P


    P
    P
    Don’t need to prepare hand-made type erasure
    P

    View Slide

  34. any some
    Existential Type Opaque Type

    View Slide

  35. any some
    Existential Type Opaque Type

    View Slide

  36. Existential Type’s cost
    func method(_ p:


    .
    .
    .


    }


    ‘any’ uses Existential Container
    P) {
    any

    View Slide

  37. Existential Type’s cost
    Opaque Type (‘some’) doesn’t require Existential Container


    (SE-341 Opaque Parameter Declarations)
    some
    func method(_ p:


    .
    .
    .


    }


    P) {

    View Slide

  38. Generics and Opaque Type in argument
    func method(_ p: T) {


    .
    .
    .


    }

    View Slide

  39. Generics and Opaque Type in argument
    • Generics doesn’t use Existential Container because “Keeps type information"
    func method(_ p: T) {


    .
    .
    .


    }

    View Slide

  40. Generics and Opaque Type in argument
    func method(_ p: some P) {


    .
    .
    .


    }

    • Generics doesn’t use Existential Container because “Keeps type information"
    • Opaque Type argument is syntax sugar of Generics
    func method(_ p: T) {


    .
    .
    .


    }
    swiftc

    View Slide

  41. func method() -> some P { // Opaque result type??


    .
    .
    .


    }

    View Slide

  42. Type-level abstraction
    Value-level abstraction
    Argument Return
    Protocol abstraction

    View Slide

  43. Type-level abstraction
    protocol Shape {


    .
    .
    .


    }
    func draw(shape: T) {


    .
    .
    .


    }
    • Example: Generics (argument), Opaque Type (argument and return)
    • Abstract by using concrete type information (Type variable)

    View Slide

  44. Value-level abstraction
    protocol Shape {


    .
    .
    .


    }
    var shape: any Shape = Rectangle()


    shape = Triangle()
    • Example: Existential Type (argument and return)
    • Abstract by erasing type information of value and use underlying (protocol) type

    View Slide

  45. Return
    Argument
    Existential type
    Type-level abstraction
    Value-level abstraction
    Generics
    ~ Swift 4
    Protocol abstraction

    View Slide

  46. Return
    Argument
    Existential type
    Type-level abstraction
    Value-level abstraction
    Generics
    Existential type
    ~ Swift 4
    Protocol abstraction

    View Slide

  47. ??? P


    func method


    PImpl()


    }
    () -> {

    View Slide

  48. PType


    func method


    PImpl()


    }
    () -> {

    View Slide

  49. Cannot convert return expression of type ‘P‘ to return type ‘PImpl'
    PType


    func method


    PImpl()


    }
    () -> {

    View Slide

  50. Paying cost of Existential Container
    any P


    func method


    PImpl()


    }
    () -> {

    View Slide

  51. PImpl


    func method


    PImpl()


    }
    () -> {

    View Slide

  52. PImpl


    Too opened implementation Detail
    func method


    PImpl()


    }
    () -> {

    View Slide

  53. let p = method()


    p.pImplFunc()
    No room for the developer to change the return type of the API
    Too opened implementation Detail
    PImpl


    func method


    PImpl()


    }
    () -> {

    View Slide

  54. PImpl


    func method


    PImpl()


    }
    () -> {

    View Slide

  55. -> PType


    “Reverse Generics” (not implemented)
    func method


    PImpl()


    }
    ()
    () -> {

    View Slide

  56. var p = method()
    “Reverse Generics” (not implemented)
    Treat type as “some type conforms P”
    -> PType


    func method


    PImpl()


    }
    () -> {

    View Slide

  57. var p = method()
    Introduced by SE-0244
    “Opaque Result Type”
    some P


    func method


    PImpl()


    }
    some
    () -> {

    View Slide

  58. Return
    Argument
    Existential type
    Existential type
    Type-level abstraction
    Value-level abstraction
    Generics
    Opaque Type (5.7)
    Opaque Type (5.1)
    Swift 5 (5.1 and 5.7) ~
    Protocol abstraction

    View Slide

  59. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  60. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  61. How Existential Type works?
    var shape: any Shape = Rectangle()


    shape = Triangle()

    View Slide

  62. How Existential Type works?
    var shape: any Shape = Rectangle()


    shape = Triangle()
    Existential Container
    any Shape

    View Slide

  63. How Existential Type works?
    var shape: any Shape = Rectangle()


    shape = Triangle()
    Existential Container
    any Shape

    View Slide

  64. How Existential Type works?
    var shape: any Shape = Rectangle()


    shape = Triangle()
    Existential Container
    any Shape

    View Slide

  65. struct Triangle: Shape {


    var vertex = 3


    }


    struct BigColoredRectangle: Shape {


    var color = UIColor.green


    var vertex = 4


    }

    View Slide

  66. struct Triangle: Shape {


    var vertex = 3


    }


    struct BigColoredRectangle: Shape {


    var color = UIColor.green


    var vertex = 4


    }


    print(MemoryLayout.size(ofValue: Triangle()))


    print(MemoryLayout.size(ofValue: BigColoredRectangle()))
    8
    16

    View Slide

  67. struct Triangle: Shape {


    var vertex = 3


    }


    struct BigColoredRectangle: Shape {


    var color = UIColor.green


    var vertex = 4


    }


    print(MemoryLayout.size(ofValue: Triangle()))


    print(MemoryLayout.size(ofValue: BigColoredRectangle()))


    let shape1: any Shape = Triangle()


    let shape2: any Shape = BigColoredRectangle()
    8
    16

    View Slide

  68. struct Triangle: Shape {


    var vertex = 3


    }


    struct BigColoredRectangle: Shape {


    var color = UIColor.green


    var vertex = 4


    }


    print(MemoryLayout.size(ofValue: Triangle()))


    print(MemoryLayout.size(ofValue: BigColoredRectangle()))


    let shape1: any Shape = Triangle()


    let shape2: any Shape = BigColoredRectangle()


    print(MemoryLayout.size(ofValue: shape1))


    print(MemoryLayout.size(ofValue: shape2))
    8
    16
    40
    40

    View Slide

  69. struct VeryBigRectangle: Shape {


    .
    .
    .


    }


    print(MemoryLayout.size(ofValue: VeryBigRectangle()))
    48

    View Slide

  70. struct VeryBigRectangle: Shape {


    .
    .
    .


    }


    print(MemoryLayout.size(ofValue: VeryBigRectangle()))


    let shape: any Shape = VeryBigRectangle()


    print(MemoryLayout.size(ofValue: shape))
    48
    40

    View Slide

  71. Existential Container (struct)
    Existential Container

    View Slide

  72. any Shape
    Object in Existential Container (struct)
    small struct
    var x: Int64


    var y: Int64
    Existential Container
    let shape: = Triangle()

    View Slide

  73. Object in Existential Container (struct)
    small struct
    var x: Int64


    var y: Int64
    Existential Container
    =
    40byte
    any Shape
    let shape: Triangle()

    View Slide

  74. Object in Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    small struct
    var x: Int64


    var y: Int64
    Existential Container
    =
    any Shape
    let shape: Triangle()

    View Slide

  75. Object in Existential Container (struct)
    x: Int64
    y: Int64
    Buffer(64bit)
    small struct
    var x: Int64


    var y: Int64
    Existential Container
    =
    any Shape
    let shape: Triangle()

    View Slide

  76. VeryBigRectangle()
    Object in Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    large struct
    var x: Int64


    var y: Int64


    var z: Int64


    var w: Int64
    Existential Container
    =
    any Shape
    let shape:

    View Slide

  77. Object in Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Heap
    large struct
    var x: Int64


    var y: Int64


    var z: Int64


    var w: Int64
    Existential Container
    = VeryBigRectangle()
    any Shape
    let shape:
    Reference

    View Slide

  78. Object in Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Heap
    large struct
    var x: Int64


    var y: Int64


    var z: Int64


    var w: Int64
    Existential Container
    =
    Copy
    VeryBigRectangle()
    any Shape
    let shape:
    Reference
    x: Int


    y: Int
    z: Int


    w: Int

    View Slide

  79. Object in Existential Container (struct)
    Reference
    Buffer(64bit)
    Buffer(64bit)
    Heap
    large struct
    var x: Int64


    var y: Int64


    var z: Int64


    var w: Int64
    Existential Container
    =
    Copy
    VeryBigRectangle()
    any Shape
    let shape:
    x: Int


    y: Int
    z: Int


    w: Int

    View Slide

  80. struct VeryBigRectangle: Shape {


    .
    .
    .


    }


    print(MemoryLayout.size(ofValue: VeryBigRectangle()))


    let shape: any Shape = VeryBigRectangle()


    print(MemoryLayout.size(ofValue: shape))
    40 (without Heap area size)

    View Slide

  81. Method Dispatch of Existential Container
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    Existential Container

    View Slide

  82. Method Dispatch of Existential Container
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    Existential Container
    Protocol
    Witness Table

    View Slide

  83. Witness Table
    Value Witness Table
    Protocol Witness Table

    View Slide

  84. Witness Table
    Value Witness Table
    Protocol Witness Table
    Information of Type


    Allocation, Deinitialization, size, stride …

    View Slide

  85. Witness Table
    Value Witness Table
    Protocol Witness Table
    Information for Method Dispatch (Method Table)


    Used when selecting the function of protocol
    Information of Type


    Allocation, Deinitialization, size, stride …

    View Slide

  86. Method Dispatch of Existential Container
    x: Int64
    y: Int64
    Buffer(64bit)
    small struct
    var x: Int64


    var y: Int64
    Existential Container
    =
    Protocol
    Witness Table
    any Shape
    let shape: Triangle()

    View Slide

  87. Method Dispatch of Existential Container
    x: Int64
    y: Int64
    Buffer(64bit)
    small struct
    var x: Int64


    var y: Int64


    func draw()
    Existential Container
    =
    Shape protocol
    func draw()
    Protocol
    Witness Table
    any Shape
    let shape: Triangle()

    View Slide

  88. x: Int64
    y: Int64
    Buffer(64bit)
    small struct
    var x: Int64


    var y: Int64


    func draw()
    Existential Container
    =
    Shape protocol
    func draw()
    Name Which func needs call?
    draw() Triangle.draw()
    Protocol
    Witness Table
    Method Dispatch of Existential Container
    any Shape
    let shape: Triangle()
    Triangle’s Protocol Witness Table

    View Slide

  89. x: Int64
    y: Int64
    Buffer(64bit)
    small struct
    var x: Int64


    var y: Int64


    func draw()
    Existential Container
    =
    Shape protocol
    func draw()
    Name Which func needs call?
    draw() Triangle.draw()
    Protocol
    Witness Table
    Method Dispatch of Existential Container
    any Shape
    let : Triangle()
    Triangle’s Protocol Witness Table
    shape

    View Slide

  90. x: Int64
    y: Int64
    Buffer(64bit)
    Existential Container
    .draw()
    Protocol
    Witness Table
    Method Dispatch of Existential Container
    shape

    View Slide

  91. x: Int64
    y: Int64
    Buffer(64bit)
    Existential Container
    Name Which func needs call?
    draw() Triangle.draw()
    combine() Triangle.combine()
    … …
    Protocol
    Witness Table
    Method Dispatch of Existential Container
    shape .draw()

    View Slide

  92. x: Int64
    y: Int64
    Buffer(64bit)
    Existential Container
    Protocol
    Witness Table
    Method Dispatch of Existential Container
    shape .draw()
    Name Which func needs call?
    draw() Triangle.draw()
    combine() Triangle.combine()
    … …

    View Slide

  93. Method Dispatch of Existential Type
    Dynamic Dispatch
    Static Dispatch

    View Slide

  94. Method Dispatch of Existential Type
    Dynamic Dispatch
    Static Dispatch
    Method is selected on runtime


    Existential Container (Witness Table), Class (Virtual Table),
    Objective-C Runtime (Message Dispatch) …
    Method is selected on compile time


    struct, global function call, extension …
    Runtime Cost

    View Slide

  95. Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    Existential Container
    Protocol
    Witness Table

    View Slide

  96. Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    Existential Container
    Metadata
    Protocol
    Witness Table
    Record of type information with Value Witness Table

    View Slide

  97. Existential Container (struct)
    Buffer(64bit)
    Buffer(64bit)
    Buffer(64bit)
    Existential Container
    Metadata
    Protocol
    Witness Table
    64bit + 64bit + 64bit = 192bit (24byte)
    64bit (8byte)
    8byte
    40byte

    View Slide

  98. Summary: Existential Container

    View Slide

  99. Summary: Existential Container
    • Existential Container size is fixed regardless of type (struct: 40Byte in 64bit CPU)

    View Slide

  100. Summary: Existential Container
    • Existential Container allocates heap if the object exceeds its buffer
    • Existential Container size is fixed regardless of type (struct: 40Byte in 64bit CPU)

    View Slide

  101. Summary: Existential Container
    • Existential Container size is fixed regardless of type (struct: 40Byte in 64bit CPU)
    • Existential Container allocates heap if the object exceeds its buffer
    • Existential Container method dispatch: Dynamic Dispatch

    View Slide

  102. How Existential Container actually works?

    View Slide

  103. How Existential Container actually works?
    • We can see how Existential type is initialized via Swift Intermediate Language(SIL)

    View Slide

  104. How Existential Container actually works?
    • We can see how Existential type is initialized via Swift Intermediate Language(SIL)
    AST AST
    (Typed)
    SIL IR
    (LLVM)
    📱
    App

    View Slide

  105. How Existential Container actually works?
    • We can see how Existential type is initialized via Swift Intermediate Language(SIL)
    AST AST
    (Typed)
    SIL IR
    (LLVM)
    📱
    App
    • SIL shows many codes which is hidden in normal Swift code
    • Reference Count
    • Witness Table …

    View Slide

  106. protocol Shape {


    func draw()


    }


    struct Triangle: Shape {


    func draw() {}


    }


    let shape: any Shape = Triangle()


    shape.draw()

    View Slide

  107. protocol Shape {


    func draw()


    }


    struct Triangle: Shape {


    func draw() {}


    }


    let shape: any Shape = Triangle()


    shape.draw()
    $ swiftc heroes.swift -emit-sil -o heroes.sil

    View Slide

  108. sil_stage canonical


    import Builtin


    import Swift


    import SwiftShims


    protocol Shape {


    func draw()


    }


    struct Triangle : Shape {


    func draw()


    init()


    }


    @_hasStorage @_hasInitialValue let shape: Shape { get }


    // shape


    sil_global hidden [let] @$s6heroes5shapeAA5Shape_pvp : $Shape


    // main


    sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 {


    bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>):


    alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape // users: %9, %9, %8


    %8 = witness_method $@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape, #Shape.draw : (Self) -> () -> (), %7 : $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7;
    user: %9


    %9 = apply %8<@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7


    %10 = integer_literal $Builtin.Int32, 0 // user: %11


    %11 = struct $Int32 (%10 : $Builtin.Int32) // user: %12


    return %11 : $Int32 // id: %12


    } // end sil function 'main'


    // Triangle.draw()


    sil hidden @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () {


    // %0 "self" // user: %1


    bb0(%0 : $Triangle):


    debug_value %0 : $Triangle, let, name "self", argno 1, implicit // id: %1


    %2 = tuple () // user: %3


    return %2 : $() // id: %3


    } // end sil function '$s6heroes8TriangleV4drawyyF'


    // Triangle.init()


    sil hidden @$s6heroes8TriangleVACycfC : $@convention(method) (@thin Triangle.Type) -> Triangle {


    // %0 "$metatype"


    bb0(%0 : $@thin Triangle.Type):


    %1 = struct $Triangle () // user: %2


    return %1 : $Triangle // id: %2


    } // end sil function '$s6heroes8TriangleVACycfC'


    // protocol witness for Shape.draw() in conformance Triangle


    sil private [transparent] [thunk] @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Triangle) -> () {


    // %0 // user: %1


    bb0(%0 : $*Triangle):


    %1 = load %0 : $*Triangle // user: %3


    // function_ref Triangle.draw()


    %2 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user: %3


    %3 = apply %2(%1) : $@convention(method) (Triangle) -> ()


    %4 = tuple () // user: %5


    return %4 : $() // id: %5


    } // end sil function ‘$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW’


    // Mappings from '#fileID' to '#filePath':


    // 'heroes/heroes.swift' => 'heroes.swift'
    sil_witness_table hidden Triangle: Shape module heroes {


    method #Shape.draw: (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW // protocol witness for Shape.draw() in conformance Triangle


    }

    View Slide

  109. sil_witness_table hidden Triangle: Shape module heroes {


    method #Shape.draw: (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
    }
    Protocol Witness Table of ‘Triangle’

    View Slide

  110. sil_witness_table hidden Triangle: Shape module heroes {


    method #Shape.draw: (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
    }
    Protocol Function (‘Shape.draw’)
    Protocol Witness Table of ‘Triangle’

    View Slide

  111. sil_witness_table hidden Triangle: Shape module heroes {


    method #Shape.draw: (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
    }
    Protocol Function (‘Shape.draw’) Symbol of ‘Triangle.draw’
    Protocol Witness Table of ‘Triangle’

    View Slide

  112. sil_stage canonical


    import Builtin


    import Swift


    import SwiftShims


    protocol Shape {


    func draw()


    }


    struct Triangle : Shape {


    func draw()


    init()


    }


    @_hasStorage @_hasInitialValue let shape: Shape { get }


    // shape


    sil_global hidden [let] @$s6heroes5shapeAA5Shape_pvp : $Shape


    // main


    sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 {


    bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>):




    %10 = integer_literal $Builtin.Int32, 0 // user: %11


    %11 = struct $Int32 (%10 : $Builtin.Int32) // user: %12


    return %11 : $Int32 // id: %12


    } // end sil function 'main'


    // Triangle.draw()


    sil hidden @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () {


    // %0 "self" // user: %1


    bb0(%0 : $Triangle):


    debug_value %0 : $Triangle, let, name "self", argno 1, implicit // id: %1


    %2 = tuple () // user: %3


    return %2 : $() // id: %3


    } // end sil function '$s6heroes8TriangleV4drawyyF'


    // Triangle.init()


    sil hidden @$s6heroes8TriangleVACycfC : $@convention(method) (@thin Triangle.Type) -> Triangle {


    // %0 "$metatype"


    bb0(%0 : $@thin Triangle.Type):


    %1 = struct $Triangle () // user: %2


    return %1 : $Triangle // id: %2


    } // end sil function '$s6heroes8TriangleVACycfC'


    // protocol witness for Shape.draw() in conformance Triangle


    sil private [transparent] [thunk] @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Triangle) -> () {


    // %0 // user: %1


    bb0(%0 : $*Triangle):


    %1 = load %0 : $*Triangle // user: %3


    // function_ref Triangle.draw()


    %2 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user: %3


    %3 = apply %2(%1) : $@convention(method) (Triangle) -> ()


    %4 = tuple () // user: %5


    return %4 : $() // id: %5


    } // end sil function ‘$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW’


    // Mappings from '#fileID' to '#filePath':


    // 'heroes/heroes.swift' => 'heroes.swift'
    sil_witness_table hidden Triangle: Shape module heroes {


    method #Shape.draw: (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW // protocol witness for Shape.draw() in conformance Triangle


    }
    alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape // users: %9, %9, %8


    %8 = witness_method $@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape, #Shape.draw : (Self) -> () -> (), %7 : $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7; user


    %9 = apply %8<@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7

    View Slide

  113. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to $*@opened("CA01F70C-D958-11ED-8F40-
    %8 = witness_method $@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape, #Shape.draw : %9 = apply %8<@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape>(%7) : $@convention(witness
    let shape: any Shape = Triangle()


    shape.draw()
    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8
    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@

    View Slide

  114. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()

    View Slide

  115. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    Allocating memory area for shape

    View Slide

  116. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    Call ‘Triangle’s initializer to create object
    Allocating memory area for shape

    View Slide

  117. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    Create Existential Container and assign ‘Triangle’ object
    Call ‘Triangle’s initializer to create object
    Allocating memory area for shape

    View Slide

  118. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()

    View Slide

  119. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()

    View Slide

  120. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    Load Existential Container

    View Slide

  121. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    Load Existential Container
    Get function symbol of Shape.draw from Protocol Witness Table in Existential Container

    View Slide

  122. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, #Shape.draw : (Self) -> () -> (), %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    #Shape.draw : (Self) -> () -> ()
    Load Existential Container
    Get function symbol of Shape.draw from Protocol Witness Table in Existential Container

    View Slide

  123. sil_witness_table hidden Triangle: Shape module heroes {


    method : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
    }
    Protocol Function (‘Shape.draw’) Symbol of ‘Triangle.draw’
    #Shape.draw : (Self) -> () -> () @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
    Protocol Witness Table of ‘Triangle’

    View Slide

  124. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, , %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    #Shape.draw : (Self) -> () -> ()
    Obtained: @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
    Load Existential Container
    Get function symbol of Shape.draw from Protocol Witness Table in Existential Container

    View Slide

  125. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2


    %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5


    %4 = struct $Triangle () // user: %6


    %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6


    store %4 to %5 : $*Triangle // id: %6


    %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8


    %8 = witness_method … Shape, , %7 : $
    %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
    let shape: any Shape = Triangle()


    shape.draw()
    #Shape.draw : (Self) -> () -> ()
    The function is called with selected information (Dynamic Dispatch)
    Load Existential Container
    Get function symbol of Shape.draw from Protocol Witness Table in Existential Container
    Obtained: @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW

    View Slide

  126. let shape = Triangle()


    shape.draw()

    View Slide

  127. %8 = load %3 : $*Triangle // user: %10


    // function_ref Triangle.draw()


    %9 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user:
    %10 = apply %9(%8) : $@convention(method) (Triangle) -> ()
    let shape = Triangle()


    shape.draw()

    View Slide

  128. %8 = load %3 : $*Triangle // user: %10


    // function_ref Triangle.draw()


    %9 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user:
    %10 = apply %9(%8) : $@convention(method) (Triangle) -> ()
    Function symbol ($s6heroes8TriangleV4drawyyF) is selected (Static Dispatch)
    let shape = Triangle()


    shape.draw()

    View Slide

  129. let shape: some Shape = Triangle()


    shape.draw()

    View Slide

  130. let shape: some Shape = Triangle()


    shape.draw()
    ???
    Please try:$ swiftc heroes.swift -emit-sil -o heroes.sil

    View Slide

  131. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  132. ‘any’ and ‘some’ evolution
    ‘any’ Mechanism with Existential Container
    Optimization of ‘any’ and ‘some’

    View Slide

  133. func draw(shape: T) // draw is called inside of function

    View Slide

  134. // draw(shape:)


    sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlF : .
    .
    . {


    .
    .
    .


    %11 = witness_method $T .
    .
    .

    .
    .
    .


    }
    func draw(shape: T) // draw is called inside of function
    Compile to SIL code

    View Slide

  135. // draw(shape:)


    sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlF : .
    .
    . {


    .
    .
    .


    %11 = witness_method $T .
    .
    .

    .
    .
    .


    }
    Try to check Witness Table of ‘shape’ from type variable ’T’
    func draw(shape: T) // draw is called inside of function
    Compile to SIL code

    View Slide

  136. • Generics also uses Witness Table if Swift compiler doesn’t optimize function code
    Specialization of function argument with Generics

    View Slide

  137. • Generics also uses Witness Table if Swift compiler doesn’t optimize function code
    • Optimizer in Swift compiler generates “function with specialized argument“
    Specialization of function argument with Generics

    View Slide

  138. func draw(shape: T)


    draw(shape: Triangle())

    View Slide

  139. func draw(shape: T)


    draw(shape: Triangle())
    Compile code with optimization (‘-O’ flag)
    // specialized draw(shape:)


    sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlFAA8TriangleC_Tg5

    View Slide

  140. func draw(shape: T)


    draw(shape: Triangle())
    Compile code with optimization (‘-O’ flag)
    // specialized draw(shape:)


    sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlFAA8TriangleC_Tg5
    ‘draw(shape: Triangle)’

    View Slide

  141. • Specialized function has concrete type information, and witness_table isn’t needed
    • Generics also uses Witness Table if Swift compiler doesn’t optimize code
    • Swift compiler’s optimizer generates “function with specialized argument“
    Specialization of function argument with Generics

    View Slide

  142. public func draw(shape: T)


    draw(shape: Triangle())
    Module A
    Module B
    class OriginalShape: ModuleA.Shape


    draw(shape: OriginalShape())

    View Slide

  143. public func draw(shape: T)


    draw(shape: Triangle())
    Module A
    Module B
    class OriginalShape: ModuleA.Shape


    draw(shape: OriginalShape())
    Specialized

    View Slide

  144. public func draw(shape: T)


    draw(shape: Triangle())
    Module A
    Module B
    class OriginalShape: ModuleA.Shape


    draw(shape: OriginalShape())
    Specialized
    Not-Specialized

    View Slide

  145. • Function’s argument is not specialized in some cases
    Limitation of argument Specialization

    View Slide

  146. • Different module, complex type … etc
    • Function’s argument is not specialized in some cases
    Limitation of argument Specialization

    View Slide

  147. • Use ‘@inlinable’ attribute to make specializable in outside of modules (or @_specialize)
    public func draw(shape: T)


    draw(shape: Triangle())
    @inlinable
    • Different module, complex type … etc
    • Function’s argument is not specialized in some cases
    Limitation of argument Specialization

    View Slide

  148. Specialization of ‘any’ and ‘some’ argument

    View Slide

  149. Specialization of ‘any’ and ‘some’ argument
    • ‘some’ argument still uses Witness Table as default

    View Slide

  150. • ‘some’ argument still uses Witness Table as default
    • ‘any’ and ‘some’ argument are also specialized if possible
    Specialization of ‘any’ and ‘some’ argument

    View Slide

  151. func draw(shape: any Shape)


    draw(shape: Triangle())

    View Slide

  152. // specialized draw(shape:)


    sil hidden @$s6heroes4draw5shapeyAA5Shape_p_..._nAA8TriangleC_Tg5
    func draw(shape: any Shape)


    draw(shape: Triangle())
    Compile code with optimization (‘-O’ flag)
    ‘draw(shape: Triangle)’

    View Slide

  153. • ‘some’ still uses Witness Table as default
    • ‘any’ and ‘some’ argument are also specialized if possible
    Specialization of ‘some’ and ‘any’ argument

    View Slide

  154. • ‘some’ still uses Witness Table as default
    • ‘any’ and ‘some’ argument are also specialized if possible
    Specialization of ‘some’ and ‘any’ argument
    • Use appropriate ‘some’ and ‘any’ regardless of specialization
    any
    some YES
    YES NO (inline only)
    YES
    Same module Different module(inlinable)
    Specialization of some an any

    View Slide

  155. Summary: Optimization

    View Slide

  156. Summary: Optimization
    • Existential Type, Opaque Type and Generics are “specialized” by compiler as possible

    View Slide

  157. Summary: Optimization
    • Existential Type, Opaque Type and Generics are “specialized” by compiler as possible
    • Use appropriate ‘some’ and ‘any’ as possible regardless of specialization

    View Slide

  158. Recap

    View Slide

  159. Recap
    • Existential type and Opaque type evolution

    View Slide

  160. Recap
    • Existential type and Opaque type evolution
    • Existential Container and Performance

    View Slide

  161. Recap
    • Existential type and Opaque type evolution
    • Existential Container and Performance
    • Specialization for Generics, Existential type and Opaque type

    View Slide

  162. Learn more evolution
    SE-0360 Opaque result types with limited availability apple/swift-evolution
    SE-0375 Opening existential arguments to optional parameters
    SE-0352 Implicitly Opened Existentials
    SE-0328 Structural opaque result types
    apple/swift-evolution
    apple/swift-evolution
    apple/swift-evolution

    View Slide

  163. Learn API Design with ‘any’ and ‘some’
    Embrace Swift generics WWDC 22
    Design protocol interfaces in Swift WWDC 22

    View Slide

  164. Other Talks
    Generic Protocols and Associated types - POWER TOOLS FOR UNIT TESTING SwiftHeroes 2023
    Swift Optimizing (at Compiler World) NSSpain 2020

    View Slide

  165. Thank you!
    Yuki ‘freddi’ Aki - iOS Engineer at LINE
    • Github @ freddi-kit
    • Twitter @ _ _ _freddi_ _ _
    Special Thanks
    @koher (Author of “Heart of Swift” https://heart-of-swift.github.io/)
    @kateinoigakukun (Swift Compiler specialist)


    Engineers and Interpreters in LINE Corp


    SwiftHeroes Team

    View Slide