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

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

    } takeX(Stone()) class Cat { ... } takeX(Cat()) 10
  2. 12.

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

    { ... } Stone() // 24όΠτ Cat() // 8όΠτ 12
  3. 23.

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

    } $ swiftc -emit-sil aaa.swift > aaa.sil 23
  4. 29.

    LLVM-IR Ͱͷ takeX ; aaa.ll define hidden swiftcc void @"$S3aaa5takeXyyxlF"(

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

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

    • ୈ1Ҿ਺ͷܕ͸ %swift.opaque* • * ͸ϙΠϯλ • opaque͸ʮෆ໌ͳԿ͔ʯ • Swiftʹ͓͚ΔҾ਺ x: X ͱରԠʁ • ৗʹϙΠϯλͳΒαΠζ͸Ұఆ 30
  6. 31.

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

    • ୈ2Ҿ਺ͷܕ͸ %swift.type* • ୈ2Ҿ਺ͷ໊લ͸ %X • Swiftʹ͓͚Δܕύϥϝʔλ <X> ͱରԠʁ 31
  7. 34.

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

    Ͱ %swift.type* ͸͍ͭ͘ʁ • X ͸ x1 ͱ x2 ͕͋Δ͔Βɺ2ͭʁ • <X> ͸1͔ͭͩΒɺ1ͭʁ 34
  8. 35.

    LLVM-IR Ͱͷ take2X define hidden swiftcc void @"$S3aaa6take2Xyyx_xtlF"( %swift.opaque* noalias

    nocapture, %swift.opaque* noalias nocapture, %swift.type* %X) 35
  9. 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
  10. 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
  11. 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
  12. 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
  13. 45.

    Signature Signature::getUncached(IRGenModule &IGM, CanSILFunctionType formalType) { GenericContextScope scope(IGM, formalType->getGenericSignature()); SignatureExpansion

    expansion(IGM, formalType); expansion.expandFunctionType(); return expansion.getSignature(); } ॲཧΛܕʹ੾Γग़͍ͯ͠Δ 45
  14. 46.

    Signature Signature::getUncached(IRGenModule &IGM, CanSILFunctionType formalType) { GenericContextScope scope(IGM, formalType->getGenericSignature()); SignatureExpansion

    expansion(IGM, formalType); expansion.expandFunctionType(); return expansion.getSignature(); } ؔ਺ͷܕΛੜ੒ 46
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 55.

    class ExpandPolymorphicSignature : public PolymorphicConvention { ... void expand(SmallVectorImpl<llvm::Type*> &out)

    { ... enumerateUnfulfilledRequirements([&](GenericRequirement reqt) { out.push_back(reqt.Protocol ? IGM.WitnessTablePtrTy : IGM.TypeMetadataPtrTy); }); } ... } 55
  23. 56.

    class ExpandPolymorphicSignature : public PolymorphicConvention { ... void expand(SmallVectorImpl<llvm::Type*> &out)

    { ... enumerateUnfulfilledRequirements([&](GenericRequirement reqt) { out.push_back(reqt.Protocol ? IGM.WitnessTablePtrTy : IGM.TypeMetadataPtrTy); }); } ... } δΣωϦοΫύϥϝʔλͱɺϓϩτίϧ੍໿ʹ͍ͭͯ܁Γฦ ͢ɻ 56
  24. 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
  25. 60.

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

    ϝιουݺ΂ͳ͍ • initͰ͖ͳ͍ 60
  26. 62.

    func take<X>(_ x: X) { let x2: X = x

    } • x2ͷྖҬͷ֬อ • x2΁ͷίϐʔ • x2ͷഁغ (ؔ਺୤ग़࣌) • x2ͷྖҬͷղ์ (ؔ਺୤ग़࣌) 62
  27. 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
  28. 68.

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

    ίϐʔݩ Λഁغ͢Δ͔Ͳ͏͔ɻ • ഁغ͠ͳ͍: copy • ഁغ͢Δ: take • 2x2ͷ૊Έ߹ΘͤͰ4छྨ 68
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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