Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

some any

Slide 8

Slide 8 text

any some Existential Type Opaque Type

Slide 9

Slide 9 text

any some Existential Type Opaque Type

Slide 10

Slide 10 text

Example of Existential Type

Slide 11

Slide 11 text

protocol Shape { . . . } Example of Existential Type

Slide 12

Slide 12 text

protocol Shape { . . . } struct Rectangle: Shape {} struct Triangle: Shape {} Example of Existential Type

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

protocol Shape { . . . } var shape: Shape = Rectangle() shape = Triangle() Example of Existential Type

Slide 16

Slide 16 text

protocol Shape { . . . } var shape: shape = Triangle() ‘Shape’ is Existential Type Example of Existential Type Shape = Rectangle() Shape

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Existential Type’s cost Existential Container any Shape Read value Call Functions Performance Cost var shape: shape = Triangle() Shape = Rectangle() any

Slide 20

Slide 20 text

var shape: Shape Existential Type? Class? Structure?

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

SE-0309 Unlock existentials for all protocols protocol P { associatedtype A }

Slide 24

Slide 24 text

let x: SE-0309 Unlock existentials for all protocols protocol P { associatedtype A } ~ Swift 5.6 P = /* ... */

Slide 25

Slide 25 text

SE-0309 Unlock existentials for all protocols protocol P { associatedtype A } Swift 5.7 ~ let x: P = /* ... */ any

Slide 26

Slide 26 text

protocol P { associatedtype A }

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

func (_ collection: C) where C.Element == Int { . . . } Collection apply

Slide 30

Slide 30 text

func apply(_ collection: AnyCollection) { . . . }

Slide 31

Slide 31 text

func apply(_ collection: any Collection) { . . . } Introduced by SE-0353 “Constrained Existential Types”

Slide 34

Slide 34 text

any some Existential Type Opaque Type

Slide 35

Slide 35 text

any some Existential Type Opaque Type

Slide 36

Slide 36 text

Existential Type’s cost func method(_ p: . . . } ‘any’ uses Existential Container P) { any

Slide 37

Slide 37 text

Existential Type’s cost Opaque Type (‘some’) doesn’t require Existential Container (SE-341 Opaque Parameter Declarations) some func method(_ p: . . . } P) {

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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)

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

??? P func method PImpl() } () -> {

Slide 48

Slide 48 text

PType func method PImpl() } () -> {

Slide 49

Slide 49 text

Cannot convert return expression of type ‘P‘ to return type ‘PImpl' PType func method PImpl() } () -> {

Slide 50

Slide 50 text

Paying cost of Existential Container any P func method PImpl() } () -> {

Slide 51

Slide 51 text

PImpl func method PImpl() } () -> {

Slide 52

Slide 52 text

PImpl Too opened implementation Detail func method PImpl() } () -> {

Slide 53

Slide 53 text

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() } () -> {

Slide 54

Slide 54 text

PImpl func method PImpl() } () -> {

Slide 55

Slide 55 text

-> PType “Reverse Generics” (not implemented) func method PImpl() } () () -> {

Slide 56

Slide 56 text

var p = method() “Reverse Generics” (not implemented) Treat type as “some type conforms P” -> PType func method PImpl() } () -> {

Slide 57

Slide 57 text

var p = method() Introduced by SE-0244 “Opaque Result Type” some P func method PImpl() } some () -> {

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

How Existential Type works? var shape: any Shape = Rectangle() shape = Triangle() Existential Container any Shape

Slide 63

Slide 63 text

How Existential Type works? var shape: any Shape = Rectangle() shape = Triangle() Existential Container any Shape

Slide 64

Slide 64 text

How Existential Type works? var shape: any Shape = Rectangle() shape = Triangle() Existential Container any Shape

Slide 65

Slide 65 text

struct Triangle: Shape { var vertex = 3 } struct BigColoredRectangle: Shape { var color = UIColor.green var vertex = 4 }

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

struct VeryBigRectangle: Shape { . . . } print(MemoryLayout.size(ofValue: VeryBigRectangle())) 48

Slide 70

Slide 70 text

struct VeryBigRectangle: Shape { . . . } print(MemoryLayout.size(ofValue: VeryBigRectangle())) let shape: any Shape = VeryBigRectangle() print(MemoryLayout.size(ofValue: shape)) 48 40

Slide 71

Slide 71 text

Existential Container (struct) Existential Container

Slide 72

Slide 72 text

any Shape Object in Existential Container (struct) small struct var x: Int64 var y: Int64 Existential Container let shape: = Triangle()

Slide 73

Slide 73 text

Object in Existential Container (struct) small struct var x: Int64 var y: Int64 Existential Container = 40byte any Shape let shape: Triangle()

Slide 74

Slide 74 text

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()

Slide 75

Slide 75 text

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()

Slide 76

Slide 76 text

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:

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

struct VeryBigRectangle: Shape { . . . } print(MemoryLayout.size(ofValue: VeryBigRectangle())) let shape: any Shape = VeryBigRectangle() print(MemoryLayout.size(ofValue: shape)) 40 (without Heap area size)

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Witness Table Value Witness Table Protocol Witness Table

Slide 84

Slide 84 text

Witness Table Value Witness Table Protocol Witness Table Information of Type Allocation, Deinitialization, size, stride …

Slide 85

Slide 85 text

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 …

Slide 86

Slide 86 text

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()

Slide 87

Slide 87 text

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()

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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()

Slide 92

Slide 92 text

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() … …

Slide 93

Slide 93 text

Method Dispatch of Existential Type Dynamic Dispatch Static Dispatch

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

Summary: Existential Container

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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)

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

How Existential Container actually works?

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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 …

Slide 106

Slide 106 text

protocol Shape { func draw() } struct Triangle: Shape { func draw() {} } let shape: any Shape = Triangle() shape.draw()

Slide 107

Slide 107 text

protocol Shape { func draw() } struct Triangle: Shape { func draw() {} } let shape: any Shape = Triangle() shape.draw() $ swiftc heroes.swift -emit-sil -o heroes.sil

Slide 108

Slide 108 text

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 }

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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’

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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 : (%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> (@

Slide 114

Slide 114 text

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()

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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()

Slide 119

Slide 119 text

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()

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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’

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

let shape = Triangle() shape.draw()

Slide 127

Slide 127 text

%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()

Slide 128

Slide 128 text

%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()

Slide 129

Slide 129 text

let shape: some Shape = Triangle() shape.draw()

Slide 130

Slide 130 text

let shape: some Shape = Triangle() shape.draw() ??? Please try:$ swiftc heroes.swift -emit-sil -o heroes.sil

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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

Slide 133

Slide 133 text

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

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

• 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

Slide 138

Slide 138 text

func draw(shape: T) draw(shape: Triangle())

Slide 139

Slide 139 text

func draw(shape: T) draw(shape: Triangle()) Compile code with optimization (‘-O’ flag) // specialized draw(shape:) sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlFAA8TriangleC_Tg5

Slide 140

Slide 140 text

func draw(shape: T) draw(shape: Triangle()) Compile code with optimization (‘-O’ flag) // specialized draw(shape:) sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlFAA8TriangleC_Tg5 ‘draw(shape: Triangle)’

Slide 141

Slide 141 text

• 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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

public func draw(shape: T) draw(shape: Triangle()) Module A Module B class OriginalShape: ModuleA.Shape draw(shape: OriginalShape()) Specialized

Slide 144

Slide 144 text

public func draw(shape: T) draw(shape: Triangle()) Module A Module B class OriginalShape: ModuleA.Shape draw(shape: OriginalShape()) Specialized Not-Specialized

Slide 145

Slide 145 text

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

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

• 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

Slide 148

Slide 148 text

Specialization of ‘any’ and ‘some’ argument

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

func draw(shape: any Shape) draw(shape: Triangle())

Slide 152

Slide 152 text

// 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)’

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

• ‘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

Slide 155

Slide 155 text

Summary: Optimization

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

Recap

Slide 159

Slide 159 text

Recap • Existential type and Opaque type evolution

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

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