//===--- GenProto.cpp - Swift IR Generation for Protocols -----------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements IR generation for protocols in Swift. // // Protocols serve two masters: generic algorithms and existential // types. In either case, the size and structure of a type is opaque // to the code manipulating a value. Local values of the type must // be stored in fixed-size buffers (which can overflow to use heap // allocation), and basic operations on the type must be dynamically // delegated to a collection of information that "witnesses" the // truth that a particular type implements the protocol. // // In the comments throughout this file, three type names are used: // 'B' is the type of a fixed-size buffer // 'T' is the type which implements a protocol // 'W' is the type of a witness to the protocol // //===----------------------------------------------------------------------===// #include "swift/ABI/MetadataValues.h" #include "swift/AST/ASTContext.h" #include "swift/AST/CanTypeVisitor.h" #include "swift/AST/Types.h" #include "swift/AST/Decl.h" #include "swift/AST/IRGenOptions.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILValue.h" #include "swift/SIL/SILWitnessVisitor.h" #include "swift/SIL/TypeLowering.h" #include "clang/AST/DeclObjC.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/Module.h" #include "CallEmission.h" #include "Explosion.h" #include "FixedTypeInfo.h" #include "GenClass.h" #include "GenHeap.h" #include "GenMeta.h" #include "GenOpaque.h" #include "GenPoly.h" #include "GenType.h" #include "HeapTypeInfo.h" #include "IndirectTypeInfo.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenModule.h" #include "Linking.h" #include "NecessaryBindings.h" #include "NonFixedTypeInfo.h" #include "ProtocolInfo.h" #include "TypeInfo.h" #include "UnownedTypeInfo.h" #include "WeakTypeInfo.h" #include "GenProto.h" using namespace swift; using namespace irgen; namespace { /// The layout of an existential buffer. This is intended to be a /// small, easily-computed type that can be passed around by value. class OpaqueExistentialLayout { private: unsigned NumTables; // If you add anything to the layout computation, you might need // to update certain uses; check the external uses of getNumTables(). // For example, getAssignExistentialsFunction relies on being uniqued // for different layout kinds. public: explicit OpaqueExistentialLayout(unsigned numTables) : NumTables(numTables) {} unsigned getNumTables() const { return NumTables; } Size getSize(IRGenModule &IGM) const { return getFixedBufferSize(IGM) + IGM.getPointerSize() * (getNumTables() + 1); } Alignment getAlignment(IRGenModule &IGM) const { return getFixedBufferAlignment(IGM); } /* friend bool operator==(ExistentialLayout a, ExistentialLayout b) { return a.NumTables == b.NumTables; }*/ /// Given the address of an existential object, drill down to the /// buffer. Address projectExistentialBuffer(IRGenFunction &IGF, Address addr) const { return IGF.Builder.CreateStructGEP(addr, 0, Size(0)); } /// Given the address of an existential object, drill down to the /// witness-table field. Address projectWitnessTable(IRGenFunction &IGF, Address addr, unsigned which) const { assert(which < getNumTables()); return IGF.Builder.CreateStructGEP(addr, which + 2, getFixedBufferSize(IGF.IGM) + IGF.IGM.getPointerSize() * (which + 1)); } /// Given the address of an existential object, load its witness table. llvm::Value *loadWitnessTable(IRGenFunction &IGF, Address addr, unsigned which) const { return IGF.Builder.CreateLoad(projectWitnessTable(IGF, addr, which), "witness-table"); } /// Given the address of an existential object, drill down to the /// metadata field. Address projectMetadataRef(IRGenFunction &IGF, Address addr) { return IGF.Builder.CreateStructGEP(addr, 1, getFixedBufferSize(IGF.IGM)); } /// Given the address of an existential object, load its metadata /// object. llvm::Value *loadMetadataRef(IRGenFunction &IGF, Address addr) { return IGF.Builder.CreateLoad(projectMetadataRef(IGF, addr), addr.getAddress()->getName() + ".metadata"); } }; /// A concrete witness table, together with its known layout. class WitnessTable { llvm::Value *Table; const ProtocolInfo &Info; public: WitnessTable(llvm::Value *wtable, const ProtocolInfo &info) : Table(wtable), Info(info) {} llvm::Value *getTable() const { return Table; } const ProtocolInfo &getInfo() const { return Info; } }; } /// Given the address of an existential object, destroy it. static void emitDestroyExistential(IRGenFunction &IGF, Address addr, OpaqueExistentialLayout layout) { llvm::Value *metadata = layout.loadMetadataRef(IGF, addr); Address object = layout.projectExistentialBuffer(IGF, addr); emitDestroyBufferCall(IGF, metadata, object); } static llvm::Constant *getAssignExistentialsFunction(IRGenModule &IGM, llvm::Type *objectPtrTy, OpaqueExistentialLayout layout); namespace { /// A class which lays out a witness table in the abstract. class WitnessTableLayout : public SILWitnessVisitor { unsigned NumWitnesses = 0; SmallVector Entries; WitnessIndex getNextIndex() { return WitnessIndex(NumWitnesses++, /*isPrefix=*/false); } public: /// The next witness is an out-of-line base protocol. void addOutOfLineBaseProtocol(ProtocolDecl *baseProto) { Entries.push_back( WitnessTableEntry::forOutOfLineBase(baseProto, getNextIndex())); } void addMethod(FuncDecl *func) { Entries.push_back(WitnessTableEntry::forFunction(func, getNextIndex())); } void addConstructor(ConstructorDecl *ctor) { Entries.push_back(WitnessTableEntry::forFunction(ctor, getNextIndex())); } void addAssociatedType(AssociatedTypeDecl *ty, ArrayRef protos) { // An associated type takes up a spot for the type metadata and for the // witnesses to all its conformances. Entries.push_back( WitnessTableEntry::forAssociatedType(ty, getNextIndex())); for (auto *proto : protos) if (Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) ++NumWitnesses; } unsigned getNumWitnesses() const { return NumWitnesses; } ArrayRef getEntries() const { return Entries; } }; /// A path through a protocol hierarchy. class ProtocolPath { IRGenModule &IGM; /// The destination protocol. ProtocolDecl *Dest; /// The path from the selected origin down to the destination /// protocol. SmallVector ReversePath; /// The origin index to use. unsigned OriginIndex; /// The best path length we found. unsigned BestPathLength; public: /// Find a path from the given set of origins to the destination /// protocol. /// /// T needs to provide a couple of member functions: /// ProtocolDecl *getProtocol() const; /// const ProtocolInfo &getInfo() const; template ProtocolPath(IRGenModule &IGM, ArrayRef origins, ProtocolDecl *dest) : IGM(IGM), Dest(dest), BestPathLength(~0U) { // Consider each of the origins in turn, breaking out if any of // them yields a zero-length path. for (unsigned i = 0, e = origins.size(); i != e; ++i) { auto &origin = origins[i]; if (considerOrigin(origin.getProtocol(), origin.getInfo(), i)) break; } // Sanity check that we actually found a path at all. assert(BestPathLength != ~0U); assert(BestPathLength == ReversePath.size()); } /// Returns the index of the origin protocol we chose. unsigned getOriginIndex() const { return OriginIndex; } /// Apply the path to the given witness table. llvm::Value *apply(IRGenFunction &IGF, llvm::Value *wtable) const { for (unsigned i = ReversePath.size(); i != 0; --i) { wtable = emitLoadOfOpaqueWitness(IGF, wtable, ReversePath[i-1]); wtable = IGF.Builder.CreateBitCast(wtable, IGF.IGM.WitnessTablePtrTy); } return wtable; } private: /// Consider paths starting from a new origin protocol. /// Returns true if there's no point in considering other origins. bool considerOrigin(ProtocolDecl *origin, const ProtocolInfo &originInfo, unsigned originIndex) { assert(BestPathLength != 0); // If the origin *is* the destination, we can stop here. if (origin == Dest) { OriginIndex = originIndex; BestPathLength = 0; ReversePath.clear(); return true; } // Otherwise, if the origin gives rise to a better path, that's // also cool. if (findBetterPath(origin, originInfo, 0)) { OriginIndex = originIndex; return BestPathLength == 0; } return false; } /// Consider paths starting at the given protocol. bool findBetterPath(ProtocolDecl *proto, const ProtocolInfo &protoInfo, unsigned lengthSoFar) { assert(lengthSoFar < BestPathLength); assert(proto != Dest); // Keep track of whether we found a better path than the // previous best. bool foundBetter = false; for (auto base : proto->getInheritedProtocols(nullptr)) { auto &baseEntry = protoInfo.getWitnessEntry(base); assert(baseEntry.isBase()); // Compute the length down to this base. unsigned lengthToBase = lengthSoFar; if (baseEntry.isOutOfLineBase()) { lengthToBase++; // Don't consider this path if we reach a length that can't // possibly be better than the best so far. if (lengthToBase == BestPathLength) continue; } assert(lengthToBase < BestPathLength); // If this base *is* the destination, go ahead and start // building the path into ReversePath. if (base == Dest) { // Reset the collected best-path information. BestPathLength = lengthToBase; ReversePath.clear(); // Otherwise, if there isn't a better path through this base, // don't accumulate anything in the path. } else if (!findBetterPath(base, IGM.getProtocolInfo(base), lengthToBase)) { continue; } // Okay, we've found a better path, and ReversePath contains a // path leading from base to Dest. assert(BestPathLength >= lengthToBase); foundBetter = true; // Add the link from proto to base if necessary. if (baseEntry.isOutOfLineBase()) { ReversePath.push_back(baseEntry.getOutOfLineBaseIndex()); // If it isn't necessary, then we might be able to // short-circuit considering the bases of this protocol. } else { if (lengthSoFar == BestPathLength) return true; } } return foundBetter; } }; /// An entry in an existential type's list of known protocols. class ProtocolEntry { ProtocolDecl *Protocol; const ProtocolInfo &Impl; public: explicit ProtocolEntry(ProtocolDecl *proto, const ProtocolInfo &impl) : Protocol(proto), Impl(impl) {} ProtocolDecl *getProtocol() const { return Protocol; } const ProtocolInfo &getInfo() const { return Impl; } }; /// A helper class for implementing existential type infos that /// store an existential value of some sort. template class ExistentialTypeInfoBase : public Base { /// The number of non-trivial protocols for this existential. unsigned NumStoredProtocols; ProtocolEntry *getStoredProtocolsBuffer() { return reinterpret_cast(&asDerived() + 1); } const ProtocolEntry *getStoredProtocolsBuffer() const { return reinterpret_cast(&asDerived() + 1); } protected: const ExistentialTypeInfoBase &asExistentialTI() const { return *this; } const Derived &asDerived() const { return *static_cast(this); } Derived &asDerived() { return *static_cast(this); } template ExistentialTypeInfoBase(ArrayRef protocols, As &&...args) : Base(std::forward(args)...), NumStoredProtocols(protocols.size()) { for (unsigned i = 0, e = protocols.size(); i != e; ++i) { new (&getStoredProtocolsBuffer()[i]) ProtocolEntry(protocols[i]); } } public: template static const Derived * create(ArrayRef protocols, As &&...args) { void *buffer = operator new(sizeof(Derived) + protocols.size() * sizeof(ProtocolEntry)); return new (buffer) Derived(protocols, std::forward(args)...); } /// Returns the number of protocol witness tables directly carried /// by values of this type. unsigned getNumStoredProtocols() const { return NumStoredProtocols; } /// Returns the protocols that values of this type are known to /// implement. This can be empty, meaning that values of this /// type are not know to implement any protocols, although we do /// still know how to manipulate them. ArrayRef getStoredProtocols() const { return ArrayRef(getStoredProtocolsBuffer(), NumStoredProtocols); } /// Given an existential object, find the witness table /// corresponding to the given protocol. llvm::Value *findWitnessTable(IRGenFunction &IGF, Explosion &container, ProtocolDecl *protocol) const { assert(NumStoredProtocols != 0 && "finding a witness table in a trivial existential"); ProtocolPath path(IGF.IGM, getStoredProtocols(), protocol); llvm::Value *witness = asDerived().extractWitnessTable(IGF, container, path.getOriginIndex()); return path.apply(IGF, witness); } /// Given the address of an existential object, find the witness /// table corresponding to the given protocol. llvm::Value *findWitnessTable(IRGenFunction &IGF, Address obj, ProtocolDecl *protocol) const { assert(NumStoredProtocols != 0 && "finding a witness table in a trivial existential"); ProtocolPath path(IGF.IGM, getStoredProtocols(), protocol); llvm::Value *originTable = asDerived().loadWitnessTable(IGF, obj, path.getOriginIndex()); return path.apply(IGF, originTable); } /// Given the witness table vector from an existential object, find the /// witness table corresponding to the given protocol. llvm::Value *findWitnessTable(IRGenFunction &IGF, ArrayRef witnesses, ProtocolDecl *protocol) const { ProtocolPath path(IGF.IGM, getStoredProtocols(), protocol); return path.apply(IGF, witnesses[path.getOriginIndex()]); } /// Given the address of an existential object, find the witness /// table of a directly-stored witness table. llvm::Value *loadWitnessTable(IRGenFunction &IGF, Address obj, unsigned which) const { return IGF.Builder.CreateLoad( asDerived().projectWitnessTable(IGF, obj, which)); } void emitCopyOfTables(IRGenFunction &IGF, Address dest, Address src) const { if (NumStoredProtocols == 0) return; Explosion temp; asDerived().emitLoadOfTables(IGF, src, temp); asDerived().emitStoreOfTables(IGF, temp, dest); } void emitLoadOfTables(IRGenFunction &IGF, Address existential, Explosion &out) const { for (unsigned i = 0; i != NumStoredProtocols; ++i) { auto tableAddr = asDerived().projectWitnessTable(IGF, existential, i); out.add(IGF.Builder.CreateLoad(tableAddr)); } } void emitStoreOfTables(IRGenFunction &IGF, Explosion &in, Address existential) const { for (unsigned i = 0; i != NumStoredProtocols; ++i) { auto tableAddr = asDerived().projectWitnessTable(IGF, existential, i); IGF.Builder.CreateStore(in.claimNext(), tableAddr); } } }; /// A TypeInfo implementation for existential types, i.e., types like: /// Printable /// protocol /// with the semantic translation: /// \exists t : Printable . t /// t here is an ArchetypeType. /// /// This is used for both ProtocolTypes and ProtocolCompositionTypes. class OpaqueExistentialTypeInfo : public ExistentialTypeInfoBase> { using super = ExistentialTypeInfoBase>; friend super; // FIXME: We could get spare bits out of the metadata and/or witness // pointers. OpaqueExistentialTypeInfo(ArrayRef protocols, llvm::Type *ty, Size size, Alignment align) : super(protocols, ty, size, SpareBitVector::getConstant(size.getValueInBits(), false), align, IsNotPOD, IsNotBitwiseTakable) {} public: OpaqueExistentialLayout getLayout() const { return OpaqueExistentialLayout(getNumStoredProtocols()); } Address projectWitnessTable(IRGenFunction &IGF, Address obj, unsigned index) const { return getLayout().projectWitnessTable(IGF, obj, index); } void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const { auto objPtrTy = dest.getAddress()->getType(); auto fn = getAssignExistentialsFunction(IGF.IGM, objPtrTy, getLayout()); auto call = IGF.Builder.CreateCall2(fn, dest.getAddress(), src.getAddress()); call->setCallingConv(IGF.IGM.RuntimeCC); call->setDoesNotThrow(); } llvm::Value *copyType(IRGenFunction &IGF, Address dest, Address src) const { auto layout = getLayout(); llvm::Value *metadata = layout.loadMetadataRef(IGF, src); IGF.Builder.CreateStore(metadata, layout.projectMetadataRef(IGF, dest)); // Load the witness tables and copy them into the new object. emitCopyOfTables(IGF, dest, src); return metadata; } void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const { llvm::Value *metadata = copyType(IGF, dest, src); auto layout = getLayout(); // Project down to the buffers and ask the witnesses to do a // copy-initialize. Address srcBuffer = layout.projectExistentialBuffer(IGF, src); Address destBuffer = layout.projectExistentialBuffer(IGF, dest); emitInitializeBufferWithCopyOfBufferCall(IGF, metadata, destBuffer, srcBuffer); } void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const { llvm::Value *metadata = copyType(IGF, dest, src); auto layout = getLayout(); // Project down to the buffers and ask the witnesses to do a // take-initialize. Address srcBuffer = layout.projectExistentialBuffer(IGF, src); Address destBuffer = layout.projectExistentialBuffer(IGF, dest); emitInitializeBufferWithTakeOfBufferCall(IGF, metadata, destBuffer, srcBuffer); } void destroy(IRGenFunction &IGF, Address addr, SILType T) const { emitDestroyExistential(IGF, addr, getLayout()); } }; /// A type implementation for 'weak' existential types. class WeakClassExistentialTypeInfo : public ExistentialTypeInfoBase> { using super = ExistentialTypeInfoBase>; public: WeakClassExistentialTypeInfo(ArrayRef protocols, llvm::Type *ty, Size size, Alignment align, SpareBitVector &&spareBits) : super(protocols, ty, size, align, std::move(spareBits)) { } Address projectWitnessTable(IRGenFunction &IGF, Address container, unsigned index) const { assert(index < getNumStoredProtocols()); return IGF.Builder.CreateStructGEP(container, index + 1, (index + 1) * IGF.IGM.getPointerSize()); } Address projectValue(IRGenFunction &IGF, Address existential) const { return IGF.Builder.CreateStructGEP(existential, 0, Size(0), existential.getAddress()->getName() + ".weakref"); } void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { Address destValue = projectValue(IGF, dest); Address srcValue = projectValue(IGF, src); IGF.emitUnknownWeakCopyAssign(destValue, srcValue); emitCopyOfTables(IGF, dest, src); } void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { Address destValue = projectValue(IGF, dest); Address srcValue = projectValue(IGF, src); IGF.emitUnknownWeakCopyInit(destValue, srcValue); emitCopyOfTables(IGF, dest, src); } void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { Address destValue = projectValue(IGF, dest); Address srcValue = projectValue(IGF, src); IGF.emitUnknownWeakTakeAssign(destValue, srcValue); emitCopyOfTables(IGF, dest, src); } void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { Address destValue = projectValue(IGF, dest); Address srcValue = projectValue(IGF, src); IGF.emitUnknownWeakTakeInit(destValue, srcValue); emitCopyOfTables(IGF, dest, src); } void destroy(IRGenFunction &IGF, Address existential, SILType T) const override { Address valueAddr = projectValue(IGF, existential); IGF.emitUnknownWeakDestroy(valueAddr); } /// Given an explosion with multiple pointer elements in them, merge them /// together into a single huge integer. This is used for mapping to /// Swift.Optional and ImplicitlyUnwrappedOptional types, which have a known /// layout. static llvm::Value *mergeExplosion(Explosion &E, IRGenFunction &IGF) { unsigned PointerBitwidth = IGF.IGM.getPointerSize().getValueInBits(); unsigned NumWords = E.size(); auto *BigType = IGF.Builder.getIntNTy(PointerBitwidth*NumWords); // We always have at least one entry. auto *part = E.claimNext(); part = IGF.Builder.CreatePtrToInt(part, IGF.IGM.IntPtrTy); auto *result = IGF.Builder.CreateZExtOrBitCast(part, BigType); for (unsigned i = 1; i != NumWords; ++i) { part = E.claimNext(); part = IGF.Builder.CreatePtrToInt(part, IGF.IGM.IntPtrTy); part = IGF.Builder.CreateZExtOrBitCast(part, BigType); part = IGF.Builder.CreateShl(part, PointerBitwidth*i); result = IGF.Builder.CreateOr(result, part); } return result; } // Given a large integer type which contains the weak pointer and the // witness table pointers, consume the input explosion and generate a result // one. static void decomposeExplosion(Explosion &InE, Explosion &OutE, IRGenFunction &IGF) { llvm::Value *inValue = InE.claimNext(); unsigned InputBitwidth = inValue->getType()->getIntegerBitWidth(); unsigned PointerBitwidth = IGF.IGM.getPointerSize().getValueInBits(); unsigned NumElements = InputBitwidth / PointerBitwidth; assert((InputBitwidth % PointerBitwidth) == 0 && "explosion not a multiple of the word size"); // The first entry is always the weak*. auto *entry = IGF.Builder.CreateTruncOrBitCast(inValue, IGF.IGM.IntPtrTy); OutE.add(IGF.Builder.CreateIntToPtr(entry, IGF.IGM.UnknownRefCountedPtrTy)); for (unsigned i = 1; i != NumElements; ++i) { entry = IGF.Builder.CreateLShr(inValue, PointerBitwidth*i); entry = IGF.Builder.CreateTruncOrBitCast(entry, IGF.IGM.IntPtrTy); OutE.add(IGF.Builder.CreateIntToPtr(entry, IGF.IGM.WitnessTablePtrTy)); } } // These explosions must follow the same schema as // ClassExistentialTypeInfo, i.e. first the value, then the tables. void weakLoadStrong(IRGenFunction &IGF, Address existential, Explosion &out) const override { Explosion temp; Address valueAddr = projectValue(IGF, existential); temp.add(IGF.emitUnknownWeakLoadStrong(valueAddr, IGF.IGM.UnknownRefCountedPtrTy)); emitLoadOfTables(IGF, existential, temp); out.add(mergeExplosion(temp, IGF)); } void weakTakeStrong(IRGenFunction &IGF, Address existential, Explosion &out) const override { Explosion temp; Address valueAddr = projectValue(IGF, existential); temp.add(IGF.emitUnknownWeakTakeStrong(valueAddr, IGF.IGM.UnknownRefCountedPtrTy)); emitLoadOfTables(IGF, existential, temp); out.add(mergeExplosion(temp, IGF)); } void weakInit(IRGenFunction &IGF, Explosion &in, Address existential) const override { Explosion temp; decomposeExplosion(in, temp, IGF); llvm::Value *value = temp.claimNext(); assert(value->getType() == IGF.IGM.UnknownRefCountedPtrTy); emitStoreOfTables(IGF, temp, existential); Address valueAddr = projectValue(IGF, existential); IGF.emitUnknownWeakInit(value, valueAddr); } void weakAssign(IRGenFunction &IGF, Explosion &in, Address existential) const override { Explosion temp; decomposeExplosion(in, temp, IGF); llvm::Value *value = temp.claimNext(); assert(value->getType() == IGF.IGM.UnknownRefCountedPtrTy); emitStoreOfTables(IGF, temp, existential); Address valueAddr = projectValue(IGF, existential); IGF.emitUnknownWeakAssign(value, valueAddr); } }; /// A helper class for working with existential types that can be /// exploded into scalars. /// /// The subclass must provide: /// void emitPayloadRetain(IRGenFunction &IGF, llvm::Value *payload) const; /// void emitPayloadRelease(IRGenFunction &IGF, llvm::Value *payload) const; /// void emitPayloadFixLifetime(IRGenFunction &IGF, /// llvm::Value *payload) const; /// const LoadableTypeInfo & /// getPayloadTypeInfoForExtraInhabitants(IRGenModule &IGM) const; /// The payload type info is only used to manage extra inhabitants, so it's /// okay for it to implement different semantics. template class ScalarExistentialTypeInfoBase : public ExistentialTypeInfoBase> { using super = ExistentialTypeInfoBase>; protected: template ScalarExistentialTypeInfoBase(T &&...args) : super(std::forward(args)...) {} using super::asDerived; public: /// The storage type of a class existential is a struct containing /// a refcounted pointer to the class instance value followed by /// witness table pointers for each conformed-to protocol. Unlike opaque /// existentials, a class existential does not need to store type /// metadata as an additional element, since it can be derived from the /// class instance. llvm::StructType *getStorageType() const { return cast(TypeInfo::getStorageType()); } using super::getNumStoredProtocols; unsigned getExplosionSize() const override { return 1 + getNumStoredProtocols(); } void getSchema(ExplosionSchema &schema) const override { llvm::StructType *ty = getStorageType(); for (unsigned i = 0, e = getExplosionSize(); i != e; ++i) schema.add(ExplosionSchema::Element::forScalar(ty->getElementType(i))); } /// Given the address of a class existential container, returns /// the address of a witness table pointer. Address projectWitnessTable(IRGenFunction &IGF, Address address, unsigned n) const { assert(n < getNumStoredProtocols() && "witness table index out of bounds"); return IGF.Builder.CreateStructGEP(address, n+1, IGF.IGM.getPointerSize() * (n+1)); } /// Given the address of a class existential container, returns /// the address of its instance pointer. Address projectValue(IRGenFunction &IGF, Address address) const { return IGF.Builder.CreateStructGEP(address, 0, Size(0)); } llvm::Value *loadValue(IRGenFunction &IGF, Address addr) const { return IGF.Builder.CreateLoad(projectValue(IGF, addr)); } /// Given a class existential container, returns a witness table /// pointer out of the container, and the type metadata pointer for the /// value. llvm::Value * extractWitnessTable(IRGenFunction &IGF, Explosion &container, unsigned which) const { assert(which < getNumStoredProtocols() && "witness table index out of bounds"); ArrayRef values = container.claim(getExplosionSize()); return values[which+1]; } /// Deconstruct an existential object into witness tables and instance /// pointer. std::pair, llvm::Value*> getWitnessTablesAndValue(Explosion &container) const { llvm::Value *instance = container.claimNext(); ArrayRef witnesses = container.claim(getNumStoredProtocols()); return {witnesses, instance}; } /// Given an existential object, returns the payload value. llvm::Value *getValue(IRGenFunction &IGF, Explosion &container) const { llvm::Value *instance = container.claimNext(); container.claim(getNumStoredProtocols()); return instance; } void loadAsCopy(IRGenFunction &IGF, Address address, Explosion &out) const override { // Load the instance pointer, which is unknown-refcounted. llvm::Value *instance = IGF.Builder.CreateLoad(projectValue(IGF, address)); asDerived().emitPayloadRetain(IGF, instance); out.add(instance); // Load the witness table pointers. asDerived().emitLoadOfTables(IGF, address, out); } void loadAsTake(IRGenFunction &IGF, Address address, Explosion &e) const override { // Load the instance pointer. e.add(IGF.Builder.CreateLoad(projectValue(IGF, address))); // Load the witness table pointers. asDerived().emitLoadOfTables(IGF, address, e); } void assign(IRGenFunction &IGF, Explosion &e, Address address) const override { // Assign the value. Address instanceAddr = projectValue(IGF, address); llvm::Value *old = IGF.Builder.CreateLoad(instanceAddr); IGF.Builder.CreateStore(e.claimNext(), instanceAddr); asDerived().emitPayloadRelease(IGF, old); // Store the witness table pointers. asDerived().emitStoreOfTables(IGF, e, address); } void initialize(IRGenFunction &IGF, Explosion &e, Address address) const override { // Store the instance pointer. IGF.Builder.CreateStore(e.claimNext(), projectValue(IGF, address)); // Store the witness table pointers. asDerived().emitStoreOfTables(IGF, e, address); } void copy(IRGenFunction &IGF, Explosion &src, Explosion &dest) const override { // Copy the instance pointer. llvm::Value *value = src.claimNext(); dest.add(value); asDerived().emitPayloadRetain(IGF, value); // Transfer the witness table pointers. src.transferInto(dest, getNumStoredProtocols()); } void consume(IRGenFunction &IGF, Explosion &src) const override { // Copy the instance pointer. llvm::Value *value = src.claimNext(); asDerived().emitPayloadRelease(IGF, value); // Throw out the witness table pointers. src.claim(getNumStoredProtocols()); } void fixLifetime(IRGenFunction &IGF, Explosion &src) const override { // Copy the instance pointer. llvm::Value *value = src.claimNext(); asDerived().emitPayloadFixLifetime(IGF, value); // Throw out the witness table pointers. src.claim(getNumStoredProtocols()); } void destroy(IRGenFunction &IGF, Address addr, SILType T) const override { llvm::Value *value = IGF.Builder.CreateLoad(projectValue(IGF, addr)); asDerived().emitPayloadRelease(IGF, value); } llvm::Value *packEnumPayload(IRGenFunction &IGF, Explosion &src, unsigned bitWidth, unsigned offset) const override { PackEnumPayload pack(IGF, bitWidth); pack.moveToOffset(offset); pack.add(src.claimNext()); for (unsigned i = 0; i < getNumStoredProtocols(); ++i) pack.add(src.claimNext()); return pack.get(); } void unpackEnumPayload(IRGenFunction &IGF, llvm::Value *payload, Explosion &dest, unsigned offset) const override { UnpackEnumPayload unpack(IGF, payload); unpack.moveToOffset(offset); ExplosionSchema schema; getSchema(schema); dest.add(unpack.claim(schema[0].getScalarType())); for (unsigned i = 0; i < getNumStoredProtocols(); ++i) dest.add(unpack.claim(IGF.IGM.WitnessTablePtrTy)); } // Extra inhabitants of the various scalar existential containers. // We use the heap object extra inhabitants over the class pointer value. // We could get even more extra inhabitants from the witness table // pointer(s), but it's unlikely we would ever need to. bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { assert(asDerived().getPayloadTypeInfoForExtraInhabitants(IGM) .mayHaveExtraInhabitants(IGM)); return true; } unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { return asDerived().getPayloadTypeInfoForExtraInhabitants(IGM) .getFixedExtraInhabitantCount(IGM); } llvm::ConstantInt *getFixedExtraInhabitantValue(IRGenModule &IGM, unsigned bits, unsigned index) const override { // Note that we pass down the original bit-width. return asDerived().getPayloadTypeInfoForExtraInhabitants(IGM) .getFixedExtraInhabitantValue(IGM, bits, index); } llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, SILType T) const override { // NB: We assume that the witness table slots are zero if an extra // inhabitant is stored in the container. src = projectValue(IGF, src); return asDerived().getPayloadTypeInfoForExtraInhabitants(IGF.IGM) .getExtraInhabitantIndex(IGF, src, SILType()); } void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, Address dest, SILType T) const override { Address valueDest = projectValue(IGF, dest); asDerived().getPayloadTypeInfoForExtraInhabitants(IGF.IGM) .storeExtraInhabitant(IGF, index, valueDest, SILType()); } llvm::Value *maskFixedExtraInhabitant(IRGenFunction &IGF, llvm::Value *bits) const override { // Truncate down to the payload type. llvm::Type *originalType = bits->getType(); bits = IGF.Builder.CreateTrunc(bits, IGF.IGM.IntPtrTy); // Ask the payload type to apply a mask, in case that matters. bits = asDerived().getPayloadTypeInfoForExtraInhabitants(IGF.IGM) .maskFixedExtraInhabitant(IGF, bits); // Zext out to the size of the existential. bits = IGF.Builder.CreateZExt(bits, originalType); return bits; } }; /// A type implementation for [unowned] class existential types. class UnownedClassExistentialTypeInfo : public ScalarExistentialTypeInfoBase { public: UnownedClassExistentialTypeInfo(ArrayRef storedProtocols, llvm::Type *ty, const SpareBitVector &spareBits, Size size, Alignment align) : ScalarExistentialTypeInfoBase(storedProtocols, ty, size, spareBits, align) {} const LoadableTypeInfo & getPayloadTypeInfoForExtraInhabitants(IRGenModule &IGM) const { return IGM.getUnknownObjectTypeInfo(); } void emitPayloadRetain(IRGenFunction &IGF, llvm::Value *value) const { IGF.emitUnknownUnownedRetain(value); } void emitPayloadRelease(IRGenFunction &IGF, llvm::Value *value) const { IGF.emitUnknownUnownedRelease(value); } void emitPayloadFixLifetime(IRGenFunction &IGF, llvm::Value *value) const { IGF.emitFixLifetime(value); } }; /// A type implementation for @unowned(unsafe) class existential types. class UnmanagedClassExistentialTypeInfo : public ScalarExistentialTypeInfoBase { public: UnmanagedClassExistentialTypeInfo(ArrayRef storedProtocols, llvm::Type *ty, const SpareBitVector &spareBits, Size size, Alignment align) : ScalarExistentialTypeInfoBase(storedProtocols, ty, size, spareBits, align, IsPOD) {} const LoadableTypeInfo & getPayloadTypeInfoForExtraInhabitants(IRGenModule &IGM) const { return IGM.getUnknownObjectTypeInfo(); } void emitPayloadRetain(IRGenFunction &IGF, llvm::Value *value) const { // do nothing } void emitPayloadRelease(IRGenFunction &IGF, llvm::Value *value) const { // do nothing } void emitPayloadFixLifetime(IRGenFunction &IGF, llvm::Value *value) const { // do nothing } }; /// A type info implementation for class existential types, that is, /// an existential type known to conform to one or more class protocols. /// Class existentials can be represented directly as an aggregation /// of a refcounted pointer plus witness tables instead of using an indirect /// buffer. class ClassExistentialTypeInfo : public ScalarExistentialTypeInfoBase { friend ExistentialTypeInfoBase; ClassExistentialTypeInfo(ArrayRef protocols, llvm::Type *ty, Size size, SpareBitVector &&spareBits, Alignment align) : ScalarExistentialTypeInfoBase(protocols, ty, size, std::move(spareBits), align) {} public: /// Class existentials are single refcounted pointers if they have no /// witness tables. Right now we have no way of constraining an existential /// to Swift-refcounted types. bool isSingleSwiftRetainablePointer(ResilienceScope scope) const override { return false; } bool isSingleUnknownRetainablePointer(ResilienceScope scope) const override{ return getNumStoredProtocols() == 0; } const LoadableTypeInfo & getPayloadTypeInfoForExtraInhabitants(IRGenModule &IGM) const { return IGM.getUnknownObjectTypeInfo(); } void retain(IRGenFunction &IGF, Explosion &e) const override { // The instance is treated as unknown-refcounted. IGF.emitUnknownRetainCall(e.claimNext()); e.claim(getNumStoredProtocols()); } void release(IRGenFunction &IGF, Explosion &e) const override { // The instance is treated as unknown-refcounted. IGF.emitUnknownRelease(e.claimNext()); e.claim(getNumStoredProtocols()); } void retainUnowned(IRGenFunction &IGF, Explosion &e) const override { // The instance is treated as unknown-refcounted. IGF.emitUnknownRetainUnowned(e.claimNext()); e.claim(getNumStoredProtocols()); } void unownedRetain(IRGenFunction &IGF, Explosion &e) const override { // The instance is treated as unknown-refcounted. IGF.emitUnknownUnownedRetain(e.claimNext()); e.claim(getNumStoredProtocols()); } void unownedRelease(IRGenFunction &IGF, Explosion &e) const override { // The instance is treated as unknown-refcounted. IGF.emitUnknownUnownedRelease(e.claimNext()); e.claim(getNumStoredProtocols()); } void emitPayloadRetain(IRGenFunction &IGF, llvm::Value *value) const { IGF.emitUnknownRetainCall(value); } void emitPayloadRelease(IRGenFunction &IGF, llvm::Value *value) const { IGF.emitUnknownRelease(value); } void emitPayloadFixLifetime(IRGenFunction &IGF, llvm::Value *value) const { IGF.emitFixLifetime(value); } LoadedRef loadRefcountedPtr(IRGenFunction &IGF, SourceLoc loc, Address addr) const override { return LoadedRef(IGF.emitLoadUnknownRefcountedPtr(addr), true); } const UnownedTypeInfo * createUnownedStorageType(TypeConverter &TC) const override { // We can just re-use the storage type for the @unowned(safe) type. return UnownedClassExistentialTypeInfo::create(getStoredProtocols(), getStorageType(), getSpareBits(), getFixedSize(), getFixedAlignment()); } const TypeInfo * createUnmanagedStorageType(TypeConverter &TC) const override { // We can just re-use the storage type for the @unowned(unsafe) type. return UnmanagedClassExistentialTypeInfo::create(getStoredProtocols(), getStorageType(), getSpareBits(), getFixedSize(), getFixedAlignment()); } const WeakTypeInfo * createWeakStorageType(TypeConverter &TC) const override { Size size = TC.IGM.getWeakReferenceSize() + getNumStoredProtocols() * TC.IGM.getPointerSize(); Alignment align = TC.IGM.getWeakReferenceAlignment(); assert(align == TC.IGM.getPointerAlignment() && "[weak] alignment not pointer alignment; fix existential layout"); (void)align; // We need to build a new struct for the [weak] type because the weak // component is not necessarily pointer-sized. SmallVector fieldTys; fieldTys.push_back(TC.IGM.WeakReferencePtrTy->getElementType()); fieldTys.resize(getNumStoredProtocols() + 1, TC.IGM.WitnessTablePtrTy); auto storageTy = llvm::StructType::get(TC.IGM.getLLVMContext(), fieldTys); SpareBitVector spareBits = TC.IGM.getWeakReferenceSpareBits(); for (unsigned i = 0, e = getNumStoredProtocols(); i != e; ++i) spareBits.append(TC.IGM.getWitnessTablePtrSpareBits()); return WeakClassExistentialTypeInfo::create(getStoredProtocols(), storageTy, size, align, std::move(spareBits)); } }; /// A type implementation for existential metatypes. class ExistentialMetatypeTypeInfo : public ScalarExistentialTypeInfoBase { const LoadableTypeInfo &MetatypeTI; friend ExistentialTypeInfoBase; ExistentialMetatypeTypeInfo(ArrayRef storedProtocols, llvm::Type *ty, Size size, SpareBitVector &&spareBits, Alignment align, const LoadableTypeInfo &metatypeTI) : ScalarExistentialTypeInfoBase(storedProtocols, ty, size, std::move(spareBits), align, IsPOD), MetatypeTI(metatypeTI) {} public: const LoadableTypeInfo & getPayloadTypeInfoForExtraInhabitants(IRGenModule &IGM) const { return MetatypeTI; } void emitPayloadRetain(IRGenFunction &IGF, llvm::Value *value) const { // do nothing } void emitPayloadRelease(IRGenFunction &IGF, llvm::Value *value) const { // do nothing } void emitPayloadFixLifetime(IRGenFunction &IGF, llvm::Value *value) const { // do nothing } }; /// Common type implementation details for all archetypes. class ArchetypeTypeInfoBase { protected: unsigned NumStoredProtocols; ProtocolEntry *StoredProtocolsBuffer; ArchetypeTypeInfoBase(void *protocolsBuffer, ArrayRef protocols) : NumStoredProtocols(protocols.size()), StoredProtocolsBuffer(reinterpret_cast(protocolsBuffer)) { for (unsigned i = 0, e = protocols.size(); i != e; ++i) { ::new (&StoredProtocolsBuffer[i]) ProtocolEntry(protocols[i]); } } public: unsigned getNumStoredProtocols() const { return NumStoredProtocols; } ArrayRef getStoredProtocols() const { return llvm::makeArrayRef(StoredProtocolsBuffer, getNumStoredProtocols()); } /// Return the witness table that's been set for this type. llvm::Value *getWitnessTable(IRGenFunction &IGF, CanArchetypeType archetype, unsigned which) const { assert(which < getNumStoredProtocols()); return IGF.getLocalTypeData(archetype, LocalTypeData::forArchetypeProtocolWitness(which)); } }; /// A type implementation for an ArchetypeType, otherwise known as a /// type variable: for example, This in a protocol declaration, or T /// in a generic declaration like foo(x : T) -> T. The critical /// thing here is that performing an operation involving archetypes /// is dependent on the witness binding we can see. class OpaqueArchetypeTypeInfo : public IndirectTypeInfo>, public ArchetypeTypeInfoBase { OpaqueArchetypeTypeInfo(llvm::Type *type, ArrayRef protocols) : IndirectTypeInfo(type, Alignment(1), IsNotPOD, IsNotBitwiseTakable), ArchetypeTypeInfoBase(this + 1, protocols) {} public: static const OpaqueArchetypeTypeInfo *create(llvm::Type *type, ArrayRef protocols) { void *buffer = operator new(sizeof(OpaqueArchetypeTypeInfo) + protocols.size() * sizeof(ProtocolEntry)); return ::new (buffer) OpaqueArchetypeTypeInfo(type, protocols); } void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { emitAssignWithCopyCall(IGF, T, dest.getAddress(), src.getAddress()); } void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { emitAssignWithTakeCall(IGF, T, dest.getAddress(), src.getAddress()); } void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { emitInitializeWithCopyCall(IGF, T, dest.getAddress(), src.getAddress()); } void initializeArrayWithCopy(IRGenFunction &IGF, Address dest, Address src, llvm::Value *count, SILType T) const override { emitInitializeArrayWithCopyCall(IGF, T, dest.getAddress(), src.getAddress(), count); } void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { emitInitializeWithTakeCall(IGF, T, dest.getAddress(), src.getAddress()); } void initializeArrayWithTakeFrontToBack(IRGenFunction &IGF, Address dest, Address src, llvm::Value *count, SILType T) const override { emitInitializeArrayWithTakeFrontToBackCall(IGF, T, dest.getAddress(), src.getAddress(), count); } void initializeArrayWithTakeBackToFront(IRGenFunction &IGF, Address dest, Address src, llvm::Value *count, SILType T) const override { emitInitializeArrayWithTakeBackToFrontCall(IGF, T, dest.getAddress(), src.getAddress(), count); } void destroy(IRGenFunction &IGF, Address addr, SILType T) const override { emitDestroyCall(IGF, T, addr.getAddress()); } void destroyArray(IRGenFunction &IGF, Address addr, llvm::Value *count, SILType T) const override { emitDestroyArrayCall(IGF, T, addr.getAddress(), count); } std::pair getSizeAndAlignment(IRGenFunction &IGF, SILType T) const { auto size = emitLoadOfSize(IGF, T); auto align = emitLoadOfAlignmentMask(IGF, T); return std::make_pair(size, align); } llvm::Value *getSize(IRGenFunction &IGF, SILType T) const override { return emitLoadOfSize(IGF, T); } llvm::Value *getAlignment(IRGenFunction &IGF, SILType T) const { return emitLoadOfAlignmentMask(IGF, T); } llvm::Value *getStride(IRGenFunction &IGF, SILType T) const override { return emitLoadOfStride(IGF, T); } llvm::Constant *getStaticSize(IRGenModule &IGM) const override { return nullptr; } llvm::Constant *getStaticAlignment(IRGenModule &IGM) const { return nullptr; } llvm::Constant *getStaticStride(IRGenModule &IGM) const override { return nullptr; } void initializeMetadata(IRGenFunction &IGF, llvm::Value *metadata, llvm::Value *vwtable, SILType T) const override { // Archetypes always refer to an existing type. A witness table should // never be independently initialized for one. llvm_unreachable("initializing value witness table for archetype?!"); } bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { return true; } llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, SILType T) const override { return emitGetExtraInhabitantIndexCall(IGF, T, src.getAddress()); } void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, Address dest, SILType T) const override { emitStoreExtraInhabitantCall(IGF, T, index, dest.getAddress()); } }; /// A type implementation for a class archetype, that is, an archetype /// bounded by a class protocol constraint. These archetypes can be /// represented by a refcounted pointer instead of an opaque value buffer. /// We use an unknown-refcounted pointer in order to allow ObjC or Swift /// classes to conform to the type variable. class ClassArchetypeTypeInfo : public HeapTypeInfo, public ArchetypeTypeInfoBase { ReferenceCounting RefCount; ClassArchetypeTypeInfo(llvm::PointerType *storageType, Size size, const SpareBitVector &spareBits, Alignment align, ArrayRef protocols, ReferenceCounting refCount) : HeapTypeInfo(storageType, size, spareBits, align), ArchetypeTypeInfoBase(this + 1, protocols), RefCount(refCount) {} public: static const ClassArchetypeTypeInfo *create(llvm::PointerType *storageType, Size size, const SpareBitVector &spareBits, Alignment align, ArrayRef protocols, ReferenceCounting refCount) { void *buffer = operator new(sizeof(ClassArchetypeTypeInfo) + protocols.size() * sizeof(ProtocolEntry)); return ::new (buffer) ClassArchetypeTypeInfo(storageType, size, spareBits, align, protocols, refCount); } ReferenceCounting getReferenceCounting() const { return RefCount; } }; /// Return the ArchetypeTypeInfoBase information from the TypeInfo for any /// archetype. static const ArchetypeTypeInfoBase & getArchetypeInfo(IRGenFunction &IGF, CanArchetypeType t, const TypeInfo &ti) { if (t->requiresClass()) return ti.as(); return ti.as(); } } static void setMetadataRef(IRGenFunction &IGF, ArchetypeType *archetype, llvm::Value *metadata) { assert(metadata->getType() == IGF.IGM.TypeMetadataPtrTy); IGF.setUnscopedLocalTypeData(CanType(archetype), LocalTypeData::forMetatype(), metadata); // Create a shadow copy of the metadata in an alloca for the debug info. StringRef Name = metadata->getName(); if (!IGF.IGM.Opts.Optimize) { auto Alloca = IGF.createAlloca(metadata->getType(), IGF.IGM.getPointerAlignment(), Name); IGF.Builder.CreateAlignedStore(metadata, Alloca.getAddress(), IGF.IGM.getPointerAlignment().getValue()); metadata = Alloca.getAddress(); } // Emit debug info for the metadata. if (IGF.IGM.DebugInfo) IGF.IGM.DebugInfo->emitTypeMetadata(IGF, metadata, Name); } static void setWitnessTable(IRGenFunction &IGF, ArchetypeType *archetype, unsigned protocolIndex, llvm::Value *wtable) { assert(wtable->getType() == IGF.IGM.WitnessTablePtrTy); assert(protocolIndex < archetype->getConformsTo().size()); IGF.setUnscopedLocalTypeData(CanType(archetype), LocalTypeData::forArchetypeProtocolWitness(protocolIndex), wtable); } /// Detail about how an object conforms to a protocol. class irgen::ConformanceInfo { friend class ProtocolInfo; public: virtual ~ConformanceInfo() {} virtual llvm::Value *getTable(IRGenFunction &IGF) const = 0; /// Try to get this table as a constant pointer. This might just /// not be supportable at all. virtual llvm::Constant *tryGetConstantTable(IRGenModule &IGM) const = 0; }; namespace { /// Conformance info for a witness table that can be directly generated. class DirectConformanceInfo : public ConformanceInfo { friend class ProtocolInfo; const NormalProtocolConformance *RootConformance; public: DirectConformanceInfo(const NormalProtocolConformance *C) : RootConformance(C) {} llvm::Value *getTable(IRGenFunction &IGF) const override { return IGF.IGM.getAddrOfWitnessTable(RootConformance); } llvm::Constant *tryGetConstantTable(IRGenModule &IGM) const override { return IGM.getAddrOfWitnessTable(RootConformance); } }; } //end anonymous namespace static bool isNeverAllocated(FixedPacking packing) { switch (packing) { case FixedPacking::OffsetZero: return true; case FixedPacking::Allocate: return false; case FixedPacking::Dynamic: return false; } llvm_unreachable("bad FixedPacking value"); } namespace { /// An operation to be peformed for various kinds of packing. struct DynamicPackingOperation { virtual ~DynamicPackingOperation() = default; /// Emit the operation at a concrete packing kind. /// /// Immediately after this call, there will be an unconditional /// branch to the continuation block. virtual void emitForPacking(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing) = 0; /// Given that we are currently at the beginning of the /// continuation block, complete the operation. virtual void complete(IRGenFunction &IGF, SILType T, const TypeInfo &type) = 0; }; /// A class for merging a particular kind of value across control flow. template class DynamicPackingPHIMapping; /// An implementation of DynamicPackingPHIMapping for a single LLVM value. template <> class DynamicPackingPHIMapping { llvm::PHINode *PHI = nullptr; public: void collect(IRGenFunction &IGF, SILType T, const TypeInfo &type, llvm::Value *value) { // Add the result to the phi, creating it (unparented) if necessary. if (!PHI) PHI = llvm::PHINode::Create(value->getType(), 2, "dynamic-packing.result"); PHI->addIncoming(value, IGF.Builder.GetInsertBlock()); } void complete(IRGenFunction &IGF, SILType T, const TypeInfo &type) { assert(PHI); IGF.Builder.Insert(PHI); } llvm::Value *get(IRGenFunction &IGF, SILType T, const TypeInfo &type) { assert(PHI); return PHI; } }; /// An implementation of DynamicPackingPHIMapping for Addresses. template <> class DynamicPackingPHIMapping
: private DynamicPackingPHIMapping { typedef DynamicPackingPHIMapping super; public: void collect(IRGenFunction &IGF, SILType T, const TypeInfo &type, Address value) { super::collect(IGF, T, type, value.getAddress()); } void complete(IRGenFunction &IGF, SILType T, const TypeInfo &type) { super::complete(IGF, T, type); } Address get(IRGenFunction &IGF, SILType T, const TypeInfo &type) { return type.getAddressForPointer(super::get(IGF, T, type)); } }; /// An implementation of packing operations based around a lambda. template class LambdaDynamicPackingOperation : public DynamicPackingOperation { FnTy Fn; DynamicPackingPHIMapping Mapping; public: explicit LambdaDynamicPackingOperation(FnTy &&fn) : Fn(fn) {} void emitForPacking(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing) override { Mapping.collect(IGF, T, type, Fn(IGF, T, type, packing)); } void complete(IRGenFunction &IGF, SILType T, const TypeInfo &type) override { Mapping.complete(IGF, T, type); } ResultTy get(IRGenFunction &IGF, SILType T, const TypeInfo &type) { return Mapping.get(IGF, T, type); } }; /// A partial specialization for lambda-based packing operations /// that return 'void'. template class LambdaDynamicPackingOperation : public DynamicPackingOperation { FnTy Fn; public: explicit LambdaDynamicPackingOperation(FnTy &&fn) : Fn(fn) {} void emitForPacking(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing) override { Fn(IGF, T, type, packing); } void complete(IRGenFunction &IGF, SILType T, const TypeInfo &type) override {} void get(IRGenFunction &IGF, SILType T, const TypeInfo &type) {} }; } /// Dynamic check for the enabling conditions of different kinds of /// packing into a fixed-size buffer, and perform an operation at each /// of them. static void emitDynamicPackingOperation(IRGenFunction &IGF, SILType T, const TypeInfo &type, DynamicPackingOperation &operation) { auto indirectBB = IGF.createBasicBlock("dynamic-packing.indirect"); auto directBB = IGF.createBasicBlock("dynamic-packing.direct"); auto contBB = IGF.createBasicBlock("dynamic-packing.cont"); // Branch. auto isInline = type.isDynamicallyPackedInline(IGF, T); IGF.Builder.CreateCondBr(isInline, directBB, indirectBB); // Emit the indirect path. IGF.Builder.emitBlock(indirectBB); operation.emitForPacking(IGF, T, type, FixedPacking::Allocate); IGF.Builder.CreateBr(contBB); // Emit the direct path. IGF.Builder.emitBlock(directBB); operation.emitForPacking(IGF, T, type, FixedPacking::OffsetZero); IGF.Builder.CreateBr(contBB); // Enter the continuation block and add the PHI if required. IGF.Builder.emitBlock(contBB); operation.complete(IGF, T, type); } /// A helper function for creating a lambda-based DynamicPackingOperation. template LambdaDynamicPackingOperation makeLambdaDynamicPackingOperation(FnTy &&fn) { return LambdaDynamicPackingOperation(std::move(fn)); } /// Perform an operation on a type that requires dynamic packing. template static ResultTy emitForDynamicPacking(IRGenFunction &IGF, ResultTy (*fn)(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, ArgTys... args), SILType T, const TypeInfo &type, // using enable_if to block template argument deduction typename std::enable_if::type... args) { auto operation = makeLambdaDynamicPackingOperation( [&](IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing) { return fn(IGF, T, type, packing, args...); }); emitDynamicPackingOperation(IGF, T, type, operation); return operation.get(IGF, T, type); } /// Emit a 'projectBuffer' operation. Always returns a T*. static Address emitProjectBuffer(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address buffer) { llvm::PointerType *resultTy = type.getStorageType()->getPointerTo(); switch (packing) { case FixedPacking::Allocate: { Address slot = IGF.Builder.CreateBitCast(buffer, resultTy->getPointerTo(), "storage-slot"); llvm::Value *address = IGF.Builder.CreateLoad(slot); return type.getAddressForPointer(address); } case FixedPacking::OffsetZero: { return IGF.Builder.CreateBitCast(buffer, resultTy, "object"); } case FixedPacking::Dynamic: return emitForDynamicPacking(IGF, &emitProjectBuffer, T, type, buffer); } llvm_unreachable("bad packing!"); } namespace swift { namespace irgen { using ::emitProjectBuffer; } } /// Project to the address of a value in a value buffer. Address irgen::emitProjectBuffer(IRGenFunction &IGF, SILType valueType, Address buffer) { const TypeInfo &valueTI = IGF.getTypeInfo(valueType); FixedPacking packing = valueTI.getFixedPacking(IGF.IGM); return ::emitProjectBuffer(IGF, valueType, valueTI, packing, buffer); } /// Emit an 'allocateBuffer' operation. Always returns a T*. static Address emitAllocateBuffer(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address buffer) { switch (packing) { case FixedPacking::Allocate: { auto sizeAndAlign = type.getSizeAndAlignmentMask(IGF, T); llvm::Value *addr = IGF.emitAllocRawCall(sizeAndAlign.first, sizeAndAlign.second); buffer = IGF.Builder.CreateBitCast(buffer, IGF.IGM.Int8PtrPtrTy); IGF.Builder.CreateStore(addr, buffer); addr = IGF.Builder.CreateBitCast(addr, type.getStorageType()->getPointerTo()); return type.getAddressForPointer(addr); } case FixedPacking::OffsetZero: return emitProjectBuffer(IGF, T, type, packing, buffer); case FixedPacking::Dynamic: return emitForDynamicPacking(IGF, &emitAllocateBuffer, T, type, buffer); } llvm_unreachable("bad packing!"); } namespace swift { namespace irgen { using ::emitAllocateBuffer; } } /// Allocate space for a value in a value buffer. Address irgen::emitAllocateBuffer(IRGenFunction &IGF, SILType valueType, Address buffer) { const TypeInfo &valueTI = IGF.getTypeInfo(valueType); FixedPacking packing = valueTI.getFixedPacking(IGF.IGM); return emitAllocateBuffer(IGF, valueType, valueTI, packing, buffer); } /// Emit a 'deallocateBuffer' operation. static void emitDeallocateBuffer(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address buffer) { switch (packing) { case FixedPacking::Allocate: { Address slot = IGF.Builder.CreateBitCast(buffer, IGF.IGM.Int8PtrPtrTy); llvm::Value *addr = IGF.Builder.CreateLoad(slot, "storage"); auto sizeAndAlignMask = type.getSizeAndAlignmentMask(IGF, T); IGF.emitDeallocRawCall(addr, sizeAndAlignMask.first, sizeAndAlignMask.second); return; } case FixedPacking::OffsetZero: return; case FixedPacking::Dynamic: return emitForDynamicPacking(IGF, &emitDeallocateBuffer, T, type, buffer); } llvm_unreachable("bad packing!"); } namespace swift { namespace irgen { using ::emitDeallocateBuffer; } } /// Deallocate space for a value in a value buffer. void irgen::emitDeallocateBuffer(IRGenFunction &IGF, SILType valueType, Address buffer) { const TypeInfo &valueTI = IGF.getTypeInfo(valueType); FixedPacking packing = valueTI.getFixedPacking(IGF.IGM); emitDeallocateBuffer(IGF, valueType, valueTI, packing, buffer); } /// Emit a 'destroyBuffer' operation. static void emitDestroyBuffer(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address buffer) { // Special-case dynamic packing in order to thread the jumps. if (packing == FixedPacking::Dynamic) return emitForDynamicPacking(IGF, &emitDestroyBuffer, T, type, buffer); Address object = emitProjectBuffer(IGF, T, type, packing, buffer); type.destroy(IGF, object, T); emitDeallocateBuffer(IGF, T, type, packing, buffer); } /// Emit an 'initializeWithCopy' operation. static void emitInitializeWithCopy(IRGenFunction &IGF, SILType T, const TypeInfo &type, Address dest, Address src) { type.initializeWithCopy(IGF, dest, src, T); } /// Emit an 'initializeWithTake' operation. static void emitInitializeWithTake(IRGenFunction &IGF, SILType T, const TypeInfo &type, Address dest, Address src) { type.initializeWithTake(IGF, dest, src, T); } /// Emit an 'initializeBufferWithCopyOfBuffer' operation. /// Returns the address of the destination object. static Address emitInitializeBufferWithCopyOfBuffer(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address dest, Address src) { // Special-case dynamic packing in order to thread the jumps. if (packing == FixedPacking::Dynamic) return emitForDynamicPacking(IGF, &emitInitializeBufferWithCopyOfBuffer, T, type, dest, src); Address destObject = emitAllocateBuffer(IGF, T, type, packing, dest); Address srcObject = emitProjectBuffer(IGF, T, type, packing, src); emitInitializeWithCopy(IGF, T, type, destObject, srcObject); return destObject; } /// Emit an 'initializeBufferWithTakeOfBuffer' operation. /// Returns the address of the destination object. static Address emitInitializeBufferWithTakeOfBuffer(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address dest, Address src) { switch (packing) { case FixedPacking::Dynamic: // Special-case dynamic packing in order to thread the jumps. return emitForDynamicPacking(IGF, &emitInitializeBufferWithTakeOfBuffer, T, type, dest, src); case FixedPacking::OffsetZero: { // Both of these allocations/projections should be no-ops. Address destObject = emitAllocateBuffer(IGF, T, type, packing, dest); Address srcObject = emitProjectBuffer(IGF, T, type, packing, src); emitInitializeWithTake(IGF, T, type, destObject, srcObject); return destObject; } case FixedPacking::Allocate: { // Just copy the out-of-line storage pointers. llvm::Type *ptrTy = type.getStorageType()->getPointerTo()->getPointerTo(); src = IGF.Builder.CreateBitCast(src, ptrTy); llvm::Value *addr = IGF.Builder.CreateLoad(src); dest = IGF.Builder.CreateBitCast(dest, ptrTy); IGF.Builder.CreateStore(addr, dest); return type.getAddressForPointer(addr); } } llvm_unreachable("bad fixed packing"); } /// Emit an 'initializeBufferWithCopy' operation. /// Returns the address of the destination object. static Address emitInitializeBufferWithCopy(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address dest, Address srcObject) { Address destObject = emitAllocateBuffer(IGF, T, type, packing, dest); emitInitializeWithCopy(IGF, T, type, destObject, srcObject); return destObject; } /// Emit an 'initializeBufferWithTake' operation. /// Returns the address of the destination object. static Address emitInitializeBufferWithTake(IRGenFunction &IGF, SILType T, const TypeInfo &type, FixedPacking packing, Address dest, Address srcObject) { Address destObject = emitAllocateBuffer(IGF, T, type, packing, dest); emitInitializeWithTake(IGF, T, type, destObject, srcObject); return destObject; } static llvm::Value *getArg(llvm::Function::arg_iterator &it, StringRef name) { llvm::Value *arg = it++; arg->setName(name); return arg; } /// Get the next argument as a pointer to the given storage type. static Address getArgAs(IRGenFunction &IGF, llvm::Function::arg_iterator &it, const TypeInfo &type, StringRef name) { llvm::Value *arg = getArg(it, name); llvm::Value *result = IGF.Builder.CreateBitCast(arg, type.getStorageType()->getPointerTo()); return type.getAddressForPointer(result); } /// Get the next argument as a pointer to the given storage type. static Address getArgAsBuffer(IRGenFunction &IGF, llvm::Function::arg_iterator &it, StringRef name) { llvm::Value *arg = getArg(it, name); return Address(arg, getFixedBufferAlignment(IGF.IGM)); } /// Get the next argument and use it as the 'self' type metadata. static void getArgAsLocalSelfTypeMetadata(IRGenFunction &IGF, llvm::Function::arg_iterator &it, CanType abstractType); /// Build a value witness that initializes an array front-to-back. static void emitInitializeArrayFrontToBackWitness(IRGenFunction &IGF, llvm::Function::arg_iterator argv, CanType abstractType, SILType concreteType, const TypeInfo &type, IsTake_t take) { Address destArray = getArgAs(IGF, argv, type, "dest"); Address srcArray = getArgAs(IGF, argv, type, "src"); llvm::Value *count = getArg(argv, "count"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); emitInitializeArrayFrontToBack(IGF, type, destArray, srcArray, count, concreteType, take); destArray = IGF.Builder.CreateBitCast(destArray, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(destArray.getAddress()); } /// Build a value witness that initializes an array back-to-front. static void emitInitializeArrayBackToFrontWitness(IRGenFunction &IGF, llvm::Function::arg_iterator argv, CanType abstractType, SILType concreteType, const TypeInfo &type, IsTake_t take) { Address destArray = getArgAs(IGF, argv, type, "dest"); Address srcArray = getArgAs(IGF, argv, type, "src"); llvm::Value *count = getArg(argv, "count"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); emitInitializeArrayBackToFront(IGF, type, destArray, srcArray, count, concreteType, take); destArray = IGF.Builder.CreateBitCast(destArray, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(destArray.getAddress()); } /// Build a specific value-witness function. static void buildValueWitnessFunction(IRGenModule &IGM, llvm::Function *fn, ValueWitness index, FixedPacking packing, CanType abstractType, SILType concreteType, const TypeInfo &type) { assert(isValueWitnessFunction(index)); IRGenFunction IGF(IGM, fn); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, fn); auto argv = fn->arg_begin(); switch (index) { case ValueWitness::AllocateBuffer: { Address buffer = getArgAsBuffer(IGF, argv, "buffer"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); Address result = emitAllocateBuffer(IGF, concreteType, type, packing, buffer); result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(result.getAddress()); return; } case ValueWitness::AssignWithCopy: { Address dest = getArgAs(IGF, argv, type, "dest"); Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); type.assignWithCopy(IGF, dest, src, concreteType); dest = IGF.Builder.CreateBitCast(dest, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(dest.getAddress()); return; } case ValueWitness::AssignWithTake: { Address dest = getArgAs(IGF, argv, type, "dest"); Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); type.assignWithTake(IGF, dest, src, concreteType); dest = IGF.Builder.CreateBitCast(dest, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(dest.getAddress()); return; } case ValueWitness::DeallocateBuffer: { Address buffer = getArgAsBuffer(IGF, argv, "buffer"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); emitDeallocateBuffer(IGF, concreteType, type, packing, buffer); IGF.Builder.CreateRetVoid(); return; } case ValueWitness::Destroy: { Address object = getArgAs(IGF, argv, type, "object"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); type.destroy(IGF, object, concreteType); IGF.Builder.CreateRetVoid(); return; } case ValueWitness::DestroyArray: { Address array = getArgAs(IGF, argv, type, "array"); llvm::Value *count = getArg(argv, "count"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); auto entry = IGF.Builder.GetInsertBlock(); auto iter = IGF.createBasicBlock("iter"); auto loop = IGF.createBasicBlock("loop"); auto exit = IGF.createBasicBlock("exit"); IGF.Builder.CreateBr(iter); IGF.Builder.emitBlock(iter); auto counter = IGF.Builder.CreatePHI(IGM.SizeTy, 2); counter->addIncoming(count, entry); auto elementVal = IGF.Builder.CreatePHI(array.getType(), 2); elementVal->addIncoming(array.getAddress(), entry); Address element(elementVal, array.getAlignment()); auto done = IGF.Builder.CreateICmpEQ(counter, llvm::ConstantInt::get(IGM.SizeTy, 0)); IGF.Builder.CreateCondBr(done, exit, loop); IGF.Builder.emitBlock(loop); type.destroy(IGF, element, concreteType); auto nextCounter = IGF.Builder.CreateSub(counter, llvm::ConstantInt::get(IGM.SizeTy, 1)); auto nextElement = type.indexArray(IGF, element, llvm::ConstantInt::get(IGM.SizeTy, 1), concreteType); auto loopEnd = IGF.Builder.GetInsertBlock(); counter->addIncoming(nextCounter, loopEnd); elementVal->addIncoming(nextElement.getAddress(), loopEnd); IGF.Builder.CreateBr(iter); IGF.Builder.emitBlock(exit); IGF.Builder.CreateRetVoid(); return; } case ValueWitness::DestroyBuffer: { Address buffer = getArgAsBuffer(IGF, argv, "buffer"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); emitDestroyBuffer(IGF, concreteType, type, packing, buffer); IGF.Builder.CreateRetVoid(); return; } case ValueWitness::InitializeBufferWithCopyOfBuffer: { Address dest = getArgAsBuffer(IGF, argv, "dest"); Address src = getArgAsBuffer(IGF, argv, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); Address result = emitInitializeBufferWithCopyOfBuffer(IGF, concreteType, type, packing, dest, src); result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(result.getAddress()); return; } case ValueWitness::InitializeBufferWithTakeOfBuffer: { Address dest = getArgAsBuffer(IGF, argv, "dest"); Address src = getArgAsBuffer(IGF, argv, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); Address result = emitInitializeBufferWithTakeOfBuffer(IGF, concreteType, type, packing, dest, src); result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(result.getAddress()); return; } case ValueWitness::InitializeBufferWithCopy: { Address dest = getArgAsBuffer(IGF, argv, "dest"); Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); Address result = emitInitializeBufferWithCopy(IGF, concreteType, type, packing, dest, src); result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(result.getAddress()); return; } case ValueWitness::InitializeBufferWithTake: { Address dest = getArgAsBuffer(IGF, argv, "dest"); Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); Address result = emitInitializeBufferWithTake(IGF, concreteType, type, packing, dest, src); result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(result.getAddress()); return; } case ValueWitness::InitializeWithCopy: { Address dest = getArgAs(IGF, argv, type, "dest"); Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); emitInitializeWithCopy(IGF, concreteType, type, dest, src); dest = IGF.Builder.CreateBitCast(dest, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(dest.getAddress()); return; } case ValueWitness::InitializeArrayWithCopy: { emitInitializeArrayFrontToBackWitness(IGF, argv, abstractType, concreteType, type, IsNotTake); return; } case ValueWitness::InitializeWithTake: { Address dest = getArgAs(IGF, argv, type, "dest"); Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); emitInitializeWithTake(IGF, concreteType, type, dest, src); dest = IGF.Builder.CreateBitCast(dest, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(dest.getAddress()); return; } case ValueWitness::InitializeArrayWithTakeFrontToBack: { emitInitializeArrayFrontToBackWitness(IGF, argv, abstractType, concreteType, type, IsTake); return; } case ValueWitness::InitializeArrayWithTakeBackToFront: { emitInitializeArrayBackToFrontWitness(IGF, argv, abstractType, concreteType, type, IsTake); return; } case ValueWitness::ProjectBuffer: { Address buffer = getArgAsBuffer(IGF, argv, "buffer"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); Address result = emitProjectBuffer(IGF, concreteType, type, packing, buffer); result = IGF.Builder.CreateBitCast(result, IGF.IGM.OpaquePtrTy); IGF.Builder.CreateRet(result.getAddress()); return; } case ValueWitness::StoreExtraInhabitant: { Address dest = getArgAs(IGF, argv, type, "dest"); llvm::Value *index = getArg(argv, "index"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); type.storeExtraInhabitant(IGF, index, dest, concreteType); IGF.Builder.CreateRetVoid(); return; } case ValueWitness::GetExtraInhabitantIndex: { Address src = getArgAs(IGF, argv, type, "src"); getArgAsLocalSelfTypeMetadata(IGF, argv, abstractType); llvm::Value *idx = type.getExtraInhabitantIndex(IGF, src, concreteType); IGF.Builder.CreateRet(idx); return; } // TODO case ValueWitness::GetEnumTag: case ValueWitness::InplaceProjectEnumData: { IGF.Builder.CreateUnreachable(); return; } case ValueWitness::Size: case ValueWitness::Flags: case ValueWitness::Stride: case ValueWitness::ExtraInhabitantFlags: llvm_unreachable("these value witnesses aren't functions"); } llvm_unreachable("bad value witness kind!"); } static llvm::Constant *asOpaquePtr(IRGenModule &IGM, llvm::Constant *in) { return llvm::ConstantExpr::getBitCast(in, IGM.Int8PtrTy); } /// Should we be defining the given helper function? static llvm::Function *shouldDefineHelper(IRGenModule &IGM, llvm::Constant *fn) { llvm::Function *def = dyn_cast(fn); if (!def) return nullptr; if (!def->empty()) return nullptr; def->setLinkage(llvm::Function::LinkOnceODRLinkage); def->setVisibility(llvm::Function::HiddenVisibility); def->setDoesNotThrow(); def->setCallingConv(IGM.RuntimeCC); return def; } /// Return a function which performs an assignment operation on two /// existentials. /// /// Existential types are nominal, so we potentially need to cast the /// function to the appropriate object-pointer type. static llvm::Constant *getAssignExistentialsFunction(IRGenModule &IGM, llvm::Type *objectPtrTy, OpaqueExistentialLayout layout) { llvm::Type *argTys[] = { objectPtrTy, objectPtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.VoidTy, argTys, false); // __swift_assign_existentials_N is the well-known function for // assigning existential types with N witness tables. llvm::SmallString<40> fnName; llvm::raw_svector_ostream(fnName) << "__swift_assign_existentials_" << layout.getNumTables(); llvm::Constant *fn = IGM.Module.getOrInsertFunction(fnName, fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, getFixedBufferAlignment(IGM)); Address src(it++, getFixedBufferAlignment(IGM)); // If doing a self-assignment, we're done. llvm::BasicBlock *doneBB = IGF.createBasicBlock("done"); llvm::BasicBlock *contBB = IGF.createBasicBlock("cont"); llvm::Value *isSelfAssign = IGF.Builder.CreateICmpEQ(dest.getAddress(), src.getAddress(), "isSelfAssign"); IGF.Builder.CreateCondBr(isSelfAssign, doneBB, contBB); // Project down to the buffers. IGF.Builder.emitBlock(contBB); Address destBuffer = layout.projectExistentialBuffer(IGF, dest); Address srcBuffer = layout.projectExistentialBuffer(IGF, src); // Load the metadata tables. Address destMetadataSlot = layout.projectMetadataRef(IGF, dest); llvm::Value *destMetadata = IGF.Builder.CreateLoad(destMetadataSlot); llvm::Value *srcMetadata = layout.loadMetadataRef(IGF, src); // Check whether the metadata match. llvm::BasicBlock *matchBB = IGF.createBasicBlock("match"); llvm::BasicBlock *noMatchBB = IGF.createBasicBlock("no-match"); llvm::Value *sameMetadata = IGF.Builder.CreateICmpEQ(destMetadata, srcMetadata, "sameMetadata"); IGF.Builder.CreateCondBr(sameMetadata, matchBB, noMatchBB); { // (scope to avoid contaminating other branches with these values) // If so, do a direct assignment. IGF.Builder.emitBlock(matchBB); llvm::Value *destObject = emitProjectBufferCall(IGF, destMetadata, destBuffer); llvm::Value *srcObject = emitProjectBufferCall(IGF, destMetadata, srcBuffer); emitAssignWithCopyCall(IGF, destMetadata, destObject, srcObject); IGF.Builder.CreateBr(doneBB); } // Otherwise, destroy and copy-initialize. // TODO: should we copy-initialize and then destroy? That's // possible if we copy aside, which is a small expense but // always safe. Otherwise the destroy (which can invoke user code) // could see invalid memory at this address. These are basically // the madnesses that boost::variant has to go through, with the // advantage of address-invariance. IGF.Builder.emitBlock(noMatchBB); // Store the metadata ref. IGF.Builder.CreateStore(srcMetadata, destMetadataSlot); // Store the protocol witness tables. unsigned numTables = layout.getNumTables(); for (unsigned i = 0, e = numTables; i != e; ++i) { Address destTableSlot = layout.projectWitnessTable(IGF, dest, i); llvm::Value *srcTable = layout.loadWitnessTable(IGF, src, i); // Overwrite the old witness table. IGF.Builder.CreateStore(srcTable, destTableSlot); } // Destroy the old value. emitDestroyBufferCall(IGF, destMetadata, destBuffer); // Copy-initialize with the new value. Again, pull a value // witness table from the source metadata if we can't use a // protocol witness table. emitInitializeBufferWithCopyOfBufferCall(IGF, srcMetadata, destBuffer, srcBuffer); IGF.Builder.CreateBr(doneBB); // All done. IGF.Builder.emitBlock(doneBB); IGF.Builder.CreateRetVoid(); } return fn; } /// Return a function which takes two pointer arguments and returns /// void immediately. static llvm::Constant *getNoOpVoidFunction(IRGenModule &IGM) { llvm::Type *argTys[] = { IGM.Int8PtrTy, IGM.TypeMetadataPtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.VoidTy, argTys, false); llvm::Constant *fn = IGM.Module.getOrInsertFunction("__swift_noop_void_return", fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { llvm::BasicBlock *entry = llvm::BasicBlock::Create(IGM.getLLVMContext(), "entry", def); IRBuilder B(IGM.getLLVMContext()); B.SetInsertPoint(entry); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(*IGM.SILMod, B, def); B.CreateRetVoid(); } return fn; } /// Return a function which takes two pointer arguments and returns /// the first one immediately. static llvm::Constant *getReturnSelfFunction(IRGenModule &IGM) { llvm::Type *argTys[] = { IGM.Int8PtrTy, IGM.TypeMetadataPtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.Int8PtrTy, argTys, false); llvm::Constant *fn = IGM.Module.getOrInsertFunction("__swift_noop_self_return", fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { llvm::BasicBlock *entry = llvm::BasicBlock::Create(IGM.getLLVMContext(), "entry", def); IRBuilder B(IGM.getLLVMContext()); B.SetInsertPoint(entry); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(*IGM.SILMod, B, def); B.CreateRet(def->arg_begin()); } return fn; } /// Return a function which takes three pointer arguments and does a /// retaining assignWithCopy on the first two: it loads a pointer from /// the second, retains it, loads a pointer from the first, stores the /// new pointer in the first, and releases the old pointer. static llvm::Constant *getAssignWithCopyStrongFunction(IRGenModule &IGM) { llvm::Type *ptrPtrTy = IGM.RefCountedPtrTy->getPointerTo(); llvm::Type *argTys[] = { ptrPtrTy, ptrPtrTy, IGM.WitnessTablePtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(ptrPtrTy, argTys, false); llvm::Constant *fn = IGM.Module.getOrInsertFunction("__swift_assignWithCopy_strong", fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, IGM.getPointerAlignment()); Address src(it++, IGM.getPointerAlignment()); llvm::Value *newValue = IGF.Builder.CreateLoad(src, "new"); IGF.emitRetainCall(newValue); llvm::Value *oldValue = IGF.Builder.CreateLoad(dest, "old"); IGF.Builder.CreateStore(newValue, dest); IGF.emitRelease(oldValue); IGF.Builder.CreateRet(dest.getAddress()); } return fn; } /// Return a function which takes three pointer arguments and does a /// retaining assignWithTake on the first two: it loads a pointer from /// the second, retains it, loads a pointer from the first, stores the /// new pointer in the first, and releases the old pointer. static llvm::Constant *getAssignWithTakeStrongFunction(IRGenModule &IGM) { llvm::Type *ptrPtrTy = IGM.RefCountedPtrTy->getPointerTo(); llvm::Type *argTys[] = { ptrPtrTy, ptrPtrTy, IGM.WitnessTablePtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(ptrPtrTy, argTys, false); llvm::Constant *fn = IGM.Module.getOrInsertFunction("__swift_assignWithTake_strong", fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, IGM.getPointerAlignment()); Address src(it++, IGM.getPointerAlignment()); llvm::Value *newValue = IGF.Builder.CreateLoad(src, "new"); llvm::Value *oldValue = IGF.Builder.CreateLoad(dest, "old"); IGF.Builder.CreateStore(newValue, dest); IGF.emitRelease(oldValue); IGF.Builder.CreateRet(dest.getAddress()); } return fn; } /// Return a function which takes three pointer arguments and does a /// retaining initWithCopy on the first two: it loads a pointer from /// the second, retains it, and stores that in the first. static llvm::Constant *getInitWithCopyStrongFunction(IRGenModule &IGM) { llvm::Type *ptrPtrTy = IGM.RefCountedPtrTy->getPointerTo(); llvm::Type *argTys[] = { ptrPtrTy, ptrPtrTy, IGM.WitnessTablePtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(ptrPtrTy, argTys, false); llvm::Constant *fn = IGM.Module.getOrInsertFunction("__swift_initWithCopy_strong", fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, IGM.getPointerAlignment()); Address src(it++, IGM.getPointerAlignment()); llvm::Value *newValue = IGF.Builder.CreateLoad(src, "new"); IGF.emitRetainCall(newValue); IGF.Builder.CreateStore(newValue, dest); IGF.Builder.CreateRet(dest.getAddress()); } return fn; } /// Return a function which takes two pointer arguments, loads a /// pointer from the first, and calls swift_release on it immediately. static llvm::Constant *getDestroyStrongFunction(IRGenModule &IGM) { llvm::Type *argTys[] = { IGM.Int8PtrPtrTy, IGM.WitnessTablePtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.VoidTy, argTys, false); llvm::Constant *fn = IGM.Module.getOrInsertFunction("__swift_destroy_strong", fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); Address arg(def->arg_begin(), IGM.getPointerAlignment()); IGF.emitRelease(IGF.Builder.CreateLoad(arg)); IGF.Builder.CreateRetVoid(); } return fn; } /// Return a function which takes two pointer arguments, memcpys /// from the second to the first, and returns the first argument. static llvm::Constant *getMemCpyFunction(IRGenModule &IGM, const TypeInfo &objectTI) { llvm::Type *argTys[] = { IGM.Int8PtrTy, IGM.Int8PtrTy, IGM.TypeMetadataPtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.Int8PtrTy, argTys, false); // If we don't have a fixed type, use the standard copy-opaque-POD // routine. It's not quite clear how in practice we'll be able to // conclude that something is known-POD without knowing its size, // but it's (1) conceivable and (2) needed as a general export anyway. auto *fixedTI = dyn_cast(&objectTI); if (!fixedTI) return IGM.getCopyPODFn(); // We need to unique by both size and alignment. Note that we're // assuming that it's safe to call a function that returns a pointer // at a site that assumes the function returns void. llvm::SmallString<40> name; { llvm::raw_svector_ostream nameStream(name); nameStream << "__swift_memcpy"; nameStream << fixedTI->getFixedSize().getValue(); nameStream << '_'; nameStream << fixedTI->getFixedAlignment().getValue(); } llvm::Constant *fn = IGM.Module.getOrInsertFunction(name, fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, fixedTI->getFixedAlignment()); Address src(it++, fixedTI->getFixedAlignment()); IGF.emitMemCpy(dest, src, fixedTI->getFixedSize()); IGF.Builder.CreateRet(dest.getAddress()); } return fn; } /// Return a function which takes two buffer arguments, copies /// a pointer from the second to the first, and returns the pointer. static llvm::Constant *getCopyOutOfLinePointerFunction(IRGenModule &IGM) { llvm::Type *argTys[] = { IGM.Int8PtrPtrTy, IGM.Int8PtrPtrTy, IGM.TypeMetadataPtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.Int8PtrTy, argTys, false); StringRef name = "__swift_copy_outline_pointer"; llvm::Constant *fn = IGM.Module.getOrInsertFunction(name, fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, IGM.getPointerAlignment()); Address src(it++, IGM.getPointerAlignment()); auto ptr = IGF.Builder.CreateLoad(src); IGF.Builder.CreateStore(ptr, dest); IGF.Builder.CreateRet(ptr); } return fn; } namespace { enum class MemMoveOrCpy { MemMove, MemCpy }; } /// Return a function which takes two pointer arguments and a count, memmoves /// or memcpys from the second to the first, and returns the first argument. static llvm::Constant *getMemOpArrayFunction(IRGenModule &IGM, const TypeInfo &objectTI, MemMoveOrCpy kind) { llvm::Type *argTys[] = { IGM.Int8PtrTy, IGM.Int8PtrTy, IGM.SizeTy, IGM.TypeMetadataPtrTy }; llvm::FunctionType *fnTy = llvm::FunctionType::get(IGM.Int8PtrTy, argTys, false); // TODO: Add a copyPODArray runtime entry point for bitwise-takable but non- // fixed-size types. Currently only fixed-layout types should be known // bitwise-takable. auto &fixedTI = cast(objectTI); // We need to unique by both size and alignment. Note that we're // assuming that it's safe to call a function that returns a pointer // at a site that assumes the function returns void. llvm::SmallString<40> name; { llvm::raw_svector_ostream nameStream(name); switch (kind) { case MemMoveOrCpy::MemCpy: nameStream << "__swift_memcpy_array"; break; case MemMoveOrCpy::MemMove: nameStream << "__swift_memmove_array"; break; } nameStream << fixedTI.getFixedStride().getValue(); nameStream << '_'; nameStream << fixedTI.getFixedAlignment().getValue(); } llvm::Constant *fn = IGM.Module.getOrInsertFunction(name, fnTy); if (llvm::Function *def = shouldDefineHelper(IGM, fn)) { IRGenFunction IGF(IGM, def); if (IGM.DebugInfo) IGM.DebugInfo->emitArtificialFunction(IGF, def); auto it = def->arg_begin(); Address dest(it++, fixedTI.getFixedAlignment()); Address src(it++, fixedTI.getFixedAlignment()); llvm::Value *count = it++; llvm::Value *stride = llvm::ConstantInt::get(IGM.SizeTy, fixedTI.getFixedStride().getValue()); llvm::Value *totalCount = IGF.Builder.CreateNUWMul(count, stride); switch (kind) { case MemMoveOrCpy::MemMove: IGF.Builder.CreateMemMove(dest.getAddress(), src.getAddress(), totalCount, fixedTI.getFixedAlignment().getValue()); break; case MemMoveOrCpy::MemCpy: IGF.Builder.CreateMemCpy(dest.getAddress(), src.getAddress(), totalCount, fixedTI.getFixedAlignment().getValue()); break; } IGF.Builder.CreateRet(dest.getAddress()); } return fn; } static llvm::Constant *getMemMoveArrayFunction(IRGenModule &IGM, const TypeInfo &objectTI) { return getMemOpArrayFunction(IGM, objectTI, MemMoveOrCpy::MemMove); } static llvm::Constant *getMemCpyArrayFunction(IRGenModule &IGM, const TypeInfo &objectTI) { return getMemOpArrayFunction(IGM, objectTI, MemMoveOrCpy::MemCpy); } /// Find a witness to the fact that a type is a value type. /// Always returns an i8*. static llvm::Constant *getValueWitness(IRGenModule &IGM, ValueWitness index, FixedPacking packing, CanType abstractType, SILType concreteType, const TypeInfo &concreteTI) { // Try to use a standard function. switch (index) { case ValueWitness::DeallocateBuffer: if (isNeverAllocated(packing)) return asOpaquePtr(IGM, getNoOpVoidFunction(IGM)); goto standard; case ValueWitness::DestroyBuffer: if (concreteTI.isPOD(ResilienceScope::Local)) { if (isNeverAllocated(packing)) return asOpaquePtr(IGM, getNoOpVoidFunction(IGM)); } else if (concreteTI.isSingleSwiftRetainablePointer(ResilienceScope::Local)) { assert(isNeverAllocated(packing)); return asOpaquePtr(IGM, getDestroyStrongFunction(IGM)); } goto standard; case ValueWitness::Destroy: if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getNoOpVoidFunction(IGM)); } else if (concreteTI.isSingleSwiftRetainablePointer(ResilienceScope::Local)) { return asOpaquePtr(IGM, getDestroyStrongFunction(IGM)); } goto standard; case ValueWitness::DestroyArray: if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getNoOpVoidFunction(IGM)); } // TODO: A standard "destroy strong array" entrypoint for arrays of single // refcounted pointer types. goto standard; case ValueWitness::InitializeBufferWithCopyOfBuffer: case ValueWitness::InitializeBufferWithCopy: if (packing == FixedPacking::OffsetZero) { if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); } else if (concreteTI.isSingleSwiftRetainablePointer(ResilienceScope::Local)) { return asOpaquePtr(IGM, getInitWithCopyStrongFunction(IGM)); } } goto standard; case ValueWitness::InitializeBufferWithTakeOfBuffer: if (packing == FixedPacking::Allocate) { return asOpaquePtr(IGM, getCopyOutOfLinePointerFunction(IGM)); } else if (packing == FixedPacking::OffsetZero && concreteTI.isBitwiseTakable(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); } goto standard; case ValueWitness::InitializeBufferWithTake: if (concreteTI.isBitwiseTakable(ResilienceScope::Local) && packing == FixedPacking::OffsetZero) return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); goto standard; case ValueWitness::InitializeWithTake: if (concreteTI.isBitwiseTakable(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); } goto standard; case ValueWitness::InitializeArrayWithTakeFrontToBack: if (concreteTI.isBitwiseTakable(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemMoveArrayFunction(IGM, concreteTI)); } goto standard; case ValueWitness::InitializeArrayWithTakeBackToFront: if (concreteTI.isBitwiseTakable(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemMoveArrayFunction(IGM, concreteTI)); } goto standard; case ValueWitness::AssignWithCopy: if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); } else if (concreteTI.isSingleSwiftRetainablePointer(ResilienceScope::Local)) { return asOpaquePtr(IGM, getAssignWithCopyStrongFunction(IGM)); } goto standard; case ValueWitness::AssignWithTake: if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); } else if (concreteTI.isSingleSwiftRetainablePointer(ResilienceScope::Local)) { return asOpaquePtr(IGM, getAssignWithTakeStrongFunction(IGM)); } goto standard; case ValueWitness::InitializeWithCopy: if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyFunction(IGM, concreteTI)); } else if (concreteTI.isSingleSwiftRetainablePointer(ResilienceScope::Local)) { return asOpaquePtr(IGM, getInitWithCopyStrongFunction(IGM)); } goto standard; case ValueWitness::InitializeArrayWithCopy: if (concreteTI.isPOD(ResilienceScope::Local)) { return asOpaquePtr(IGM, getMemCpyArrayFunction(IGM, concreteTI)); } // TODO: A standard "copy strong array" entrypoint for arrays of single // refcounted pointer types. goto standard; case ValueWitness::AllocateBuffer: case ValueWitness::ProjectBuffer: if (packing == FixedPacking::OffsetZero) return asOpaquePtr(IGM, getReturnSelfFunction(IGM)); goto standard; case ValueWitness::Size: { if (auto value = concreteTI.getStaticSize(IGM)) return llvm::ConstantExpr::getIntToPtr(value, IGM.Int8PtrTy); // Just fill in null here if the type can't be statically laid out. return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } case ValueWitness::Flags: { // If we locally know that the type has fixed layout, we can emit // meaningful flags for it. if (auto *fixedTI = dyn_cast(&concreteTI)) { uint64_t flags = fixedTI->getFixedAlignment().getValue() - 1; if (!fixedTI->isPOD(ResilienceScope::Local)) flags |= ValueWitnessFlags::IsNonPOD; assert(packing == FixedPacking::OffsetZero || packing == FixedPacking::Allocate); if (packing != FixedPacking::OffsetZero) flags |= ValueWitnessFlags::IsNonInline; if (fixedTI->getFixedExtraInhabitantCount(IGM) > 0) flags |= ValueWitnessFlags::Enum_HasExtraInhabitants; if (!fixedTI->isBitwiseTakable(ResilienceScope::Local)) flags |= ValueWitnessFlags::IsNonBitwiseTakable; auto value = IGM.getSize(Size(flags)); return llvm::ConstantExpr::getIntToPtr(value, IGM.Int8PtrTy); } // Just fill in null here if the type can't be statically laid out. return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } case ValueWitness::Stride: { if (auto value = concreteTI.getStaticStride(IGM)) return llvm::ConstantExpr::getIntToPtr(value, IGM.Int8PtrTy); // Just fill in null here if the type can't be statically laid out. return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } case ValueWitness::StoreExtraInhabitant: case ValueWitness::GetExtraInhabitantIndex: { assert(concreteTI.mayHaveExtraInhabitants(IGM)); goto standard; } case ValueWitness::ExtraInhabitantFlags: { assert(concreteTI.mayHaveExtraInhabitants(IGM)); // If we locally know that the type has fixed layout, we can emit // meaningful flags for it. if (auto *fixedTI = dyn_cast(&concreteTI)) { uint64_t numExtraInhabitants = fixedTI->getFixedExtraInhabitantCount(IGM); assert(numExtraInhabitants <= ExtraInhabitantFlags::NumExtraInhabitantsMask); auto value = IGM.getSize(Size(numExtraInhabitants)); return llvm::ConstantExpr::getIntToPtr(value, IGM.Int8PtrTy); } // Otherwise, just fill in null here if the type can't be statically // queried for extra inhabitants. return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } /// TODO: case ValueWitness::GetEnumTag: case ValueWitness::InplaceProjectEnumData: return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } llvm_unreachable("bad value witness kind"); standard: llvm::Function *fn = IGM.getAddrOfValueWitness(abstractType, index, ForDefinition); if (fn->empty()) buildValueWitnessFunction(IGM, fn, index, packing, abstractType, concreteType, concreteTI); return asOpaquePtr(IGM, fn); } namespace { /// A class which lays out a specific conformance to a protocol. class WitnessTableBuilder : public SILWitnessVisitor { IRGenModule &IGM; SmallVectorImpl &Table; CanType ConcreteType; GenericParamList *ConcreteGenerics = nullptr; const TypeInfo &ConcreteTI; const ProtocolConformance &Conformance; ArrayRef Substitutions; ArrayRef SILEntries; #ifndef NDEBUG const ProtocolInfo &PI; #endif void computeSubstitutionsForType() { // FIXME: This is a bit of a hack; the AST doesn't directly encode // substitutions for the conformance of a generic type to a // protocol, so we have to dig them out. Type ty = ConcreteType; while (ty) { if (auto nomTy = ty->getAs()) ty = nomTy->getParent(); else break; } if (ty) { if (auto boundTy = ty->getAs()) { ConcreteGenerics = boundTy->getDecl()->getGenericParams(); Substitutions = boundTy->getSubstitutions(/*FIXME:*/nullptr, nullptr); } else { assert(!ty || !ty->isSpecialized()); } } } public: WitnessTableBuilder(IRGenModule &IGM, SmallVectorImpl &table, SILWitnessTable *SILWT) : IGM(IGM), Table(table), ConcreteType(SILWT->getConformance()->getType()->getCanonicalType()), ConcreteTI( IGM.getTypeInfoForUnlowered(SILWT->getConformance()->getType())), Conformance(*SILWT->getConformance()), SILEntries(SILWT->getEntries()) #ifndef NDEBUG , PI(IGM.getProtocolInfo(SILWT->getConformance()->getProtocol())) #endif { computeSubstitutionsForType(); } /// A base protocol is witnessed by a pointer to the conformance /// of this type to that protocol. void addOutOfLineBaseProtocol(ProtocolDecl *baseProto) { #ifndef NDEBUG auto &entry = SILEntries.front(); assert(entry.getKind() == SILWitnessTable::BaseProtocol && "sil witness table does not match protocol"); assert(entry.getBaseProtocolWitness().Requirement == baseProto && "sil witness table does not match protocol"); auto piEntry = PI.getWitnessEntry(baseProto); assert(piEntry.getOutOfLineBaseIndex().getValue() == Table.size() && "offset doesn't match ProtocolInfo layout"); #endif SILEntries = SILEntries.slice(1); // TODO: Use the witness entry instead of falling through here. // Look for a protocol type info. const ProtocolInfo &basePI = IGM.getProtocolInfo(baseProto); const ProtocolConformance *astConf = Conformance.getInheritedConformance(baseProto); const ConformanceInfo &conf = basePI.getConformance(IGM, ConcreteType, ConcreteTI, baseProto, *astConf); llvm::Constant *baseWitness = conf.tryGetConstantTable(IGM); assert(baseWitness && "couldn't get a constant table!"); Table.push_back(asOpaquePtr(IGM, baseWitness)); } void addMethodFromSILWitnessTable(AbstractFunctionDecl *iface) { auto &entry = SILEntries.front(); SILEntries = SILEntries.slice(1); // Handle missing optional requirements. if (entry.getKind() == SILWitnessTable::MissingOptional) { Table.push_back(llvm::ConstantPointerNull::get(IGM.Int8PtrTy)); return; } #ifndef NDEBUG assert(entry.getKind() == SILWitnessTable::Method && "sil witness table does not match protocol"); assert(entry.getMethodWitness().Requirement.getDecl() == iface && "sil witness table does not match protocol"); auto piEntry = PI.getWitnessEntry(iface); assert(piEntry.getFunctionIndex().getValue() == Table.size() && "offset doesn't match ProtocolInfo layout"); #endif SILFunction *Func = entry.getMethodWitness().Witness; llvm::Constant *witness = nullptr; if (Func) { witness = IGM.getAddrOfSILFunction(Func, NotForDefinition); witness = llvm::ConstantExpr::getBitCast(witness, IGM.Int8PtrTy); } else { // The method is removed by dead method elimination. // It should be never called. We add a pointer to an error function. witness = llvm::ConstantExpr::getBitCast(IGM.getDeadMethodErrorFn(), IGM.Int8PtrTy); } Table.push_back(witness); return; } void addMethod(FuncDecl *iface) { return addMethodFromSILWitnessTable(iface); } void addConstructor(ConstructorDecl *iface) { return addMethodFromSILWitnessTable(iface); } void addAssociatedType(AssociatedTypeDecl *ty, ArrayRef protos) { #ifndef NDEBUG auto &entry = SILEntries.front(); assert(entry.getKind() == SILWitnessTable::AssociatedType && "sil witness table does not match protocol"); assert(entry.getAssociatedTypeWitness().Requirement == ty && "sil witness table does not match protocol"); auto piEntry = PI.getWitnessEntry(ty); assert(piEntry.getAssociatedTypeIndex().getValue() == Table.size() && "offset doesn't match ProtocolInfo layout"); #endif SILEntries = SILEntries.slice(1); // FIXME: Use info from SILWitnessTable instead of falling through. // Determine whether the associated type has static metadata. If it // doesn't, then this witness table is a template that requires runtime // instantiation. // FIXME: Add static type metadata. Table.push_back(llvm::ConstantPointerNull::get(IGM.Int8PtrTy)); // FIXME: Add static witness tables for type conformances. for (auto protocol : protos) { if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; auto &entry = SILEntries.front(); (void)entry; assert(entry.getKind() == SILWitnessTable::AssociatedTypeProtocol && "sil witness table does not match protocol"); assert(entry.getAssociatedTypeProtocolWitness().Requirement == ty && "sil witness table does not match protocol"); assert(entry.getAssociatedTypeProtocolWitness().Protocol == protocol && "sil witness table does not match protocol"); SILEntries = SILEntries.slice(1); // FIXME: Use info from SILWitnessTable instead of falling through. // FIXME: Add static witness table reference. Table.push_back(llvm::ConstantPointerNull::get(IGM.Int8PtrTy)); } } }; } /// Collect the value witnesses for a particular type. static void addValueWitnesses(IRGenModule &IGM, FixedPacking packing, CanType abstractType, SILType concreteType, const TypeInfo &concreteTI, SmallVectorImpl &table) { for (unsigned i = 0; i != NumRequiredValueWitnesses; ++i) { table.push_back(getValueWitness(IGM, ValueWitness(i), packing, abstractType, concreteType, concreteTI)); } if (concreteTI.mayHaveExtraInhabitants(IGM)) { for (auto i = unsigned(ValueWitness::First_ExtraInhabitantValueWitness); i <= unsigned(ValueWitness::Last_ExtraInhabitantValueWitness); ++i) { table.push_back(getValueWitness(IGM, ValueWitness(i), packing, abstractType, concreteType, concreteTI)); } } } /// True if a type has a generic-parameter-dependent value witness table. /// Currently, this is true if the size and/or alignment of the type is /// dependent on its generic parameters. bool irgen::hasDependentValueWitnessTable(IRGenModule &IGM, CanType ty) { if (auto ugt = dyn_cast(ty)) ty = ugt->getDecl()->getDeclaredTypeInContext()->getCanonicalType(); return !IGM.getTypeInfoForUnlowered(ty).isFixedSize(); } static void addValueWitnessesForAbstractType(IRGenModule &IGM, CanType abstractType, SmallVectorImpl &witnesses) { // Instantiate unbound generic types on their context archetypes. CanType concreteFormalType = abstractType; if (auto ugt = dyn_cast(abstractType)) { concreteFormalType = ugt->getDecl()->getDeclaredTypeInContext()->getCanonicalType(); } auto concreteLoweredType = IGM.SILMod->Types.getLoweredType(concreteFormalType); auto &concreteTI = IGM.getTypeInfo(concreteLoweredType); FixedPacking packing = concreteTI.getFixedPacking(IGM); addValueWitnesses(IGM, packing, abstractType, concreteLoweredType, concreteTI, witnesses); } /// Emit a value-witness table for the given type, which is assumed to /// be non-dependent. llvm::Constant *irgen::emitValueWitnessTable(IRGenModule &IGM, CanType abstractType) { // We shouldn't emit global value witness tables for generic type instances. assert(!isa(abstractType) && "emitting VWT for generic instance"); // We shouldn't emit global value witness tables for non-fixed-layout types. assert(!hasDependentValueWitnessTable(IGM, abstractType) && "emitting global VWT for dynamic-layout type"); SmallVector witnesses; addValueWitnessesForAbstractType(IGM, abstractType, witnesses); auto tableTy = llvm::ArrayType::get(IGM.Int8PtrTy, witnesses.size()); auto table = llvm::ConstantArray::get(tableTy, witnesses); auto addr = IGM.getAddrOfValueWitnessTable(abstractType, table->getType()); auto global = cast(addr); global->setConstant(true); global->setInitializer(table); return llvm::ConstantExpr::getBitCast(global, IGM.WitnessTablePtrTy); } /// Emit the elements of a dependent value witness table template into a /// vector. void irgen::emitDependentValueWitnessTablePattern(IRGenModule &IGM, CanType abstractType, SmallVectorImpl &fields) { // We shouldn't emit global value witness tables for generic type instances. assert(!isa(abstractType) && "emitting VWT for generic instance"); // We shouldn't emit global value witness tables for fixed-layout types. assert(hasDependentValueWitnessTable(IGM, abstractType) && "emitting VWT pattern for fixed-layout type"); addValueWitnessesForAbstractType(IGM, abstractType, fields); } /// Do a memoized witness-table layout for a protocol. const ProtocolInfo &IRGenModule::getProtocolInfo(ProtocolDecl *protocol) { return Types.getProtocolInfo(protocol); } /// Do a memoized witness-table layout for a protocol. const ProtocolInfo &TypeConverter::getProtocolInfo(ProtocolDecl *protocol) { // Check whether we've already translated this protocol. auto it = Protocols.find(protocol); if (it != Protocols.end()) return *it->second; // If not, lay out the protocol's witness table, if it needs one. WitnessTableLayout layout; if (Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) layout.visitProtocolDecl(protocol); // Create a ProtocolInfo object from the layout. ProtocolInfo *info = ProtocolInfo::create(layout.getNumWitnesses(), layout.getEntries()); info->NextConverted = FirstProtocol; FirstProtocol = info; // Memoize. Protocols.insert(std::make_pair(protocol, info)); // Done. return *info; } /// Allocate a new ProtocolInfo. ProtocolInfo *ProtocolInfo::create(unsigned numWitnesses, ArrayRef table) { unsigned numEntries = table.size(); size_t bufferSize = sizeof(ProtocolInfo) + numEntries * sizeof(WitnessTableEntry); void *buffer = ::operator new(bufferSize); return new(buffer) ProtocolInfo(numWitnesses, table); } ProtocolInfo::~ProtocolInfo() { for (auto &conf : Conformances) { delete conf.second; } } /// Find the conformance information for a protocol. const ConformanceInfo & ProtocolInfo::getConformance(IRGenModule &IGM, CanType concreteType, const TypeInfo &concreteTI, ProtocolDecl *protocol, const ProtocolConformance &conformance) const { // Check whether we've already cached this. auto it = Conformances.find(&conformance); if (it != Conformances.end()) return *it->second; // Drill down to the root normal conformance. auto normalConformance = conformance.getRootNormalConformance(); // Emit a direct-referencing conformance. // FIXME: For some conformances we need to do lazy initialization or runtime // instantiation. ConformanceInfo *info = new DirectConformanceInfo(normalConformance); auto res = Conformances.insert(std::make_pair(&conformance, info)); return *res.first->second; } std::pair getTypeReferenceForProtocolConformanceRecord(IRGenModule &IGM, ProtocolConformance *conformance) { ProtocolConformanceTypeKind typeKind; llvm::Constant *typeRef; // TODO: Should use accessor kind for lazy conformances ProtocolConformanceReferenceKind conformanceKind = ProtocolConformanceReferenceKind::WitnessTable; auto conformingType = conformance->getType()->getCanonicalType(); if (auto bgt = dyn_cast(conformingType)) { // Conformances for generics are represented by referencing the metadata // pattern for the generic type. typeKind = ProtocolConformanceTypeKind::UniqueGenericPattern; typeRef = IGM.getAddrOfTypeMetadata(bgt->getDecl()->getDeclaredType() ->getCanonicalType(), /* indirect */ false, /* pattern */ true); } else if (auto ct = dyn_cast(conformingType)) { auto clas = ct->getDecl(); if (clas->isForeign()) { typeKind = ProtocolConformanceTypeKind::NonuniqueDirectType; typeRef = IGM.getAddrOfForeignTypeMetadataCandidate(ct); } else { // TODO: We should indirectly reference classes. For now directly // reference the class object, which is totally wrong for ObjC interop. typeKind = ProtocolConformanceTypeKind::UniqueDirectClass; typeRef = IGM.getAddrOfTypeMetadata(ct, /* indirect */ false, /* pattern */ false); } } else if (auto nom = conformingType->getNominalOrBoundGenericNominal()) { // TODO: Metadata for Clang types should be uniqued like foreign classes. if (nom->hasClangNode()) { typeKind = ProtocolConformanceTypeKind::NonuniqueDirectType; typeRef = IGM.getAddrOfForeignTypeMetadataCandidate(conformingType); } else { // We can reference the canonical metadata for native value types // directly. typeKind = ProtocolConformanceTypeKind::UniqueDirectType; typeRef = IGM.getAddrOfTypeMetadata(nom->getDeclaredType() ->getCanonicalType(), /* indirect */ false, /* pattern */ false); } } else { // TODO: Universal and/or structural conformances llvm_unreachable("unhandled protocol conformance"); } // Cast the type reference to OpaquePtrTy. typeRef = llvm::ConstantExpr::getBitCast(typeRef, IGM.OpaquePtrTy); auto flags = ProtocolConformanceFlags() .withTypeKind(typeKind) .withConformanceKind(conformanceKind); return {flags.getValue(), typeRef}; } void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) { // Don't emit a witness table if it is a declaration. if (wt->isDeclaration()) return; // Don't emit a witness table that is available externally if we are emitting // code for the JIT. We do not do any optimization for the JIT and it has // problems with external symbols that get merged with non-external symbols. if (Opts.UseJIT && isAvailableExternally(wt->getLinkage())) return; // Build the witnesses. SmallVector witnesses; WitnessTableBuilder(*this, witnesses, wt) .visitProtocolDecl(wt->getConformance()->getProtocol()); assert(getProtocolInfo(wt->getConformance()->getProtocol()) .getNumWitnesses() == witnesses.size() && "witness table size doesn't match ProtocolInfo"); // Produce the initializer value. auto tableTy = llvm::ArrayType::get(FunctionPtrTy, witnesses.size()); auto initializer = llvm::ConstantArray::get(tableTy, witnesses); auto global = cast( getAddrOfWitnessTable(wt->getConformance(), tableTy)); global->setConstant(true); global->setInitializer(initializer); global->setAlignment(getWitnessTableAlignment().getValue()); // Build the conformance record, if it lives in this TU. if (isAvailableExternally(wt->getLinkage())) return; unsigned flags; llvm::Constant *typeRef; std::tie(flags, typeRef) = getTypeReferenceForProtocolConformanceRecord(*this, wt->getConformance()); llvm::Constant *recordFields[] = { // Protocol descriptor getAddrOfProtocolDescriptor(wt->getConformance()->getProtocol(), NotForDefinition), // Type reference typeRef, // Witness table // TODO: This needs to be a generator function if the witness table requires // lazy initialization or instantiation. llvm::ConstantExpr::getBitCast(global, OpaquePtrTy), // Flags llvm::ConstantInt::get(Int32Ty, flags), }; auto record = llvm::ConstantStruct::get(ProtocolConformanceRecordTy, recordFields); addProtocolConformanceRecord(record); // TODO: We should record what access mode the witness table requires: // direct, lazily initialized, or runtime instantiated template. } namespace { /// Type info for error existentials, currently the only kind of boxed /// existential. class ErrorExistentialTypeInfo : public HeapTypeInfo { ProtocolEntry ErrorProtocolEntry; public: ErrorExistentialTypeInfo(llvm::PointerType *storage, Size size, SpareBitVector spareBits, Alignment align, const ProtocolEntry &errorProtocolEntry) : HeapTypeInfo(storage, size, spareBits, align), ErrorProtocolEntry(errorProtocolEntry) {} ReferenceCounting getReferenceCounting() const { // ErrorType uses its own rc entry points. return ReferenceCounting::Error; } ArrayRef getStoredProtocols() const { return ErrorProtocolEntry; } }; } // end anonymous namespace static const TypeInfo *createErrorExistentialTypeInfo(IRGenModule &IGM, ArrayRef protocols) { // The ErrorType existential has a special boxed representation. It has space // only for witnesses to the ErrorType protocol. assert(protocols.size() == 1 && *protocols[0]->getKnownProtocolKind() == KnownProtocolKind::_ErrorType); const ProtocolInfo &impl = IGM.getProtocolInfo(protocols[0]); return new ErrorExistentialTypeInfo(IGM.ErrorPtrTy, IGM.getPointerSize(), IGM.getHeapObjectSpareBits(), IGM.getPointerAlignment(), ProtocolEntry(protocols[0], impl)); } static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, TypeBase *T, ArrayRef protocols) { SmallVector fields; SmallVector entries; // Check for special existentials. if (protocols.size() == 1) { switch (getSpecialProtocolID(protocols[0])) { case SpecialProtocol::ErrorType: // ErrorType has a special runtime representation. return createErrorExistentialTypeInfo(IGM, protocols); // Other existentials have standard representations. case SpecialProtocol::AnyObject: case SpecialProtocol::None: break; } } llvm::StructType *type; if (auto *protoT = T->getAs()) type = IGM.createNominalType(protoT->getDecl()); else if (auto *compT = T->getAs()) // Protocol composition types are not nominal, but we name them anyway. type = IGM.createNominalType(compT); else llvm_unreachable("unknown existential type kind"); assert(type->isOpaque() && "creating existential type in concrete struct"); // In an opaque metadata, the first two fields are the fixed buffer // followed by the metadata reference. In a class metadata, the // first field is the class instance. // // Leave space in the buffer for both, but make sure we set it up later. fields.push_back(nullptr); fields.push_back(nullptr); bool requiresClass = false; bool allowsTaggedPointers = true; for (auto protocol : protocols) { // The existential container is class-constrained if any of its protocol // constraints are. requiresClass |= protocol->requiresClass(); if (protocol->getAttrs().hasAttribute()) allowsTaggedPointers = false; // ObjC protocols need no layout or witness table info. All dispatch is done // through objc_msgSend. if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; // Find the protocol layout. const ProtocolInfo &impl = IGM.getProtocolInfo(protocol); entries.push_back(ProtocolEntry(protocol, impl)); // Each protocol gets a witness table. fields.push_back(IGM.WitnessTablePtrTy); } // If the existential is class, lower it to a class // existential representation. if (requiresClass) { // Replace the type metadata pointer with the class instance. fields[1] = IGM.UnknownRefCountedPtrTy; auto classFields = llvm::makeArrayRef(fields).slice(1); type->setBody(classFields); Alignment align = IGM.getPointerAlignment(); Size size = classFields.size() * IGM.getPointerSize(); SpareBitVector spareBits; // The class pointer is an unknown heap object, so it may be a tagged // pointer, if the platform has those. if (allowsTaggedPointers && IGM.TargetInfo.hasObjCTaggedPointers()) { spareBits.appendClearBits(IGM.getPointerSize().getValueInBits()); } else { // If the platform doesn't use ObjC tagged pointers, we can go crazy. spareBits.append(IGM.getHeapObjectSpareBits()); } for (unsigned i = 1, e = classFields.size(); i < e; ++i) { spareBits.append(IGM.getWitnessTablePtrSpareBits()); } return ClassExistentialTypeInfo::create(entries, type, size, std::move(spareBits), align); } // Set up the first two fields. fields[0] = IGM.getFixedBufferTy(); fields[1] = IGM.TypeMetadataPtrTy; type->setBody(fields); OpaqueExistentialLayout layout(entries.size()); Alignment align = layout.getAlignment(IGM); Size size = layout.getSize(IGM); return OpaqueExistentialTypeInfo::create(entries, type, size, align); } const TypeInfo *TypeConverter::convertProtocolType(ProtocolType *T) { // Protocol types are nominal. return createExistentialTypeInfo(IGM, T, T->getDecl()); } const TypeInfo * TypeConverter::convertProtocolCompositionType(ProtocolCompositionType *T) { // Find the canonical protocols. There might not be any. SmallVector protocols; T->getAnyExistentialTypeProtocols(protocols); return createExistentialTypeInfo(IGM, T, protocols); } const TypeInfo * TypeConverter::convertExistentialMetatypeType(ExistentialMetatypeType *T) { assert(T->hasRepresentation() && "metatype should have been assigned a representation by SIL"); SmallVector protocols; T->getAnyExistentialTypeProtocols(protocols); SmallVector entries; SmallVector fields; SpareBitVector spareBits; assert(T->getRepresentation() != MetatypeRepresentation::Thin && "existential metatypes cannot have thin representation"); auto &baseTI = getMetatypeTypeInfo(T->getRepresentation()); fields.push_back(baseTI.getStorageType()); spareBits.append(baseTI.getSpareBits()); for (auto protocol : protocols) { if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; // Find the protocol layout. const ProtocolInfo &impl = IGM.getProtocolInfo(protocol); entries.push_back(ProtocolEntry(protocol, impl)); // Each protocol gets a witness table. fields.push_back(IGM.WitnessTablePtrTy); spareBits.append(IGM.getWitnessTablePtrSpareBits()); } llvm::StructType *type = llvm::StructType::get(IGM.getLLVMContext(), fields); Size size = IGM.getPointerSize() * fields.size(); Alignment align = IGM.getPointerAlignment(); return ExistentialMetatypeTypeInfo::create(entries, type, size, std::move(spareBits), align, baseTI); } const TypeInfo *TypeConverter::convertArchetypeType(ArchetypeType *archetype) { assert(isExemplarArchetype(archetype) && "lowering non-exemplary archetype"); // Compute layouts for the protocols we ascribe to. SmallVector protocols; for (auto protocol : archetype->getConformsTo()) { const ProtocolInfo &impl = IGM.getProtocolInfo(protocol); protocols.push_back(ProtocolEntry(protocol, impl)); } // If the archetype is class-constrained, use a class pointer // representation. if (archetype->requiresClass()) { // Fully general archetypes can't be assumed to have any particular // refcounting scheme. ReferenceCounting refcount = ReferenceCounting::Unknown; llvm::PointerType *reprTy = IGM.UnknownRefCountedPtrTy; // If the archetype has a superclass constraint, it has at least the // retain semantics of its superclass, and it can be represented with // the supertype's pointer type. if (Type super = archetype->getSuperclass()) { ClassDecl *superClass = super->getClassOrBoundGenericClass(); refcount = getReferenceCountingForClass(IGM, superClass); auto &superTI = IGM.getTypeInfoForUnlowered(super); reprTy = cast(superTI.StorageType); } // As a hack, assume class archetypes never have spare bits. There's a // corresponding hack in MultiPayloadEnumImplStrategy::completeEnumTypeLayout // to ignore spare bits of dependent-typed payloads. auto spareBits = SpareBitVector::getConstant(IGM.getPointerSize().getValueInBits(), false); return ClassArchetypeTypeInfo::create(reprTy, IGM.getPointerSize(), spareBits, IGM.getPointerAlignment(), protocols, refcount); } // Otherwise, for now, always use an opaque indirect type. llvm::Type *storageType = IGM.OpaquePtrTy->getElementType(); return OpaqueArchetypeTypeInfo::create(storageType, protocols); } /// Inform IRGenFunction that the given archetype has the given value /// witness value within this scope. void IRGenFunction::bindArchetype(ArchetypeType *archetype, llvm::Value *metadata, ArrayRef wtables) { // Set the metadata pointer. bool setNames = !archetype->getOpenedExistentialType(); if (setNames) metadata->setName(archetype->getFullName()); setMetadataRef(*this, archetype, metadata); // Set the protocol witness tables. unsigned wtableI = 0; for (unsigned i = 0, e = archetype->getConformsTo().size(); i != e; ++i) { auto proto = archetype->getConformsTo()[i]; if (!Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) continue; auto wtable = wtables[wtableI++]; if (setNames) { wtable->setName(Twine(archetype->getFullName()) + "." + proto->getName().str()); } setWitnessTable(*this, archetype, i, wtable); } assert(wtableI == wtables.size()); } /// True if a function's signature in LLVM carries polymorphic parameters. /// Generic functions and protocol witnesses carry polymorphic parameters. bool irgen::hasPolymorphicParameters(CanSILFunctionType ty) { switch (ty->getRepresentation()) { case SILFunctionTypeRepresentation::CFunctionPointer: case SILFunctionTypeRepresentation::Block: // Should never be polymorphic. assert(!ty->isPolymorphic() && "polymorphic C function?!"); return false; case SILFunctionTypeRepresentation::ObjCMethod: // An ObjC witness_method reference will notionally have polymorphic type // (...) -> (...), but there are no polymorphic parameters that // can't be solved from the usual ObjC metadata. return false; case SILFunctionTypeRepresentation::Thick: case SILFunctionTypeRepresentation::Thin: case SILFunctionTypeRepresentation::Method: return ty->isPolymorphic(); case SILFunctionTypeRepresentation::WitnessMethod: // Always carries polymorphic parameters for the Self type. return true; } } namespace { struct Fulfillment { Fulfillment() = default; Fulfillment(unsigned sourceIndex, unsigned depth, unsigned index) : SourceIndex(sourceIndex), Depth(depth), Index(index) {} /// The source index. unsigned SourceIndex; /// The distance up the metadata chain. /// 0 is the origin metadata, 1 is the parent of that, etc. unsigned Depth; /// The generic argument index. unsigned Index; }; typedef std::pair FulfillmentKey; /// A class for computing how to pass arguments to a polymorphic /// function. The subclasses of this are the places which need to /// be updated if the convention changes. class PolymorphicConvention { public: enum class SourceKind { /// The polymorphic arguments are derived from a source class /// pointer. ClassPointer, /// The polymorphic arguments are derived from a type metadata /// pointer. Metadata, /// The polymorphic arguments are passed from generic type /// metadata for the origin type. GenericLValueMetadata, /// The polymorphic arguments are derived from a Self type binding /// passed via the WitnessMethod convention. WitnessSelf, /// The polymorphic arguments are derived from a Self type binding /// embedded in a thick WitnessMethod function value. WitnessExtraData, }; static bool requiresSourceIndex(SourceKind kind) { return (kind == SourceKind::ClassPointer || kind == SourceKind::Metadata || kind == SourceKind::GenericLValueMetadata); } enum : unsigned { InvalidSourceIndex = ~0U }; class Source { /// The kind of source this is. SourceKind Kind; /// The parameter index, for ClassPointer and Metadata sources. unsigned Index; public: SmallVector TypesForDepths; Source(SourceKind kind, unsigned index) : Kind(kind), Index(index) { assert(index != InvalidSourceIndex || !requiresSourceIndex(kind)); } SourceKind getKind() const { return Kind; } unsigned getParamIndex() const { assert(requiresSourceIndex(getKind())); return Index; } }; protected: CanSILFunctionType FnType; std::vector Sources; llvm::DenseMap Fulfillments; ArchetypeBuilder ParamArchetypes; // Retrieve a representative archetype for a dependent type if it refers to // a generic parameter or a member of a generic parameter, or return null // if it does not. ArchetypeType::NestedType getRepresentativeArchetype(Type depType) { assert(depType->isDependentType() && "considering non-dependent type?!"); auto *potential = ParamArchetypes.resolveArchetype(depType); if (!potential) return ArchetypeType::NestedType(); return potential->getType(ParamArchetypes); } public: PolymorphicConvention(CanSILFunctionType fnType, Module &M) : FnType(fnType), ParamArchetypes(M, M.getASTContext().Diags) { assert(hasPolymorphicParameters(fnType)); // Build archetypes from the generic signature so we can consult the // protocol requirements on the parameters and dependent types. // // TODO: The ArchetypeBuilder should be cached in the generic signature. ParamArchetypes.addGenericSignature(fnType->getGenericSignature(), false); // Protocol witnesses always derive all polymorphic parameter information // from the Self argument. We also *cannot* consider other arguments; // doing so would potentially make the signature incompatible with other // witnesses for the same method. if (fnType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { // The metadata for a witness is provided from the type of the // self argument. Sources.emplace_back(SourceKind::WitnessSelf, InvalidSourceIndex); // Testify to generic parameters in the Self type. CanType selfTy = fnType->getSelfParameter().getType(); if (auto metaTy = dyn_cast(selfTy)) selfTy = metaTy.getInstanceType(); if (auto nomTy = dyn_cast(selfTy)) considerNominalType(nomTy, 0); else if (auto bgTy = dyn_cast(selfTy)) considerBoundGenericType(bgTy, 0); else if (auto param = dyn_cast(selfTy)) considerWitnessParamType(param); else if (isa(selfTy)) // A bound Self archetype from a protocol. Nothing to do. (void)0; else llvm_unreachable("witness for non-nominal type?!"); return; } // We don't need to pass anything extra as long as all of the // archetypes (and their requirements) are producible from // arguments. auto params = fnType->getParameters(); unsigned selfIndex = ~0U; // Consider 'self' first. if (fnType->hasSelfParam()) { selfIndex = params.size() - 1; considerParameter(params[selfIndex], selfIndex, true); } // Now consider the rest of the parameters. for (auto index : indices(params)) { if (index != selfIndex) considerParameter(params[index], index, false); } } /// Extract dependent type metadata for a value witness function of the given /// type. PolymorphicConvention(NominalTypeDecl *ntd, Module &M) : FnType(getNotionalFunctionType(ntd)), ParamArchetypes(M, M.getASTContext().Diags) { Sources.emplace_back(SourceKind::Metadata, 0); // Build archetypes from the generic signature so we can consult the // protocol requirements on the parameters and dependent types. // // TODO: The ArchetypeBuilder should be cached in the generic signature. ParamArchetypes.addGenericSignature(FnType->getGenericSignature(), false); auto paramType = FnType->getParameters()[0].getType(); considerBoundGenericType(cast(paramType), 0); } ArrayRef getSources() const { return Sources; } GenericSignatureWitnessIterator getAllDependentTypes() const { if (auto gs = FnType->getGenericSignature()) return gs->getAllDependentTypes(); return GenericSignatureWitnessIterator::emptyRange(); } private: static CanSILFunctionType getNotionalFunctionType(NominalTypeDecl *D) { ASTContext &ctx = D->getASTContext(); SILFunctionType::ExtInfo extInfo(SILFunctionType::Representation::Method, /*noreturn*/ false); SILResultInfo result(TupleType::getEmpty(ctx), ResultConvention::Unowned); SILParameterInfo param(D->getDeclaredInterfaceType()->getCanonicalType(), ParameterConvention::Direct_Owned); CanGenericSignature sig = D->getGenericSignatureOfContext() ? D->getGenericSignatureOfContext()->getCanonicalSignature() : nullptr; return SILFunctionType::get(sig, extInfo, ParameterConvention::Direct_Unowned, param, result, None, ctx); } template void considerNewSource(SourceKind kind, unsigned paramIndex, const T &consider) { // Remember how many fulfillments we currently have. auto numFulfillments = Fulfillments.size(); // Prospectively add a source. Sources.emplace_back(kind, paramIndex); // Consider the source. consider(); // If we didn't add anything, remove the source. if (Fulfillments.size() == numFulfillments) Sources.pop_back(); } void considerNewTypeSource(SourceKind kind, unsigned paramIndex, CanType type) { if (auto nomTy = dyn_cast(type)) { considerNewSource(kind, paramIndex, [&] { considerNominalType(nomTy, 0); }); } else if (auto boundTy = dyn_cast(type)) { considerNewSource(kind, paramIndex, [&] { considerBoundGenericType(boundTy, 0); }); } } void considerParameter(SILParameterInfo param, unsigned paramIndex, bool isSelfParameter) { auto type = param.getType(); switch (param.getConvention()) { // Out-parameters don't give us a value we can use. case ParameterConvention::Indirect_Out: return; // In-parameters do, but right now we don't bother, for no good // reason. But if this is 'self', consider passing an extra // metatype. case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_Inout: if (!isSelfParameter) return; considerNewTypeSource(SourceKind::GenericLValueMetadata, paramIndex, type); return; case ParameterConvention::Direct_Owned: case ParameterConvention::Direct_Unowned: case ParameterConvention::Direct_Guaranteed: case ParameterConvention::Direct_Deallocating: // Classes are sources of metadata. if (auto classTy = dyn_cast(type)) { considerNewSource(SourceKind::ClassPointer, paramIndex, [&] { considerNominalType(classTy, 0); }); return; } else if (auto boundTy = dyn_cast(type)) { considerNewSource(SourceKind::ClassPointer, paramIndex, [&] { considerBoundGenericType(boundTy, 0); }); return; } // Thick metatypes are sources of metadata. if (auto metatypeTy = dyn_cast(type)) { if (metatypeTy->getRepresentation() != MetatypeRepresentation::Thick) return; CanType objTy = metatypeTy.getInstanceType(); considerNewTypeSource(SourceKind::Metadata, paramIndex, objTy); return; } return; } llvm_unreachable("bad parameter convention"); } void considerParentType(CanType parent, unsigned depth) { // We might not have a parent type. if (!parent) return; // If we do, it has to be nominal one way or another. depth++; if (auto nom = dyn_cast(parent)) considerNominalType(nom, depth); else considerBoundGenericType(cast(parent), depth); } void considerNominalType(NominalType *type, unsigned depth) { assert(Sources.back().TypesForDepths.size() == depth); Sources.back().TypesForDepths.push_back(type->getDecl()); // Nominal types add no generic arguments themselves, but they // may have the arguments of their parents. considerParentType(CanType(type->getParent()), depth); } void considerBoundGenericType(BoundGenericType *type, unsigned depth) { assert(Sources.back().TypesForDepths.size() == depth); Sources.back().TypesForDepths.push_back(type->getDecl()); auto params = type->getDecl()->getGenericParams()->getAllArchetypes(); auto substitutions = type->getSubstitutions(/*FIXME:*/nullptr, nullptr); assert(params.size() >= substitutions.size() && "generic decl archetypes should parallel generic type subs"); for (unsigned i = 0, e = substitutions.size(); i != e; ++i) { auto sub = substitutions[i]; CanType arg = sub.getReplacement()->getCanonicalType(); // Right now, we can only pull things out of the direct // arguments, not out of nested metadata. For example, this // prevents us from realizing that we can rederive T and U in the // following: // \forall T U . VectorU> -> () if (arg->isDependentType()) { // Find the archetype from the dependent type. considerDependentType(arg, params[i], depth, i); } } // Match against the parent first. The polymorphic type // will start with any arguments from the parent. considerParentType(CanType(type->getParent()), depth); } /// We found a reference to the dependent arg type at the given depth /// and index. Add any fulfillments this gives us. void considerDependentType(Type arg, ArchetypeType *param, unsigned depth, unsigned index) { // If we don't have a representative archetype for this dependent type, // don't try to fulfill it. It doesn't directly correspond to one of our // parameters or their associated types. auto representative = getRepresentativeArchetype(arg); if (!representative) return; auto arch = representative.getAsArchetype(); if (!arch) return; // First, record that we can find this dependent type at this point. addFulfillment(arg, nullptr, depth, index); // Now consider each of the protocols that the parameter guarantees. for (auto protocol : param->getConformsTo()) { if (requiresFulfillment(arch, protocol)) addFulfillment(arg, protocol, depth, index); } } /// We're binding an archetype for a protocol witness. void considerWitnessParamType(CanGenericTypeParamType arg) { assert(arg->getDepth() == 0 && arg->getIndex() == 0); auto representative = getRepresentativeArchetype(arg); assert(representative && "no representative for dependent type?!"); // First of all, the archetype or concrete type fulfills its own // requirements. if (auto arch = representative.getAsArchetype()) considerDependentType(arg, arch, 0, 0); // FIXME: We can't pass associated types of Self through the witness // CC, so as a hack, fake up impossible fulfillments for the associated // types. For now all conformances are concrete, so the associated types // can be recovered by substitution on the implementation side. For // default implementations, we will need to get associated types from // witness tables anyway. for (auto depTy : getAllDependentTypes()) { // Is this a dependent member? auto depMemTy = dyn_cast(CanType(depTy)); if (!depMemTy) continue; // Is it rooted in a generic parameter? CanType rootTy; do { rootTy = depMemTy.getBase(); } while ((depMemTy = dyn_cast(rootTy))); auto rootParamTy = dyn_cast(rootTy); if (!rootParamTy) continue; // If so, suppress providing metadata for the type by making up a bogus // fulfillment. if (rootParamTy == arg) { auto depRep = getRepresentativeArchetype(depTy); assert(depRep && "no representative for dependent type?!"); if (auto depArch = depRep.getAsArchetype()) considerDependentType(depTy, depArch, ~0u, ~0u); } } } /// Does the given archetype require the given protocol to be fulfilled? static bool requiresFulfillment(ArchetypeType *representative, ProtocolDecl *proto) { // TODO: protocol inheritance should be considered here somehow. for (auto argProto : representative->getConformsTo()) { if (argProto == proto) return true; } return false; } /// Testify that there's a fulfillment at the given depth and level. void addFulfillment(Type arg, ProtocolDecl *proto, unsigned depth, unsigned index) { assert(!Sources.empty() && "adding fulfillment without source?"); auto sourceIndex = Sources.size() - 1; // Only add a fulfillment if we don't have any previous // fulfillment for that value. assert(arg->isDependentType() && "fulfilling non-dependent type?!"); auto key = FulfillmentKey(arg, proto); Fulfillments.insert(std::make_pair(key, Fulfillment(sourceIndex, depth, index))); } }; /// A class for binding type parameters of a generic function. class EmitPolymorphicParameters : public PolymorphicConvention { IRGenFunction &IGF; GenericParamList *ContextParams; struct SourceValue { SmallVector MetadataForDepths; }; std::vector SourceValues; public: EmitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn) : PolymorphicConvention(Fn.getLoweredFunctionType(), *IGF.IGM.SILMod->getSwiftModule()), IGF(IGF), ContextParams(Fn.getContextGenericParams()) {} void emit(Explosion &in, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter); /// Emit polymorphic parameters for a generic value witness. EmitPolymorphicParameters(IRGenFunction &IGF, NominalTypeDecl *ntd) : PolymorphicConvention(ntd, *IGF.IGM.SILMod->getSwiftModule()), IGF(IGF), ContextParams(ntd->getGenericParams()) {} void emitForGenericValueWitness(llvm::Value *selfMeta); private: // Emit metadata bindings after the source, if any, has been bound. void emitWithSourcesBound(Explosion &in); CanType getArgTypeInContext(unsigned paramIndex) const { return ArchetypeBuilder::mapTypeIntoContext( IGF.IGM.SILMod->getSwiftModule(), ContextParams, FnType->getParameters()[paramIndex].getType()) ->getCanonicalType(); } /// Emit the source value for parameters. llvm::Value *emitSourceForParameters(const Source &source, Explosion &in, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { switch (source.getKind()) { case SourceKind::Metadata: return getParameter(source.getParamIndex()); case SourceKind::ClassPointer: { unsigned paramIndex = source.getParamIndex(); llvm::Value *instanceRef = getParameter(paramIndex); SILType instanceType = SILType::getPrimitiveObjectType(getArgTypeInContext(paramIndex)); return emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType); } case SourceKind::GenericLValueMetadata: { llvm::Value *metatype = in.claimNext(); metatype->setName("Self"); // Mark this as the cached metatype for the l-value's object type. CanType argTy = getArgTypeInContext(source.getParamIndex()); IGF.setUnscopedLocalTypeData(argTy, LocalTypeData::forMetatype(), metatype); return metatype; } case SourceKind::WitnessSelf: { assert(witnessMetadata && "no metadata for witness method"); llvm::Value *metatype = witnessMetadata->SelfMetadata; assert(metatype && "no Self metadata for witness method"); // Mark this as the cached metatype for Self. CanType argTy = getArgTypeInContext(FnType->getParameters().size() - 1); IGF.setUnscopedLocalTypeData(argTy, LocalTypeData::forMetatype(), metatype); return metatype; } case SourceKind::WitnessExtraData: { // The 'Self' parameter is provided last. // TODO: For default implementations, the witness table pointer for // the 'Self : P' conformance must be provided last along with the // metatype. llvm::Value *metatype = in.takeLast(); metatype->setName("Self"); return metatype; } } llvm_unreachable("bad source kind!"); } /// Produce the metadata value for the given depth, using the /// given cache. llvm::Value *getMetadataForDepth(unsigned sourceIndex, unsigned depth) { auto &source = getSources()[sourceIndex]; auto &sourceValue = SourceValues[sourceIndex]; assert(!sourceValue.MetadataForDepths.empty()); // Drill down until we get to that depth. while (depth >= sourceValue.MetadataForDepths.size()) { auto child = sourceValue.MetadataForDepths.back(); auto childDecl = source.TypesForDepths[sourceValue.MetadataForDepths.size()]; auto parent = emitParentMetadataRef(IGF, childDecl, child); sourceValue.MetadataForDepths.push_back(parent); } return sourceValue.MetadataForDepths[depth]; } /// Produce the base metadata value and declaration for the given /// fulfillment. std::pair getAncestorForFulfillment(const Fulfillment &fulfillment) { auto ancestor = getMetadataForDepth(fulfillment.SourceIndex, fulfillment.Depth); auto ancestorDecl = Sources[fulfillment.SourceIndex].TypesForDepths[fulfillment.Depth]; return std::make_pair(ancestor, ancestorDecl); } }; }; /// Emit a polymorphic parameters clause, binding all the metadata necessary. void EmitPolymorphicParameters::emit(Explosion &in, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { SourceValues.reserve(getSources().size()); for (const Source &source : getSources()) { llvm::Value *value = emitSourceForParameters(source, in, witnessMetadata, getParameter); SourceValues.emplace_back(); SourceValues.back().MetadataForDepths.push_back(value); } emitWithSourcesBound(in); } /// Emit a polymorphic parameters clause for a generic value witness, binding /// all the metadata necessary. void EmitPolymorphicParameters::emitForGenericValueWitness(llvm::Value *selfMeta) { // We get the source metadata verbatim from the value witness signature. assert(getSources().size() == 1); SourceValues.emplace_back(); SourceValues.back().MetadataForDepths.push_back(selfMeta); // All our archetypes should be satisfiable from the source. Explosion empty; emitWithSourcesBound(empty); } void EmitPolymorphicParameters::emitWithSourcesBound(Explosion &in) { for (auto depTy : getAllDependentTypes()) { // Get the corresponding context archetype. auto contextTy = ArchetypeBuilder::mapTypeIntoContext(IGF.IGM.SILMod->getSwiftModule(), ContextParams, depTy) ->getAs(); if (!contextTy) continue; // Derive the appropriate metadata reference. llvm::Value *metadata; // If the reference is fulfilled by the source, go for it. auto it = Fulfillments.find(FulfillmentKey(depTy, nullptr)); if (it != Fulfillments.end()) { auto &fulfillment = it->second; auto ancestorAndDecl = getAncestorForFulfillment(fulfillment); metadata = emitArgumentMetadataRef(IGF, ancestorAndDecl.second, fulfillment.Index, ancestorAndDecl.first); // Otherwise, it's just next in line. } else { metadata = in.claimNext(); } // Collect all the witness tables. SmallVector wtables; for (auto protocol : contextTy->getConformsTo()) { if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; llvm::Value *wtable; // If the protocol witness table is fulfilled by the source, go for it. auto it = Fulfillments.find(FulfillmentKey(depTy, protocol)); if (it != Fulfillments.end()) { auto &fulfillment = it->second; auto ancestorAndDecl = getAncestorForFulfillment(fulfillment); wtable = emitArgumentWitnessTableRef(IGF, ancestorAndDecl.second, fulfillment.Index, protocol, ancestorAndDecl.first); // Otherwise, it's just next in line. } else { wtable = in.claimNext(); } wtables.push_back(wtable); } IGF.bindArchetype(contextTy, metadata, wtables); } } /// Collect any required metadata for a witness method from the end of /// the given parameter list. void irgen::collectTrailingWitnessMetadata(IRGenFunction &IGF, SILFunction &fn, Explosion ¶ms, WitnessMetadata &witnessMetadata) { assert(fn.getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); llvm::Value *metatype = params.takeLast(); assert(metatype->getType() == IGF.IGM.TypeMetadataPtrTy && "parameter signature mismatch: witness metadata didn't " "end in metatype?"); metatype->setName("Self"); witnessMetadata.SelfMetadata = metatype; } /// Perform all the bindings necessary to emit the given declaration. void irgen::emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn, Explosion &in, WitnessMetadata *witnessMetadata, const GetParameterFn &getParameter) { EmitPolymorphicParameters(IGF, Fn).emit(in, witnessMetadata, getParameter); } /// Perform the metadata bindings necessary to emit a generic value witness. void irgen::emitPolymorphicParametersForGenericValueWitness(IRGenFunction &IGF, NominalTypeDecl *ntd, llvm::Value *selfMeta) { // Nothing to do if the type isn't generic. if (!ntd->getGenericParamsOfContext()) return; EmitPolymorphicParameters(IGF, ntd).emitForGenericValueWitness(selfMeta); // Register the 'Self' argument as generic metadata for the type. IGF.setUnscopedLocalTypeData(ntd->getDeclaredTypeInContext()->getCanonicalType(), LocalTypeData::forMetatype(), selfMeta); } /// Get the next argument and use it as the 'self' type metadata. static void getArgAsLocalSelfTypeMetadata(IRGenFunction &IGF, llvm::Function::arg_iterator &it, CanType abstractType) { llvm::Value *arg = getArg(it, "Self"); assert(arg->getType() == IGF.IGM.TypeMetadataPtrTy && "Self argument is not a type?!"); if (auto ugt = dyn_cast(abstractType)) { emitPolymorphicParametersForGenericValueWitness(IGF, ugt->getDecl(), arg); } } namespace { /// A CRTP class for finding the archetypes we need to bind in order /// to perform value operations on the given type. struct FindArchetypesForValueOperations : CanTypeVisitor { NecessaryBindings &Bindings; public: FindArchetypesForValueOperations(NecessaryBindings &bindings) : Bindings(bindings) {} // We're collecting archetypes. void visitArchetypeType(CanArchetypeType type) { Bindings.addArchetype(type); } // We need to walk into tuples. void visitTupleType(CanTupleType tuple) { for (auto eltType : tuple.getElementTypes()) { visit(eltType); } } // Walk into on-stack block storage. void visitSILBlockStorageType(CanSILBlockStorageType t) { visit(t->getCaptureType()); } // We do not need to walk into any of these types, because their // value operations do not depend on the specifics of their // sub-structure (or they have none). void visitAnyFunctionType(CanAnyFunctionType fn) {} void visitSILFunctionType(CanSILFunctionType fn) {} void visitBuiltinType(CanBuiltinType type) {} void visitAnyMetatypeType(CanAnyMetatypeType type) {} void visitModuleType(CanModuleType type) {} void visitDynamicSelfType(CanDynamicSelfType type) {} void visitProtocolCompositionType(CanProtocolCompositionType type) {} void visitReferenceStorageType(CanReferenceStorageType type) {} // L-values are impossible. void visitLValueType(CanLValueType type) { llvm_unreachable("cannot store l-value type directly"); } void visitInOutType(CanInOutType type) { llvm_unreachable("cannot store inout type directly"); } // Bind archetypes from the parent of nominal types. void visitNominalType(CanNominalType type) { if (auto parent = CanType(type->getParent())) visit(parent); } // Bind archetypes from bound generic types and their parents. void visitBoundGenericType(CanBoundGenericType type) { if (auto parent = CanType(type->getParent())) visit(parent); for (auto arg : type->getGenericArgs()) visit(CanType(arg)); } // FIXME: Will need to bind the archetype that this eventually refers to. void visitGenericTypeParamType(CanGenericTypeParamType type) { } // FIXME: Will need to bind the archetype that this eventually refers to. void visitDependentMemberType(CanDependentMemberType type) { } }; } NecessaryBindings NecessaryBindings::forFunctionInvocations(IRGenModule &IGM, CanSILFunctionType origType, CanSILFunctionType substType, ArrayRef subs) { NecessaryBindings bindings; // Collect bindings required by the polymorphic parameters to the function. for (auto &sub : subs) { sub.getReplacement().findIf([&](Type t) -> bool { if (auto archetype = dyn_cast(t->getCanonicalType())) { bindings.addArchetype(archetype); } return false; }); } return bindings; } NecessaryBindings NecessaryBindings::forValueOperations(IRGenModule &IGM, CanType type) { NecessaryBindings bindings; FindArchetypesForValueOperations(bindings).visit(type); return bindings; } Size NecessaryBindings::getBufferSize(IRGenModule &IGM) const { unsigned numPointers = 0; // We need one pointer for each archetype and witness table. for (auto type : Types) { numPointers++; for (auto proto : type->getConformsTo()) if (Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) numPointers++; } return IGM.getPointerSize() * numPointers; } void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer) const { if (Types.empty()) return; // Cast the buffer to %type**. auto metatypePtrPtrTy = IGF.IGM.TypeMetadataPtrTy->getPointerTo(); buffer = IGF.Builder.CreateBitCast(buffer, metatypePtrPtrTy); for (unsigned archetypeI = 0, e = Types.size(), metadataI = 0; archetypeI != e; ++archetypeI) { auto archetype = Types[archetypeI]; // GEP to the appropriate slot. Address slot = buffer; if (metadataI) slot = IGF.Builder.CreateConstArrayGEP(slot, metadataI, IGF.IGM.getPointerSize()); ++metadataI; // Load the archetype's metatype. llvm::Value *metatype = IGF.Builder.CreateLoad(slot); metatype->setName(archetype->getFullName()); setMetadataRef(IGF, archetype, metatype); // Load the witness tables for the archetype's protocol constraints. for (unsigned protocolI : indices(archetype->getConformsTo())) { auto protocol = archetype->getConformsTo()[protocolI]; if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; Address witnessSlot = IGF.Builder.CreateConstArrayGEP(buffer, metadataI, IGF.IGM.getPointerSize()); witnessSlot = IGF.Builder.CreateBitCast(witnessSlot, IGF.IGM.WitnessTablePtrTy->getPointerTo()); ++metadataI; llvm::Value *witness = IGF.Builder.CreateLoad(witnessSlot); setWitnessTable(IGF, archetype, protocolI, witness); } } } void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const { if (Types.empty()) return; // Cast the buffer to %type**. auto metatypePtrPtrTy = IGF.IGM.TypeMetadataPtrTy->getPointerTo(); buffer = IGF.Builder.CreateBitCast(buffer, metatypePtrPtrTy); for (unsigned typeI = 0, typeE = Types.size(), metadataI = 0; typeI != typeE; ++typeI) { auto archetype = Types[typeI]; // GEP to the appropriate slot. Address slot = buffer; if (metadataI) slot = IGF.Builder.CreateConstArrayGEP(slot, metadataI, IGF.IGM.getPointerSize()); ++metadataI; // Find the metatype for the appropriate archetype and store it in // the slot. llvm::Value *metatype = IGF.getLocalTypeData(CanType(archetype), LocalTypeData::forMetatype()); IGF.Builder.CreateStore(metatype, slot); // Find the witness tables for the archetype's protocol constraints and // store them in the slot. for (unsigned protocolI : indices(archetype->getConformsTo())) { auto protocol = archetype->getConformsTo()[protocolI]; if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; Address witnessSlot = IGF.Builder.CreateConstArrayGEP(buffer, metadataI, IGF.IGM.getPointerSize()); witnessSlot = IGF.Builder.CreateBitCast(witnessSlot, IGF.IGM.WitnessTablePtrTy->getPointerTo()); ++metadataI; llvm::Value *witness = IGF.getLocalTypeData(CanType(archetype), LocalTypeData::forArchetypeProtocolWitness(protocolI)); IGF.Builder.CreateStore(witness, witnessSlot); } } } void NecessaryBindings::addArchetype(CanArchetypeType type) { if (Types.insert(type)) // Collect the associated archetypes. for (auto nested : type->getNestedTypes()) if (auto assocArchetype = nested.second.getAsArchetype()) addArchetype(CanArchetypeType(assocArchetype)); } /// Emit a single protocol witness table reference. llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, CanArchetypeType archetype, ProtocolDecl *proto) { assert(Lowering::TypeConverter::protocolRequiresWitnessTable(proto) && "looking up witness table for protocol that doesn't have one"); auto &archTI = getArchetypeInfo(IGF, archetype, IGF.getTypeInfoForLowered(archetype)); ProtocolPath path(IGF.IGM, archTI.getStoredProtocols(), proto); auto wtable = archTI.getWitnessTable(IGF, archetype, path.getOriginIndex()); wtable = path.apply(IGF, wtable); return wtable; } /// Emit a protocol witness table for a conformance. llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, CanType srcType, const TypeInfo &srcTI, ProtocolDecl *proto, const ProtocolInfo &protoI, ProtocolConformance *conformance) { assert(Lowering::TypeConverter::protocolRequiresWitnessTable(proto) && "protocol does not have witness tables?!"); // If the source type is an archetype and we don't have concrete conformance // info, the conformance must be via one of the protocol requirements of the // archetype. Look at what's locally bound. if (!conformance) { auto archetype = cast(srcType); return emitWitnessTableRef(IGF, archetype, proto); } // All other source types should be concrete enough that we have conformance // info for them. auto &conformanceI = protoI.getConformance(IGF.IGM, srcType, srcTI, proto, *conformance); return conformanceI.getTable(IGF); } /// Emit the witness table references required for the given type /// substitution. void irgen::emitWitnessTableRefs(IRGenFunction &IGF, const Substitution &sub, SmallVectorImpl &out) { auto conformances = sub.getConformances(); // We don't need to do anything if we have no protocols to conform to. auto archetypeProtos = sub.getArchetype()->getConformsTo(); assert(!conformances.size() || archetypeProtos.size() == conformances.size()); if (archetypeProtos.empty()) return; // Look at the replacement type. CanType replType = sub.getReplacement()->getCanonicalType(); auto &replTI = IGF.getTypeInfoForUnlowered(replType); for (unsigned j = 0, je = archetypeProtos.size(); j != je; ++j) { auto proto = archetypeProtos[j]; if (!Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) continue; auto conformance = conformances.size() ? conformances[j] : nullptr; auto wtable = emitWitnessTableRef(IGF, replType, replTI, proto, IGF.IGM.getProtocolInfo(proto), conformance); out.push_back(wtable); } } namespace { class EmitPolymorphicArguments : public PolymorphicConvention { IRGenFunction &IGF; public: EmitPolymorphicArguments(IRGenFunction &IGF, CanSILFunctionType polyFn) : PolymorphicConvention(polyFn, *IGF.IGM.SILMod->getSwiftModule()), IGF(IGF) {} void emit(CanType substInputType, ArrayRef subs, WitnessMetadata *witnessMetadata, Explosion &out); private: void emitEarlySources(CanType substInputType, Explosion &out) { for (auto &source : getSources()) { switch (source.getKind()) { // Already accounted for in the parameters. case SourceKind::ClassPointer: case SourceKind::Metadata: continue; // Needs a special argument. case SourceKind::GenericLValueMetadata: { out.add(IGF.emitTypeMetadataRef(substInputType)); continue; } // Witness 'Self' argument(s) are added as a special case in // EmitPolymorphicArguments::emit. case SourceKind::WitnessSelf: continue; // The 'Self' argument(s) are added implicitly from ExtraData // of the function value. case SourceKind::WitnessExtraData: continue; } llvm_unreachable("bad source kind!"); } } }; } void irgen::emitTrailingWitnessArguments(IRGenFunction &IGF, WitnessMetadata &witnessMetadata, Explosion &args) { llvm::Value *self = witnessMetadata.SelfMetadata; assert(self && "no Self value bound"); args.add(self); } /// Pass all the arguments necessary for the given function. void irgen::emitPolymorphicArguments(IRGenFunction &IGF, CanSILFunctionType origFnType, CanSILFunctionType substFnType, ArrayRef subs, WitnessMetadata *witnessMetadata, Explosion &out) { // Grab the apparent 'self' type. If there isn't a 'self' type, // we're not going to try to access this anyway. CanType substInputType; if (!substFnType->getParameters().empty()) { auto selfParam = substFnType->getParameters().back(); substInputType = selfParam.getType(); // If the parameter is a direct metatype parameter, this is a static method // of the instance type. We can assume this because: // - metatypes cannot directly conform to protocols // - even if they could, they would conform as a value type 'self' and thus // be passed indirectly as an @in or @inout parameter. if (auto meta = dyn_cast(substInputType)) { if (!selfParam.isIndirect()) substInputType = meta.getInstanceType(); } } EmitPolymorphicArguments(IGF, origFnType).emit(substInputType, subs, witnessMetadata, out); } void EmitPolymorphicArguments::emit(CanType substInputType, ArrayRef subs, WitnessMetadata *witnessMetadata, Explosion &out) { // Add all the early sources. emitEarlySources(substInputType, out); // For now, treat all archetypes independently. // FIXME: Later, we'll want to emit only the minimal set of archetypes, // because non-primary archetypes (which correspond to associated types) // will have their witness tables embedded in the witness table corresponding // to their parent. for (auto depTy : getAllDependentTypes()) { // The substitutions should be in the same order. const Substitution &sub = subs.front(); subs = subs.slice(1); CanType argType = sub.getReplacement()->getCanonicalType(); // If same-type constraints have eliminated the genericity of this // parameter, it doesn't need an independent metadata parameter. auto type = getRepresentativeArchetype(depTy); assert(type && "no potential archetype for dependent type?!"); auto arch = type.getAsArchetype(); if (!arch) continue; // Add the metadata reference unless it's fulfilled. if (!Fulfillments.count(FulfillmentKey(depTy, nullptr))) { out.add(IGF.emitTypeMetadataRef(argType)); } // Nothing else to do if there aren't any protocols to witness. auto protocols = arch->getConformsTo(); auto conformances = sub.getConformances(); assert(!conformances.size() || protocols.size() == conformances.size()); if (protocols.empty()) continue; auto &argTI = IGF.getTypeInfoForUnlowered(argType); // Add witness tables for each of the required protocols. for (unsigned i = 0, e = protocols.size(); i != e; ++i) { auto protocol = protocols[i]; // Skip this if the protocol doesn't require a witness table. if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; // Skip this if it's fulfilled by the source. if (Fulfillments.count(FulfillmentKey(depTy, protocol))) continue; auto conformance = conformances.size() ? conformances[i] : nullptr; auto wtable = emitWitnessTableRef(IGF, argType, argTI, protocol, IGF.IGM.getProtocolInfo(protocol), conformance); out.add(wtable); } } assert(subs.empty() && "did not use all substitutions?!"); // For a witness call, add the Self argument metadata arguments last. for (auto &source : getSources()) { switch (source.getKind()) { case SourceKind::Metadata: case SourceKind::ClassPointer: // Already accounted for in the arguments. continue; case SourceKind::GenericLValueMetadata: // Added in the early phase. continue; case SourceKind::WitnessSelf: { assert(witnessMetadata && "no metadata structure for witness method"); auto self = IGF.emitTypeMetadataRef(substInputType); witnessMetadata->SelfMetadata = self; continue; } case SourceKind::WitnessExtraData: // The 'Self' argument(s) are added implicitly from ExtraData of the // function value. continue; } llvm_unreachable("bad source kind"); } } namespace { /// A class for expanding a polymorphic signature. class ExpandPolymorphicSignature : public PolymorphicConvention { IRGenModule &IGM; public: ExpandPolymorphicSignature(IRGenModule &IGM, CanSILFunctionType fn) : PolymorphicConvention(fn, *IGM.SILMod->getSwiftModule()), IGM(IGM) {} void expand(SmallVectorImpl &out) { for (auto &source : getSources()) addEarlySource(source, out); for (auto depTy : getAllDependentTypes()) { // Only emit parameters for independent parameters that haven't been // constrained to concrete types. auto representative = getRepresentativeArchetype(depTy); assert(representative && "no representative archetype for param?!"); auto arch = representative.getAsArchetype(); if (!arch) continue; // Pass the type argument if not fulfilled. if (!Fulfillments.count(FulfillmentKey(depTy, nullptr))) out.push_back(IGM.TypeMetadataPtrTy); // Pass each signature requirement that needs a witness table // separately (unless fulfilled). for (auto protocol : arch->getConformsTo()) { if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) continue; if (!Fulfillments.count(FulfillmentKey(depTy, protocol))) out.push_back(IGM.WitnessTablePtrTy); } } } private: /// Add signature elements for the source metadata. void addEarlySource(const Source &source, SmallVectorImpl &out) { switch (source.getKind()) { case SourceKind::ClassPointer: return; // already accounted for case SourceKind::Metadata: return; // already accounted for case SourceKind::GenericLValueMetadata: return out.push_back(IGM.TypeMetadataPtrTy); case SourceKind::WitnessSelf: return; // handled as a special case in expand() case SourceKind::WitnessExtraData: return; // added implicitly as ExtraData } llvm_unreachable("bad source kind"); } }; } /// Given a generic signature, add the argument types required in order to call it. void irgen::expandPolymorphicSignature(IRGenModule &IGM, CanSILFunctionType polyFn, SmallVectorImpl &out) { ExpandPolymorphicSignature(IGM, polyFn).expand(out); } void irgen::expandTrailingWitnessSignature(IRGenModule &IGM, CanSILFunctionType polyFn, SmallVectorImpl &out) { assert(polyFn->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod); assert(getTrailingWitnessSignatureLength(IGM, polyFn) == 1); // A witness method always provides Self. out.push_back(IGM.TypeMetadataPtrTy); // TODO: Should also provide the protocol witness table, // for default implementations. } /// Retrieve the protocol witness table for a conformance. static llvm::Value *getProtocolWitnessTable(IRGenFunction &IGF, CanType srcType, const TypeInfo &srcTI, ProtocolEntry protoEntry, ProtocolConformance *conformance) { return emitWitnessTableRef(IGF, srcType, srcTI, protoEntry.getProtocol(), protoEntry.getInfo(), conformance); } /// Emit protocol witness table pointers for the given protocol conformances, /// passing each emitted witness table index into the given function body. static void forEachProtocolWitnessTable(IRGenFunction &IGF, CanType srcType, CanType destType, ArrayRef protocols, ArrayRef conformances, std::function body) { // Collect the conformances that need witness tables. SmallVector destProtocols; destType.getAnyExistentialTypeProtocols(destProtocols); SmallVector witnessConformances; assert(destProtocols.size() == conformances.size() && "mismatched protocol conformances"); for (unsigned i = 0, size = destProtocols.size(); i < size; ++i) if (Lowering::TypeConverter::protocolRequiresWitnessTable(destProtocols[i])) witnessConformances.push_back(conformances[i]); assert(protocols.size() == witnessConformances.size() && "mismatched protocol conformances"); auto &srcTI = IGF.getTypeInfoForUnlowered(srcType); for (unsigned i = 0, e = protocols.size(); i < e; ++i) { auto table = getProtocolWitnessTable(IGF, srcType, srcTI, protocols[i], witnessConformances[i]); body(i, table); } } #ifndef NDEBUG static bool _isErrorType(SILType baseTy) { llvm::SmallVector protos; return baseTy.getSwiftRValueType()->isExistentialType(protos) && protos.size() == 1 && protos[0]->getKnownProtocolKind() && *protos[0]->getKnownProtocolKind() == KnownProtocolKind::_ErrorType; } #endif /// Project the address of the value inside a boxed existential container, /// and open an archetype to its contained type. Address irgen::emitBoxedExistentialProjection(IRGenFunction &IGF, Explosion &base, SILType baseTy, CanArchetypeType openedArchetype){ // TODO: Non-ErrorType boxed existentials. assert(_isErrorType(baseTy)); // Get the reference to the existential box. llvm::Value *box = base.claimNext(); // Allocate scratch space to invoke the runtime. Address scratch = IGF.createAlloca(IGF.IGM.Int8PtrTy, IGF.IGM.getPointerAlignment(), "project_error_scratch"); Address out = IGF.createAlloca(IGF.IGM.OpenedErrorTripleTy, IGF.IGM.getPointerAlignment(), "project_error_out"); IGF.Builder.CreateCall3(IGF.IGM.getGetErrorValueFn(), box, scratch.getAddress(), out.getAddress()); // Load the 'out' values. auto &openedTI = IGF.getTypeInfoForLowered(openedArchetype); auto projectedPtrAddr = IGF.Builder.CreateStructGEP(out, 0, Size(0)); auto projectedPtr = IGF.Builder.CreateLoad(projectedPtrAddr); auto projected = openedTI.getAddressForPointer(projectedPtr); auto metadataAddr = IGF.Builder.CreateStructGEP(out, 1, IGF.IGM.getPointerSize()); auto metadata = IGF.Builder.CreateLoad(metadataAddr); auto witnessAddr = IGF.Builder.CreateStructGEP(out, 2, 2 * IGF.IGM.getPointerSize()); auto witness = IGF.Builder.CreateLoad(witnessAddr); IGF.bindArchetype(openedArchetype, metadata, witness); return projected; } /// Allocate a boxed existential container with uninitialized space to hold a /// value of a given type. Address irgen::emitBoxedExistentialContainerAllocation(IRGenFunction &IGF, Explosion &dest, SILType destType, CanType formalSrcType, SILType loweredSrcType, ArrayRef conformances){ // TODO: Non-ErrorType boxed existentials. assert(_isErrorType(destType)); auto &destTI = IGF.getTypeInfo(destType).as(); auto &srcTI = IGF.getTypeInfo(loweredSrcType); auto srcMetadata = IGF.emitTypeMetadataRef(formalSrcType); // Should only be one conformance, for the _ErrorType protocol. assert(conformances.size() == 1 && destTI.getStoredProtocols().size() == 1); const ProtocolEntry &entry = destTI.getStoredProtocols()[0]; auto witness = getProtocolWitnessTable(IGF, formalSrcType, srcTI, entry, conformances[0]); // Call the runtime to allocate the box. auto result = IGF.Builder.CreateCall2(IGF.IGM.getAllocErrorFn(), srcMetadata, witness); // Extract the box and value address from the result. auto box = IGF.Builder.CreateExtractValue(result, 0); auto addr = IGF.Builder.CreateExtractValue(result, 1); dest.add(box); addr = IGF.Builder.CreateBitCast(addr, srcTI.getStorageType()->getPointerTo()); return srcTI.getAddressForPointer(addr); } /// Deallocate a boxed existential container with uninitialized space to hold a /// value of a given type. void irgen::emitBoxedExistentialContainerDeallocation(IRGenFunction &IGF, Explosion &container, SILType containerType, CanType valueType) { // TODO: Non-ErrorType boxed existentials. assert(_isErrorType(containerType)); auto box = container.claimNext(); auto srcMetadata = IGF.emitTypeMetadataRef(valueType); IGF.Builder.CreateCall2(IGF.IGM.getDeallocErrorFn(), box, srcMetadata); } /// "Deinitialize" an existential container whose contained value is allocated /// but uninitialized, by deallocating the buffer owned by the container if any. void irgen::emitOpaqueExistentialContainerDeinit(IRGenFunction &IGF, Address container, SILType type) { assert(type.isExistentialType()); assert(!type.isClassExistentialType()); auto &ti = IGF.getTypeInfo(type).as(); auto layout = ti.getLayout(); llvm::Value *metadata = layout.loadMetadataRef(IGF, container); Address buffer = layout.projectExistentialBuffer(IGF, container); emitDeallocateBufferCall(IGF, metadata, buffer); } /// Emit a class existential container from a class instance value /// as an explosion. void irgen::emitClassExistentialContainer(IRGenFunction &IGF, Explosion &out, SILType outType, llvm::Value *instance, CanType instanceFormalType, SILType instanceLoweredType, ArrayRef conformances) { // As a special case, an ErrorType existential can represented as a reference // to an already existing NSError or CFError instance. SmallVector protocols; if (outType.getSwiftRValueType()->isExistentialType(protocols) && protocols.size() == 1) { switch (getSpecialProtocolID(protocols[0])) { case SpecialProtocol::ErrorType: { // Bitcast the incoming class reference to ErrorType. out.add(IGF.Builder.CreateBitCast(instance, IGF.IGM.ErrorPtrTy)); return; } case SpecialProtocol::AnyObject: case SpecialProtocol::None: break; } } assert(outType.isClassExistentialType() && "creating a non-class existential type"); auto &destTI = IGF.getTypeInfo(outType).as(); // Cast the instance pointer to an opaque refcounted pointer. llvm::Value *opaqueInstance = IGF.Builder.CreateBitCast(instance, IGF.IGM.UnknownRefCountedPtrTy); out.add(opaqueInstance); // Emit the witness table pointers. forEachProtocolWitnessTable(IGF, instanceFormalType, outType.getSwiftRValueType(), destTI.getStoredProtocols(), conformances, [&](unsigned i, llvm::Value *ptable) { out.add(ptable); }); } /// Emit an existential container initialization operation for a concrete type. /// Returns the address of the uninitialized buffer for the concrete value. Address irgen::emitOpaqueExistentialContainerInit(IRGenFunction &IGF, Address dest, SILType destType, CanType formalSrcType, SILType loweredSrcType, ArrayRef conformances) { assert(!destType.isClassExistentialType() && "initializing a class existential container as opaque"); auto &destTI = IGF.getTypeInfo(destType).as(); auto &srcTI = IGF.getTypeInfo(loweredSrcType); OpaqueExistentialLayout destLayout = destTI.getLayout(); assert(destTI.getStoredProtocols().size() == conformances.size()); // First, write out the metadata. llvm::Value *metadata = IGF.emitTypeMetadataRef(formalSrcType); IGF.Builder.CreateStore(metadata, destLayout.projectMetadataRef(IGF, dest)); // Compute basic layout information about the type. If we have a // concrete type, we need to know how it packs into a fixed-size // buffer. If we don't, we need a value witness table. FixedPacking packing; bool needValueWitnessToAllocate; if (!isa(srcTI)) { packing = (FixedPacking) -1; needValueWitnessToAllocate = true; } else { packing = srcTI.getFixedPacking(IGF.IGM); needValueWitnessToAllocate = false; } // Next, write the protocol witness tables. forEachProtocolWitnessTable(IGF, formalSrcType, destType.getSwiftRValueType(), destTI.getStoredProtocols(), conformances, [&](unsigned i, llvm::Value *ptable) { Address ptableSlot = destLayout.projectWitnessTable(IGF, dest, i); IGF.Builder.CreateStore(ptable, ptableSlot); }); // Finally, evaluate into the buffer. // Project down to the destination fixed-size buffer. Address buffer = destLayout.projectExistentialBuffer(IGF, dest); // If the type is provably empty, we're done. if (srcTI.isKnownEmpty()) { assert(packing == FixedPacking::OffsetZero); return buffer; } // Otherwise, allocate if necessary. if (needValueWitnessToAllocate) { // If we're using a witness-table to do this, we need to emit a // value-witness call to allocate the fixed-size buffer. return Address(emitAllocateBufferCall(IGF, loweredSrcType, buffer), Alignment(1)); } else { // Otherwise, allocate using what we know statically about the type. return emitAllocateBuffer(IGF, loweredSrcType, srcTI, packing, buffer); } } /// Emit an existential metatype container from a metatype value /// as an explosion. void irgen::emitExistentialMetatypeContainer(IRGenFunction &IGF, Explosion &out, SILType outType, llvm::Value *metatype, SILType metatypeType, ArrayRef conformances) { assert(outType.is()); auto &destTI = IGF.getTypeInfo(outType).as(); out.add(metatype); auto srcType = metatypeType.castTo().getInstanceType(); auto destType = outType.castTo().getInstanceType(); while (auto destMetatypeType = dyn_cast(destType)) { destType = destMetatypeType.getInstanceType(); srcType = cast(srcType).getInstanceType(); } // Emit the witness table pointers. forEachProtocolWitnessTable(IGF, srcType, destType, destTI.getStoredProtocols(), conformances, [&](unsigned i, llvm::Value *ptable) { out.add(ptable); }); } void irgen::emitWitnessMethodValue(IRGenFunction &IGF, CanType baseTy, SILDeclRef member, ProtocolConformance *conformance, Explosion &out) { auto fn = cast(member.getDecl()); // The protocol we're calling on. ProtocolDecl *fnProto = cast(fn->getDeclContext()); // Find the witness table. // FIXME conformance for concrete type auto &baseTI = IGF.getTypeInfoForUnlowered(baseTy); auto &fnProtoInfo = IGF.IGM.getProtocolInfo(fnProto); llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseTI, fnProto, fnProtoInfo, conformance); // Find the witness we're interested in. auto index = fnProtoInfo.getWitnessEntry(fn).getFunctionIndex(); llvm::Value *witness = emitLoadOfOpaqueWitness(IGF, wtable, index); // Cast the witness pointer to i8*. witness = IGF.Builder.CreateBitCast(witness, IGF.IGM.Int8PtrTy); // Build the value. out.add(witness); } llvm::Value *irgen::emitDynamicTypeOfOpaqueArchetype(IRGenFunction &IGF, Address addr, SILType type) { auto archetype = type.castTo(); // Acquire the archetype's static metadata. llvm::Value *metadata = IGF.getLocalTypeData(archetype, LocalTypeData::forMetatype()); return IGF.Builder.CreateCall2(IGF.IGM.getGetDynamicTypeFn(), addr.getAddress(), metadata); } void irgen::emitMetatypeOfOpaqueExistential(IRGenFunction &IGF, Address addr, SILType type, Explosion &out) { assert(type.isExistentialType()); assert(!type.isClassExistentialType()); auto &baseTI = IGF.getTypeInfo(type).as(); // Get the static metadata. auto existLayout = baseTI.getLayout(); llvm::Value *metadata = existLayout.loadMetadataRef(IGF, addr); // Project the buffer and apply the 'typeof' value witness. Address buffer = existLayout.projectExistentialBuffer(IGF, addr); llvm::Value *object = emitProjectBufferCall(IGF, metadata, buffer); llvm::Value *dynamicType = IGF.Builder.CreateCall2(IGF.IGM.getGetDynamicTypeFn(), object, metadata); out.add(dynamicType); // Get the witness tables. baseTI.emitLoadOfTables(IGF, addr, out); } void irgen::emitMetatypeOfClassExistential(IRGenFunction &IGF, Explosion &value, SILType metatypeTy, SILType existentialTy, Explosion &out) { assert(existentialTy.isClassExistentialType()); auto &baseTI = IGF.getTypeInfo(existentialTy).as(); // Extract the class instance pointer. auto tablesAndValue = baseTI.getWitnessTablesAndValue(value); // Get the type metadata. llvm::Value *instance = tablesAndValue.second; auto metaTy = metatypeTy.castTo(); auto repr = metaTy->getRepresentation(); assert(repr != MetatypeRepresentation::Thin && "Class metatypes should have a thin representation"); assert((IGF.IGM.ObjCInterop || repr != MetatypeRepresentation::ObjC) && "Class metatypes should not have ObjC representation without runtime"); if (repr == MetatypeRepresentation::Thick) { auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance); out.add(dynamicType); } else if (repr == MetatypeRepresentation::ObjC) { auto dynamicType = emitHeapMetadataRefForUnknownHeapObject(IGF, instance); out.add(dynamicType); } else { llvm_unreachable("Unknown metatype representation"); } // Get the witness tables. out.add(tablesAndValue.first); } void irgen::emitMetatypeOfMetatype(IRGenFunction &IGF, Explosion &value, SILType existentialTy, Explosion &out) { assert(existentialTy.is()); auto &baseTI = IGF.getTypeInfo(existentialTy).as(); auto tablesAndValue = baseTI.getWitnessTablesAndValue(value); llvm::Value *dynamicType = IGF.Builder.CreateCall( IGF.IGM.getGetMetatypeMetadataFn(), tablesAndValue.second); out.add(dynamicType); out.add(tablesAndValue.first); } /// Emit a projection from an existential container to its concrete value /// buffer with the type metadata for the contained value. /// /// \param _openedArchetype When non-null, the opened archetype /// that captures the details of this existential. std::pair irgen::emitIndirectExistentialProjectionWithMetadata(IRGenFunction &IGF, Address base, SILType baseTy, CanType _openedArchetype) { CanArchetypeType openedArchetype; if (_openedArchetype) openedArchetype = cast(_openedArchetype); assert(baseTy.isExistentialType()); if (baseTy.isClassExistentialType()) { auto &baseTI = IGF.getTypeInfo(baseTy).as(); auto valueAddr = baseTI.projectValue(IGF, base); auto value = IGF.Builder.CreateLoad(valueAddr); auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value); // If we are projecting into an opened archetype, capture the // witness tables. if (openedArchetype) { SmallVector wtables; for (unsigned i = 0, n = baseTI.getNumStoredProtocols(); i != n; ++i) { auto wtableAddr = baseTI.projectWitnessTable(IGF, base, i); wtables.push_back(IGF.Builder.CreateLoad(wtableAddr)); } IGF.bindArchetype(openedArchetype, metadata, wtables); } return {valueAddr, metadata}; } else { auto &baseTI = IGF.getTypeInfo(baseTy).as(); auto layout = baseTI.getLayout(); llvm::Value *metadata = layout.loadMetadataRef(IGF, base); Address buffer = layout.projectExistentialBuffer(IGF, base); llvm::Value *object = emitProjectBufferCall(IGF, metadata, buffer); // If we are projecting into an opened archetype, capture the // witness tables. if (openedArchetype) { SmallVector wtables; for (unsigned i = 0, n = layout.getNumTables(); i != n; ++i) { wtables.push_back(layout.loadWitnessTable(IGF, base, i)); } IGF.bindArchetype(openedArchetype, metadata, wtables); } return {Address(object, Alignment(1)), metadata}; } } /// Emit a projection from an existential container to its concrete value /// buffer. Address irgen::emitOpaqueExistentialProjection(IRGenFunction &IGF, Address base, SILType baseTy, CanArchetypeType openedArchetype) { return emitIndirectExistentialProjectionWithMetadata(IGF, base, baseTy, openedArchetype) .first; } /// Extract the instance pointer from a class existential value. llvm::Value * irgen::emitClassExistentialProjection(IRGenFunction &IGF, Explosion &base, SILType baseTy, CanArchetypeType openedArchetype) { assert(baseTy.isClassExistentialType()); auto &baseTI = IGF.getTypeInfo(baseTy).as(); if (!openedArchetype) return baseTI.getValue(IGF, base); // Capture the metadata and witness tables from this existential // into the given archetype. ArrayRef wtables; llvm::Value *value; std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base); auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value); IGF.bindArchetype(openedArchetype, metadata, wtables); return value; } /// Extract the metatype pointer from a class existential value. llvm::Value * irgen::emitExistentialMetatypeProjection(IRGenFunction &IGF, Explosion &base, SILType baseTy, CanType openedTy) { assert(baseTy.is()); auto &baseTI = IGF.getTypeInfo(baseTy).as(); if (!openedTy) return baseTI.getValue(IGF, base); // Capture the metadata and witness tables from this existential // into the given archetype. ArrayRef wtables; llvm::Value *value; std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base); auto existentialType = baseTy.castTo(); auto targetType = cast(openedTy); // If we're starting with an ObjC representation, convert it to a // class type and let's go. llvm::Value *metatype; if (existentialType->getRepresentation() == MetatypeRepresentation::ObjC) { metatype = emitObjCMetadataRefForMetadata(IGF, value); // Otherwise, we have type metadata. } else { assert(existentialType->getRepresentation() == MetatypeRepresentation::Thick); metatype = value; // The type we need to bind to the archetype is the one that's // deep in the type. while (!isa(targetType.getInstanceType())) { targetType = cast(targetType.getInstanceType()); existentialType = cast(existentialType.getInstanceType()); metatype = emitMetatypeInstanceType(IGF, metatype); } } auto openedArchetype = cast(targetType.getInstanceType()); IGF.bindArchetype(openedArchetype, metatype, wtables); return value; }