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

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

3781f49ea2c76d6ecf0c6cda46096d49?s=128

omochimetaru

January 17, 2020
Tweet

Transcript

  1. 3.

    3

  2. 5.

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

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

    6

  4. 7.

    7

  5. 11.

    %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. 12.

    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. 13.

    ఏҊ • modifyΞΫηα struct GetModify { var storage: String =

    "" var property: String { get { return storage } modify { yield &storage } } } 13
  8. 16.

    2016 LLVM Developers’ Meeting: G. Nishanov “LLVM Coroutines” 6 •

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

    17

  10. 20.

    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. 22.

    ίϧʔνϯͱίϯςΩετ 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. 25.

    Generator • Pitchʹࡌ͍ͬͯΔྫ extension Array: MutableGenerator { func generateMutably() {

    for i in 0..<count { yield &self[i] } } } for &x in myArray { x += 1 } 25
  13. 26.

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

    28

  15. 29.

    29

  16. 30.

    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.

    31

  18. 35.

    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. 40.

    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. 44.

    • 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. 45.

    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. 46.

    // 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. 47.

    %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. 49.

    // 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. 50.

    LLVM (ίϧʔνϯม׵લ) • ṖͷΦϓγϣϯΛ༩͑ΔͱίϧʔνϯΛLower ͍ͯ͠ͳ͍LLVM͕ग़Δ • $ swiftc -emit-ir -Xfrontend

    -disable- swift-specific-llvm-optzns c5.swift • ͜ΕΛ΍ΔͱLLVM-IR͸ग़ͤΔ͕όΠφϦ͸ੜ ੒Ͱ͖ͳ͍(ඞਢม׵) 50
  26. 51.

    ; 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. 53.

    %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. 56.

    %"$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. 58.

    %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.

    60