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

Swiftのジェネリクスはどうやって動いているのかコンパイラのソースから探る

3781f49ea2c76d6ecf0c6cda46096d49?s=47 omochimetaru
September 02, 2018

 Swiftのジェネリクスはどうやって動いているのかコンパイラのソースから探る

iOSDC2018で発表しました
https://fortee.jp/iosdc-japan-2018/proposal/3d60b2ae-5841-4f60-8dd7-830377dc110e

Swiftにはジェネリクスという言語機能があり、
利用時の型が不確定なコードを書くことできます。
Swiftには厳密な型システムや、余計なメタ情報を持たない値型などがあるのに、
どのようにしてジェネリクスを動作させているのでしょうか。
このトークではそれを実現する言語機能である
witness tableについて、
コンパイラのソースコードを追いながら解説します。

3781f49ea2c76d6ecf0c6cda46096d49?s=128

omochimetaru

September 02, 2018
Tweet

Transcript

  1. SwiftͷδΣωϦΫε͸ Ͳ͏΍ͬͯಈ͍͍ͯΔͷ ͔ίϯύΠϥͷιʔε͔ Β୳Δ 1

  2. ࣗݾ঺հ • omochimetaru • Qoncept, Inc • ήʔϜ • Swift

    • SwiftίϯύΠϥ 2
  3. ͜ͷൃදͷझࢫ • δΣωϦΫεͷ࢓૊ΈΛղઆ • ίϯύΠϥΛྲྀߦΒͤΔ 3

  4. Generics 4

  5. Generics // δΣωϦοΫؔ਺ func takeX<X>(_ x: X) // δΣωϦοΫܕ class

    Box<T> { var value: T } 5
  6. ͲΜͳܕͰ΋౉ͤΔ func takeX<X>(_ x: X) 6

  7. func takeX<X>(_ x: X) takeX(3) 7

  8. func takeX<X>(_ x: X) takeX(3) takeX("hello") 8

  9. func takeX<X>(_ x: X) takeX(3) takeX("hello") struct Stone { ...

    } takeX(Stone()) 9
  10. func takeX<X>(_ x: X) takeX(3) takeX("hello") struct Stone { ...

    } takeX(Stone()) class Cat { ... } takeX(Cat()) 10
  11. ෆࢥٞ • Swift͸੩తܕ෇͚ • ͔͠͠, ࣮ߦ࣌·Ͱܕ͕Θ͔Βͳ͍ 11

  12. ෆࢥٞ • αΠζͷҧ͏Ҿ਺ͷड͚౉͠ struct Stone { ... } class Cat

    { ... } Stone() // 24όΠτ Cat() // 8όΠτ 12
  13. ෆࢥٞ • ࢀরܕ͚ͩͳΒΫϥε৘ใʹΞΫηεͰ͖Δ • SwiftͷδΣωϦΫε͸஋ܕʹ΋ରԠ • ஋ܕʹ͸ͦͷΑ͏ͳϝλ৘ใ͕ແ͍ 13

  14. Swiftͷٙ໰ • ݴޠ࢓༷ͷٙ໰ • จॻ͕ͨ͘͞Μ͋Δ • ݴޠ ࣮૷ ࢓༷ͷٙ໰ •

    จॻ͕গͳ͍ • ͜ͷ࿩ͷςʔϚ͸ͪ͜Β 14
  15. ݴޠ࣮૷࢓༷ΛֶͿཧ༝ • ϓϩάϥϛϯάݴޠΛબ୒͢Δࡍͷ൑அࡐྉ • ྫ͑͹ɺ Swift vs Objective-C • ύϑΥʔϚϯε࠷దԽʹ໾ཱͭ

    • ଎͍ॻ͖ํɺ஗͍ॻ͖ํɺͦͷཧ༝ • ָ͍͠ • ίϯύΠϥ͸ٕज़ͷਮ 15
  16. Swiftݴޠ࣮૷࢓༷ͷֶͼํ • จॻ • গͳ͍ • ݹ͘ͳΔ • ਅِͷ֬ೝ͕Ͱ͖ͳ͍ •

    ίϯύΠϥͷग़ྗ • ίϯύΠϥͷιʔείʔυ 16
  17. ίϯύΠϥͷಈ͖ • Swift͸ଟஈ֊ʹม׵ • Swift → SIL → LLVM-IR →

    X86_64 17
  18. ίϯύΠϥͷग़ྗΛ؍࡯ • ίϯύΠϥͷ్தঢ়ଶΛग़ྗ • ࢼͨ͠έʔε͔͠Θ͔Βͳ͍ 18

  19. ίϯύΠϥͷιʔείʔυΛಡΉ • ໢ཏతͳ೺Ѳ͕Մೳ • Φʔϓϯιʔε • https://github.com/apple/swift 19

  20. Generics ͷ࣮૷ ग़ྗͷ؍࡯ 20

  21. ίϯύΠϧखॱͷ࠶ܝ Swift → SIL → LLVM-IR → X86_64 21

  22. SIL ʹม׵ͯ͠ΈΔ func takeX<X>(_ x: X) { ... } 22

  23. SIL ʹม׵ͯ͠ΈΔ // aaa.swift func takeX<X>(_ x: X) { ...

    } $ swiftc -emit-sil aaa.swift > aaa.sil 23
  24. SIL Ͱͷ takeX sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed

    X) -> () 24
  25. sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed X) -> ()

    <X>: δΣωϦοΫύϥϝʔλɻ 25
  26. SILͷܕγεςϜ • SIL͸SwiftͷܕγεςϜΛ࣋ͭ • δΣωϦΫε΋࣋ͭ 26

  27. LLVM-IR ʹม׵ͯ͠ΈΔ func takeX<X>(_ x: X) 27

  28. LLVM-IR ʹม׵ͯ͠ΈΔ // aaa.swift func takeX<X>(_ x: X) $ swiftc

    -emit-ir aaa.swift > aaa.ll 28
  29. LLVM-IR Ͱͷ takeX ; aaa.ll define hidden swiftcc void @"$S3aaa5takeXyyxlF"(

    %swift.opaque* noalias nocapture, %swift.type* %X) ߏจ define ଐੑ... ฦΓ஋ͷܕ ؔ਺໊ ( ୈ1Ҿ਺ͷܕ ୈ1Ҿ਺ͷ໊લ, ୈ2Ҿ਺ͷܕ ୈ2Ҿ਺ͷ໊લ, ...) 29
  30. define hidden swiftcc void @"$S3aaa5takeXyyxlF"( %swift.opaque* noalias nocapture, %swift.type* %X)

    • ୈ1Ҿ਺ͷܕ͸ %swift.opaque* • * ͸ϙΠϯλ • opaque͸ʮෆ໌ͳԿ͔ʯ • Swiftʹ͓͚ΔҾ਺ x: X ͱରԠʁ • ৗʹϙΠϯλͳΒαΠζ͸Ұఆ 30
  31. define hidden swiftcc void @"$S3aaa5takeXyyxlF"( %swift.opaque* noalias nocapture, %swift.type* %X)

    • ୈ2Ҿ਺ͷܕ͸ %swift.type* • ୈ2Ҿ਺ͷ໊લ͸ %X • Swiftʹ͓͚Δܕύϥϝʔλ <X> ͱରԠʁ 31
  32. δΣωϦΫεͷ͘͠Έ • X ܕͷ஋͸ৗʹϙΠϯλ౉͠ • Swiftͷܕύϥϝʔλ <X> → Ҿ਺ %X

    • <X> ʹ͍ͭͯͷϝλ৘ใʁ 32
  33. ༧૝͕ͨ͘͞Μ • ܕ͔͠ௐ΂͍ͯͳ͍ • ݺͼग़͠ଆ΋ௐ΂ΒΕΔ • ݺͼग़͞Εଆ΋ௐ΂ΒΕΔ 33

  34. ΫΠζ func take2X<X>(_ x1: X, _ x2: X) • LLVM-IR

    Ͱ %swift.type* ͸͍ͭ͘ʁ • X ͸ x1 ͱ x2 ͕͋Δ͔Βɺ2ͭʁ • <X> ͸1͔ͭͩΒɺ1ͭʁ 34
  35. LLVM-IR Ͱͷ take2X define hidden swiftcc void @"$S3aaa6take2Xyyx_xtlF"( %swift.opaque* noalias

    nocapture, %swift.opaque* noalias nocapture, %swift.type* %X) 35
  36. ؍࡯ख๏ͷ݀ • ؍࡯ͨ͠ࣄ͔͠Θ͔Βͳ͍ɻ • Ҿ਺͕3ͭͷ৔߹͸ʁ 36

  37. Genericsͷ࣮૷ ίϯύΠϥͷιʔεΛಡΉ 37

  38. IRGen SIL → LLVM-IR ͷม׵Ϟδϡʔϧ swift/lib/IRGen 38

  39. swift/lib/IRGen/GenDecl.cpp llvm::Function * IRGenModule::getAddrOfSILFunction(SILFunction *f, ForDefinition_t forDefinition) SILͷؔ਺ʹରԠ͢ΔLLVM-IRͷؔ਺Λऔಘɺ ·ͨ͸ੜ੒͢Δؔ਺ɻ 39

  40. llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, ForDefinition_t forDefinition) { LinkEntity entity = LinkEntity::forSILFunction(f);

    // Check whether we've created the function already. // FIXME: We should integrate this into the LinkEntity cache more cleanly. llvm::Function *fn = Module.getFunction(f->getName()); if (fn) { if (forDefinition) updateLinkageForDefinition(*this, fn, entity); return fn; } // If it's a Clang declaration, ask Clang to generate the IR declaration. // This might generate new functions, so we should do it before computing // the insert-before point. llvm::Constant *clangAddr = nullptr; if (auto clangDecl = f->getClangDecl()) { auto globalDecl = getClangGlobalDeclForFunction(clangDecl); clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition); } bool isDefinition = f->isDefinition(); bool hasOrderNumber = isDefinition; unsigned orderNumber = ~0U; llvm::Function *insertBefore = nullptr; // If the SIL function has a definition, we should have an order // number for it; make sure to insert it in that position relative // to other ordered functions. if (hasOrderNumber) { orderNumber = IRGen.getFunctionOrder(f); if (auto emittedFunctionIterator = EmittedFunctionsByOrder.findLeastUpperBound(orderNumber)) insertBefore = *emittedFunctionIterator; } // If it's a Clang declaration, check whether Clang gave us a declaration. if (clangAddr) { fn = dyn_cast<llvm::Function>(clangAddr->stripPointerCasts()); // If we have a function, move it to the appropriate position. if (fn) { if (hasOrderNumber) { auto &fnList = Module.getFunctionList(); fnList.remove(fn); fnList.insert(llvm::Module::iterator(insertBefore), fn); EmittedFunctionsByOrder.insert(orderNumber, fn); } return fn; } // Otherwise, if we have a lazy definition for it, be sure to queue that up. } else if (isDefinition && !forDefinition && !f->isPossiblyUsedExternally() && !hasCodeCoverageInstrumentation(*f, getSILModule())) { IRGen.addLazyFunction(f); } Signature signature = getSignature(f->getLoweredFunctionType()); auto &attrs = signature.getMutableAttributes(); LinkInfo link = LinkInfo::get(*this, entity, forDefinition); switch (f->getInlineStrategy()) { case NoInline: attrs = attrs.addAttribute(signature.getType()->getContext(), llvm::AttributeList::FunctionIndex, llvm::Attribute::NoInline); break; case AlwaysInline: // FIXME: We do not currently transfer AlwaysInline since doing so results // in test failures, which must be investigated first. case InlineDefault: break; } if (isReadOnlyFunction(f)) { attrs = attrs.addAttribute(signature.getType()->getContext(), llvm::AttributeList::FunctionIndex, llvm::Attribute::ReadOnly); } fn = createFunction(*this, link, signature, insertBefore, f->getOptimizationMode()); // If `hasCReferences` is true, then the function is either marked with // @_silgen_name OR @_cdecl. If it is the latter, it must have a definition // associated with it. The combination of the two allows us to identify the // @_silgen_name functions. These are locally defined function thunks used in // the standard library. Do not give them DLLImport DLL Storage. if (useDllStorage() && f->hasCReferences() && !forDefinition) fn->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass); // If we have an order number for this function, set it up as appropriate. if (hasOrderNumber) { EmittedFunctionsByOrder.insert(orderNumber, fn); } return fn; } 40
  41. llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, ForDefinition_t forDefinition) { ... llvm::Function *fn =

    Module.getFunction(f->getName()); if (fn) { ... return fn; } ... Signature signature = getSignature(f->getLoweredFunctionType()); ... fn = createFunction(*this, link, signature, insertBefore, f->getOptimizationMode()); ... return fn; } Ωϟογϡύλʔϯ 41
  42. llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, ForDefinition_t forDefinition) { ... llvm::Function *fn =

    Module.getFunction(f->getName()); if (fn) { ... return fn; } ... Signature signature = getSignature(f->getLoweredFunctionType()); ... fn = createFunction(*this, link, signature, insertBefore, f->getOptimizationMode()); ... return fn; } SILؔ਺ͷܕˠLLVM-IRؔ਺ͷܕ 42
  43. Signature IRGenModule::getSignature(CanSILFunctionType type) { auto &sigInfo = getFuncSignatureInfoForLowered(*this, type); return

    sigInfo.getSignature(*this); } ॲཧΛܕʹ੾Γग़͍ͯ͠Δ 43
  44. Signature FuncSignatureInfo::getSignature(IRGenModule &IGM) const { // If it's already been

    filled in, we're done. if (TheSignature.isValid()) return TheSignature; // Update the cache and return. TheSignature = Signature::getUncached(IGM, FormalType); assert(TheSignature.isValid()); return TheSignature; } Ωϟογϡύλʔϯ 44
  45. Signature Signature::getUncached(IRGenModule &IGM, CanSILFunctionType formalType) { GenericContextScope scope(IGM, formalType->getGenericSignature()); SignatureExpansion

    expansion(IGM, formalType); expansion.expandFunctionType(); return expansion.getSignature(); } ॲཧΛܕʹ੾Γग़͍ͯ͠Δ 45
  46. Signature Signature::getUncached(IRGenModule &IGM, CanSILFunctionType formalType) { GenericContextScope scope(IGM, formalType->getGenericSignature()); SignatureExpansion

    expansion(IGM, formalType); expansion.expandFunctionType(); return expansion.getSignature(); } ؔ਺ͷܕΛੜ੒ 46
  47. void SignatureExpansion::expandFunctionType() { switch (FnType->getLanguage()) { case SILFunctionLanguage::Swift: { expandResult();

    expandParameters(); return; } case SILFunctionLanguage::C: expandExternalSignatureTypes(); return; } llvm_unreachable("bad abstract calling convention"); } 47
  48. void SignatureExpansion::expandFunctionType() { switch (FnType->getLanguage()) { case SILFunctionLanguage::Swift: { expandResult();

    expandParameters(); return; } case SILFunctionLanguage::C: expandExternalSignatureTypes(); return; } llvm_unreachable("bad abstract calling convention"); } ฦΓ஋ͷܕΛੜ੒ 48
  49. void SignatureExpansion::expandFunctionType() { switch (FnType->getLanguage()) { case SILFunctionLanguage::Swift: { expandResult();

    expandParameters(); return; } case SILFunctionLanguage::C: expandExternalSignatureTypes(); return; } llvm_unreachable("bad abstract calling convention"); } Ҿ਺ͷܕΛੜ੒ 49
  50. void SignatureExpansion::expandParameters() { assert(FnType->getRepresentation() != SILFunctionTypeRepresentation::Block && "block with non-C

    calling conv?!"); // First, if this is a coroutine, add the coroutine-context parameter. switch (FnType->getCoroutineKind()) { case SILCoroutineKind::None: break; case SILCoroutineKind::YieldOnce: case SILCoroutineKind::YieldMany: addCoroutineContextParameter(); break; } // Next, the formal parameters. But 'self' is treated as the // context if it has pointer representation. auto params = FnType->getParameters(); bool hasSelfContext = false; if (hasSelfContextParameter(FnType)) { hasSelfContext = true; params = params.drop_back(); } for (auto param : params) { expand(param); } // Next, the generic signature. if (hasPolymorphicParameters(FnType)) expandPolymorphicSignature(IGM, FnType, ParamIRTypes); // Context is next. if (hasSelfContext) { auto curLength = ParamIRTypes.size(); (void) curLength; if (claimSelf()) IGM.addSwiftSelfAttributes(Attrs, curLength); expand(FnType->getSelfParameter()); assert(ParamIRTypes.size() == curLength + 1 && "adding 'self' added unexpected number of parameters"); } else { auto needsContext = [=]() -> bool { switch (FnType->getRepresentation()) { case SILFunctionType::Representation::Block: llvm_unreachable("adding block parameter in Swift CC expansion?"); // Always leave space for a context argument if we have an error result. case SILFunctionType::Representation::CFunctionPointer: case SILFunctionType::Representation::Method: case SILFunctionType::Representation::WitnessMethod: case SILFunctionType::Representation::ObjCMethod: case SILFunctionType::Representation::Thin: case SILFunctionType::Representation::Closure: return FnType->hasErrorResult(); case SILFunctionType::Representation::Thick: return true; } llvm_unreachable("bad representation kind"); }; if (needsContext()) { if (claimSelf()) IGM.addSwiftSelfAttributes(Attrs, ParamIRTypes.size()); ParamIRTypes.push_back(IGM.RefCountedPtrTy); } } // Error results are last. We always pass them as a pointer to the // formal error type; LLVM will magically turn this into a non-pointer // if we set the right attribute. if (FnType->hasErrorResult()) { if (claimError()) IGM.addSwiftErrorAttributes(Attrs, ParamIRTypes.size()); llvm::Type *errorType = IGM.getStorageType( getSILFuncConventions().getSILType(FnType->getErrorResult())); ParamIRTypes.push_back(errorType->getPointerTo()); } // Witness methods have some extra parameter types. if (FnType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { expandTrailingWitnessSignature(IGM, FnType, ParamIRTypes); } } 50
  51. void SignatureExpansion::expandParameters() { ... auto params = FnType->getParameters(); ... for

    (auto param : params) { expand(param); } // Next, the generic signature. if (hasPolymorphicParameters(FnType)) expandPolymorphicSignature(IGM, FnType, ParamIRTypes); ... } 51
  52. void SignatureExpansion::expandParameters() { ... auto params = FnType->getParameters(); ... for

    (auto param : params) { expand(param); } // Next, the generic signature. if (hasPolymorphicParameters(FnType)) expandPolymorphicSignature(IGM, FnType, ParamIRTypes); ... } SILؔ਺ͷҾ਺ͦΕͧΕʹ͍ͭͯɺ LLVM-IRؔ਺ͷҾ਺Λੜ੒ 52
  53. void SignatureExpansion::expandParameters() { ... auto params = FnType->getParameters(); ... for

    (auto param : params) { expand(param); } // Next, the generic signature. if (hasPolymorphicParameters(FnType)) expandPolymorphicSignature(IGM, FnType, ParamIRTypes); ... } δΣωϦΫεͷ৘ใʹ͍ͭͯɺ LLVM-IRؔ਺ͷҾ਺Λੜ੒ 53
  54. void irgen::expandPolymorphicSignature(IRGenModule &IGM, CanSILFunctionType polyFn, SmallVectorImpl<llvm::Type*> &out) { ExpandPolymorphicSignature(IGM, polyFn).expand(out);

    } ॲཧΛܕʹ੾Γग़͍ͯ͠Δ 54
  55. class ExpandPolymorphicSignature : public PolymorphicConvention { ... void expand(SmallVectorImpl<llvm::Type*> &out)

    { ... enumerateUnfulfilledRequirements([&](GenericRequirement reqt) { out.push_back(reqt.Protocol ? IGM.WitnessTablePtrTy : IGM.TypeMetadataPtrTy); }); } ... } 55
  56. class ExpandPolymorphicSignature : public PolymorphicConvention { ... void expand(SmallVectorImpl<llvm::Type*> &out)

    { ... enumerateUnfulfilledRequirements([&](GenericRequirement reqt) { out.push_back(reqt.Protocol ? IGM.WitnessTablePtrTy : IGM.TypeMetadataPtrTy); }); } ... } δΣωϦοΫύϥϝʔλͱɺϓϩτίϧ੍໿ʹ͍ͭͯ܁Γฦ ͢ɻ 56
  57. class ExpandPolymorphicSignature : public PolymorphicConvention { ... void expand(SmallVectorImpl<llvm::Type*> &out)

    { ... enumerateUnfulfilledRequirements([&](GenericRequirement reqt) { out.push_back(reqt.Protocol ? IGM.WitnessTablePtrTy : IGM.TypeMetadataPtrTy); }); } ... } IGM.TypeMetadataPtrTy = %swift.type* 57
  58. ͜͜·Ͱ·ͱΊ ܕύϥϝʔλ͸ίϯύΠϧޙ͸ܕͷ৘ใΛද͢஋ͷҾ਺ʹͳΔ 58

  59. X ͷऔѻ • X ΛऔΓѻ͏ͨΊͷ৘ใ͕ɺҾ਺Ͱ౉͞ΕΔ • ͲΜͳ৘ใʁ • X ΛऔΓѻ͏

    ͱ͸Կ͔ʁ 59
  60. X ʹରͯ͠Կ͕Ͱ͖Δʁ func takeX<X>(_ x: X) { ... } •

    ϝιουݺ΂ͳ͍ • initͰ͖ͳ͍ 60
  61. X ʹରͯ͠Ͱ͖Δ͜ͱ func take<X>(_ x: X) { let x2: X

    = x } • ֓Ͷ͜ΕͰશͯ 61
  62. func take<X>(_ x: X) { let x2: X = x

    } • x2ͷྖҬͷ֬อ • x2΁ͷίϐʔ • x2ͷഁغ (ؔ਺୤ग़࣌) • x2ͷྖҬͷղ์ (ؔ਺୤ग़࣌) 62
  63. ྖҬͷ֬อʹඞཁͳ৘ใ • ܕͷαΠζͱΞϥΠϝϯτ • ղ์͸֬อ࣌ͷ৘ใͷΈͰOK 63

  64. ίϐʔͱഁغʹඞཁͳૢ࡞ ࢀরܕͷ৔߹: retain countૢ࡞ ஋ܕͷ৔߹: stored property ͝ͱʹૢ࡞ struct Entry

    { var id: Int var name: String var view: UIView } 64
  65. Value Witness Table • ܕΛऔΓѻ͏ͨΊͷؔ਺΍஋Λఆٛͨ͠ςʔϒϧ • ܕ͝ͱʹ1ͭఆٛ • ϝλλΠϓ(Int.self ͳͲ)͔ΒऔΓग़ͤΔ

    https://github.com/apple/swift/blob/master/docs/ABI/ TypeMetadata.rst 65
  66. Value Witness Table ͷٙࣅίʔυ var vwt = VWT() vwt[0] =

    ... vwt[1] = destroy vwt[2] = initializeWithCopy vwt[3] = assignWithCopy vwt[4] = initializeWithTake vwt[5] = assignWithTake vwt[6] = ... vwt[7] = ... vwt[8] = size vwt[9] = flags (alignment) vwt[10] = stride 66
  67. 4ͭͷίϐʔ 67

  68. • ίϐʔઌ ͕ະॳظԽ͔Ͳ͏͔ɻ • ະॳظԽ: initialize • ॳظԽࡁΈ: assign •

    ίϐʔݩ Λഁغ͢Δ͔Ͳ͏͔ɻ • ഁغ͠ͳ͍: copy • ഁغ͢Δ: take • 2x2ͷ૊Έ߹ΘͤͰ4छྨ 68
  69. ૊Έ߹Θͤͷྫ initializeWithTake • initialize: ະॳظԽͷίϐʔઌΛॳظԽ͢Δ • take: ίϐʔݩ͸ഁغ͢Δ 69

  70. Value Witness Table Λௐ΂Δ • ࢖༻͢Δଆ, ݺͼग़͞Εଆ • ݺͼग़͠ଆ •

    ςʔϒϧͷੜ੒ॲཧ 70
  71. Value Witness Table ࢖༻͢Δଆ, Swift 71

  72. func takeX<X>(_ x: X) { let x2 = x }

    72
  73. Value Witness Table ࢖༻͢Δଆ, SIL 73

  74. // takeX<A>(_:) sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed X)

    -> () { // %0 // users: %3, %1 bb0(%0 : $*X): debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1 %2 = alloc_stack $X, let, name "x2" // users: %5, %4, %3 copy_addr %0 to [initialization] %2 : $*X // id: %3 destroy_addr %2 : $*X // id: %4 dealloc_stack %2 : $*X // id: %5 %6 = tuple () // user: %7 return %6 : $() // id: %7 } // end sil function '$S3aaa5takeXyyxlF' 74
  75. // takeX<A>(_:) sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed X)

    -> () { // %0 // users: %3, %1 bb0(%0 : $*X): debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1 %2 = alloc_stack $X, let, name "x2" // users: %5, %4, %3 copy_addr %0 to [initialization] %2 : $*X // id: %3 destroy_addr %2 : $*X // id: %4 dealloc_stack %2 : $*X // id: %5 %6 = tuple () // user: %7 return %6 : $() // id: %7 } // end sil function '$S3aaa5takeXyyxlF' 75
  76. // takeX<A>(_:) sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed X)

    -> () { // %0 // users: %3, %1 bb0(%0 : $*X): debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1 %2 = alloc_stack $X, let, name "x2" // users: %5, %4, %3 copy_addr %0 to [initialization] %2 : $*X // id: %3 destroy_addr %2 : $*X // id: %4 dealloc_stack %2 : $*X // id: %5 %6 = tuple () // user: %7 return %6 : $() // id: %7 } // end sil function '$S3aaa5takeXyyxlF' 76
  77. // takeX<A>(_:) sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed X)

    -> () { // %0 // users: %3, %1 bb0(%0 : $*X): debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1 %2 = alloc_stack $X, let, name "x2" // users: %5, %4, %3 copy_addr %0 to [initialization] %2 : $*X // id: %3 destroy_addr %2 : $*X // id: %4 dealloc_stack %2 : $*X // id: %5 %6 = tuple () // user: %7 return %6 : $() // id: %7 } // end sil function '$S3aaa5takeXyyxlF' 77
  78. // takeX<A>(_:) sil hidden @$S3aaa5takeXyyxlF : $@convention(thin) <X> (@in_guaranteed X)

    -> () { // %0 // users: %3, %1 bb0(%0 : $*X): debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1 %2 = alloc_stack $X, let, name "x2" // users: %5, %4, %3 copy_addr %0 to [initialization] %2 : $*X // id: %3 destroy_addr %2 : $*X // id: %4 dealloc_stack %2 : $*X // id: %5 %6 = tuple () // user: %7 return %6 : $() // id: %7 } // end sil function '$S3aaa5takeXyyxlF' 78
  79. Value Witness Table ࢖༻͢Δଆ, LLVM-IR 79

  80. define hidden swiftcc void @"$S3aaa5takeXyyxlF"(%swift.opaque* noalias nocapture, %swift.type* %X) #0

    { entry: %X1 = alloca %swift.type*, align 8 %x2.addr = alloca i8*, align 8 %1 = bitcast i8** %x2.addr to %swift.opaque** store %swift.opaque* null, %swift.opaque** %1, align 8 store %swift.type* %X, %swift.type** %X1, align 8 %2 = bitcast %swift.type* %X to i8*** %3 = getelementptr inbounds i8**, i8*** %2, i64 -1 %X.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !13, !dereferenceable !14 %4 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 8 %5 = load i8*, i8** %4, align 8, !invariant.load !13 %size = ptrtoint i8* %5 to i64 %x2 = alloca i8, i64 %size, align 16 call void @llvm.lifetime.start.p0i8(i64 -1, i8* %x2) %6 = bitcast i8* %x2 to %swift.opaque* store i8* %x2, i8** %x2.addr, align 8 %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 %10 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 1 %11 = load i8*, i8** %10, align 8, !invariant.load !13 %destroy = bitcast i8* %11 to void (%swift.opaque*, %swift.type*)* call void %destroy(%swift.opaque* noalias %6, %swift.type* %X) #3 %12 = bitcast %swift.opaque* %6 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret void } 80
  81. define hidden swiftcc void @"$S3aaa5takeXyyxlF"( %swift.opaque* noalias nocapture, %swift.type* %X)

    #0 { entry: %X1 = alloca %swift.type*, align 8 %x2.addr = alloca i8*, align 8 %1 = bitcast i8** %x2.addr to %swift.opaque** store %swift.opaque* null, %swift.opaque** %1, align 8 store %swift.type* %X, %swift.type** %X1, align 8 %2 = bitcast %swift.type* %X to i8*** %3 = getelementptr inbounds i8**, i8*** %2, i64 -1 %X.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !13, !dereferenceable !14 %4 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 8 %5 = load i8*, i8** %4, align 8, !invariant.load !13 %size = ptrtoint i8* %5 to i64 %x2 = alloca i8, i64 %size, align 16 81
  82. define hidden swiftcc void @"$S3aaa5takeXyyxlF"( %swift.opaque* noalias nocapture, %swift.type* %X)

    #0 { entry: %X1 = alloca %swift.type*, align 8 %x2.addr = alloca i8*, align 8 %1 = bitcast i8** %x2.addr to %swift.opaque** store %swift.opaque* null, %swift.opaque** %1, align 8 store %swift.type* %X, %swift.type** %X1, align 8 %2 = bitcast %swift.type* %X to i8*** %3 = getelementptr inbounds i8**, i8*** %2, i64 -1 %X.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !13, !dereferenceable !14 %4 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 8 %5 = load i8*, i8** %4, align 8, !invariant.load !13 %size = ptrtoint i8* %5 to i64 %x2 = alloca i8, i64 %size, align 16 82
  83. var vwt = VWT() vwt[0] = ... vwt[1] = destroy

    vwt[2] = initializeWithCopy vwt[3] = assignWithCopy vwt[4] = initializeWithTake vwt[5] = assignWithTake vwt[6] = ... vwt[7] = ... vwt[8] = size vwt[9] = flags (alignment) vwt[10] = stride 83
  84. %x2 = alloca i8, i64 %size, align 16 call void

    @llvm.lifetime.start.p0i8(i64 -1, i8* %x2) %6 = bitcast i8* %x2 to %swift.opaque* store i8* %x2, i8** %x2.addr, align 8 %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 %10 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 1 %11 = load i8*, i8** %10, align 8, !invariant.load !13 %destroy = bitcast i8* %11 to void (%swift.opaque*, %swift.type*)* call void %destroy(%swift.opaque* noalias %6, %swift.type* %X) #3 %12 = bitcast %swift.opaque* %6 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret void } 84
  85. var vwt = VWT() vwt[0] = ... vwt[1] = destroy

    vwt[2] = initializeWithCopy vwt[3] = assignWithCopy vwt[4] = initializeWithTake vwt[5] = assignWithTake vwt[6] = ... vwt[7] = ... vwt[8] = size vwt[9] = flags (alignment) vwt[10] = stride 85
  86. %x2 = alloca i8, i64 %size, align 16 call void

    @llvm.lifetime.start.p0i8(i64 -1, i8* %x2) %6 = bitcast i8* %x2 to %swift.opaque* store i8* %x2, i8** %x2.addr, align 8 %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 %10 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 1 %11 = load i8*, i8** %10, align 8, !invariant.load !13 %destroy = bitcast i8* %11 to void (%swift.opaque*, %swift.type*)* call void %destroy(%swift.opaque* noalias %6, %swift.type* %X) #3 %12 = bitcast %swift.opaque* %6 to i8* call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12) ret void } 86
  87. var vwt = VWT() vwt[0] = ... vwt[1] = destroy

    vwt[2] = initializeWithCopy vwt[3] = assignWithCopy vwt[4] = initializeWithTake vwt[5] = assignWithTake vwt[6] = ... vwt[7] = ... vwt[8] = size vwt[9] = flags (alignment) vwt[10] = stride 87
  88. Value Witness Table ࢖༻͢Δଆ, ίϯύΠϥ 88

  89. SIL: copy_addr ໋ྩ // takeX<A>(_:) sil hidden @$S3aaa5takeXyyxlF : $@convention(thin)

    <X> (@in_guaranteed X) -> () { ... copy_addr %0 to [initialization] %2 : $*X // id: %3 ... } // end sil function '$S3aaa5takeXyyxlF' LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 89
  90. IRGenSILFunction VisitorύλʔϯͰSIL໋ྩ͝ͱʹม׵ void IRGenSILFunction::visitCopyAddrInst(swift::CopyAddrInst *i) 90

  91. void IRGenSILFunction::visitCopyAddrInst(swift::CopyAddrInst *i) { SILType addrTy = i->getSrc()->getType(); const TypeInfo

    &addrTI = getTypeInfo(addrTy); Address src = getLoweredAddress(i->getSrc()); // See whether we have a deferred fixed-size buffer initialization. auto &loweredDest = getLoweredValue(i->getDest()); assert(!loweredDest.isUnallocatedAddressInBuffer()); Address dest = loweredDest.getAnyAddress(); if (i->isInitializationOfDest()) { if (i->isTakeOfSrc()) { addrTI.initializeWithTake(*this, dest, src, addrTy, false); } else { addrTI.initializeWithCopy(*this, dest, src, addrTy, false); } } else { if (i->isTakeOfSrc()) { addrTI.assignWithTake(*this, dest, src, addrTy, false); } else { addrTI.assignWithCopy(*this, dest, src, addrTy, false); } } } 91
  92. void IRGenSILFunction::visitCopyAddrInst(swift::CopyAddrInst *i) { SILType addrTy = i->getSrc()->getType(); const TypeInfo

    &addrTI = getTypeInfo(addrTy); Address src = getLoweredAddress(i->getSrc()); // See whether we have a deferred fixed-size buffer initialization. auto &loweredDest = getLoweredValue(i->getDest()); assert(!loweredDest.isUnallocatedAddressInBuffer()); Address dest = loweredDest.getAnyAddress(); 92
  93. if (i->isInitializationOfDest()) { if (i->isTakeOfSrc()) { addrTI.initializeWithTake(*this, dest, src, addrTy,

    false); } else { addrTI.initializeWithCopy(*this, dest, src, addrTy, false); } } else { if (i->isTakeOfSrc()) { addrTI.assignWithTake(*this, dest, src, addrTy, false); } else { addrTI.assignWithCopy(*this, dest, src, addrTy, false); } } } 93
  94. TypeInfo ΫϥεܧঝΛ࢖ͬͯܕͷ෼ྨ͝ͱʹ ίϯύΠϧॲཧΛ੾Γସ͑Δɻ struct: LoadableStructTypeInfo class: ClassTypeInfo <X>: OpaqueArchetypeTypeInfo 94

  95. OpaqueArchetypeTypeInfo initializeWithCopy͸਌Ϋϥεͷ ResilientTypeInfoͷ࣮૷Λܧঝɻ template <class Impl> class ResilientTypeInfo { ...

    void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const override { emitInitializeWithCopyCall(IGF, T, dest, src); } ... }; 95
  96. void irgen::emitInitializeWithCopyCall(IRGenFunction &IGF, SILType T, Address dest, Address src) {

    llvm::Value *metadata; auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithCopy); auto destPtr = emitCastToOpaquePtr(IGF, dest); auto srcPtr = emitCastToOpaquePtr(IGF, src); IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata}); } 96
  97. C++ auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithCopy); auto destPtr =

    emitCastToOpaquePtr(IGF, dest); auto srcPtr = emitCastToOpaquePtr(IGF, src); IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata}); LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 97
  98. C++ auto fn = IGF.emitValueWitnessFunctionRef(T, metadata, ValueWitness::InitializeWithCopy); auto destPtr =

    emitCastToOpaquePtr(IGF, dest); auto srcPtr = emitCastToOpaquePtr(IGF, src); IGF.Builder.CreateCall(fn, {destPtr, srcPtr, metadata}); LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 98
  99. FunctionPointer IRGenFunction::emitValueWitnessFunctionRef(SILType type, llvm::Value *&metadataSlot, ValueWitness index) { assert(isValueWitnessFunction(index)); auto

    key = LocalTypeDataKind::forValueWitness(index); if (auto witness = tryGetLocalTypeDataForLayout(type, key)) { metadataSlot = emitTypeMetadataRefForLayout(type); auto signature = IGM.getValueWitnessSignature(index); return FunctionPointer(witness, signature); } auto vwtable = emitValueWitnessTableRef(type, &metadataSlot); auto witness = emitLoadOfValueWitnessFunction(*this, vwtable, index); setScopedLocalTypeDataForLayout(type, key, witness.getPointer()); return witness; } 99
  100. FunctionPointer IRGenFunction::emitValueWitnessFunctionRef(SILType type, llvm::Value *&metadataSlot, ValueWitness index) { ... auto

    vwtable = emitValueWitnessTableRef(type, &metadataSlot); auto witness = emitLoadOfValueWitnessFunction(*this, vwtable, index); ... return witness; } ValueWitness::InitializeWithCopy == 2 100
  101. static FunctionPointer emitLoadOfValueWitnessFunction(IRGenFunction &IGF, llvm::Value *table, ValueWitness index) { assert(isValueWitnessFunction(index));

    llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, table, index); auto label = getValueWitnessLabel(index); auto signature = IGF.IGM.getValueWitnessSignature(index); auto type = signature.getType()->getPointerTo(); witness = IGF.Builder.CreateBitCast(witness, type, label); return FunctionPointer(witness, signature); } 101
  102. C++ llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, table, index); auto label =

    getValueWitnessLabel(index); auto signature = IGF.IGM.getValueWitnessSignature(index); auto type = signature.getType()->getPointerTo(); witness = IGF.Builder.CreateBitCast(witness, type, label); return FunctionPointer(witness, signature); LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 102
  103. C++ llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, table, index); auto label =

    getValueWitnessLabel(index); auto signature = IGF.IGM.getValueWitnessSignature(index); auto type = signature.getType()->getPointerTo(); witness = IGF.Builder.CreateBitCast(witness, type, label); return FunctionPointer(witness, signature); LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 103
  104. llvm::Value *irgen::emitInvariantLoadOfOpaqueWitness(IRGenFunction &IGF, llvm::Value *table, WitnessIndex index) { assert(table->getType() ==

    IGF.IGM.WitnessTablePtrTy); // GEP to the appropriate index, avoiding spurious IR in the trivial case. llvm::Value *slot = table; if (index.getValue() != 0) slot = IGF.Builder.CreateConstInBoundsGEP1_32( /*Ty=*/nullptr, table, index.getValue()); auto witness = IGF.Builder.CreateLoad(Address(slot, IGF.IGM.getPointerAlignment())); IGF.setInvariantLoad(witness); return witness; } 104
  105. C++; index.getValue() == 2 // GEP to the appropriate index,

    avoiding spurious IR in the trivial case. llvm::Value *slot = table; if (index.getValue() != 0) slot = IGF.Builder.CreateConstInBoundsGEP1_32( /*Ty=*/nullptr, table, index.getValue()); auto witness = IGF.Builder.CreateLoad(Address(slot, IGF.IGM.getPointerAlignment())); IGF.setInvariantLoad(witness); return witness; LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 105
  106. C++ // GEP to the appropriate index, avoiding spurious IR

    in the trivial case. llvm::Value *slot = table; if (index.getValue() != 0) slot = IGF.Builder.CreateConstInBoundsGEP1_32( /*Ty=*/nullptr, table, index.getValue()); auto witness = IGF.Builder.CreateLoad(Address(slot, IGF.IGM.getPointerAlignment())); IGF.setInvariantLoad(witness); return witness; LLVM-IR %7 = getelementptr inbounds i8*, i8** %X.valueWitnesses, i32 2 %8 = load i8*, i8** %7, align 8, !invariant.load !13 %initializeWithCopy = bitcast i8* %8 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)* %9 = call %swift.opaque* %initializeWithCopy( %swift.opaque* noalias %6, %swift.opaque* noalias %0, %swift.type* %X) #3 106
  107. Value Witness Table • ݺͼग़͠ଆ • ςʔϒϧͷੜ੒ λΠϜΞοϓ 107

  108. ·ͱΊ • δΣωϦοΫύϥϝʔλ͸࣮ߦ࣌ʹ͸ܕ৘ใͷҾ਺ʹͳΔ • ܕ৘ใʹ͸ Value Witness Table ͕ೖ͍ͬͯͯɺ औΓѻ͏ͨΊͷؔ਺܈͕ೖ͍ͬͯΔɻ

    • Swift࣮૷͸தؒग़ྗͱίϯύΠϥιʔεͷ྆ํ͔Βֶ΅͏ɻ 108
  109. એ఻ Θ͍Θ͍swiftc: SwiftίϯύΠϥͷษڧձ https://iosdiscord.connpass.com/event/92738/ ࣍ճ ୈ5ճ 9/12 109

  110. એ఻ Θ͍Θ͍swiftc: SwiftίϯύΠϥͷษڧձ https://iosdiscord.connpass.com/event/92738/ ࣍ճ ୈ5ճ 9/12 110

  111. એ఻ Θ͍Θ͍swiftc: SwiftίϯύΠϥͷษڧձ https://iosdiscord.connpass.com/event/92738/ ͜ͷ͋ͱ͙͢ʂ ࣍࣍ճ ୈ5ճ 9/12 111