Slide 1

Slide 1 text

SwiftͷmodifyΞΫηαͱί ϧʔνϯ Θ͍Θ͍swiftc #17 @omochimetaru 1

Slide 2

Slide 2 text

Forum: [Pitch] Modify Accessors 1 1 https://forums.swift.org/t/modify-accessors/31872 2

Slide 3

Slide 3 text

3

Slide 4

Slide 4 text

ಈػ • get/set͸஗͍ 4

Slide 5

Slide 5 text

struct GetSet { var storage: String = "" var property: String { get { return storage } set { storage = newValue } } } 5

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

஗͍ཧ༝ • a.property.appendͱ͍͏ࣜ͸ɺԼهͷखॱ ʹίϯύΠϧ͞ΕΔɻ var temp = a.property temp.append(text) a.property = temp • ࡶʹݴ͑͹ɺແବͳίϐʔ͕ੜ͍ͯ͡ΔͷͰ஗ ͍ɻ 8

Slide 9

Slide 9 text

• ਖ਼֬ʹ͸ɺίϐʔࣗମ͸CoW͕ޮ͘ͷͰίε τ͸͔͔Βͳ͍ɻ • ͔͠͠ɺgetͯ͠࡞ͬͨςϯϙϥϦίϐʔͷ Stringͷຊମ͸ɺa.storage͔Β΋ࢀর͞Ε ͍ͯΔͨΊϢχʔΫͳࢀরʹͳΒͳ͍ɻ • ϢχʔΫͳࢀরͰ͸ͳ͍ͷͰɺappendͷλΠ ϛϯάͰશจίϐʔ͕ੜ͡Δɻ • CoW͸ສೳͰ͸ͳ͘ɺίϐʔͷϖφϧςΟ͕ ݦݱ͢Δࣄ͕͋Δɻ 9

Slide 10

Slide 10 text

SILͰ֬ೝ func main() { var a = GetSet() a.property.append("str") } • $ swiftc -emit-sil c2.swift 10

Slide 11

Slide 11 text

%12 = alloc_stack $String // users: %18, %15, %22, %17 // function_ref GetSet.property.getter %13 = function_ref @$s2c26GetSetV8propertySSvg : $@convention(method) (@guaranteed GetSet) -> @owned String // user: %14 %14 = apply %13(%3) : $@convention(method) (@guaranteed GetSet) -> @owned String // user: %15 store %14 to %12 : $*String // id: %15 // function_ref String.append(_:) %16 = function_ref @$sSS6appendyySSF : $@convention(method) (@guaranteed String, @inout String) -> () // user: %17 %17 = apply %16(%10, %12) : $@convention(method) (@guaranteed String, @inout String) -> () %18 = load %12 : $*String // user: %20 // function_ref GetSet.property.setter %19 = function_ref @$s2c26GetSetV8propertySSvs : $@convention(method) (@owned String, @inout GetSet) -> () // user: %20 %20 = apply %19(%18, %11) : $@convention(method) (@owned String, @inout GetSet) -> () 11

Slide 12

Slide 12 text

stored propertyͷ৔߹ func main() { var a = GetSet() a.storage.append("str") } %12 = struct_element_addr %11 : $*GetSet, #GetSet.storage // user: %14 // function_ref String.append(_:) %13 = function_ref @$sSS6appendyySSF : $@convention(method) (@guaranteed String, @inout String) -> () // user: %14 %14 = apply %13(%10, %12) : $@convention(method) (@guaranteed String, @inout String) -> () 12

Slide 13

Slide 13 text

ఏҊ • modifyΞΫηα struct GetModify { var storage: String = "" var property: String { get { return storage } modify { yield &storage } } } 13

Slide 14

Slide 14 text

• modifyΞΫηα͸setͱҟͳΓɺؔ਺Ͱ͸ͳ ͘ɺίϧʔνϯ • ίϧʔνϯͷຊจͰ͸yieldจ͕࢖͑Δ 14

Slide 15

Slide 15 text

ίϧʔνϯ 15

Slide 16

Slide 16 text

2016 LLVM Developers’ Meeting: G. Nishanov “LLVM Coroutines” 6 • llvmͷcoroͷ঺հ 6 https://youtu.be/Ztr8QvMhqmQ 16

Slide 17

Slide 17 text

17

Slide 18

Slide 18 text

modifyίϧʔνϯ • yieldͰ͸ϓϩύςΟͷܕͷinoutࢀরΛฦ͢ (ؔ਺ͷreturnͰ͸Ͱ͖ͳ͍ࣄ) • ࢀরΛฦ͢ࣄͰίϐʔΛճආͯ͠ߴ଎Խ͢Δ 18

Slide 19

Slide 19 text

• ςϯϙϥϦίϐʔͰget/set͢Δ୅ΘΓʹɺ modifyΞΫηαΛ࣮ߦɺฦ͖ͬͯͨࢀরΛͦ ͷ··ར༻͠ɺmodifyΞΫηαΛ࠶։͢Δɻ // ಈ࡞Πϝʔδ let coro = a.property inout temp = coro.resume() temp.append("str") coro.resume() 19

Slide 20

Slide 20 text

yieldͱલޙॲཧ struct GetModify { var storage: String = "" var property: String { get { return storage } modify { print("before yield") yield &storage print("after yield") } } } var a = GetModify() print("before append") a.property.append("str") print("after append") /* before append before yield after yield after append */ 20

Slide 21

Slide 21 text

• modifyίϧʔνϯ͸औಘͨ͠ࢀরΛ࢖༻ͯ͠ ͍Δؒ͸தஅ͍ͯͯ͠ɺ࢖༻͕ऴΘͬͨΒ࠶։ ͔ͯ͠Βऴྃ͞ΕΔ 21

Slide 22

Slide 22 text

ίϧʔνϯͱίϯςΩετ struct GetModify { var num: Int? var property: String { get { return "" } modify { var str = num?.description ?? "" yield &str self.num = Int(str) } } } var a = GetModify() a.property.append("1") print(a.num ?? 0) // => 1 a.property.append("2") print(a.num ?? 0) // => 12 a.property.append("a") print(a.num ?? 0) // => 0 22

Slide 23

Slide 23 text

• ίϧʔνϯ͕தஅ͍ͯͯ͠΋ɺίϧʔνϯ͕ऴ ྃ͢Δ·Ͱ͸ίϯςΩετ(είʔϓ)͕ੜ͖ͯ ͍ΔͷͰɺϩʔΧϧม਺΁ͷࢀরΛฦ٫Ͱ͖Δ 23

Slide 24

Slide 24 text

ͦͷଞͷίϧʔνϯ 24

Slide 25

Slide 25 text

Generator • Pitchʹࡌ͍ͬͯΔྫ extension Array: MutableGenerator { func generateMutably() { for i in 0..

Slide 26

Slide 26 text

• OwnershipManifesto2ʹࡌ͍ͬͯΔྫ mutating generator iterateMutable() -> inout Element { var i = startIndex, e = endIndex while i != e { yield &self[i] self.formIndex(after: &i) } } for inout employee in company.employees { employee.respected = true } 2 OwnershipManifesto 26

Slide 27

Slide 27 text

Async/Await • Asnyc/Awaitϓϩϙʔβϧυϥϑτ3͕ίϧʔ νϯͷಋೖΛఏҊ͍ͯ͠Δ 3 Async/Await for Swift 27

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

29

Slide 30

Slide 30 text

func loadWebResource(_ path: String) async -> Resource func decodeImage(_ r1: Resource, _ r2: Resource) async -> Image func dewarpAndCleanupImage(_ i : Image) async -> Image func processImageData1() async -> Image { let dataResource = await loadWebResource("dataprofile.txt") let imageResource = await loadWebResource("imagedata.dat") let imageTmp = await decodeImage(dataResource, imageResource) let imageResult = await dewarpAndCleanupImage(imageTmp) return imageResult } 30

Slide 31

Slide 31 text

31

Slide 32

Slide 32 text

modifyͷதࢭ(abort) 32

Slide 33

Slide 33 text

modifyͷதࢭ • ίϧʔνϯͷ࣮ߦதʹݺͼग़͠ଆͰྫ֎͕ൃੜ ͨ͠৔߹ɺίϧʔνϯ͕தࢭ͞ΕɺyieldΑΓ ޙଓͷॲཧʹ໭Βͳ͍ func storeText(to str: inout String) throws { ... } var a = GetModify() try storeText(to: &a.property) 33

Slide 34

Slide 34 text

• yieldΑΓલʹॻ͍ͨdefer͸தࢭͨ͠৔߹Ͱ ΋࣮ߦ͞ΕΔ 34

Slide 35

Slide 35 text

extension Array { var first: Element? { get { isEmpty ? nil : self[0] } modify { var tmp: Optional = nil if isEmpty { tmp = nil yield &tmp if let newValue = tmp { self.append(newValue) } } else { // put array into temporarily invalid state tmp = _storage.move() // code to restore valid state put in defer defer { if let newValue = tmp { _storage.initialize(to: newValue) } else { _storage.moveInitialize(from: _storage + 1, count: self.count - 1) self.count -= 1 } } // yield that moved value to the caller yield &tmp } } } 35

Slide 36

Slide 36 text

modifyͷ࣮૷ 36

Slide 37

Slide 37 text

llvm.coro • SILʹ΋ίϧʔνϯ͕࣮૷͞ΕΔ • LLVM-IRʹ͓͍ͯɺίϧʔνϯΛ࣮ݱ͢Δ௨ৗ ͷίʔυʹLower͢Δ • llvm.coroܥ໋ྩ 37

Slide 38

Slide 38 text

2ͭͷLowering • Switched-Resume Lowering: ࠷ॳʹC++޲͚ ʹ࡞ΒΕ࣮ͨ૷ • Returned-Continuation Lowering: Swift޲͚ʹ ࡞ΒΕ࣮ͨ૷ 38

Slide 39

Slide 39 text

Switched-Resume Lowering 39

Slide 40

Slide 40 text

Returned-Continuation Lowering • 2018 LLVM Developers’ Meeting: J. McCall “Coroutine Representations and ABIs in LLVM”7 • yield͝ͱʹؔ਺Λ෼͚ͯɺʮ࣍ͷؔ਺ʯΛฦ Γ஋ͱҰॹʹฦ͢ • ίϛοτ8 8 https://github.com/apple/swift-llvm/commit/eb7e7fd4f6c0d4c4c97879d9439d82f7eadde324 7 https://youtu.be/wyAbV8AM9PM 40

Slide 41

Slide 41 text

LLVM-IR ৄࡉ • Coroutines in LLVM5 5 Coroutines in LLVM 41

Slide 42

Slide 42 text

modifyΛࢼ͢ 42

Slide 43

Slide 43 text

• modify͸_modifyΩʔϫʔυͰ࣮૷ࡁΈ • ඪ४ϥΠϒϥϦͰطʹ࢖༻͍ͯ͠Δ 43

Slide 44

Slide 44 text

• Array.swift4 extension Array: RandomAccessCollection, MutableCollection { @inlinable public subscript(index: Int) -> Element { get { // This call may be hoisted or eliminated by the optimizer. If // there is an inout violation, this value may be stale so needs to be // checked again below. let wasNativeTypeChecked = _hoistableIsNativeTypeChecked() // Make sure the index is in range and wasNativeTypeChecked is // still valid. let token = _checkSubscript( index, wasNativeTypeChecked: wasNativeTypeChecked) return _getElement( index, wasNativeTypeChecked: wasNativeTypeChecked, matchingSubscriptCheck: token) } _modify { _makeMutableAndUnique() // makes the array native, too _checkSubscript_native(index) let address = _buffer.subscriptBaseAddress + index yield &address.pointee } } } 4 stdlib/public/core/Array.swift 44

Slide 45

Slide 45 text

SIL struct GetModify { var storage: String = "" var property: String { get { return storage } _modify { yield &storage } } } func main() { var a = GetModify() a.property.append("str") } 45

Slide 46

Slide 46 text

// GetModify.property.modify sil hidden @$s2c59GetModifyV8propertySSvM : $@yield_once @convention(method) (@inout GetModify) -> @yields @inout String { // %0 // users: %2, %1 bb0(%0 : $*GetModify): debug_value_addr %0 : $*GetModify, var, name "self", argno 1 // id: %1 %2 = begin_access [modify] [static] %0 : $*GetModify // users: %5, %8, %3 %3 = struct_element_addr %2 : $*GetModify, #GetModify.storage // user: %4 yield %3 : $*String, resume bb1, unwind bb2 // id: %4 bb1: // Preds: bb0 end_access %2 : $*GetModify // id: %5 %6 = tuple () // user: %7 return %6 : $() // id: %7 bb2: // Preds: bb0 end_access %2 : $*GetModify // id: %8 unwind // id: %9 } // end sil function '$s2c59GetModifyV8propertySSvM' 46

Slide 47

Slide 47 text

%12 = function_ref @$s2c59GetModifyV8propertySSvM : $@yield_once @convention(method) (@inout GetModify) -> @yields @inout String // user: %13 (%13, %14) = begin_apply %12(%11) : $@yield_once @convention(method) (@inout GetModify) -> @yields @inout String // users: %16, %17 // function_ref String.append(_:) %15 = function_ref @$sSS6appendyySSF : $@convention(method) (@guaranteed String, @inout String) -> () // user: %16 %16 = apply %15(%10, %13) : $@convention(method) (@guaranteed String, @inout String) -> () end_apply %14 // id: %17 47

Slide 48

Slide 48 text

தࢭͷॲཧ func main() { var a = GetModify() try! storeText(to: &a.property) } 48

Slide 49

Slide 49 text

// function_ref GetModify.property.modify %6 = function_ref @$s2c59GetModifyV8propertySSvM : $@yield_once @convention(method) (@inout GetModify) -> @yields @inout String // user: %7 (%7, %8) = begin_apply %6(%5) : $@yield_once @convention(method) (@inout GetModify) -> @yields @inout String // users: %10, %12, %19 // function_ref storeText(to:) %9 = function_ref @$s2c59storeText2toySSz_tKF : $@convention(thin) (@inout String) -> @error Error // user: %10 try_apply %9(%7) : $@convention(thin) (@inout String) -> @error Error, normal bb1, error bb2 // id: %10 bb1(%11 : $()): // Preds: bb0 end_apply %8 // id: %12 ... // %18 // user: %27 bb2(%18 : $Error): // Preds: bb0 abort_apply %8 // id: %19 49

Slide 50

Slide 50 text

LLVM (ίϧʔνϯม׵લ) • ṖͷΦϓγϣϯΛ༩͑ΔͱίϧʔνϯΛLower ͍ͯ͠ͳ͍LLVM͕ग़Δ • $ swiftc -emit-ir -Xfrontend -disable- swift-specific-llvm-optzns c5.swift • ͜ΕΛ΍ΔͱLLVM-IR͸ग़ͤΔ͕όΠφϦ͸ੜ ੒Ͱ͖ͳ͍(ඞਢม׵) 50

Slide 51

Slide 51 text

; Function Attrs: noinline define hidden swiftcc { i8*, %TSS* } @"$s2c59GetModifyV8propertySSvM"( i8* noalias dereferenceable(32), %T2c59GetModifyV* nocapture swiftself dereferenceable(16)) #1 { entry: %2 = call token @llvm.coro.id.retcon.once( i32 32, i32 8, i8* %0, i8* bitcast (void (i8*, i1)* @"$s2c59GetModifyVIetMl_TC" to i8*), i8* bitcast (i8* (i64)* @malloc to i8*), i8* bitcast (void (i8*)* @free to i8*)) %3 = call i8* @llvm.coro.begin(token %2, i8* null) %.storage = getelementptr inbounds %T2c59GetModifyV, %T2c59GetModifyV* %1, i32 0, i32 0 %4 = call i1 (...) @llvm.coro.suspend.retcon.i1(%TSS* %.storage) br i1 %4, label %6, label %5 ; :5: ; preds = %entry br label %coro.end ; :6: ; preds = %entry br label %coro.end coro.end: ; preds = %5, %6 %7 = call i1 @llvm.coro.end(i8* %3, i1 false) unreachable } 51

Slide 52

Slide 52 text

• llvm.coroܥ໋ྩ͕ग़ͯ͘Δ • γάωνϟͰ͸selfʹՃ͑ͯɺṖͷ32όΠτ ͷϝϞϦΛҾ਺ʹड͚͍ͯΔ • coro.suspendͷฦΓ஋ͱͯ͠i1Λ࢖͍ɺ resume/abortΛ۠ผ͍ͯ͠Δ 52

Slide 53

Slide 53 text

%2 = alloca [32 x i8], align 8 ... %10 = getelementptr inbounds [32 x i8], [32 x i8]* %2, i32 0, i32 0 call void @llvm.lifetime.start.p0i8(i64 32, i8* %10) %11 = call i8* @llvm.coro.prepare.retcon( i8* bitcast ({ i8*, %TSS* } (i8*, %T2c59GetModifyV*)* @"$s2c59GetModifyV8propertySSvM" to i8*)) %12 = bitcast i8* %11 to { i8*, %TSS* } (i8*, %T2c59GetModifyV*)* %13 = call swiftcc { i8*, %TSS* } %12( i8* noalias dereferenceable(32) %10, %T2c59GetModifyV* nocapture swiftself dereferenceable(16) %0) %14 = extractvalue { i8*, %TSS* } %13, 0 %15 = extractvalue { i8*, %TSS* } %13, 1 call swiftcc void @"$sSS6appendyySSF"( i64 %8, %swift.bridge* %9, %TSS* nocapture swiftself dereferenceable(16) %15) %16 = bitcast i8* %14 to void (i8*, i1)* call swiftcc void %16(i8* noalias dereferenceable(32) %10, i1 false) call void @llvm.lifetime.end.p0i8(i64 32, i8* %10) 53

Slide 54

Slide 54 text

• coro.prepare.retcon͕ؔ਺ϙΠϯλ(%11, %12)Λฦ͢ • ͦΕΛݺͼग़͢ͱɺ·ͨؔ਺ϙΠϯλ(%13, %14)Λฦ͢ • ͦΕΛݺͼग़͢(%14, %16)ͱ͖ʹresumeΛҙຯ ͢ΔfalseΛ౉͢ 54

Slide 55

Slide 55 text

LLVM • ී௨ʹ΍Δͱίϧʔνϯ͕Lower͞Εͨ΋ͷ͕ ग़Δɻ • $ swiftc -emit-ir c5.swift 55

Slide 56

Slide 56 text

%"$s2c59GetModifyV8propertySSvM.Frame" = type {} ; Function Attrs: noinline define hidden swiftcc { i8*, %TSS* } @"$s2c59GetModifyV8propertySSvM"( i8* noalias dereferenceable(32), %T2c59GetModifyV* nocapture swiftself dereferenceable(16)) #1 { entry: %.storage = getelementptr inbounds %T2c59GetModifyV, %T2c59GetModifyV* %1, i32 0, i32 0 %2 = insertvalue { i8*, %TSS* } { i8* bitcast (void (i8*, i1)* @"$s2c59GetModifyV8propertySSvM.resume.0" to i8*), %TSS* undef }, %TSS* %.storage, 1 ret { i8*, %TSS* } %2 } define internal swiftcc void @"$s2c59GetModifyV8propertySSvM.resume.0"( i8* noalias nonnull dereferenceable(32), i1) #0 { entryresume.0: %FramePtr = bitcast i8* %0 to %"$s2c59GetModifyV8propertySSvM.Frame"* %vFrame = bitcast %"$s2c59GetModifyV8propertySSvM.Frame"* %FramePtr to i8* ret void } 56

Slide 57

Slide 57 text

• ؔ਺resume.0͕ੜ੒͞ΕɺΞΫηα͸͜Εͱ ετϨʔδϙΠϯλΛฦ͢ • resume.0ͷ࣮૷͔Βɺ32όΠτͷϝϞϦ͸ϑ ϨʔϜϝϞϦ΁ͷϙΠϯλͩͱΘ͔Δ • resume.0ͷୈ2Ҿ਺͸resumeϑϥά 57

Slide 58

Slide 58 text

%2 = alloca [32 x i8], align 8 ... %10 = getelementptr inbounds [32 x i8], [32 x i8]* %2, i32 0, i32 0 call void @llvm.lifetime.start.p0i8(i64 32, i8* %10) %11 = call swiftcc { i8*, %TSS* } @"$s2c59GetModifyV8propertySSvM"( i8* noalias dereferenceable(32) %10, %T2c59GetModifyV* nocapture swiftself dereferenceable(16) %0) %12 = extractvalue { i8*, %TSS* } %11, 0 %13 = extractvalue { i8*, %TSS* } %11, 1 call swiftcc void @"$sSS6appendyySSF"( i64 %8, %swift.bridge* %9, %TSS* nocapture swiftself dereferenceable(16) %13) %14 = bitcast i8* %12 to void (i8*, i1)* call swiftcc void %14(i8* noalias dereferenceable(32) %10, i1 false) call void @llvm.lifetime.end.p0i8(i64 32, i8* %10) 58

Slide 59

Slide 59 text

• ΞΫηαΛݺͿͱؔ਺ϙΠϯλͱετϨʔδϙ Πϯλ͕ฦͬͯ͘Δ • ؔ਺ϙΠϯλʹresumeϑϥάΛ౉ͯ͠ݺΜͰ ऴΘΓ 59

Slide 60

Slide 60 text

60