Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

᮫ԙզ • John Lin • Twitter: @johnlinvc • Senior Solution Architect, DevOps 
 @ West Pharmaceutical Service (୆ᖯҖ๏෰຿) • Swift Taipei meetup ओ㭎ਓ

Slide 3

Slide 3 text

Swift Taipei Meetup • https://swift.taipei • 㑌݄ୈҰݸᜌ፨ೋᎯ㭎ɻ • ౼࿦ Swift ૬᮫తࢿ㘤ɼ৽஌ɼመ࡞౳౳ɻ • ݱ৔༗ఏڙ᫊৯࿨ञਫ਼ᢛඇञਫ਼ҿྉɻ • ᓣܴိ٣׃⇭ᡅɻ

Slide 4

Slide 4 text

େߝ • Actor model • Swift actor • Swift actor መ຿ൣྫ • Swift ฤᩄྲྀఔ • Swift actor ฤᩄྲྀఔ

Slide 5

Slide 5 text

Actor model • Actor: ԋһ • model: ໛ܕɼయൣɻ

Slide 6

Slide 6 text

֌٢ཁࡏ৽ిӨཫԋ֌٢

Slide 7

Slide 7 text

Actor model • Ұݸ༻ိ႔ཧฒߦӡࢉతఔࣜ໛ܕ • 1973 ೥༝ Carl Hewitt, Peter Bishop & Richard Steiger ఏग़ɻ • ༝ၷେ෦෼૊੒ Actor & message • ෆधཁ೚Կಛผత async featureɼ
 ՄҎࡏၚ౷త blocking ؀ڥதመ࡞ɻ

Slide 8

Slide 8 text

Actor • ఔࣜӡࢉతᄸݩɻ༗ࣗݾᘐཱతهԱᱪ࿨ӡࢉೳྗɻ • ༗ᘐಛత໊ࢠʢ஍ᅿʣ • ՄҎ၏Լ໘ز݅ࣄɻ • ઀Ꮕ㘤ଉ • 㗞ੜ৽త actorɻ • ૹ㘤ଉڅଖଞ actorɻ • վᏓࣗݾతهԱᱪʢվᏓߦҝʣ

Slide 9

Slide 9 text

Message • Actor ೭ؒޓ૬ߏ௨తػ੍ɻ • Actor ՄҎૹmessageڅଖଞత actorɻ • ୠੋ୞ೳᏅࣗݾత messageɻ • Obj-C message send ༗ 87% ૾

Slide 10

Slide 10 text

actor model ӡ࡞

Slide 11

Slide 11 text

Swift

Slide 12

Slide 12 text

Swift

Slide 13

Slide 13 text

Swift • 2011 ೥༝ LLVM ࡞ऀ Chris Lattner ։ᚙత৽ੈ୅ఔࣜޠݴ • 2015 ೥։ݯɼओཁߩᘔऀҝ Apple • ࢧԉ Linux, macOS, iOS & Windowsɻ • ࠷৽൛ຊҝ 5.5ɻ

Slide 14

Slide 14 text

Swift ಛ৭ • Objective-C without C • ಉ࣌ࢧԉ OOP & FPɻ • ࢧԉ Value Type & Reference Typeɻ • ABI stableɼ޲ԼࢧԉલҰ൛తޠ๏ɻ • Generic, Protocol ɻ • 㚎ݐ Package managerɻ

Slide 15

Slide 15 text

Swift Async / Await • Swift 5.5 త৽ޭೳ func fetch() async -> Int {...} let x = await fetch() async let y = fetch() await y

Slide 16

Slide 16 text

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 
 } 
 }

Slide 17

Slide 17 text

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 
 } 
 }

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Lexer / Parser • ሡ७จࣈతఔࣜᛰ᫚׵੒ந৅ޠ๏थ(AST, abstract syntax tree)ɻ • ෛ੹ᒾҰࣈ໘্తޠ๏ੋ൱ਖ਼֬ɻ

Slide 23

Slide 23 text

Semantic analysis • ෼ੳޠ๏थɻ • ᒾҰྨผੋ൱ਖ਼֬ɻ • ॏഉӡࢉࢠ༏ઌᒟɻ • 㢨ఔࣜิ্༬ઃతመ࡞ɻ

Slide 24

Slide 24 text

SIL generation • ሡޠ๏थ᫚׵੒ SIL ( Swift Intermediate Language )ɻ • SIL ੋ SSA Form త Platform agnostic IRɻሢҝ Swift ઃܭతɻ • ༗Ұࠣ࠷ՂԽࡏ IR ൺ Tree ޷၏ɻॴҎ༗ྃ SILɻ

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

SIL Optimizations • ਐߦ IR త࠷ՂԽɻ᮫ᎃ࠷ՂԽ (-Onone) త࿩ब။௓ա • Inline, Specialization, Devirtualization ౳౳ɻ

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Swift Actor ൣྫ actor Counter { 
 var count:Int = 0 
 func inc() async { 
 self.count += 1 
 } 
 }

Slide 30

Slide 30 text

ၷେॏᴍ • Async : ೗Կ֬อಉ࣌୞။༗Ұݸ actor method ӡߦɻ • State Isolation : ೗Կ֬อᏓᏐෆ။ඃଖଞ஍ํଘऔɻ

Slide 31

Slide 31 text

Lexer / Parser • መࡍ্ੋᙛ੒ class ိ parse • lib/Parse/ParseDecl.cpp line 7532 ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, DeclAttributes &Attributes) { bool isExplicitActorDecl = 
 Tok.isContextualKeyword("actor");

Slide 32

Slide 32 text

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); }

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

ฤᩄޙత 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 ߦ

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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; }

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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'

Slide 39

Slide 39 text

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'

Slide 40

Slide 40 text

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; }

Slide 41

Slide 41 text

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); }

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

ฤᩄޙత 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 ߦ

Slide 44

Slide 44 text

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 }

Slide 45

Slide 45 text

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) ... }

Slide 46

Slide 46 text

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); }

Slide 47

Slide 47 text

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(); ... }

Slide 48

Slide 48 text

LLVM IR Generation • lib/IRGen/GenFunc.cpp Line 2438 llvm::Function *IRGenFunction::createAsyncSuspendFn() { ... auto *suspendCall = Builder.CreateCall( IGM.getTaskSwitchFuncFn(), { context, resumeFunction, targetExecutorFirst, targetExecutorSecond });

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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 }

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Async : ೗Կ֬อಉ࣌୞။༗Ұݸ actor method ӡߦ • ࡏ semantic analysis త࣌ީ䔪ᎃᔒ༗ async తݺڣɻ • ೺ method ຊᱪ แࡏҰݸ async helper ཫɻ • ࡏ async helper औಘ actor త thread(Executor)ɻ • ࡏ async helper ೺ method ຊᱪഉਐ actor thread 
 త task queueɻ • ༻task queue ိࣥߦ method ຊᱪɻ

Slide 53

Slide 53 text

State Isolation : ೗Կ֬อᏓᏐෆ။ඃଖଞ஍ํଘऔ • ࡏ semantic analysis త࣌ީ䔪ᎃଖଞ஍ํతଘऔ • ೺ଘऔ౎༻ getter/setter method แىိɻ • ࡏ getter/setter ཫ࢖༻ swift_beginAccess/swift_endAccess 
 ိ࠯ఆᏓᏐ

Slide 54

Slide 54 text

݁࿦ • 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
 ཫংྻࣥߦɻ

Slide 55

Slide 55 text

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 •

Slide 56

Slide 56 text

Q&A John Lin Twitter: @johnlinvc ౤Өย QR code ໢ᅿɿ
 https://tinyurl.com/3ur2stp8