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

Swiftのmodifyアクセサとコルーチン

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

 Swiftのmodifyアクセサとコルーチン

Avatar for omochimetaru

omochimetaru

January 17, 2020
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. 3

  2. struct GetSet { var storage: String = "" var property:

    String { get { return storage } set { storage = newValue } } } 5
  3. 6

  4. 7

  5. %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
  6. 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
  7. ఏҊ • modifyΞΫηα struct GetModify { var storage: String =

    "" var property: String { get { return storage } modify { yield &storage } } } 13
  8. 2016 LLVM Developers’ Meeting: G. Nishanov “LLVM Coroutines” 6 •

    llvmͷcoroͷ঺հ 6 https://youtu.be/Ztr8QvMhqmQ 16
  9. 17

  10. 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
  11. ίϧʔνϯͱίϯςΩετ 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
  12. Generator • Pitchʹࡌ͍ͬͯΔྫ extension Array: MutableGenerator { func generateMutably() {

    for i in 0..<count { yield &self[i] } } } for &x in myArray { x += 1 } 25
  13. • 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
  14. 28

  15. 29

  16. 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
  17. 31

  18. extension Array { var first: Element? { get { isEmpty

    ? nil : self[0] } modify { var tmp: Optional<Element> = 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
  19. 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
  20. • 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
  21. 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
  22. // 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
  23. %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
  24. // 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
  25. LLVM (ίϧʔνϯม׵લ) • ṖͷΦϓγϣϯΛ༩͑ΔͱίϧʔνϯΛLower ͍ͯ͠ͳ͍LLVM͕ग़Δ • $ swiftc -emit-ir -Xfrontend

    -disable- swift-specific-llvm-optzns c5.swift • ͜ΕΛ΍ΔͱLLVM-IR͸ग़ͤΔ͕όΠφϦ͸ੜ ੒Ͱ͖ͳ͍(ඞਢม׵) 50
  26. ; 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 ; <label>:5: ; preds = %entry br label %coro.end ; <label>:6: ; preds = %entry br label %coro.end coro.end: ; preds = %5, %6 %7 = call i1 @llvm.coro.end(i8* %3, i1 false) unreachable } 51
  27. %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
  28. %"$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
  29. %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
  30. 60