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

Opaque Result Typeの実装

Opaque Result Typeの実装

3781f49ea2c76d6ecf0c6cda46096d49?s=128

omochimetaru

May 24, 2019
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. Opaque Result Typeͷ࣮૷ omochimetaru Θ͍Θ͍swiftc #11 1

  2. ٙ໰ 2

  3. ORTʹΑΔந৅Խ ޙ͔ΒਅͷܕΛมߋՄೳ ϦίϯύΠϧͤͣʹมߋՄೳ ͲͷΑ͏ʹ࣮ݱ͢Δͷ͔ʁ 3

  4. ਅͷܕʹΑΔಛघԽ ਅͷܕ͕Θ͔Δͱ͖͸ɺͦͷܕͰಛघԽͯ͠औΓ ѻ͏ ͲͷΑ͏ʹ࣮ݱ͢Δͷ͔ʁ 4

  5. ౴͑ 5

  6. ந৅Խͷํ๏ • ORTͷΞΠσϯςΟςΟʹରԠ͢ΔOpaque Type Descriptor(OTD)͕࡞ΒΕΔ • OTD͔Β͸ϥϯλΠϜؔ਺Λհͯ͠Metatype ͱProtocol Witness Table͕औಘͰ͖Δ

    • ΋ͪΖΜɺMetatype͔Β͸Value Witness Table͕औಘͰ͖Δ 6
  7. ಛघԽͷํ๏ • ݺͼग़͠ଆͰϙΠϯλΛΩϟετ͢Δ 7

  8. ࣮ݧ 8

  9. protocol P { func print() } extension Int : P

    {} func f() -> some P { return Int(3) } func callF() { let p1 = f() // ORTΛฦؔ͢਺ͷݺͼग़͠ let p2 = p1 // ORTܕಉ࢜ͷίϐʔ p2.print() // ϓϩτίϧϝιουͷݺͼग़͠ } 9
  10. callee SIL 10

  11. // f() sil hidden @$s1a1fQryF : $@convention(thin) () -> @out

    @_opaqueReturnTypeOf("$s1a1fQryF", 0) { // %0 // user: %1 bb0(%0 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) ): %1 = unchecked_addr_cast %0 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) to $*Int // user: %4 %2 = integer_literal $Builtin.Int64, 3 // user: %3 %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 store %3 to %1 : $*Int // id: %4 %5 = tuple () // user: %6 return %5 : $() // id: %6 } // end sil function '$s1a1fQryF' 11
  12. • ฦΓ஋ͷܕͷදݱ͸ @_opaqueReturnTypeOf("$s1a1fQryF", 0) ܕ໊ʹؔ਺໊͕ຒΊࠐ·Ε͍ͯΔ • @out͕͍͍ͭͯΔͷͰɺϙΠϯλʹΑΔؒ઀ฦ ͠ • bb0ͷҾ਺ͷܕΛݟͯ΋ϙΠϯλͩͱΘ͔Δ

    12
  13. • unchecked_addr_castͰIntͷϙΠϯλʹΩϟ ετ • storeͰͦͷϙΠϯλʹ݁ՌΛॻ͖ࠐΉ 13

  14. ! // lib/AST/ASTPrinter.cpp Printer << "@_opaqueReturnTypeOf("; OpaqueTypeDecl *decl = T->getDecl();

    Printer.printEscapedStringLiteral( decl->getOpaqueReturnTypeIdentifier().str()); Printer << ", " << T->getInterfaceType() ->castTo<GenericTypeParamType>() ->getIndex(); Printer << u8") \U0001F9B8"; printGenericArgs(T->getSubstitutions().getReplacementTypes()); Ṗ 14
  15. caller SIL 15

  16. // callF() sil hidden @$s1a5callFyyF : $@convention(thin) () -> ()

    { bb0: %0 = alloc_stack $@_opaqueReturnTypeOf("$s1a1fQryF", 0) , let, name "p1" // users: %10, %9, %4, %2 // function_ref f() %1 = function_ref @$s1a1fQryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s1a1fQryF", 0) // user: %2 %2 = apply %1(%0) : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s1a1fQryF", 0) %3 = alloc_stack $@_opaqueReturnTypeOf("$s1a1fQryF", 0) , let, name "p2" // users: %8, %7, %6, %4 copy_addr %0 to [initialization] %3 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) ! // id: %4 %5 = witness_method $@_opaqueReturnTypeOf("$s1a1fQryF", 0) , #P.print!1 : <Self where Self : P> (Self) -> () -> () : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %6 %6 = apply %5<@_opaqueReturnTypeOf("$s1a1fQryF", 0) >(%3) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () destroy_addr %3 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) // id: %7 dealloc_stack %3 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) // id: %8 destroy_addr %0 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) // id: %9 dealloc_stack %0 : $*@_opaqueReturnTypeOf("$s1a1fQryF", 0) // id: %10 %11 = tuple () // user: %12 return %11 : $() // id: %12 } // end sil function '$s1a5callFyyF' 16
  17. • $@_opaqueReturnTypeOf("$s1a1fQryF", 0) ܕ Ͱalloc_stackͯ͠ɺ ݁ՌΛड͚औΔม਺ΛελοΫʹ֬อ ͦͷΞυϨεΛ%0ʹ୅ೖ • function_ref,applyͰfΛݺͼग़͢ Ҿ਺ʹ֬อͨ͠ϝϞϦྖҬΛ౉͢

    • ݺͼग़͠ޙ͸%0ʹ݁Ռ͕ೖ͍ͬͯΔ 17
  18. • ίϐʔઌม਺Λಉ༷ʹ࡞੒ɺ %3ͰΞυϨεΛอ࣋ • copy_addrͰ%0͔Β%3ʹίϐʔ 18

  19. • witness_methodͰɺ $@_opaqueReturnTypeOf("$s1a1fQryF", 0) ܕ ͷP.printϝιουΛऔಘ • applyͰݺͼग़͢ 19

  20. • destroy_addrͰ%3ͷத਎Λഁغ • dealloc_stackͰ%3ͷϝϞϦΛղ์ • destroy_addrͰ%0ͷத਎Λഁغ • dealloc_stackͰ%0ͷϝϞϦΛղ์ 20

  21. • ؔ਺໊ΛຒΊࠐΜͩܕ໊ɺ $@_opaqueReturnTypeOf("$s1a1fQryF", 0) Ͱදݱ • callee͸ਅͷܕΛΩϟετͯ͠ॻ͖ࠐΈ • caller͸ܕ໊Ҏ֎ී௨ 21

  22. callee LLVM-IR 22

  23. define hidden swiftcc void @"$s1a1fQryF" (%swift.opaque* noalias nocapture sret) #0

    { entry: %1 = bitcast %swift.opaque* %0 to %TSi* %._value = getelementptr inbounds %TSi, %TSi* %1, i32 0, i32 0 store i64 3, i64* %._value, align 1 ret void } 23
  24. • ܕ໊͸ফ͍͑ͯΔ • Ҿ਺ͰopaqueϙΠϯλΛड͚औΔ • ͦΕΛIntͷϙΠϯλʹΩϟετ • storeͰॻ͖ࠐΈ 24

  25. caller LLVM-IR 25

  26. define hidden swiftcc void @"$s1a5callFyyF"() #0 { entry: %p2.debug =

    alloca i8*, align 8 %0 = bitcast i8** %p2.debug to i8* call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false) %p1.debug = alloca i8*, align 8 %1 = bitcast i8** %p1.debug to i8* call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false) %2 = call swiftcc %swift.metadata_response @swift_getOpaqueTypeMetadata( i64 0, i8* undef, %swift.type_descriptor* bitcast ( <{ i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32 }>* @"$s1a1fQryFQOMQ" to %swift.type_descriptor* ), i64 0 ) #5 %3 = extractvalue %swift.metadata_response %2, 0 %4 = bitcast %swift.type* %3 to i8*** %5 = getelementptr inbounds i8**, i8*** %4, i64 -1 %.valueWitnesses = load i8**, i8*** %5, align 8, !invariant.load !42, !dereferenceable !43 %6 = bitcast i8** %.valueWitnesses to %swift.vwtable* %7 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %6, i32 0, i32 8 %size = load i64, i64* %7, align 8, !invariant.load !42 %p2 = alloca i8, i64 %size, align 16 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %p2) %8 = bitcast i8* %p2 to %swift.opaque* store i8* %p2, i8** %p2.debug, align 8 %p1 = alloca i8, i64 %size, align 16 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %p1) %9 = bitcast i8* %p1 to %swift.opaque* store i8* %p1, i8** %p1.debug, align 8 call swiftcc void @"$s1a1fQryF"(%swift.opaque* noalias nocapture sret %9) %10 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 2 %11 = load i8*, i8** %10, align 8, !invariant.load !42 %initializeWithCopy = bitcast i8* %11 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %12 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %8, %swift.opaque* noalias %9, %swift.type* %3) #3 %13 = call swiftcc i8** @swift_getOpaqueTypeConformance( i8* undef, %swift.type_descriptor* bitcast ( <{ i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32, i32, i32 }>* @"$s1a1fQryFQOMQ" to %swift.type_descriptor* ), i64 1 ) #5 %14 = getelementptr inbounds i8*, i8** %13, i32 1 %15 = load i8*, i8** %14, align 8, !invariant.load !42 %16 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*, i8**)* call swiftcc void %16(%swift.opaque* noalias nocapture swiftself %8, %swift.type* %3, i8** %13) %17 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1 %18 = load i8*, i8** %17, align 8, !invariant.load !42 %destroy = bitcast i8* %18 to void (%swift.opaque*, %swift.type*)* call void %destroy(%swift.opaque* noalias %8, %swift.type* %3) #3 call void %destroy(%swift.opaque* noalias %9, %swift.type* %3) #3 %19 = bitcast %swift.opaque* %9 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19) %20 = bitcast %swift.opaque* %8 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20) ret void } 26
  27. • swift_getOpaqueTypeMetadataͱ͍͏ϥϯλ ΠϜؔ਺ʹɺ $s1a1fQryFQOMQΛ౉͍ͯ͠Δ • $s1a1fQryFQOMQ͸ opaque type descriptor for

    <<opaque return type of a.f() -> some>> • ͜ΕͰ%3ʹMetatype͕औΓग़͞ΕΔ ࣮ࡍʹ͸ਅͷܕͰ͋ΔIntͷMetatype͕ಘΒΕ Δ 27
  28. • getelementptrͷ-1Ͱ%5ʹValue Witness TableΛऔΓग़͢ • getelementptrͷ8Ͱ%7ʹܕͷϝϞϦαΠζΛ औΓग़͢ • ͜ͷαΠζΛ࢖ͬͯɺallocaͰϝϞϦ֬อ͠ ͯɺ

    %p1ͱ%p2ʹΞυϨεΛ୅ೖ 28
  29. • bitcastͰ%p1ΛopaqueϙΠϯλʹΩϟετ • callͰfΛݺͼग़͠ɺͦͷϙΠϯλΛҾ਺ʹ౉ ͢ 29

  30. • ͖ͬ͞औΓग़ͨ͠VWT͔Βgetelementptrͷ2 Ͱ initializeWithCopyΛऔΓग़͢ • callͰͦΕΛݺͼग़ͯ͠ίϐʔΛ࣮ߦ 30

  31. • swift_getOpaqueTypeConformanceͱ͍͏ϥ ϯλΠϜؔ਺ʹɺ OTDͱ1Λ౉͍ͯ͠Δɻ ͜ͷ1͸͜ͷܕ͕४ڌ͢ΔϓϩτίϧϦετ͔ ΒPΛࢦఆ͢Δ൪߸ • ͜ΕʹΑͬͯ%13ʹProtocol Witness Table͕औ

    Γग़ͤΔ ࣮ࡍʹ͸ਅͷܕͰ͋ΔIntͷPͷPWT 31
  32. • getelementptrͷ1ͰPWT͔ΒϝιουΛ%14 ʹऔΓग़͢ • callͰϓϩτίϧϝιουͷݺͼग़͠ 32

  33. • getelementptrͷ1ͰVWT͔ΒdestroyΛऔΓ ग़͢ • callͰͦΕΛݺͼग़ͯ͠p1ͱp2Λഁغ • ϝϞϦղ์͸allocaͳͷͰίʔυແ͠ 33

  34. • Opaque Type Descriptor͕fͷίϯύΠϧ࣌ʹ Ұॹʹੜ੒͞Ε͍ͯΔͷͰɺfͷฦ͢ਅͷܕ͕ มߋ͞Εͨͱ͖ʹ͸ɺOTDͷத਎΋Ұॹʹม ߋ͞ΕΔɻ • callerଆͰ͸ɺOTDΛϥϯλΠϜؔ਺ʹ౉͠ ͯɺMetatype,

    VWT, PWTͳͲΛऔಘͯ͠ಈ࡞ ͢ΔͷͰɺOTDͷத਎͕มΘͬͯ΋ޓ׵ੑΛ ҡ࣋Ͱ͖Δɻ 34
  35. ಛघԽͷ࣮ݧ 35

  36. ࠷దԽͰfͷݺͼग़͕͠ফڈ͞Εͳ͍Α͏ʹɺ fʹ@inline(never)Λ෇͚Δɻ 36

  37. callee SIL 37

  38. sil hidden [noinline] @$s1b1fQryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s1b1fQryF",

    0) { // %0 // user: %1 bb0(%0 : $*@_opaqueReturnTypeOf("$s1b1fQryF", 0) ): %1 = unchecked_addr_cast %0 : $*@_opaqueReturnTypeOf("$s1b1fQryF", 0) to $*Int // user: %4 %2 = integer_literal $Builtin.Int64, 3 // user: %3 %3 = struct $Int (%2 : $Builtin.Int64) // user: %4 store %3 to %1 : $*Int // id: %4 %5 = tuple () // user: %6 return %5 : $() // id: %6 } // end sil function '$s1b1fQryF' 38
  39. • ࠷దԽલͱมԽແ͠ʂ 39

  40. caller SIL 40

  41. // callF() sil hidden @$s1b5callFyyF : $@convention(thin) () -> ()

    { bb0: %0 = alloc_stack $Int // users: %2, %7, %4 // function_ref f() %1 = function_ref @$s1b1fQryF : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s1b1fQryF", 0) // user: %3 %2 = unchecked_addr_cast %0 : $*Int to $*@_opaqueReturnTypeOf("$s1b1fQryF", 0) // user: %3 %3 = apply %1(%2) : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s1b1fQryF", 0) %4 = load %0 : $*Int // user: %6 // function_ref Int.print() %5 = function_ref @$sSi1bE5printyyF : $@convention(method) (Int) -> () // user: %6 %6 = apply %5(%4) : $@convention(method) (Int) -> () dealloc_stack %0 : $*Int // id: %7 %8 = tuple () // user: %9 return %8 : $() // id: %9 } // end sil function '$s1b5callFyyF' 41
  42. • ਅͷܕIntͰalloc_stackͯ͠ɺΞυϨεΛ%0 ʹ୅ೖ • ͦΕΛ @_opaqueReturnTypeOf("$s1b1fQryF", 0) ܕͷϙΠϯλʹΩϟετͯ͠ɺ fͷݺͼग़͠ʹ౉͍ͯ͠Δ •

    Intͷprintϝιου$sSi1bE5printyyFΛ௚ ઀ݺͼग़͠ • dealloc_stackͰϝϞϦղ์ 42
  43. • ORTܕͰͷૢ࡞͕ͳ͘ͳΓɺIntΛ௚઀ѻͬͯ ͍Δ • fΛݺͼग़࣌͢ʹɺInt΁ͷϙΠϯλΛORTܕ ΁ͷϙΠϯλʹΩϟετ͚ͨͩ͠ 43

  44. caller LLVM-IR 44

  45. ; Function Attrs: noinline norecurse nounwind writeonly define hidden swiftcc

    void @"$s1b1fQryF" (%swift.opaque* noalias nocapture sret) local_unnamed_addr #5 { entry: %._value = bitcast %swift.opaque* %0 to i64* store i64 3, i64* %._value, align 1 ret void } 45
  46. • ΄΅มԽແ͠ʂ 46

  47. callee LLVM-IR 47

  48. define hidden swiftcc void @"$s1b5callFyyF"() local_unnamed_addr #1 { entry: %0

    = alloca %TSi, align 8 %1 = bitcast %TSi* %0 to i8* call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) %2 = bitcast %TSi* %0 to %swift.opaque* call swiftcc void @"$s1b1fQryF"(%swift.opaque* noalias nocapture nonnull sret %2) %._value = getelementptr inbounds %TSi, %TSi* %0, i64 0, i32 0 %3 = load i64, i64* %._value, align 8 tail call swiftcc void @"$sSi1bE5printyyF"(i64 %3) call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) ret void } 48
  49. • allocaͰIntܕͷม਺Λ֬อ • ͦͷΞυϨεΛ%swift.opaque*ʹΩϟετ • ͦΕΛҾ਺ʹ౉ͯ͠fΛݺͼग़͢ • Intͷprintϝιου$sSi1bE5printyyFΛݺ ͼग़͠ 49

  50. • ฦΓ஋͕opaque pointerͰͷؒ઀ฦ͠ʹͳͬ ͍ͯΔࣄҎ֎͸ɺී௨ʹIntΛѻ͏ίʔυͱಉ ͡ɻ • ࠷దԽʹΑΓɺOTDͱϥϯλΠϜؔ਺ͷ࢖༻ ͕ফڈ͞Ε͍ͯΔɻ • ந৅ԽʹΑΔύϑΥʔϚϯεͷϩε͕ແ͍ࣄ͕

    Θ͔Δɻ 50