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

Swift Actor 實作探索

Swift Actor 實作探索

coscup 2021

Johnlin

July 16, 2021
Tweet

More Decks by Johnlin

Other Decks in Programming

Transcript

  1. Swift Actor መ࡞୳ࡧ
    John Lin (@johnlinvc)

    View full-size slide

  2. ᮫ԙզ
    • John Lin

    • Twitter: @johnlinvc

    • Senior Solution Architect, DevOps 

    @ West Pharmaceutical Service (୆ᖯҖ๏෰຿)

    • Swift Taipei meetup ओ㭎ਓ

    View full-size slide

  3. Swift Taipei Meetup
    • https://swift.taipei

    • 㑌݄ୈҰݸᜌ፨ೋᎯ㭎ɻ

    • ౼࿦ Swift ૬᮫తࢿ㘤ɼ৽஌ɼመ࡞౳౳ɻ

    • ݱ৔༗ఏڙ᫊৯࿨ञਫ਼ᢛඇञਫ਼ҿྉɻ

    • ᓣܴိ٣׃⇭ᡅɻ

    View full-size slide

  4. େߝ
    • Actor model

    • Swift actor

    • Swift actor መ຿ൣྫ

    • Swift ฤᩄྲྀఔ

    • Swift actor ฤᩄྲྀఔ

    View full-size slide

  5. Actor model
    • Actor: ԋһ

    • model: ໛ܕɼయൣɻ

    View full-size slide

  6. ֌٢ཁࡏ৽ిӨཫԋ֌٢

    View full-size slide

  7. Actor model
    • Ұݸ༻ိ႔ཧฒߦӡࢉతఔࣜ໛ܕ

    • 1973 ೥༝ Carl Hewitt, Peter Bishop & Richard Steiger ఏग़ɻ

    • ༝ၷେ෦෼૊੒ Actor & message

    • ෆधཁ೚Կಛผత async featureɼ

    ՄҎࡏၚ౷త blocking ؀ڥதመ࡞ɻ

    View full-size slide

  8. Actor
    • ఔࣜӡࢉతᄸݩɻ༗ࣗݾᘐཱతهԱᱪ࿨ӡࢉೳྗɻ

    • ༗ᘐಛత໊ࢠʢ஍ᅿʣ

    • ՄҎ၏Լ໘ز݅ࣄɻ

    • ઀Ꮕ㘤ଉ

    • 㗞ੜ৽త actorɻ

    • ૹ㘤ଉڅଖଞ actorɻ

    • վᏓࣗݾతهԱᱪʢվᏓߦҝʣ

    View full-size slide

  9. Message
    • Actor ೭ؒޓ૬ߏ௨తػ੍ɻ

    • Actor ՄҎૹmessageڅଖଞత actorɻ

    • ୠੋ୞ೳᏅࣗݾత messageɻ

    • Obj-C message send ༗ 87% ૾

    View full-size slide

  10. actor model ӡ࡞

    View full-size slide

  11. Swift
    • 2011 ೥༝ LLVM ࡞ऀ Chris Lattner ։ᚙత৽ੈ୅ఔࣜޠݴ

    • 2015 ೥։ݯɼओཁߩᘔऀҝ Apple

    • ࢧԉ Linux, macOS, iOS & Windowsɻ

    • ࠷৽൛ຊҝ 5.5ɻ

    View full-size slide

  12. Swift ಛ৭
    • Objective-C without C

    • ಉ࣌ࢧԉ OOP & FPɻ

    • ࢧԉ Value Type & Reference Typeɻ

    • ABI stableɼ޲ԼࢧԉલҰ൛తޠ๏ɻ

    • Generic, Protocol ɻ

    • 㚎ݐ Package managerɻ

    View full-size slide

  13. Swift Async / Await
    • Swift 5.5 త৽ޭೳ

    func fetch() async -> Int {...}


    let x = await fetch()


    async let y = fetch()


    await y

    View full-size slide

  14. Swift Actor
    • SE-0306 Actors መ࡞ྃ Actorɻୠੋ࿨ Actor model ฒෆ׬શ૬ಉɻ

    • ༻ىိ૾ੋ㚎ݐྃ mutex త classɻॴ༗త var/method ౎ඃ mutex อޢɻ

    actor Counter {

    var count:Int = 0

    func inc() async {

    self.count += 1

    }

    }

    View full-size slide

  15. Swift actor message passing
    • ᔒ༗ message passingɼ୞༗ typed async method callɻ

    • ୞༗ࡏࣗݾతmethod ཫ࠽ೳ௚઀मվ varɻࡏଖଞ஍ํधཁ࢖༻ async/
    awaitɻ

    actor Counter {

    var count:Int = 0

    func inc() async {

    self.count += 1

    }

    }

    View full-size slide

  16. Swift Actor vs Actor model
    Local Value
    Message
    passing
    Mailbox Reply
    Swift Yes
    Static,

    Compiler invoke
    methods
    No, compiler
    invoke methods
    Automatic,

    Typed
    Classic
    Actor
    Yes
    Dynamic

    Send anything
    Yes
    Manual,

    Need to know
    sender

    View full-size slide

  17. Swift Actor መ຿ൣྫ
    • iOS 15 UIKit Framework ࢖༻ actor ိආ໔ࡏଖଞ thread मվ UI

    private func loadUser() {


    async {


    do {


    let user = try await loader.loadUser()


    nameLabel.text = user.name


    biographyLabel.text = user.biography


    } catch {


    showError(error)


    }


    }


    }

    View full-size slide

  18. ಹኄੋዎኄ၏ग़ိత䏆ʁ

    View full-size slide

  19. Swift ฤᩄྲྀఔ
    Lexer / Parser Semantic analysis SIL generation
    SIL guaranteed
    transformations
    SIL Optimizations
    LLVM IR
    Generation
    LLVM
    Source Code
    AST AST SIL
    SIL
    SIL
    LLVM IR

    View full-size slide

  20. Lexer / Parser
    • ሡ७จࣈతఔࣜᛰ᫚׵੒ந৅ޠ๏थ(AST, abstract syntax tree)ɻ

    • ෛ੹ᒾҰࣈ໘্తޠ๏ੋ൱ਖ਼֬ɻ

    View full-size slide

  21. Semantic analysis
    • ෼ੳޠ๏थɻ

    • ᒾҰྨผੋ൱ਖ਼֬ɻ

    • ॏഉӡࢉࢠ༏ઌᒟɻ

    • 㢨ఔࣜิ্༬ઃతመ࡞ɻ

    View full-size slide

  22. SIL generation
    • ሡޠ๏थ᫚׵੒ SIL ( Swift Intermediate Language )ɻ

    • SIL ੋ SSA Form త Platform agnostic IRɻሢҝ Swift ઃܭతɻ

    • ༗Ұࠣ࠷ՂԽࡏ IR ൺ Tree ޷၏ɻॴҎ༗ྃ SILɻ

    View full-size slide

  23. SIL guaranteed transformations
    • ਐߦҰࠣඞཁత IR ᫚׵ɻෆ؅༗ᔒ༗։࠷ՂԽ (-O) ౎။䋯

    • return analysis

    • memory promotion

    View full-size slide

  24. SIL Optimizations
    • ਐߦ IR త࠷ՂԽɻ᮫ᎃ࠷ՂԽ (-Onone) త࿩ब။௓ա

    • Inline, Specialization, Devirtualization ౳౳ɻ

    View full-size slide

  25. LLVM IR Generation
    • ೺ SIL ᫚׵੒ LLVM IR (Intermediate Representationɼதհදࣔ)ɻ

    View full-size slide

  26. Actor ฤᩄྲྀఔ
    Lexer / Parser Semantic analysis SIL generation
    SIL guaranteed
    transformations
    SIL Optimizations
    LLVM IR
    Generation
    LLVM
    Source Code
    AST AST SIL
    SIL
    SIL
    LLVM IR

    View full-size slide

  27. Swift Actor ൣྫ
    actor Counter {

    var count:Int = 0

    func inc() async {

    self.count += 1

    }

    }

    View full-size slide

  28. ၷେॏᴍ
    • Async : ೗Կ֬อಉ࣌୞။༗Ұݸ actor method ӡߦɻ

    • State Isolation : ೗Կ֬อᏓᏐෆ။ඃଖଞ஍ํଘऔɻ

    View full-size slide

  29. Lexer / Parser
    • መࡍ্ੋᙛ੒ class ိ parse

    • lib/Parse/ParseDecl.cpp line 7532

    ParserResult Parser::parseDeclClass(ParseDeclOptions
    Flags,


    DeclAttributes
    &Attributes) {


    bool isExplicitActorDecl =

    Tok.isContextualKeyword("actor");

    View full-size slide

  30. Semantic analysis
    • ᒾҰ state isolationɻ lib/sema/TypeCheckConcurrency.cpp line 2256

    bool checkMemberReference(


    Expr *base, ConcreteDeclRef memberRef, SourceLoc memberLoc,


    Optional partialApply = None,


    Expr *context = nullptr) { {


    switch (auto isolation =


    ActorIsolationRestriction::forDeclaration(
    memberRef, getDeclContext())) {
    case ActorIsolationRestriction::CrossActorSelf: {


    return diagnoseNonConcurrentTypesInReference(memberRef,


    getDeclContext()->getParentModule(), memberLoc,


    ConcurrentReferenceKind::CrossActor);


    }


    View full-size slide

  31. 㗞ੜ SIL
    • swiftc -emit-sil counter.swift

    View full-size slide

  32. ฤᩄޙత SIL
    • sil_stage canonical


    • import Builtin


    • import Swift


    • import SwiftShims


    • import Foundation


    • actor Counter {


    • @_hasStorage @_hasInitialValue var count: Int { get set }


    • func inc() async


    • @objc deinit


    • init()


    • }


    • @_hasStorage @_hasInitialValue let task: Task<(), Never> { get }


    • // task


    • sil_global hidden [let] @$s7counter4tasks4TaskVyyts5NeverOGvp : $Task<(), Never>


    • // main


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


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


    • alloc_global @$s7counter4tasks4TaskVyyts5NeverOGvp // id: %2


    • %3 = global_addr @$s7counter4tasks4TaskVyyts5NeverOGvp : $*Task<(), Never> // user: %13


    • %4 = metatype $@thin Task<(), Never>.Type // user: %13


    • %5 = alloc_stack $Optional // users: %15, %14, %13, %6


    • inject_enum_addr %5 : $*Optional, #Optional.none!enumelt // id: %6


    • // function_ref closure #1 in


    • %7 = function_ref @$s7counteryyYaYbcfU_ : $@convention(thin) @Sendable @async () -> () // user: %8


    • %8 = thin_to_thick_function %7 : $@convention(thin) @Sendable @async () -> () to $@Sendable @async @callee_guaranteed () -> () // user: %10


    • // function_ref thunk for @escaping @callee_guaranteed @Sendable @async () -> ()


    • %9 = function_ref @$sIeghH_ytIeghHr_TR : $@convention(thin) @Sendable @async (@guaranteed @Sendable @async @callee_guaranteed () -> ()) -> @out () // user: %10


    • %10 = partial_apply [callee_guaranteed] %9(%8) : $@convention(thin) @Sendable @async (@guaranteed @Sendable @async @callee_guaranteed () -> ()) -> @out () // user: %11


    • %11 = convert_function %10 : $@Sendable @async @callee_guaranteed () -> @out () to $@Sendable @async @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()> // user: %13


    • // function_ref static Task<>.detached(priority:operation:)


    • %12 = function_ref @$ss4TaskVss5NeverORs_rlE8detached8priority9operationAByxADGs0A8PriorityVSg_xyYaYbcntFZ : $@convention(method) <τ_0_0, τ_0_1 where τ_0_1 == Never> (@in_guaranteed Optional, @owned @Sendable @async @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <τ_0_0>, @thin Task<τ_0_0, Never>.Type) -> @out Task<τ_0_0, Never> // user: %13


    • %13 = apply %12<(), Never>(%3, %5, %11, %4) : $@convention(method) <τ_0_0, τ_0_1 where τ_0_1 == Never> (@in_guaranteed Optional, @owned @Sendable @async @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <τ_0_0>, @thin Task<τ_0_0, Never>.Type) -> @out Task<τ_0_0, Never>


    • destroy_addr %5 : $*Optional // id: %14


    • dealloc_stack %5 : $*Optional // id: %15


    • %16 = integer_literal $Builtin.Int32, 1 // user: %17


    • %17 = struct $UInt32 (%16 : $Builtin.Int32) // user: %19


    • // function_ref _sleep


    • %18 = function_ref @_sleep : $@convention(c) (UInt32) -> UInt32 // user: %19


    • %19 = apply %18(%17) : $@convention(c) (UInt32) -> UInt32


    • %20 = integer_literal $Builtin.Int32, 0 // user: %21


    • %21 = struct $Int32 (%20 : $Builtin.Int32) // user: %22


    • return %21 : $Int32 // id: %22


    • } // end sil function 'main'


    • // variable initialization expression of Counter.count


    • sil hidden [transparent] @$s7counter7CounterC5countSivpfi : $@convention(thin) () -> Int {


    • bb0:


    • %0 = integer_literal $Builtin.Int64, 0 // user: %1


    • %1 = struct $Int (%0 : $Builtin.Int64) // user: %2


    • return %1 : $Int // id: %2


    • } // end sil function '$s7counter7CounterC5countSivpfi'


    • // Int.init(_builtinIntegerLiteral:)


    • sil public_external [transparent] @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int {


    • // %0 // user: %2


    • bb0(%0 : $Builtin.IntLiteral, %1 : $@thin Int.Type):


    • %2 = builtin "s_to_s_checked_trunc_IntLiteral_Int64"(%0 : $Builtin.IntLiteral) : $(Builtin.Int64, Builtin.Int1) // user: %3


    • %3 = tuple_extract %2 : $(Builtin.Int64, Builtin.Int1), 0 // user: %4


    • %4 = struct $Int (%3 : $Builtin.Int64) // user: %5


    • return %4 : $Int // id: %5


    • } // end sil function '$sSi22_builtinIntegerLiteralSiBI_tcfC'


    • // Counter.count.getter


    • sil hidden [transparent] @$s7counter7CounterC5countSivg : $@convention(method) (@guaranteed Counter) -> Int {


    • // %0 "self" // users: %2, %1


    • bb0(%0 : $Counter):


    • debug_value %0 : $Counter, let, name "self", argno 1 // id: %1


    • %2 = ref_element_addr %0 : $Counter, #Counter.count // user: %3


    • %3 = begin_access [read] [dynamic] %2 : $*Int // users: %4, %5


    • %4 = load %3 : $*Int // user: %6


    • end_access %3 : $*Int // id: %5


    • return %4 : $Int // id: %6


    • } // end sil function '$s7counter7CounterC5countSivg'


    • // Counter.count.setter


    • sil hidden [transparent] @$s7counter7CounterC5countSivs : $@convention(method) (Int, @guaranteed Counter) -> () {


    • // %0 "value" // users: %6, %2


    • // %1 "self" // users: %4, %3


    • bb0(%0 : $Int, %1 : $Counter):


    • debug_value %0 : $Int, let, name "value", argno 1 // id: %2


    • debug_value %1 : $Counter, let, name "self", argno 2 // id: %3


    • %4 = ref_element_addr %1 : $Counter, #Counter.count // user: %5


    • %5 = begin_access [modify] [dynamic] %4 : $*Int // users: %6, %7


    • store %0 to %5 : $*Int // id: %6


    • end_access %5 : $*Int // id: %7


    • %8 = tuple () // user: %9


    • return %8 : $() // id: %9


    • } // end sil function '$s7counter7CounterC5countSivs'


    • // Counter.count.modify


    • sil hidden [transparent] @$s7counter7CounterC5countSivM : $@yield_once @convention(method) (@guaranteed Counter) -> @yields @inout Int {


    • // %0 "self" // users: %2, %1


    • bb0(%0 : $Counter):


    • debug_value %0 : $Counter, let, name "self", argno 1 // id: %1


    • %2 = ref_element_addr %0 : $Counter, #Counter.count // user: %3


    • %3 = begin_access [modify] [dynamic] %2 : $*Int // users: %5, %8, %4


    • yield %3 : $*Int, resume bb1, unwind bb2 // id: %4


    • bb1: // Preds: bb0


    • end_access %3 : $*Int // id: %5


    • %6 = tuple () // user: %7


    • return %6 : $() // id: %7


    • bb2: // Preds: bb0


    • end_access %3 : $*Int // id: %8


    • unwind // id: %9


    • } // end sil function '$s7counter7CounterC5countSivM'


    • // Counter.inc()


    • sil hidden @$s7counter7CounterC3incyyYaF : $@convention(method) @async (@guaranteed Counter) -> () {


    • // %0 "self" // users: %5, %4, %2, %1


    • bb0(%0 : $Counter):


    • debug_value %0 : $Counter, let, name "self", argno 1 // id: %1


    • hop_to_executor %0 : $Counter // id: %2


    • %3 = integer_literal $Builtin.Int64, 1 // user: %10


    • %4 = class_method %0 : $Counter, #Counter.count!modify : (Counter) -> () -> (), $@yield_once @convention(method) (@guaranteed Counter) -> @yields @inout Int // user: %5


    • (%5, %6) = begin_apply %4(%0) : $@yield_once @convention(method) (@guaranteed Counter) -> @yields @inout Int // users: %15, %7, %17


    • %7 = struct_element_addr %5 : $*Int, #Int._value // user: %8


    • %8 = load %7 : $*Builtin.Int64 // user: %10


    • %9 = integer_literal $Builtin.Int1, -1 // user: %10


    • %10 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %3 : $Builtin.Int64, %9 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %12, %11


    • %11 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 0 // user: %14


    • %12 = tuple_extract %10 : $(Builtin.Int64, Builtin.Int1), 1 // user: %13


    • cond_fail %12 : $Builtin.Int1, "arithmetic overflow" // id: %13


    • %14 = struct $Int (%11 : $Builtin.Int64) // user: %15


    • store %14 to %5 : $*Int // id: %15


    • %16 = tuple ()


    • end_apply %6 // id: %17


    • %18 = tuple () // user: %19


    • return %18 : $() // id: %19


    • } // end sil function '$s7counter7CounterC3incyyYaF'


    • // static Int.+= infix(_:_:)


    • sil public_external [transparent] @$sSi2peoiyySiz_SitFZ : $@convention(method) (@inout Int, Int, @thin Int.Type) -> () {


    • // %0 // users: %12, %3


    • // %1 // user: %5


    • bb0(%0 : $*Int, %1 : $Int, %2 : $@thin Int.Type):


    • %3 = struct_element_addr %0 : $*Int, #Int._value // user: %4


    • %4 = load %3 : $*Builtin.Int64 // user: %7


    • %5 = struct_extract %1 : $Int, #Int._value // user: %7


    • %6 = integer_literal $Builtin.Int1, -1 // user: %7


    • %7 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64, %6 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %9, %8


    • %8 = tuple_extract %7 : $(Builtin.Int64, Builtin.Int1), 0 // user: %11


    • %9 = tuple_extract %7 : $(Builtin.Int64, Builtin.Int1), 1 // user: %10


    • cond_fail %9 : $Builtin.Int1, "arithmetic overflow" // id: %10


    • %11 = struct $Int (%8 : $Builtin.Int64) // user: %12


    • store %11 to %0 : $*Int // id: %12


    • %13 = tuple () // user: %14


    • return %13 : $() // id: %14


    • } // end sil function '$sSi2peoiyySiz_SitFZ'


    • // Counter.deinit


    • sil hidden @$s7counter7CounterCfd : $@convention(method) (@guaranteed Counter) -> @owned Builtin.NativeObject {


    • // %0 "self" // users: %3, %2, %1


    • bb0(%0 : $Counter):


    • debug_value %0 : $Counter, let, name "self", argno 1 // id: %1


    • %2 = builtin "destroyDefaultActor"(%0 : $Counter) : $()


    • %3 = unchecked_ref_cast %0 : $Counter to $Builtin.NativeObject // user: %4


    • return %3 : $Builtin.NativeObject // id: %4


    • } // end sil function '$s7counter7CounterCfd'


    • // Counter.__deallocating_deinit


    • sil hidden @$s7counter7CounterCfD : $@convention(method) (@owned Counter) -> () {


    • // %0 "self" // users: %3, %1


    • bb0(%0 : $Counter):


    • debug_value %0 : $Counter, let, name "self", argno 1 // id: %1


    • // function_ref Counter.deinit


    • %2 = function_ref @$s7counter7CounterCfd : $@convention(method) (@guaranteed Counter) -> @owned Builtin.NativeObject // user: %3


    • %3 = apply %2(%0) : $@convention(method) (@guaranteed Counter) -> @owned Builtin.NativeObject // user: %4


    • %4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $Counter // user: %5


    • dealloc_ref %4 : $Counter // id: %5


    • %6 = tuple () // user: %7


    • return %6 : $() // id: %7


    • } // end sil function '$s7counter7CounterCfD'


    360 ߦ

    View full-size slide

  33. SIL ॏᴍઅ㑚 inc
    // Counter.inc()


    sil hidden @$s7counter7CounterC3incyyYaF : $@convention(method) @async
    (@guaranteed Counter) -> () {


    // %0 "self" // users: %5, %4, %2, %1


    bb0(%0 : $Counter):


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


    hop_to_executor %0 : $Counter // id: %2


    ...


    %10 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %3 :
    $Builtin.Int64, %9 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) //
    users: %12, %11


    ...


    }


    View full-size slide

  34. SIL generation
    • ೺ actor ᙛ੒ class ိgenerate SILɻୠੋಛผ㗞ੜ hop_to_executor ɻ

    • lib/SILGen/SILGenProlog.cpp Line 407

    void SILGenFunction::emitProlog(CaptureInfo captureInfo, ...) {


    switch (actorIsolation.getKind()) {


    ...


    case ActorIsolation::ActorInstance: {
    if (F.isAsync() ||


    (wantDataRaceChecks && funcDecl->isLocalCapture())) {


    auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());


    ManagedValue selfArg = ManagedValue::forUnmanaged(F.getSelfArgument());


    ExpectedExecutor = emitLoadActorExecutor(loc, selfArg);


    }


    break;


    }


    View full-size slide

  35. SIL generation
    • ೺ actor ᙛ੒ class ိgenerate SILɻୠੋಛผ㗞ੜ hop_to_executor ɻ

    • lib/SILGen/SILGenProlog.cpp Line 569

    void SILGenFunction::emitProlog(CaptureInfo captureInfo, ...) {


    ...


    // Jump to the expected executor.


    if (ExpectedExecutor) {


    if (F.isAsync()) {


    // For an async function, hop to the executor.


    B.createHopToExecutor(


    RegularLocation::getAutoGeneratedLocation(F.getLocation()),


    ExpectedExecutor, /*mandatory*/ false);


    View full-size slide

  36. SIL ॏᴍઅ㑚 count getter
    // Counter.count.getter


    sil hidden [transparent] @$s7counter7CounterC5countSivg :
    $@convention(method) (@guaranteed Counter) -> Int {


    // %0 "self" // users: %2, %1


    bb0(%0 : $Counter):


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


    %2 = ref_element_addr %0 : $Counter, #Counter.count // user: %3


    %3 = begin_access [read] [dynamic] %2 : $*Int // users: %4, %5


    %4 = load %3 : $*Int // user: %6


    end_access %3 : $*Int // id: %5


    return %4 : $Int // id: %6


    } // end sil function '$s7counter7CounterC5countSivg'


    View full-size slide

  37. SIL ॏᴍઅ㑚 count setter
    // Counter.count.setter


    sil hidden [transparent] @$s7counter7CounterC5countSivs :
    $@convention(method) (Int, @guaranteed Counter) -> () {


    // %0 "value" // users: %6, %2


    // %1 "self" // users: %4, %3


    bb0(%0 : $Int, %1 : $Counter):


    ...


    %4 = ref_element_addr %1 : $Counter, #Counter.count // user: %5


    %5 = begin_access [modify] [dynamic] %4 : $*Int // users: %6, %7


    store %0 to %5 : $*Int // id: %6


    end_access %5 : $*Int // id: %7


    %8 = tuple () // user: %9


    return %8 : $() // id: %9


    } // end sil function '$s7counter7CounterC5countSivs'


    View full-size slide

  38. SIL generation
    • ࡏଘऔత࣌ީ။؃ੋෆੋࡏ actor ཫɼੋత࿩ब။Ճ্ beginAccess

    • lib/SILGen/SILGenLValue.cpp Line 615

    SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc,


    SILValue address, SILAccessKind kind) {


    if (!SGF.getOptions().VerifyExclusivity)


    return address;
    ...
    auto BAI =


    SGF.B.createBeginAccess(loc, address, kind,


    SILAccessEnforcement::Unsafe,


    /*hasNoNestedConflict=*/false,


    /*fromBuiltin=*/false);
    return BAI;


    }

    View full-size slide

  39. SIL generation
    • ޙ໘໵။Ճ্ሣጯత endAccess

    • lib/SILGen/SILGenLValue.cpp Line 639

    void UnenforcedAccess::emitEndAccess(SILGenFunction &SGF) {


    if (!beginAccessPtr)


    return;




    SGF.B.createEndAccess(beginAccessPtr->getLoc(),
    beginAccessPtr.get(),


    /*abort*/ false);


    }

    View full-size slide

  40. 㗞ੜ LLVM IR
    • swiftc -emit-ir counter.swift

    View full-size slide

  41. ฤᩄޙత LLVM IR
    ; ModuleID = ''


    source_filename = ""


    target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"


    target triple = "x86_64-unknown-linux-gnu"


    module asm ".section .swift1_autolink_entries,\220x80000000\22"


    %TScT = type <{ %swift.refcounted* }>


    %swift.refcounted = type { %swift.type*, i64 }


    %swift.type = type { i64 }


    %swift.async_func_pointer = type <{ i32, i32 }>


    %swift.full_boxmetadata = type { void (%swift.refcounted*)*, i8**, %swift.type, i32, i8* }


    %swift.full_type = type { i8**, %swift.type }


    %swift.protocol = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i32, i32, i32, i32, i32, i32 }


    %swift.method_descriptor = type { i32, i32 }


    %T7counter7CounterC = type <{ %swift.refcounted, %swift.defaultactor, %TSi }>


    %swift.defaultactor = type { [12 x i8*] }


    %TSi = type <{ i64 }>


    %swift.type_metadata_record = type { i32 }


    %swift.vwtable = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i64, i64, i32, i32 }


    %TScPSg = type <{}>


    %swift.opaque = type opaque


    %swift.metadata_response = type { %swift.type*, i64 }


    %swift.function = type { i8*, %swift.refcounted* }


    %swift.type_descriptor = type opaque


    %"$s7counter7CounterC5countSivM.Frame" = type { [24 x i8] }


    %swift.context = type { %swift.context*, void (%swift.context*)*, i64 }


    %"$s7counter7CounterC3incyyYaF.Frame" = type { %swift.context*, %T7counter7CounterC*, %T7counter7CounterC* }


    %"$s7counteryyYaYbcfU_.Frame" = type { %swift.context*, %T7counter7CounterC*, i64, %T7counter7CounterC*, i8*, i64, i64, i64 }


    %swift.executor = type { i64, i64 }


    %Ts28__ContiguousArrayStorageBaseC = type opaque


    %Any = type { [24 x i8], %swift.type* }


    %swift.bridge = type opaque


    %TSa = type <{ %Ts22_ContiguousArrayBufferV }>


    %Ts22_ContiguousArrayBufferV = type <{ %Ts28__ContiguousArrayStorageBaseC* }>


    %"$sIeghH_ytIeghHr_TR.Frame" = type { %swift.context*, i8* }


    %"$sIeghH_ytIeghHr_TRTA.Frame" = type { %swift.context*, i8* }


    %swift.async_task_and_context = type { %swift.task*, %swift.context* }


    %swift.task = type { %swift.refcounted, i8*, i8*, i32, i32, i8*, i8*, i8*, %swift.context*, i64 }


    %"$sxIeghHr_xs5Error_pIegHrzo_s5NeverORs_r0_lTR.Frame" = type { %swift.context*, i8* }


    %swift.error = type opaque


    %"$sxIeghHr_xs5Error_pIegHrzo_s5NeverORs_r0_lTRTA.Frame" = type { %swift.context*, i8* }


    @"$s7counter4taskScTyyts5NeverOGvp" = hidden global %TScT zeroinitializer, align 8


    @"symbolic ScPSg" = linkonce_odr hidden constant <{ [5 x i8], i8 }> <{ [5 x i8] c"ScPSg", i8 0 }>, section "swift5_typeref", align 2


    @"$sScPSgMD" = linkonce_odr hidden global { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint (<{ [5 x i8], i8 }>* @"symbolic ScPSg" to i64), i64 ptrtoint ({ i32, i32 }* @"$sScPSgMD" to i64)) to i32), i32 -5 }, align 8


    @"$s7counteryyYaYbcfU_Tu" = internal global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (void (%swift.context*)* @"$s7counteryyYaYbcfU_" to i64), i64 ptrtoint (%swift.async_func_pointer* @"$s7counteryyYaYbcfU_Tu" to i64)) to i32), i32 96 }>, section ".rodata", align 8


    @"$sIeghH_ytIeghHr_TRTu" = linkonce_odr hidden global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (void (%swift.opaque*, %swift.context*, i8*, %swift.refcounted*)* @"$sIeghH_ytIeghHr_TR" to i64), i64 ptrtoint (%swift.async_func_pointer* @"$sIeghH_ytIeghHr_TRTu" to i64)) to i32), i32 48 }>, section ".rodata", align 8


    @"symbolic IeghH_" = linkonce_odr hidden constant <{ [6 x i8], i8 }> <{ [6 x i8] c"IeghH_", i8 0 }>, section "swift5_typeref", align 2


    @"\01l__swift5_reflection_descriptor" = private constant { i32, i32, i32, i32 } { i32 1, i32 0, i32 0, i32 trunc (i64 sub (i64 ptrtoint (<{ [6 x i8], i8 }>* @"symbolic IeghH_" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32 }, { i32, i32, i32, i32 }* @"\01l__swift5_reflection_descriptor", i32 0, i32 3) to i64)) to i32) }, section "swift5_capture",
    align 4


    @metadata = private constant %swift.full_boxmetadata { void (%swift.refcounted*)* @objectdestroy, i8** null, %swift.type { i64 1024 }, i32 16, i8* bitcast ({ i32, i32, i32, i32 }* @"\01l__swift5_reflection_descriptor" to i8*) }, align 8


    @"$sIeghH_ytIeghHr_TRTATu" = internal global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (void (%swift.opaque*, %swift.context*, %swift.refcounted*)* @"$sIeghH_ytIeghHr_TRTA" to i64), i64 ptrtoint (%swift.async_func_pointer* @"$sIeghH_ytIeghHr_TRTATu" to i64)) to i32), i32 48 }>, section ".rodata", align 8


    @"$sytN" = external global %swift.full_type


    @"$s7counter7CounterC3incyyYaFTu" = hidden global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (void (%swift.context*, %T7counter7CounterC*)* @"$s7counter7CounterC3incyyYaF" to i64), i64 ptrtoint (%swift.async_func_pointer* @"$s7counter7CounterC3incyyYaFTu" to i64)) to i32), i32 48 }>, section ".rodata", align 8


    @"$sScAMp" = external global %swift.protocol, align 4


    @"got.$sScAMp" = private unnamed_addr constant %swift.protocol* @"$sScAMp"


    @"$sScA15unownedExecutorScevgTq" = external global %swift.method_descriptor, align 4


    @"got.$sScA15unownedExecutorScevgTq" = private unnamed_addr constant %swift.method_descriptor* @"$sScA15unownedExecutorScevgTq"


    @"$s7counter7CounterCScAAAMcMK" = internal global [16 x i8*] zeroinitializer


    @"$s7counter7CounterCScAAAMc" = hidden constant { i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 } { i32 add (i32 trunc (i64 sub (i64 ptrtoint (%swift.protocol** @"got.$sScAMp" to i64), i64 ptrtoint ({ i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }* @"$s7counter7CounterCScAAAMc" to i64)) to i32), i32 1), i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32,
    i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, %swift.method_descriptor, %swift.method_descriptor, %swift.method_descriptor, %swift.method_descriptor, %swift.method_descriptor }>* @"$s7counter7CounterCMn" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32
    }* @"$s7counter7CounterCScAAAMc", i32 0, i32 1) to i64)) to i32), i32 0, i32 196608, i32 1, i32 add (i32 trunc (i64 sub (i64 ptrtoint (%swift.method_descriptor** @"got.$sScA15unownedExecutorScevgTq" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }*
    @"$s7counter7CounterCScAAAMc", i32 0, i32 5) to i64)) to i32), i32 1), i32 trunc (i64 sub (i64 ptrtoint ({ i64, i64 } (%T7counter7CounterC*, %swift.type*, i8**)* @"$s7counter7CounterCScAAAScA15unownedExecutorScevgTW" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i16, i16,
    i32, i32 }* @"$s7counter7CounterCScAAAMc", i32 0, i32 6) to i64)) to i32), i16 0, i16 1, i32 0, i32 trunc (i64 sub (i64 ptrtoint ([16 x i8*]* @"$s7counter7CounterCScAAAMcMK" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }, { i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }* @"$s7counter7CounterCScAAAMc",
    i32 0, i32 10) to i64)) to i32) }, section ".rodata", align 4


    @"\01l_entry_point" = private constant { i32 } { i32 trunc (i64 sub (i64 ptrtoint (i32 (i32, i8**)* @main to i64), i64 ptrtoint ({ i32 }* @"\01l_entry_point" to i64)) to i32) }, section "swift5_entry", align 4


    @"$s7counter7CounterC5countSivpWvd" = hidden constant i64 112, align 8


    @"$sBoWV" = external global i8*, align 8


    @0 = private constant [8 x i8] c"counter\00"


    @"$s7counterMXM" = linkonce_odr hidden constant <{ i32, i32, i32 }> <{ i32 0, i32 0, i32 trunc (i64 sub (i64 ptrtoint ([8 x i8]* @0 to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32 }>, <{ i32, i32, i32 }>* @"$s7counterMXM", i32 0, i32 2) to i64)) to i32) }>, section ".rodata", align 4


    @1 = private constant [8 x i8] c"Counter\00"


    1357 ߦ

    View full-size slide

  42. LLVM IR inc ॏᴍઅ㑚
    define hidden swifttailcc void
    @"$s7counter7CounterC3incyyYaF"(%swift.context* swiftasync %0,
    %T7counter7CounterC* swiftself %1) #0 {


    entry:


    ...


    musttail call swifttailcc void @swift_task_switch(%swift.context*
    swiftasync %6, i8* bitcast (void (i8*)*
    @"$s7counter7CounterC3incyyYaFTY0_" to i8*), i64 %5, i64 0) #7


    ret void


    }


    View full-size slide

  43. LLVM IR inc ॏᴍઅ㑚
    define internal swifttailcc void
    @"$s7counter7CounterC3incyyYaFTY0_"(i8* swiftasync %0) #0 {


    entryresume.0:


    %async.ctx.frameptr1 = getelementptr inbounds i8, i8* %0, i32 24


    %FramePtr = bitcast i8* %async.ctx.frameptr1 to
    %"$s7counter7CounterC3incyyYaF.Frame"*


    ...


    %15 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %14, i64
    1)


    ...


    }


    View full-size slide

  44. LLVM IR Generation
    • lib/IRGen/IRGenSIL.cpp Line 6173

    void IRGenSILFunction::visitHopToExecutorInst(HopToExecutorInst *i)
    {


    assert(i->getTargetExecutor()->getType().getOptionalObjectType()


    .is());


    llvm::Value *resumeFn = Builder.CreateIntrinsicCall(


    llvm::Intrinsic::coro_async_resume, {});
    Explosion executor;


    getLoweredExplosion(i->getOperand(), executor);
    emitSuspensionPoint(executor, resumeFn);


    }

    View full-size slide

  45. LLVM IR Generation
    • lib/IRGen/GenFunc.cpp Line 2402

    void IRGenFunction::emitSuspensionPoint(Explosion &toExecutor,


    llvm::Value *asyncResume) {
    // Setup the suspend point.


    ...


    llvm::Function *suspendFn = createAsyncSuspendFn();


    ...


    }


    View full-size slide

  46. LLVM IR Generation
    • lib/IRGen/GenFunc.cpp Line 2438

    llvm::Function *IRGenFunction::createAsyncSuspendFn() {


    ...


    auto *suspendCall = Builder.CreateCall(


    IGM.getTaskSwitchFuncFn(),


    { context, resumeFunction, targetExecutorFirst,
    targetExecutorSecond });


    View full-size slide

  47. LLVM IR Generation
    • include/swift/Runtime/RuntimeFunctions.def Line 1569

    FUNCTION(TaskSwitchFunc,


    swift_task_switch, SwiftAsyncCC,


    ConcurrencyAvailability,


    RETURNS(VoidTy),


    ARGS(SwiftContextPtrTy, Int8PtrTy, ExecutorFirstTy,
    ExecutorSecondTy),


    ATTRS(NoUnwind))


    View full-size slide

  48. LLVM IR count getter ॏᴍઅ㑚
    define hidden swiftcc i64
    @"$s7counter7CounterC5countSivg"(%T7counter7CounterC* swiftself %0)
    #0 {


    entry:


    ...


    call void @swift_beginAccess(i8* %3, [24 x i8]* %access-scratch,
    i64 32, i8* null) #7


    %._value = getelementptr inbounds %TSi, %TSi* %1, i32 0, i32 0


    %4 = load i64, i64* %._value, align 16


    call void @swift_endAccess([24 x i8]* %access-scratch) #7


    ...


    ret i64 %4


    }

    View full-size slide

  49. LLVM IR count setter ॏᴍઅ㑚
    define hidden swiftcc void @"$s7counter7CounterC5countSivs"(i64 %0,
    %T7counter7CounterC* swiftself %1) #0 {


    entry:


    ...


    call void @swift_beginAccess(i8* %4, [24 x i8]* %access-scratch,
    i64 33, i8* null) #7


    %._value = getelementptr inbounds %TSi, %TSi* %2, i32 0, i32 0


    store i64 %0, i64* %._value, align 16


    call void @swift_endAccess([24 x i8]* %access-scratch) #7


    ...


    }

    View full-size slide

  50. Async : ೗Կ֬อಉ࣌୞။༗Ұݸ actor method ӡߦ
    • ࡏ semantic analysis త࣌ީ䔪ᎃᔒ༗ async తݺڣɻ

    • ೺ method ຊᱪ แࡏҰݸ async helper ཫɻ

    • ࡏ async helper औಘ actor త thread(Executor)ɻ

    • ࡏ async helper ೺ method ຊᱪഉਐ actor thread 

    త task queueɻ

    • ༻task queue ိࣥߦ method ຊᱪɻ

    View full-size slide

  51. State Isolation : ೗Կ֬อᏓᏐෆ။ඃଖଞ஍ํଘऔ
    • ࡏ semantic analysis త࣌ީ䔪ᎃଖଞ஍ํతଘऔ

    • ೺ଘऔ౎༻ getter/setter method แىိɻ

    • ࡏ getter/setter ཫ࢖༻ swift_beginAccess/swift_endAccess 

    ိ࠯ఆᏓᏐ

    View full-size slide

  52. ݁࿦
    • actor ੋඃᙛ੒Ұछ conform to Actor Protocol త Class ိ႔ཧɻ

    • State isolation ੋ༻ type check enforceɻ

    • State access ။༻ begin_access & end_access ࠯ఆɻ

    • ॴ༗త actor method call ౎༗Ճ্ hop_to_executor SIL

    • hop_to_executor ။Ꮣ੒ swift_task_switch IRɼࡏ task queue

    ཫংྻࣥߦɻ

    View full-size slide

  53. References
    • https://swift.org/swift-compiler/#compiler-architecture

    • https://github.com/apple/swift

    • https://www.swiftbysundell.com/articles/the-main-actor-attribute/

    • https://llvm.org/docs/Coroutines.html#switched-resume-lowering


    View full-size slide

  54. Q&A
    John Lin

    Twitter: @johnlinvc
    ౤Өย QR code

    ໢ᅿɿ

    https://tinyurl.com/3ur2stp8

    View full-size slide