//===--- GenClass.cpp - Swift IR Generation For 'class' Types -------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements IR generation for class types. // //===----------------------------------------------------------------------===// #include "GenClass.h" #include "swift/ABI/Class.h" #include "swift/ABI/MetadataValues.h" #include "swift/AST/ASTContext.h" #include "swift/AST/AttrKind.h" #include "swift/AST/Decl.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/Module.h" #include "swift/AST/Pattern.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/TypeMemberVisitor.h" #include "swift/AST/Types.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILType.h" #include "swift/SIL/SILVTableVisitor.h" #include "llvm/ADT/SmallString.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/Support/raw_ostream.h" #include "Callee.h" #include "ConstantBuilder.h" #include "Explosion.h" #include "GenFunc.h" #include "GenMeta.h" #include "GenObjC.h" #include "GenProto.h" #include "GenType.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenModule.h" #include "GenHeap.h" #include "HeapTypeInfo.h" #include "MemberAccessStrategy.h" #include "MetadataLayout.h" #include "MetadataRequest.h" using namespace swift; using namespace irgen; /// What reference counting mechanism does a class-like type have? ReferenceCounting irgen::getReferenceCountingForType(IRGenModule &IGM, CanType type) { // If ObjC interop is disabled, we have a Swift refcount. if (!IGM.ObjCInterop) return ReferenceCounting::Native; if (type->usesNativeReferenceCounting(ResilienceExpansion::Maximal)) return ReferenceCounting::Native; // Class-constrained archetypes and existentials that don't use // native reference counting and yet have a superclass must be // using ObjC reference counting. auto superclass = type->getSuperclass(); if (superclass) return ReferenceCounting::ObjC; // Otherwise, it could be either one. return ReferenceCounting::Unknown; } namespace { /// Layout information for class types. class ClassTypeInfo : public HeapTypeInfo { ClassDecl *TheClass; mutable StructLayout *Layout; mutable ClassLayout FieldLayout; /// Can we use swift reference-counting, or do we have to use /// objc_retain/release? const ReferenceCounting Refcount; void generateLayout(IRGenModule &IGM, SILType classType) const; public: ClassTypeInfo(llvm::PointerType *irType, Size size, SpareBitVector spareBits, Alignment align, ClassDecl *theClass, ReferenceCounting refcount) : HeapTypeInfo(irType, size, std::move(spareBits), align), TheClass(theClass), Layout(nullptr), Refcount(refcount) {} ReferenceCounting getReferenceCounting() const { return Refcount; } ~ClassTypeInfo() override { delete Layout; } ClassDecl *getClass() const { return TheClass; } const StructLayout &getLayout(IRGenModule &IGM, SILType classType) const; const ClassLayout &getClassLayout(IRGenModule &IGM, SILType type) const; Alignment getHeapAlignment(IRGenModule &IGM, SILType type) const { return getLayout(IGM, type).getAlignment(); } ArrayRef getElements(IRGenModule &IGM, SILType type) const { return getLayout(IGM, type).getElements(); } StructLayout *createLayoutWithTailElems(IRGenModule &IGM, SILType classType, ArrayRef tailTypes) const; }; } // end anonymous namespace /// Return the lowered type for the class's 'self' type within its context. static SILType getSelfType(ClassDecl *base) { auto loweredTy = base->getDeclaredTypeInContext()->getCanonicalType(); return SILType::getPrimitiveObjectType(loweredTy); } namespace { class ClassLayoutBuilder : public StructLayoutBuilder { SmallVector Elements; SmallVector AllStoredProperties; SmallVector AllFieldAccesses; unsigned NumInherited = 0; // For now we always lay out resiliently-typed fields as if they // were fragile. bool CompletelyFragileLayout; // Does the class metadata require dynamic initialization above and // beyond what the runtime can automatically achieve? // // This is true if the class or any of its ancestors: // - is generic, // - is resilient, // - has a parent type which isn't emittable as a constant, // - or has a field with resilient layout. bool ClassMetadataRequiresDynamicInitialization = false; // Does the class have a fixed size up until the current point? // If not, we have to access stored properties either ivar offset globals, // or through the field offset vector, based on whether the layout has // dependent layout. bool ClassHasFixedSize = true; // Does the class have identical layout under all generic substitutions? // If not, we can have to access stored properties through the field // offset vector in the instantiated type metadata. bool ClassHasConcreteLayout = true; public: ClassLayoutBuilder(IRGenModule &IGM, SILType classType, ReferenceCounting refcounting) : StructLayoutBuilder(IGM) { // Perform fragile layout if Objective-C interop is enabled. CompletelyFragileLayout = (IGM.Context.LangOpts.EnableObjCInterop && !IGM.IRGen.Opts.EnableClassResilience); // Start by adding a heap header. switch (refcounting) { case ReferenceCounting::Native: // For native classes, place a full object header. addHeapHeader(); break; case ReferenceCounting::ObjC: // For ObjC-inheriting classes, we don't reliably know the size of the // base class, but NSObject only has an `isa` pointer at most. addNSObjectHeader(); break; case ReferenceCounting::Block: case ReferenceCounting::Unknown: case ReferenceCounting::Bridge: case ReferenceCounting::Error: llvm_unreachable("not a class refcounting kind"); } // Next, add the fields for the given class. auto theClass = classType.getClassOrBoundGenericClass(); assert(theClass); addFieldsForClass(theClass, classType); // Add these fields to the builder. addFields(Elements, LayoutStrategy::Universal); } /// Adds a layout of a tail-allocated element. void addTailElement(const ElementLayout &Elt) { Elements.push_back(Elt); if (!addField(Elements.back(), LayoutStrategy::Universal)) { // For empty tail allocated elements we still add 1 padding byte. assert(cast(Elt.getTypeForLayout()).getFixedStride() == Size(1) && "empty elements should have stride 1"); StructFields.push_back(llvm::ArrayType::get(IGM.Int8Ty, 1)); CurSize += Size(1); } } /// Return the element layouts. ArrayRef getElements() const { return Elements; } ClassLayout getClassLayout() const { ClassLayout fieldLayout; auto allStoredProps = IGM.Context.AllocateCopy(AllStoredProperties); auto inheritedStoredProps = allStoredProps.slice(0, NumInherited); fieldLayout.AllStoredProperties = allStoredProps; fieldLayout.InheritedStoredProperties = inheritedStoredProps; fieldLayout.AllFieldAccesses = IGM.Context.AllocateCopy(AllFieldAccesses); fieldLayout.MetadataRequiresDynamicInitialization = ClassMetadataRequiresDynamicInitialization; return fieldLayout; } private: void addFieldsForClass(ClassDecl *theClass, SILType classType) { if (theClass->isGenericContext()) ClassMetadataRequiresDynamicInitialization = true; if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) ClassMetadataRequiresDynamicInitialization = true; if (theClass->hasSuperclass()) { SILType superclassType = classType.getSuperclass(); auto superclass = superclassType.getClassOrBoundGenericClass(); assert(superclass); // If the superclass came from another module, we may have dropped // stored properties due to the Swift language version availability of // their types. In these cases we can't precisely lay out the ivars in // the class object at compile time so we need to do runtime layout. if (classHasIncompleteLayout(IGM, superclass)) { ClassMetadataRequiresDynamicInitialization = true; } if (superclass->hasClangNode()) { // If the superclass was imported from Objective-C, its size is // not known at compile time. However, since the field offset // vector only stores offsets of stored properties defined in // Swift, we don't have to worry about indirect indexing of // the field offset vector. ClassHasFixedSize = false; // We can't use global offset variables if we are generic and layout // dependent on a generic parameter because the objective-c layout might // depend on the alignment of the generic stored property('t' in the // example below). // // class Foo : NSFoobar { // var x : AKlass = AKlass() // var y : AKlass = AKlass() // var t : T? // } if (classType.hasArchetype()) for (VarDecl *var : theClass->getStoredProperties()) { SILType type = classType.getFieldType(var, IGM.getSILModule()); auto &eltType = IGM.getTypeInfo(type); if (!eltType.isFixedSize()) { if (type.hasArchetype()) ClassHasConcreteLayout = false; } } } else if (IGM.isResilient(superclass, ResilienceExpansion::Maximal)) { ClassMetadataRequiresDynamicInitialization = true; // If the superclass is resilient to us, we cannot statically // know the layout of either its instances or its class objects. ClassHasFixedSize = false; // Furthermore, if the superclass is a generic context, we have to // assume that its layout depends on its generic parameters. // But this only propagates down to subclasses whose superclass type // depends on the subclass's generic context. if (superclassType.hasArchetype()) ClassHasConcreteLayout = false; } else { // Otherwise, we have total knowledge of the class and its // fields, so walk them to compute the layout. addFieldsForClass(superclass, superclassType); // Count the fields we got from the superclass. NumInherited = Elements.size(); } } // If this class was imported from another module, assume that we may // not know its exact layout. if (theClass->getModuleContext() != IGM.getSwiftModule()) { ClassHasFixedSize = false; if (classHasIncompleteLayout(IGM, theClass)) ClassMetadataRequiresDynamicInitialization = true; } // Access strategies should be set by the abstract class layout, // not using the concrete type information we have. const ClassLayout *abstractLayout = nullptr; SILType selfType = getSelfType(theClass); if (classType != selfType) { auto &selfTI = IGM.getTypeInfo(selfType).as(); abstractLayout = &selfTI.getClassLayout(IGM, selfType); } // Collect fields from this class and add them to the layout as a chunk. addDirectFieldsFromClass(theClass, classType, abstractLayout); } void addDirectFieldsFromClass(ClassDecl *theClass, SILType classType, const ClassLayout *abstractLayout) { for (VarDecl *var : theClass->getStoredProperties()) { SILType type = classType.getFieldType(var, IGM.getSILModule()); auto &eltTypeForAccess = IGM.getTypeInfo(type); // For now, the Objective-C runtime cannot support dynamic layout of // classes that contain resilient value types, so we just look through // the resilience boundary and assume fragile layout for class ivars // instead. Optional generateStaticLayoutRAII; if (CompletelyFragileLayout && !isa(eltTypeForAccess)) { generateStaticLayoutRAII.emplace(IGM); } auto &eltTypeForLayout = IGM.getTypeInfo(type); if (!eltTypeForLayout.isFixedSize()) { ClassMetadataRequiresDynamicInitialization = true; ClassHasFixedSize = false; if (type.hasArchetype()) ClassHasConcreteLayout = false; } size_t fieldIndex = AllStoredProperties.size(); assert(!abstractLayout || abstractLayout->getFieldIndex(var) == fieldIndex); Elements.push_back(ElementLayout::getIncomplete(eltTypeForLayout, eltTypeForAccess)); AllStoredProperties.push_back(var); AllFieldAccesses.push_back(getFieldAccess(abstractLayout, fieldIndex)); } } FieldAccess getFieldAccess(const ClassLayout *abstractLayout, size_t abstractFieldIndex) { // The class has fixed size, so the field offset is known statically. if (ClassHasFixedSize) { return FieldAccess::ConstantDirect; } // If the field offset can't be known at compile time, we need to // load a field offset, either from a global or the metadata's field // offset vector. // The global will exist only if the abstract type has concrete layout, // so if we're not laying out the abstract type, use its access rule. if (abstractLayout) { return abstractLayout->AllFieldAccesses[abstractFieldIndex]; } // If layout doesn't depend on any generic parameters, but it's // nonetheless not statically known (because either a superclass // or a member type was resilient), then we can rely on the existence // of a global field offset variable which will be initialized by // either the Objective-C or Swift runtime, depending on the // class's heritage. if (ClassHasConcreteLayout) { return FieldAccess::NonConstantDirect; } // If layout depends on generic parameters, we have to load the // offset from the class metadata. return FieldAccess::ConstantIndirect; } }; } // end anonymous namespace void ClassTypeInfo::generateLayout(IRGenModule &IGM, SILType classType) const { assert(!Layout && FieldLayout.AllStoredProperties.empty() && "already generated layout"); // Add the heap header. ClassLayoutBuilder builder(IGM, classType, Refcount); // generateLayout can call itself recursively in order to compute a layout // for the abstract type. If classType shares an exemplar types with the // abstract type, that will end up re-entrantly building the layout // of the same ClassTypeInfo. We don't have anything else to do in this // case. if (Layout) { assert(this == &IGM.getTypeInfo( getSelfType(classType.getClassOrBoundGenericClass()))); return; } // Set the body of the class type. auto classTy = cast(getStorageType()->getPointerElementType()); builder.setAsBodyOfStruct(classTy); // Record the layout. Layout = new StructLayout(builder, classType.getASTType(), classTy, builder.getElements()); FieldLayout = builder.getClassLayout(); } StructLayout * ClassTypeInfo::createLayoutWithTailElems(IRGenModule &IGM, SILType classType, ArrayRef tailTypes) const { // Add the elements for the class properties. ClassLayoutBuilder builder(IGM, classType, Refcount); // Add the tail elements. for (SILType TailTy : tailTypes) { const TypeInfo &tailTI = IGM.getTypeInfo(TailTy); builder.addTailElement(ElementLayout::getIncomplete(tailTI, tailTI)); } // Create a name for the new llvm type. llvm::StructType *classTy = cast(getStorageType()->getPointerElementType()); std::string typeName; llvm::raw_string_ostream os(typeName); os << classTy->getName() << "_tailelems" << IGM.TailElemTypeID++; // Create the llvm type. llvm::StructType *ResultTy = llvm::StructType::create(IGM.getLLVMContext(), StringRef(os.str())); builder.setAsBodyOfStruct(ResultTy); // Create the StructLayout, which is transfered to the caller (the caller is // responsible for deleting it). return new StructLayout(builder, classType.getASTType(), ResultTy, builder.getElements()); } const StructLayout & ClassTypeInfo::getLayout(IRGenModule &IGM, SILType classType) const { // Return the cached layout if available. if (Layout) return *Layout; generateLayout(IGM, classType); return *Layout; } const ClassLayout & ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType) const { // Return the cached layout if available. if (Layout) return FieldLayout; generateLayout(IGM, classType); return FieldLayout; } /// Cast the base to i8*, apply the given inbounds offset (in bytes, /// as a size_t), and cast to a pointer to the given type. llvm::Value *IRGenFunction::emitByteOffsetGEP(llvm::Value *base, llvm::Value *offset, llvm::Type *objectType, const llvm::Twine &name) { assert(offset->getType() == IGM.SizeTy || offset->getType() == IGM.Int32Ty); auto addr = Builder.CreateBitCast(base, IGM.Int8PtrTy); addr = Builder.CreateInBoundsGEP(addr, offset); return Builder.CreateBitCast(addr, objectType->getPointerTo(), name); } /// Cast the base to i8*, apply the given inbounds offset (in bytes, /// as a size_t), and create an address in the given type. Address IRGenFunction::emitByteOffsetGEP(llvm::Value *base, llvm::Value *offset, const TypeInfo &type, const llvm::Twine &name) { auto addr = emitByteOffsetGEP(base, offset, type.getStorageType(), name); return type.getAddressForPointer(addr); } /// Emit a field l-value by applying the given offset to the given base. static OwnedAddress emitAddressAtOffset(IRGenFunction &IGF, SILType baseType, llvm::Value *base, llvm::Value *offset, VarDecl *field) { auto &fieldTI = IGF.getTypeInfo(baseType.getFieldType(field, IGF.getSILModule())); auto addr = IGF.emitByteOffsetGEP(base, offset, fieldTI, base->getName() + "." + field->getName().str()); return OwnedAddress(addr, base); } llvm::Constant * irgen::tryEmitConstantClassFragilePhysicalMemberOffset(IRGenModule &IGM, SILType baseType, VarDecl *field) { auto fieldType = baseType.getFieldType(field, IGM.getSILModule()); // If the field is empty, its address doesn't matter. auto &fieldTI = IGM.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { return llvm::ConstantInt::get(IGM.SizeTy, 0); } auto &baseClassTI = IGM.getTypeInfo(baseType).as(); auto &classLayout = baseClassTI.getClassLayout(IGM, baseType); unsigned fieldIndex = classLayout.getFieldIndex(field); switch (classLayout.AllFieldAccesses[fieldIndex]) { case FieldAccess::ConstantDirect: { auto &element = baseClassTI.getElements(IGM, baseType)[fieldIndex]; return llvm::ConstantInt::get(IGM.SizeTy, element.getByteOffset().getValue()); } case FieldAccess::NonConstantDirect: case FieldAccess::ConstantIndirect: return nullptr; } } unsigned irgen::getClassFieldIndex(IRGenModule &IGM, SILType baseType, VarDecl *field) { auto &baseClassTI = IGM.getTypeInfo(baseType).as(); auto &classLayout = baseClassTI.getClassLayout(IGM, baseType); return classLayout.getFieldIndex(field); } FieldAccess irgen::getClassFieldAccess(IRGenModule &IGM, SILType baseType, VarDecl *field) { auto &baseClassTI = IGM.getTypeInfo(baseType).as(); auto &classLayout = baseClassTI.getClassLayout(IGM, baseType); unsigned fieldIndex = classLayout.getFieldIndex(field); return classLayout.AllFieldAccesses[fieldIndex]; } StructLayout * irgen::getClassLayoutWithTailElems(IRGenModule &IGM, SILType classType, ArrayRef tailTypes) { auto &ClassTI = IGM.getTypeInfo(classType).as(); return ClassTI.createLayoutWithTailElems(IGM, classType, tailTypes); } OwnedAddress irgen::projectPhysicalClassMemberAddress(IRGenFunction &IGF, llvm::Value *base, SILType baseType, SILType fieldType, VarDecl *field) { // If the field is empty, its address doesn't matter. auto &fieldTI = IGF.getTypeInfo(fieldType); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) { return OwnedAddress(fieldTI.getUndefAddress(), base); } auto &baseClassTI = IGF.getTypeInfo(baseType).as(); ClassDecl *baseClass = baseClassTI.getClass(); auto &classLayout = baseClassTI.getClassLayout(IGF.IGM, baseType); unsigned fieldIndex = classLayout.getFieldIndex(field); switch (classLayout.AllFieldAccesses[fieldIndex]) { case FieldAccess::ConstantDirect: { Address baseAddr(base, baseClassTI.getHeapAlignment(IGF.IGM, baseType)); auto &element = baseClassTI.getElements(IGF.IGM, baseType)[fieldIndex]; Address memberAddr = element.project(IGF, baseAddr, None); // We may need to bitcast the address if the field is of a generic type. if (memberAddr.getType()->getElementType() != fieldTI.getStorageType()) memberAddr = IGF.Builder.CreateBitCast(memberAddr, fieldTI.getStorageType()->getPointerTo()); return OwnedAddress(memberAddr, base); } case FieldAccess::NonConstantDirect: { Address offsetA = IGF.IGM.getAddrOfFieldOffset(field, NotForDefinition); auto offsetVar = cast(offsetA.getAddress()); offsetVar->setConstant(false); auto offset = IGF.Builder.CreateLoad(offsetA, "offset"); return emitAddressAtOffset(IGF, baseType, base, offset, field); } case FieldAccess::ConstantIndirect: { auto metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType); auto offset = emitClassFieldOffset(IGF, baseClass, field, metadata); return emitAddressAtOffset(IGF, baseType, base, offset, field); } } llvm_unreachable("bad field-access strategy"); } MemberAccessStrategy irgen::getPhysicalClassMemberAccessStrategy(IRGenModule &IGM, SILType baseType, VarDecl *field) { auto &baseClassTI = IGM.getTypeInfo(baseType).as(); ClassDecl *baseClass = baseType.getClassOrBoundGenericClass(); auto &classLayout = baseClassTI.getClassLayout(IGM, baseType); unsigned fieldIndex = classLayout.getFieldIndex(field); switch (classLayout.AllFieldAccesses[fieldIndex]) { case FieldAccess::ConstantDirect: { auto &element = baseClassTI.getElements(IGM, baseType)[fieldIndex]; return MemberAccessStrategy::getDirectFixed(element.getByteOffset()); } case FieldAccess::NonConstantDirect: { std::string symbol = LinkEntity::forFieldOffset(field).mangleAsString(); return MemberAccessStrategy::getDirectGlobal(std::move(symbol), MemberAccessStrategy::OffsetKind::Bytes_Word); } case FieldAccess::ConstantIndirect: { Size indirectOffset = getClassFieldOffsetOffset(IGM, baseClass, field); return MemberAccessStrategy::getIndirectFixed(indirectOffset, MemberAccessStrategy::OffsetKind::Bytes_Word); } } llvm_unreachable("bad field-access strategy"); } Address irgen::emitTailProjection(IRGenFunction &IGF, llvm::Value *Base, SILType ClassType, SILType TailType) { const ClassTypeInfo &classTI = IGF.getTypeInfo(ClassType).as(); llvm::Value *Offset = nullptr; auto &layout = classTI.getLayout(IGF.IGM, ClassType); Alignment HeapObjAlign = IGF.IGM.TargetInfo.HeapObjectAlignment; Alignment Align; // Get the size of the class instance. if (layout.isFixedLayout()) { Size ClassSize = layout.getSize(); Offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, ClassSize.getValue()); Align = HeapObjAlign.alignmentAtOffset(ClassSize); } else { llvm::Value *metadata = emitHeapMetadataRefForHeapObject(IGF, Base, ClassType); Offset = emitClassFragileInstanceSizeAndAlignMask(IGF, ClassType.getClassOrBoundGenericClass(), metadata).first; } // Align up to the TailType. assert(TailType.isObject()); const TypeInfo &TailTI = IGF.getTypeInfo(TailType); llvm::Value *AlignMask = TailTI.getAlignmentMask(IGF, TailType); Offset = IGF.Builder.CreateAdd(Offset, AlignMask); llvm::Value *InvertedMask = IGF.Builder.CreateNot(AlignMask); Offset = IGF.Builder.CreateAnd(Offset, InvertedMask); llvm::Value *Addr = IGF.emitByteOffsetGEP(Base, Offset, TailTI.getStorageType(), "tailaddr"); if (auto *OffsetConst = dyn_cast(Offset)) { // Try to get an accurate alignment (only possible if the Offset is a // constant). Size TotalOffset(OffsetConst->getZExtValue()); Align = HeapObjAlign.alignmentAtOffset(TotalOffset); } return Address(Addr, Align); } /// Try to stack promote a class instance with possible tail allocated arrays. /// /// Returns the alloca if successful, or nullptr otherwise. static llvm::Value *stackPromote(IRGenFunction &IGF, const StructLayout &ClassLayout, int &StackAllocSize, ArrayRef> TailArrays) { if (StackAllocSize < 0) return nullptr; if (!ClassLayout.isFixedLayout()) return nullptr; // Calculate the total size needed. // The first part is the size of the class itself. Alignment ClassAlign = ClassLayout.getAlignment(); Size TotalSize = ClassLayout.getSize(); // Add size for tail-allocated arrays. for (const auto &TailArray : TailArrays) { SILType ElemTy = TailArray.first; llvm::Value *Count = TailArray.second; // We can only calculate a constant size if the tail-count is constant. auto *CI = dyn_cast(Count); if (!CI) return nullptr; const TypeInfo &ElemTI = IGF.getTypeInfo(ElemTy); if (!ElemTI.isFixedSize()) return nullptr; const FixedTypeInfo &ElemFTI = ElemTI.as(); Alignment ElemAlign = ElemFTI.getFixedAlignment(); // This should not happen - just to be save. if (ElemAlign > ClassAlign) return nullptr; TotalSize = TotalSize.roundUpToAlignment(ElemAlign); TotalSize += ElemFTI.getFixedStride() * CI->getValue().getZExtValue(); } if (TotalSize > Size(StackAllocSize)) return nullptr; StackAllocSize = TotalSize.getValue(); if (TotalSize == ClassLayout.getSize()) { // No tail-allocated arrays: we can use the llvm class type for alloca. llvm::Type *ClassTy = ClassLayout.getType(); Address Alloca = IGF.createAlloca(ClassTy, ClassAlign, "reference.raw"); return Alloca.getAddress(); } // Use a byte-array as type for alloca. llvm::Value *SizeVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty, TotalSize.getValue()); Address Alloca = IGF.createAlloca(IGF.IGM.Int8Ty, SizeVal, ClassAlign, "reference.raw"); return Alloca.getAddress(); } std::pair irgen::appendSizeForTailAllocatedArrays(IRGenFunction &IGF, llvm::Value *size, llvm::Value *alignMask, TailArraysRef TailArrays) { for (const auto &TailArray : TailArrays) { SILType ElemTy = TailArray.first; llvm::Value *Count = TailArray.second; const TypeInfo &ElemTI = IGF.getTypeInfo(ElemTy); // Align up to the tail-allocated array. llvm::Value *ElemStride = ElemTI.getStride(IGF, ElemTy); llvm::Value *ElemAlignMask = ElemTI.getAlignmentMask(IGF, ElemTy); size = IGF.Builder.CreateAdd(size, ElemAlignMask); llvm::Value *InvertedMask = IGF.Builder.CreateNot(ElemAlignMask); size = IGF.Builder.CreateAnd(size, InvertedMask); // Add the size of the tail allocated array. llvm::Value *AllocSize = IGF.Builder.CreateMul(ElemStride, Count); size = IGF.Builder.CreateAdd(size, AllocSize); alignMask = IGF.Builder.CreateOr(alignMask, ElemAlignMask); } return {size, alignMask}; } /// Emit an allocation of a class. llvm::Value *irgen::emitClassAllocation(IRGenFunction &IGF, SILType selfType, bool objc, int &StackAllocSize, TailArraysRef TailArrays) { auto &classTI = IGF.getTypeInfo(selfType).as(); auto classType = selfType.getASTType(); // If we need to use Objective-C allocation, do so. // If the root class isn't known to use the Swift allocator, we need // to call [self alloc]. if (objc) { llvm::Value *metadata = emitClassHeapMetadataRef(IGF, classType, MetadataValueType::ObjCClass, MetadataState::Complete, /*allow uninitialized*/ true); StackAllocSize = -1; return emitObjCAllocObjectCall(IGF, metadata, selfType); } llvm::Value *metadata = emitClassHeapMetadataRef(IGF, classType, MetadataValueType::TypeMetadata, MetadataState::Complete); // FIXME: Long-term, we clearly need a specialized runtime entry point. llvm::Value *size, *alignMask; std::tie(size, alignMask) = emitClassFragileInstanceSizeAndAlignMask(IGF, selfType.getClassOrBoundGenericClass(), metadata); const StructLayout &layout = classTI.getLayout(IGF.IGM, selfType); llvm::Type *destType = layout.getType()->getPointerTo(); llvm::Value *val = nullptr; if (llvm::Value *Promoted = stackPromote(IGF, layout, StackAllocSize, TailArrays)) { val = IGF.Builder.CreateBitCast(Promoted, IGF.IGM.RefCountedPtrTy); val = IGF.emitInitStackObjectCall(metadata, val, "reference.new"); } else { // Allocate the object on the heap. std::tie(size, alignMask) = appendSizeForTailAllocatedArrays(IGF, size, alignMask, TailArrays); val = IGF.emitAllocObjectCall(metadata, size, alignMask, "reference.new"); StackAllocSize = -1; } return IGF.Builder.CreateBitCast(val, destType); } llvm::Value *irgen::emitClassAllocationDynamic(IRGenFunction &IGF, llvm::Value *metadata, SILType selfType, bool objc, TailArraysRef TailArrays) { // If we need to use Objective-C allocation, do so. if (objc) { return emitObjCAllocObjectCall(IGF, metadata, selfType); } // Otherwise, allocate using Swift's routines. llvm::Value *size, *alignMask; std::tie(size, alignMask) = emitClassResilientInstanceSizeAndAlignMask(IGF, selfType.getClassOrBoundGenericClass(), metadata); std::tie(size, alignMask) = appendSizeForTailAllocatedArrays(IGF, size, alignMask, TailArrays); llvm::Value *val = IGF.emitAllocObjectCall(metadata, size, alignMask, "reference.new"); auto &classTI = IGF.getTypeInfo(selfType).as(); auto &layout = classTI.getLayout(IGF.IGM, selfType); llvm::Type *destType = layout.getType()->getPointerTo(); return IGF.Builder.CreateBitCast(val, destType); } /// Look for the instance method: /// func __getInstanceSizeAndAlignMask() -> (Int, Int) /// and use it to populate 'size' and 'alignMask' if it's present. static bool getInstanceSizeByMethod(IRGenFunction &IGF, CanType selfType, ClassDecl *selfClass, llvm::Value *selfValue, llvm::Value *&size, llvm::Value *&alignMask) { // Look for a single instance method with the magic name. FuncDecl *fn; { auto name = IGF.IGM.Context.getIdentifier("__getInstanceSizeAndAlignMask"); SmallVector results; selfClass->lookupQualified(selfType, name, NL_KnownNonCascadingDependency, nullptr, results); if (results.size() != 1) return false; fn = dyn_cast(results[0]); if (!fn) return false; } // Check whether the SIL module defines it. (We need a type for it.) SILDeclRef fnRef(fn, SILDeclRef::Kind::Func); SILFunction *silFn = IGF.getSILModule().lookUpFunction(fnRef); if (!silFn) return false; // Check that it returns two size_t's and takes no other arguments. auto fnType = silFn->getLoweredFunctionType(); auto fnConv = silFn->getConventions(); if (fnType->getParameters().size() != 1) return false; if (fnConv.getNumDirectSILResults() != 2 || fnConv.getNumIndirectSILResults() != 0) return false; if ((fnConv.getDirectSILResults().begin()->getConvention() != ResultConvention::Unowned) || (std::next(fnConv.getDirectSILResults().begin())->getConvention() != ResultConvention::Unowned)) return false; llvm::Function *llvmFn = IGF.IGM.getAddrOfSILFunction(silFn, NotForDefinition); auto llvmFnTy = llvmFn->getFunctionType(); if (llvmFnTy->getNumParams() != 1) return false; auto returnType = dyn_cast(llvmFnTy->getReturnType()); if (!returnType || returnType->getNumElements() != 2 || returnType->getElementType(0) != IGF.IGM.SizeTy || returnType->getElementType(1) != IGF.IGM.SizeTy) return false; // Retain 'self' if necessary. if (fnType->getParameters()[0].isConsumed()) { IGF.emitNativeStrongRetain(selfValue, IGF.getDefaultAtomicity()); } // Adjust down to the defining subclass type if necessary. selfValue = IGF.Builder.CreateBitCast(selfValue, llvmFnTy->getParamType(0)); // Emit a direct call. auto result = IGF.Builder.CreateCall(llvmFn, selfValue); result->setCallingConv(llvmFn->getCallingConv()); // Extract the size and alignment. size = IGF.Builder.CreateExtractValue(result, 0, "size"); alignMask = IGF.Builder.CreateExtractValue(result, 1, "alignMask"); return true; } /// Get the instance size and alignment mask for the given class /// instance. static void getInstanceSizeAndAlignMask(IRGenFunction &IGF, SILType selfType, ClassDecl *selfClass, llvm::Value *selfValue, llvm::Value *&size, llvm::Value *&alignMask) { // Use the magic __getInstanceSizeAndAlignMask method if we can // see a declaration of it if (getInstanceSizeByMethod(IGF, selfType.getASTType(), selfClass, selfValue, size, alignMask)) return; // Try to determine the size of the object we're deallocating. auto &info = IGF.IGM.getTypeInfo(selfType).as(); auto &layout = info.getLayout(IGF.IGM, selfType); // If it's fixed, emit the constant size and alignment mask. if (layout.isFixedLayout()) { size = layout.emitSize(IGF.IGM); alignMask = layout.emitAlignMask(IGF.IGM); return; } // Otherwise, get them from the metadata. llvm::Value *metadata = emitHeapMetadataRefForHeapObject(IGF, selfValue, selfType); std::tie(size, alignMask) = emitClassFragileInstanceSizeAndAlignMask(IGF, selfClass, metadata); } void irgen::emitClassDeallocation(IRGenFunction &IGF, SILType selfType, llvm::Value *selfValue) { auto *theClass = selfType.getClassOrBoundGenericClass(); llvm::Value *size, *alignMask; getInstanceSizeAndAlignMask(IGF, selfType, theClass, selfValue, size, alignMask); selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.RefCountedPtrTy); emitDeallocateClassInstance(IGF, selfValue, size, alignMask); } void irgen::emitPartialClassDeallocation(IRGenFunction &IGF, SILType selfType, llvm::Value *selfValue, llvm::Value *metadataValue) { auto *theClass = selfType.getClassOrBoundGenericClass(); // Foreign classes should not be freed by sending -release. // They should also probably not be freed with object_dispose(), // either. // // However, in practice, the only time we should try to free an // instance of a foreign class here is inside an initializer // delegating to a factory initializer. In this case, the object // was allocated with +allocWithZone:, so calling object_dispose() // should be OK. if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::RuntimeOnly) { selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.ObjCPtrTy); IGF.Builder.CreateCall(IGF.IGM.getObjectDisposeFn(), {selfValue}); return; } llvm::Value *size, *alignMask; getInstanceSizeAndAlignMask(IGF, selfType, theClass, selfValue, size, alignMask); selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.RefCountedPtrTy); emitDeallocatePartialClassInstance(IGF, selfValue, metadataValue, size, alignMask); } llvm::Constant *irgen::tryEmitClassConstantFragileInstanceSize( IRGenModule &IGM, ClassDecl *Class) { auto selfType = getSelfType(Class); auto &classTI = IGM.getTypeInfo(selfType).as(); auto &layout = classTI.getLayout(IGM, selfType); if (layout.isFixedLayout()) return layout.emitSize(IGM); return nullptr; } llvm::Constant *irgen::tryEmitClassConstantFragileInstanceAlignMask( IRGenModule &IGM, ClassDecl *Class) { auto selfType = getSelfType(Class); auto &classTI = IGM.getTypeInfo(selfType).as(); auto &layout = classTI.getLayout(IGM, selfType); if (layout.isFixedLayout()) return layout.emitAlignMask(IGM); return nullptr; } /// emitClassDecl - Emit all the declarations associated with this class type. void IRGenModule::emitClassDecl(ClassDecl *D) { PrettyStackTraceDecl prettyStackTrace("emitting class metadata for", D); SILType selfType = getSelfType(D); auto &classTI = getTypeInfo(selfType).as(); // Emit the class metadata. emitClassMetadata(*this, D, classTI.getLayout(*this, selfType), classTI.getClassLayout(*this, selfType)); IRGen.addClassForEagerInitialization(D); emitNestedTypeDecls(D->getMembers()); emitFieldMetadataRecord(D); // If the class is resilient, emit dispatch thunks. if (isResilient(D, ResilienceExpansion::Minimal)) { struct ThunkEmitter : public SILVTableVisitor { IRGenModule &IGM; ClassDecl *D; ThunkEmitter(IRGenModule &IGM, ClassDecl *D) : IGM(IGM), D(D) { addVTableEntries(D); } void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {} void addMethod(SILDeclRef member) { auto *func = cast(member.getDecl()); if (func->getDeclContext() == D && func->getEffectiveAccess() >= AccessLevel::Public) { IGM.emitDispatchThunk(member); } } void addPlaceholder(MissingMemberDecl *m) { assert(m->getNumberOfVTableEntries() == 0 && "Should not be emitting class with missing members"); } }; ThunkEmitter emitter(*this, D); } } namespace { using CategoryNameKey = std::pair; /// Used to provide unique names to ObjC categories generated by Swift /// extensions. The first category for a class in a module gets the module's /// name as its key, e.g., NSObject (MySwiftModule). Another extension of the /// same class in the same module gets a category name with a number appended, /// e.g., NSObject (MySwiftModule1). llvm::DenseMap CategoryCounts; /// A class for building ObjC class data (in Objective-C terms, class_ro_t), /// category data (category_t), or protocol data (protocol_t). class ClassDataBuilder : public ClassMemberVisitor { IRGenModule &IGM; PointerUnion TheEntity; ExtensionDecl *TheExtension; const StructLayout *Layout; const ClassLayout *FieldLayout; ClassDecl *getClass() const { return TheEntity.get(); } ProtocolDecl *getProtocol() const { return TheEntity.get(); } bool isBuildingClass() const { return TheEntity.is() && !TheExtension; } bool isBuildingCategory() const { return TheEntity.is() && TheExtension; } bool isBuildingProtocol() const { return TheEntity.is(); } bool HasNonTrivialDestructor = false; bool HasNonTrivialConstructor = false; class MethodDescriptor { public: enum class Kind { Method, IVarInitializer, IVarDestroyer, }; private: llvm::PointerIntPair Data; static_assert(llvm::PointerLikeTypeTraits ::NumLowBitsAvailable >= 2, "llvm::Function* isn't adequately aligned"); static_assert(llvm::PointerLikeTypeTraits ::NumLowBitsAvailable >= 2, "AbstractFuncDecl* isn't adequately aligned"); MethodDescriptor(Kind kind, void *ptr) : Data(ptr, kind) {} public: MethodDescriptor(AbstractFunctionDecl *method) : Data(method, Kind::Method) { assert(method && "null method provided"); } static MethodDescriptor getIVarInitializer(llvm::Function *fn) { assert(fn && "null impl provided"); return MethodDescriptor(Kind::IVarInitializer, fn); } static MethodDescriptor getIVarDestroyer(llvm::Function *fn) { assert(fn && "null impl provided"); return MethodDescriptor(Kind::IVarDestroyer, fn); } Kind getKind() const { return Data.getInt(); } AbstractFunctionDecl *getMethod() { assert(getKind() == Kind::Method); return static_cast(Data.getPointer()); } llvm::Function *getImpl() { assert(getKind() != Kind::Method); return static_cast(Data.getPointer()); } }; llvm::SmallString<16> CategoryName; SmallVector Ivars; SmallVector InstanceMethods; SmallVector ClassMethods; SmallVector OptInstanceMethods; SmallVector OptClassMethods; SmallVector Protocols; SmallVector InstanceProperties; SmallVector ClassProperties; llvm::Constant *Name = nullptr; /// Index of the first non-inherited field in the layout. unsigned FirstFieldIndex; unsigned NextFieldIndex; SmallVectorImpl &getMethodList(ValueDecl *decl) { if (decl->getAttrs().hasAttribute()) { if (decl->isStatic()) { return OptClassMethods; } else { return OptInstanceMethods; } } else { if (decl->isStatic()) { return ClassMethods; } else { return InstanceMethods; } } } public: ClassDataBuilder(IRGenModule &IGM, ClassDecl *theClass, const StructLayout &layout, const ClassLayout &fieldLayout) : IGM(IGM), TheEntity(theClass), TheExtension(nullptr), Layout(&layout), FieldLayout(&fieldLayout) { FirstFieldIndex = fieldLayout.InheritedStoredProperties.size(); NextFieldIndex = FirstFieldIndex; visitConformances(theClass); visitMembers(theClass); if (Lowering::usesObjCAllocator(theClass)) { addIVarInitializer(); addIVarDestroyer(); } } ClassDataBuilder(IRGenModule &IGM, ClassDecl *theClass, ExtensionDecl *theExtension) : IGM(IGM), TheEntity(theClass), TheExtension(theExtension), Layout(nullptr), FieldLayout(nullptr) { FirstFieldIndex = -1; NextFieldIndex = -1; buildCategoryName(CategoryName); visitConformances(theExtension); for (Decl *member : TheExtension->getMembers()) visit(member); } ClassDataBuilder(IRGenModule &IGM, ProtocolDecl *theProtocol) : IGM(IGM), TheEntity(theProtocol), TheExtension(nullptr) { llvm::SmallSetVector protocols; // Gather protocol references for all of the directly inherited // Objective-C protocol conformances. for (ProtocolDecl *p : theProtocol->getInheritedProtocols()) { getObjCProtocols(p, protocols); } // Add any restated Objective-C protocol conformances. for (auto *attr : theProtocol ->getAttrs().getAttributes()) { getObjCProtocols(attr->Proto, protocols); } for (ProtocolDecl *proto : protocols) { Protocols.push_back(proto); } for (Decl *member : theProtocol->getMembers()) visit(member); } /// Gather protocol records for all of the explicitly-specified Objective-C /// protocol conformances. void visitConformances(DeclContext *dc) { llvm::SmallSetVector protocols; for (auto conformance : dc->getLocalConformances( ConformanceLookupKind::OnlyExplicit, nullptr, /*sorted=*/true)) { ProtocolDecl *proto = conformance->getProtocol(); getObjCProtocols(proto, protocols); } for (ProtocolDecl *proto : protocols) { Protocols.push_back(proto); } } /// Add the protocol to the vector, if it's Objective-C protocol, /// or search its superprotocols. void getObjCProtocols(ProtocolDecl *proto, llvm::SmallSetVector &result) { if (proto->isObjC()) { result.insert(proto); } else { for (ProtocolDecl *inherited : proto->getInheritedProtocols()) { // Recursively check inherited protocol for objc conformance. getObjCProtocols(inherited, result); } } } llvm::Constant *getMetaclassRefOrNull(ClassDecl *theClass) { if (theClass->isGenericContext() && !theClass->hasClangNode()) { return llvm::ConstantPointerNull::get(IGM.ObjCClassPtrTy); } else { return IGM.getAddrOfMetaclassObject(theClass, NotForDefinition); } } void buildMetaclassStub() { assert(Layout && "can't build a metaclass from a category"); // The isa is the metaclass pointer for the root class. auto rootClass = getRootClassForMetaclass(IGM, TheEntity.get()); auto rootPtr = getMetaclassRefOrNull(rootClass); // The superclass of the metaclass is the metaclass of the // superclass. Note that for metaclass stubs, we can always // ignore parent contexts and generic arguments. // // If this class has no formal superclass, then its actual // superclass is SwiftObject, i.e. the root class. llvm::Constant *superPtr; if (getClass()->hasSuperclass()) { auto base = getClass()->getSuperclass()->getClassOrBoundGenericClass(); superPtr = getMetaclassRefOrNull(base); } else { superPtr = getMetaclassRefOrNull( IGM.getObjCRuntimeBaseForSwiftRootClass(getClass())); } auto dataPtr = emitROData(ForMetaClass); dataPtr = llvm::ConstantExpr::getPtrToInt(dataPtr, IGM.IntPtrTy); llvm::Constant *fields[] = { rootPtr, superPtr, IGM.getObjCEmptyCachePtr(), IGM.getObjCEmptyVTablePtr(), dataPtr }; auto init = llvm::ConstantStruct::get(IGM.ObjCClassStructTy, makeArrayRef(fields)); auto metaclass = cast( IGM.getAddrOfMetaclassObject(getClass(), ForDefinition)); metaclass->setInitializer(init); } private: void buildCategoryName(SmallVectorImpl &s) { llvm::raw_svector_ostream os(s); // Find the module the extension is declared in. ModuleDecl *TheModule = TheExtension->getParentModule(); os << TheModule->getName(); unsigned categoryCount = CategoryCounts[{getClass(), TheModule}]++; if (categoryCount > 0) os << categoryCount; } public: llvm::Constant *emitCategory() { assert(TheExtension && "can't emit category data for a class"); ConstantInitBuilder builder(IGM); auto fields = builder.beginStruct(); // struct category_t { // char const *name; fields.add(IGM.getAddrOfGlobalString(CategoryName)); // const class_t *theClass; if (getClass()->hasClangNode()) fields.add(IGM.getAddrOfObjCClass(getClass(), NotForDefinition)); else { auto type = getSelfType(getClass()).getASTType(); llvm::Constant *metadata = tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true); assert(metadata && "extended objc class doesn't have constant metadata?"); fields.add(metadata); } // const method_list_t *instanceMethods; fields.add(buildInstanceMethodList()); // const method_list_t *classMethods; fields.add(buildClassMethodList()); // const protocol_list_t *baseProtocols; fields.add(buildProtocolList()); // const property_list_t *properties; fields.add(buildPropertyList(ForClass)); // const property_list_t *classProperties; fields.add(buildPropertyList(ForMetaClass)); // uint32_t size; // FIXME: Clang does this by using non-ad-hoc types for ObjC runtime // structures. Size size = 7 * IGM.getPointerSize() + Size(4); fields.addInt32(size.getValue()); // }; assert(fields.getNextOffsetFromGlobal() == size); return buildGlobalVariable(fields, "_CATEGORY_"); } llvm::Constant *emitProtocol() { ConstantInitBuilder builder(IGM); auto fields = builder.beginStruct(); llvm::SmallString<64> nameBuffer; assert(isBuildingProtocol() && "not emitting a protocol"); // struct protocol_t { // Class super; fields.addNullPointer(IGM.Int8PtrTy); // char const *name; fields.add(IGM.getAddrOfGlobalString(getEntityName(nameBuffer))); // const protocol_list_t *baseProtocols; fields.add(buildProtocolList()); // const method_list_t *requiredInstanceMethods; fields.add(buildInstanceMethodList()); // const method_list_t *requiredClassMethods; fields.add(buildClassMethodList()); // const method_list_t *optionalInstanceMethods; fields.add(buildOptInstanceMethodList()); // const method_list_t *optionalClassMethods; fields.add(buildOptClassMethodList()); // const property_list_t *properties; fields.add(buildPropertyList(ForClass)); // uint32_t size; // FIXME: Clang does this by using non-ad-hoc types for ObjC runtime // structures. Size size = 11 * IGM.getPointerSize() + 2 * Size(4); fields.addInt32(size.getValue()); // uint32_t flags; auto flags = ProtocolDescriptorFlags() .withSwift(!getProtocol()->hasClangNode()) .withClassConstraint(ProtocolClassConstraint::Class) .withDispatchStrategy(ProtocolDispatchStrategy::ObjC) .withSpecialProtocol(getSpecialProtocolID(getProtocol())); fields.addInt32(flags.getIntValue()); // const char ** extendedMethodTypes; fields.add(buildOptExtendedMethodTypes()); // const char *demangledName; fields.addNullPointer(IGM.Int8PtrTy); // const property_list_t *classProperties; fields.add(buildPropertyList(ForMetaClass)); // }; assert(fields.getNextOffsetFromGlobal() == size); return buildGlobalVariable(fields, "_PROTOCOL_"); } void emitRODataFields(ConstantStructBuilder &b, ForMetaClass_t forMeta) { assert(Layout && "can't emit rodata for a category"); // struct _class_ro_t { // uint32_t flags; b.addInt32(unsigned(buildFlags(forMeta))); // uint32_t instanceStart; // uint32_t instanceSize; // The runtime requires that the ivar offsets be initialized to // a valid layout of the ivars of this class, bounded by these // two values. If the instanceSize of the superclass equals the // stored instanceStart of the subclass, the ivar offsets // will not be changed. Size instanceStart; Size instanceSize; if (forMeta) { // sizeof(struct class_t) instanceSize = Size(5 * IGM.getPointerSize().getValue()); // historical nonsense instanceStart = instanceSize; } else { instanceSize = Layout->getSize(); if (Layout->getElements().empty() || Layout->getElements().size() == FirstFieldIndex) { instanceStart = instanceSize; } else if (Layout->getElement(FirstFieldIndex).getKind() == ElementLayout::Kind::Fixed || Layout->getElement(FirstFieldIndex).getKind() == ElementLayout::Kind::Empty) { // FIXME: assumes layout is always sequential! instanceStart = Layout->getElement(FirstFieldIndex).getByteOffset(); } else { instanceStart = Size(0); } } b.addInt32(instanceStart.getValue()); b.addInt32(instanceSize.getValue()); // uint32_t reserved; // only when building for 64bit targets if (IGM.getPointerAlignment().getValue() > 4) { assert(IGM.getPointerAlignment().getValue() == 8); b.addInt32(0); } // const uint8_t *ivarLayout; // GC/ARC layout. TODO. b.addNullPointer(IGM.Int8PtrTy); // const char *name; // It is correct to use the same name for both class and metaclass. b.add(buildName()); // const method_list_t *baseMethods; b.add(forMeta ? buildClassMethodList() : buildInstanceMethodList()); // const protocol_list_t *baseProtocols; // Apparently, this list is the same in the class and the metaclass. b.add(buildProtocolList()); // const ivar_list_t *ivars; if (forMeta) { b.addNullPointer(IGM.Int8PtrTy); } else { b.add(buildIvarList()); } // const uint8_t *weakIvarLayout; // More GC/ARC layout. TODO. b.addNullPointer(IGM.Int8PtrTy); // const property_list_t *baseProperties; b.add(buildPropertyList(forMeta)); // }; } llvm::Constant *emitROData(ForMetaClass_t forMeta) { ConstantInitBuilder builder(IGM); auto fields = builder.beginStruct(); emitRODataFields(fields, forMeta); auto dataSuffix = forMeta ? "_METACLASS_DATA_" : "_DATA_"; return buildGlobalVariable(fields, dataSuffix); } private: ObjCClassFlags buildFlags(ForMetaClass_t forMeta) { ObjCClassFlags flags = ObjCClassFlags::CompiledByARC; // Mark metaclasses as appropriate. if (forMeta) { flags |= ObjCClassFlags::Meta; // Non-metaclasses need us to record things whether primitive // construction/destructor is trivial. } else if (HasNonTrivialDestructor || HasNonTrivialConstructor) { flags |= ObjCClassFlags::HasCXXStructors; if (!HasNonTrivialConstructor) flags |= ObjCClassFlags::HasCXXDestructorOnly; } // FIXME: set ObjCClassFlags::Hidden when appropriate return flags; } llvm::Constant *buildName() { if (Name) return Name; // If the class is generic, we'll instantiate its name at runtime. if (getClass()->isGenericContext()) { Name = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); return Name; } llvm::SmallString<64> buffer; Name = IGM.getAddrOfGlobalString(getClass()->getObjCRuntimeName(buffer)); return Name; } llvm::Constant *null() { return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); } /*** Methods ***********************************************************/ public: /// Methods need to be collected into the appropriate methods list. void visitFuncDecl(FuncDecl *method) { if (!isBuildingProtocol() && !requiresObjCMethodDescriptor(method)) return; // getters and setters funcdecls will be handled by their parent // var/subscript. if (isa(method)) return; // Don't emit getters/setters for @NSManaged methods. if (method->getAttrs().hasAttribute()) return; getMethodList(method).push_back(method); } /// Constructors need to be collected into the appropriate methods list. void visitConstructorDecl(ConstructorDecl *constructor) { if (!isBuildingProtocol() && !requiresObjCMethodDescriptor(constructor)) return; getMethodList(constructor).push_back(constructor); } /// Determine whether the given destructor has an Objective-C /// definition. bool hasObjCDeallocDefinition(DestructorDecl *destructor) { // If we have the destructor body, we know whether SILGen // generated a -dealloc body. if (auto braceStmt = destructor->getBody()) return braceStmt->getNumElements() != 0; // We don't have a destructor body, so hunt for the SIL function // for it. auto dtorRef = SILDeclRef(destructor, SILDeclRef::Kind::Deallocator) .asForeign(); if (auto silFn = IGM.getSILModule().lookUpFunction(dtorRef)) return silFn->isDefinition(); // The Objective-C thunk was never even declared, so it is not defined. return false; } /// Destructors need to be collected into the instance methods /// list void visitDestructorDecl(DestructorDecl *destructor) { auto classDecl = cast(destructor->getDeclContext()); if (Lowering::usesObjCAllocator(classDecl) && hasObjCDeallocDefinition(destructor)) { InstanceMethods.push_back(destructor); } } void visitMissingMemberDecl(MissingMemberDecl *placeholder) { llvm_unreachable("should not IRGen classes with missing members"); } void addIVarInitializer() { if (auto fn = IGM.getAddrOfIVarInitDestroy(getClass(), /*destroy*/ false, /*isForeign=*/ true, NotForDefinition)) { InstanceMethods.push_back(MethodDescriptor::getIVarInitializer(*fn)); HasNonTrivialConstructor = true; } } void addIVarDestroyer() { if (auto fn = IGM.getAddrOfIVarInitDestroy(getClass(), /*destroy*/ true, /*isForeign=*/ true, NotForDefinition)) { InstanceMethods.push_back(MethodDescriptor::getIVarDestroyer(*fn)); HasNonTrivialDestructor = true; } } void buildMethod(ConstantArrayBuilder &descriptors, MethodDescriptor descriptor) { switch (descriptor.getKind()) { case MethodDescriptor::Kind::Method: return buildMethod(descriptors, descriptor.getMethod()); case MethodDescriptor::Kind::IVarInitializer: emitObjCIVarInitDestroyDescriptor(IGM, descriptors, getClass(), descriptor.getImpl(), false); return; case MethodDescriptor::Kind::IVarDestroyer: emitObjCIVarInitDestroyDescriptor(IGM, descriptors, getClass(), descriptor.getImpl(), true); return; } llvm_unreachable("bad method descriptor kind"); } void buildMethod(ConstantArrayBuilder &descriptors, AbstractFunctionDecl *method) { auto accessor = dyn_cast(method); if (!accessor) return emitObjCMethodDescriptor(IGM, descriptors, method); switch (accessor->getAccessorKind()) { case AccessorKind::IsGetter: return emitObjCGetterDescriptor(IGM, descriptors, accessor->getStorage()); case AccessorKind::IsSetter: return emitObjCSetterDescriptor(IGM, descriptors, accessor->getStorage()); case AccessorKind::IsWillSet: case AccessorKind::IsDidSet: case AccessorKind::IsMaterializeForSet: case AccessorKind::IsAddressor: case AccessorKind::IsMutableAddressor: llvm_unreachable("shouldn't be trying to build this accessor"); } llvm_unreachable("bad accessor kind"); } private: StringRef chooseNamePrefix(StringRef forClass, StringRef forCategory, StringRef forProtocol) { if (isBuildingCategory()) return forCategory; if (isBuildingClass()) return forClass; if (isBuildingProtocol()) return forProtocol; llvm_unreachable("not a class, category, or protocol?!"); } llvm::Constant *buildClassMethodList() { return buildMethodList(ClassMethods, chooseNamePrefix("_CLASS_METHODS_", "_CATEGORY_CLASS_METHODS_", "_PROTOCOL_CLASS_METHODS_")); } llvm::Constant *buildInstanceMethodList() { return buildMethodList(InstanceMethods, chooseNamePrefix("_INSTANCE_METHODS_", "_CATEGORY_INSTANCE_METHODS_", "_PROTOCOL_INSTANCE_METHODS_")); } llvm::Constant *buildOptClassMethodList() { return buildMethodList(OptClassMethods, "_PROTOCOL_CLASS_METHODS_OPT_"); } llvm::Constant *buildOptInstanceMethodList() { return buildMethodList(OptInstanceMethods, "_PROTOCOL_INSTANCE_METHODS_OPT_"); } llvm::Constant *buildOptExtendedMethodTypes() { if (!isBuildingProtocol()) return null(); ConstantInitBuilder builder(IGM); auto array = builder.beginArray(); buildExtMethodTypes(array, InstanceMethods); buildExtMethodTypes(array, ClassMethods); buildExtMethodTypes(array, OptInstanceMethods); buildExtMethodTypes(array, OptClassMethods); if (array.empty()) { array.abandon(); return null(); } return buildGlobalVariable(array, "_PROTOCOL_METHOD_TYPES_"); } void buildExtMethodTypes(ConstantArrayBuilder &array, ArrayRef methods) { for (auto descriptor : methods) { assert(descriptor.getKind() == MethodDescriptor::Kind::Method && "cannot emit descriptor for non-method"); auto method = descriptor.getMethod(); array.add(getMethodTypeExtendedEncoding(IGM, method)); } } /// struct method_list_t { /// uint32_t entsize; // runtime uses low bits for its own purposes /// uint32_t count; /// method_t list[count]; /// }; /// /// This method does not return a value of a predictable type. llvm::Constant *buildMethodList(ArrayRef methods, StringRef name) { return buildOptionalList(methods, 3 * IGM.getPointerSize(), name, [&](ConstantArrayBuilder &descriptors, MethodDescriptor descriptor) { buildMethod(descriptors, descriptor); }); } /*** Protocols *********************************************************/ /// typedef uintptr_t protocol_ref_t; // protocol_t*, but unremapped llvm::Constant *buildProtocolRef(ProtocolDecl *protocol) { assert(protocol->isObjC()); return IGM.getAddrOfObjCProtocolRecord(protocol, NotForDefinition); } /// struct protocol_list_t { /// uintptr_t count; /// protocol_ref_t[count]; /// }; /// /// This method does not return a value of a predictable type. llvm::Constant *buildProtocolList() { return buildOptionalList(Protocols, Size(0), chooseNamePrefix("_PROTOCOLS_", "_CATEGORY_PROTOCOLS_", "_PROTOCOL_PROTOCOLS_"), [&](ConstantArrayBuilder &descriptors, ProtocolDecl *protocol) { buildProtocol(descriptors, protocol); }); } void buildProtocol(ConstantArrayBuilder &array, ProtocolDecl *protocol) { array.add(buildProtocolRef(protocol)); } /*** Ivars *************************************************************/ public: /// Variables might be stored or computed. void visitVarDecl(VarDecl *var) { if (var->hasStorage() && !var->isStatic()) visitStoredVar(var); else visitProperty(var); } private: /// Ivars need to be collected in the ivars list, and they also /// affect flags. void visitStoredVar(VarDecl *var) { // FIXME: how to handle ivar extensions in categories? if (!Layout) return; Ivars.push_back(var); // Build property accessors for the ivar if necessary. visitProperty(var); } /// struct ivar_t { /// uintptr_t *offset; /// const char *name; /// const char *type; /// uint32_t alignment; // actually the log2 of the alignment /// uint32_t size; /// }; void buildIvar(ConstantArrayBuilder &ivars, VarDecl *ivar) { auto fields = ivars.beginStruct(); // For now, we never try to emit specialized versions of the // metadata statically, so compute the field layout using the // originally-declared type. SILType fieldType = IGM.getLoweredType(IGM.getSILTypes().getAbstractionPattern(ivar), ivar->getDeclContext() ->mapTypeIntoContext(ivar->getInterfaceType()) ->getCanonicalType()); assert(Layout && "can't build ivar for category"); // FIXME: this is not always the right thing to do! //auto &elt = Layout->getElement(NextFieldIndex++); auto &ivarTI = IGM.getTypeInfo(fieldType); llvm::Constant *offsetPtr; switch (FieldLayout->AllFieldAccesses[NextFieldIndex++]) { case FieldAccess::ConstantDirect: case FieldAccess::NonConstantDirect: { // If the field offset is fixed relative to the start of the superclass, // reference the global from the ivar metadata so that the Objective-C // runtime will slide it down. auto offsetAddr = IGM.getAddrOfFieldOffset(ivar, NotForDefinition); offsetPtr = cast(offsetAddr.getAddress()); break; } case FieldAccess::ConstantIndirect: // Otherwise, swift_initClassMetadata() will point the Objective-C // runtime into the field offset vector of the instantiated metadata. offsetPtr = llvm::ConstantPointerNull::get(IGM.IntPtrTy->getPointerTo()); break; } fields.add(offsetPtr); // TODO: clang puts this in __TEXT,__objc_methname,cstring_literals fields.add(IGM.getAddrOfGlobalString(ivar->getName().str())); // TODO: clang puts this in __TEXT,__objc_methtype,cstring_literals fields.add(IGM.getAddrOfGlobalString("")); Size size; Alignment alignment; if (auto fixedTI = dyn_cast(&ivarTI)) { size = fixedTI->getFixedSize(); alignment = fixedTI->getFixedAlignment(); } else { // FIXME: set something up to fill these in at runtime! size = Size(0); alignment = Alignment(1); } // If the size is larger than we can represent in 32-bits, // complain about the unimplementable ivar. if (uint32_t(size.getValue()) != size.getValue()) { IGM.error(ivar->getLoc(), "ivar size (" + Twine(size.getValue()) + " bytes) overflows Objective-C ivar layout"); size = Size(0); } fields.addInt32(alignment.log2()); fields.addInt32(size.getValue()); fields.finishAndAddTo(ivars); } /// struct ivar_list_t { /// uint32_t entsize; /// uint32_t count; /// ivar_t list[count]; /// }; /// /// This method does not return a value of a predictable type. llvm::Constant *buildIvarList() { Size eltSize = 3 * IGM.getPointerSize() + Size(8); return buildOptionalList(Ivars, eltSize, "_IVARS_", [&](ConstantArrayBuilder &descriptors, VarDecl *ivar) { buildIvar(descriptors, ivar); }); } /*** Properties ********************************************************/ /// Properties need to be collected in the properties list. void visitProperty(VarDecl *var) { if (requiresObjCPropertyDescriptor(IGM, var)) { if (var->isStatic()) { ClassProperties.push_back(var); } else { InstanceProperties.push_back(var); } // Don't emit descriptors for properties without accessors. auto getter = var->getGetter(); if (!getter) return; // Don't emit getter/setter descriptors for @NSManaged properties. if (var->getAttrs().hasAttribute()) return; auto &methods = getMethodList(var); methods.push_back(getter); if (auto setter = var->getSetter()) methods.push_back(setter); } } /// Build the property attribute string for a property decl. void buildPropertyAttributes(VarDecl *prop, SmallVectorImpl &out) { llvm::raw_svector_ostream outs(out); auto propTy = prop->getInterfaceType()->getReferenceStorageReferent(); // Emit the type encoding for the property. outs << 'T'; std::string typeEnc; getObjCEncodingForPropertyType(IGM, prop, typeEnc); outs << typeEnc; // Emit other attributes. // All Swift properties are (nonatomic). outs << ",N"; // @NSManaged properties are @dynamic. if (prop->getAttrs().hasAttribute()) outs << ",D"; auto isObject = prop->getDeclContext()->mapTypeIntoContext(propTy) ->hasRetainablePointerRepresentation(); auto hasObjectEncoding = typeEnc[0] == '@'; // Determine the assignment semantics. // Get-only properties are (readonly). if (!prop->isSettable(prop->getDeclContext())) outs << ",R"; // Weak and Unowned properties are (weak). else if (prop->getAttrs().hasAttribute()) outs << ",W"; // If the property is @NSCopying, or is bridged to a value class, the // property is (copy). else if (prop->getAttrs().hasAttribute() || (hasObjectEncoding && !isObject)) outs << ",C"; // If it's of a managed object type, it is (retain). else if (isObject) outs << ",&"; // Otherwise, the property is of a value type, so it is // the default (assign). else (void)0; // If the property has storage, emit the ivar name last. if (prop->hasStorage()) outs << ",V" << prop->getName(); } /// struct property_t { /// const char *name; /// const char *attributes; /// }; void buildProperty(ConstantArrayBuilder &properties, VarDecl *prop) { llvm::SmallString<16> propertyAttributes; buildPropertyAttributes(prop, propertyAttributes); auto fields = properties.beginStruct(); fields.add(IGM.getAddrOfGlobalString(prop->getObjCPropertyName().str())); fields.add(IGM.getAddrOfGlobalString(propertyAttributes)); fields.finishAndAddTo(properties); } /// struct property_list_t { /// uint32_t entsize; /// uint32_t count; /// property_t list[count]; /// }; /// /// This method does not return a value of a predictable type. llvm::Constant *buildPropertyList(ForMetaClass_t classOrMeta) { if (classOrMeta == ForClass) { return buildPropertyList(InstanceProperties, chooseNamePrefix("_PROPERTIES_", "_CATEGORY_PROPERTIES_", "_PROTOCOL_PROPERTIES_")); } // Older OSs' libobjcs can't handle class property data. if ((IGM.Triple.isMacOSX() && IGM.Triple.isMacOSXVersionLT(10, 11)) || (IGM.Triple.isiOS() && IGM.Triple.isOSVersionLT(9))) { return null(); } return buildPropertyList(ClassProperties, chooseNamePrefix("_CLASS_PROPERTIES_", "_CATEGORY_CLASS_PROPERTIES_", "_PROTOCOL_CLASS_PROPERTIES_")); } llvm::Constant *buildPropertyList(ArrayRef properties, StringRef namePrefix) { Size eltSize = 2 * IGM.getPointerSize(); return buildOptionalList(properties, eltSize, namePrefix, [&](ConstantArrayBuilder &descriptors, VarDecl *property) { buildProperty(descriptors, property); }); } /*** General ***********************************************************/ /// Build a list structure from the given array of objects. /// If the array is empty, use null. The assumption is that every /// initializer has the same size. /// /// \param optionalEltSize - if non-zero, a size which needs /// to be placed in the list header template llvm::Constant *buildOptionalList(const C &objects, Size optionalEltSize, StringRef nameBase, Fn &&buildElement) { if (objects.empty()) return null(); ConstantInitBuilder builder(IGM); auto fields = builder.beginStruct(); llvm::IntegerType *countType; // In all of the foo_list_t structs, either: // - there's a 32-bit entry size and a 32-bit count or // - there's no entry size and a uintptr_t count. if (!optionalEltSize.isZero()) { fields.addInt32(optionalEltSize.getValue()); countType = IGM.Int32Ty; } else { countType = IGM.IntPtrTy; } auto countPosition = fields.addPlaceholder(); auto array = fields.beginArray(); for (auto &element : objects) { buildElement(array, element); } // If we didn't actually make anything, declare that we're done. if (array.empty()) { array.abandon(); fields.abandon(); return null(); } // Otherwise, remember the size of the array and fill the // placeholder with it. auto count = array.size(); array.finishAndAddTo(fields); fields.fillPlaceholderWithInt(countPosition, countType, count); return buildGlobalVariable(fields, nameBase); } /// Get the name of the class or protocol to mangle into the ObjC symbol /// name. StringRef getEntityName(llvm::SmallVectorImpl &buffer) const { if (auto theClass = TheEntity.dyn_cast()) { return theClass->getObjCRuntimeName(buffer); } if (auto theProtocol = TheEntity.dyn_cast()) { return theProtocol->getObjCRuntimeName(buffer); } llvm_unreachable("not a class or protocol?!"); } /// Build a private global variable as a structure containing the /// given fields. template llvm::Constant *buildGlobalVariable(B &fields, StringRef nameBase) { llvm::SmallString<64> nameBuffer; auto var = fields.finishAndCreateGlobal(Twine(nameBase) + getEntityName(nameBuffer) + (TheExtension ? Twine("_$_") + CategoryName.str() : Twine()), IGM.getPointerAlignment(), /*constant*/ true, llvm::GlobalVariable::PrivateLinkage); switch (IGM.TargetInfo.OutputObjectFormat) { case llvm::Triple::MachO: var->setSection("__DATA, __objc_const"); break; case llvm::Triple::COFF: var->setSection(".data"); break; case llvm::Triple::ELF: var->setSection(".data"); break; default: llvm_unreachable("Don't know how to emit private global constants for " "the selected object format."); } return var; } public: /// Member types don't get any representation. /// Maybe this should change for reflection purposes? void visitTypeDecl(TypeDecl *type) {} /// Pattern-bindings don't require anything special as long as /// these initializations are performed in the constructor, not /// .cxx_construct. void visitPatternBindingDecl(PatternBindingDecl *binding) {} /// Subscripts should probably be collected in extended metadata. void visitSubscriptDecl(SubscriptDecl *subscript) { if (!requiresObjCSubscriptDescriptor(IGM, subscript)) return; auto getter = subscript->getGetter(); if (!getter) return; auto &methods = getMethodList(subscript); methods.push_back(getter); if (auto setter = subscript->getSetter()) methods.push_back(setter); } }; } // end anonymous namespace /// Emit the private data (RO-data) associated with a class. llvm::Constant *irgen::emitClassPrivateData(IRGenModule &IGM, ClassDecl *cls) { assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode"); SILType selfType = getSelfType(cls); auto &classTI = IGM.getTypeInfo(selfType).as(); auto &layout = classTI.getLayout(IGM, selfType); auto &fieldLayout = classTI.getClassLayout(IGM, selfType); ClassDataBuilder builder(IGM, cls, layout, fieldLayout); // First, build the metaclass object. builder.buildMetaclassStub(); // Then build the class RO-data. return builder.emitROData(ForClass); } std::pair irgen::emitClassPrivateDataFields(IRGenModule &IGM, ConstantStructBuilder &init, ClassDecl *cls) { assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode"); SILType selfType = getSelfType(cls); auto &classTI = IGM.getTypeInfo(selfType).as(); auto &layout = classTI.getLayout(IGM, selfType); auto &fieldLayout = classTI.getClassLayout(IGM, selfType); ClassDataBuilder builder(IGM, cls, layout, fieldLayout); Size startOfClassRO = init.getNextOffsetFromGlobal(); assert(startOfClassRO.isMultipleOf(IGM.getPointerSize())); { auto classRO = init.beginStruct(); builder.emitRODataFields(classRO, ForClass); classRO.finishAndAddTo(init); } Size startOfMetaclassRO = init.getNextOffsetFromGlobal(); assert(startOfMetaclassRO.isMultipleOf(IGM.getPointerSize())); { auto classRO = init.beginStruct(); builder.emitRODataFields(classRO, ForMetaClass); classRO.finishAndAddTo(init); } return std::make_pair(startOfClassRO, startOfMetaclassRO); } /// Emit the metadata for an ObjC category. llvm::Constant *irgen::emitCategoryData(IRGenModule &IGM, ExtensionDecl *ext) { assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode"); ClassDecl *cls = ext->getAsClassOrClassExtensionContext(); assert(cls && "generating category metadata for a non-class extension"); ClassDataBuilder builder(IGM, cls, ext); return builder.emitCategory(); } /// Emit the metadata for an ObjC protocol. llvm::Constant *irgen::emitObjCProtocolData(IRGenModule &IGM, ProtocolDecl *proto) { assert(proto->isObjC() && "not an objc protocol"); ClassDataBuilder builder(IGM, proto); return builder.emitProtocol(); } const TypeInfo * TypeConverter::convertClassType(CanType type, ClassDecl *D) { llvm::StructType *ST = IGM.createNominalType(type); llvm::PointerType *irType = ST->getPointerTo(); ReferenceCounting refcount = ::getReferenceCountingForType(IGM, type); SpareBitVector spareBits; // Classes known to be implemented in Swift can be assumed not to have tagged // pointer representations, so we can use spare bits for enum layout with // them. We can't make that assumption about imported ObjC types. if (D->hasClangNode() && IGM.TargetInfo.hasObjCTaggedPointers()) spareBits.appendClearBits(IGM.getPointerSize().getValueInBits()); else spareBits = IGM.getHeapObjectSpareBits(); return new ClassTypeInfo(irType, IGM.getPointerSize(), std::move(spareBits), IGM.getPointerAlignment(), D, refcount); } /// Lazily declare a fake-looking class to represent an ObjC runtime base class. ClassDecl *IRGenModule::getObjCRuntimeBaseClass(Identifier name, Identifier objcName) { auto found = SwiftRootClasses.find(name); if (found != SwiftRootClasses.end()) return found->second; // Make a really fake-looking class. auto SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(), MutableArrayRef(), /*generics*/ nullptr, Context.TheBuiltinModule); SwiftRootClass->computeType(); SwiftRootClass->setIsObjC(true); SwiftRootClass->getAttrs().add(ObjCAttr::createNullary(Context, objcName, /*isNameImplicit=*/true)); SwiftRootClass->setImplicit(); SwiftRootClass->setAccess(AccessLevel::Open); SwiftRootClasses.insert({name, SwiftRootClass}); return SwiftRootClass; } /// Lazily declare the ObjC runtime base class for a Swift root class. ClassDecl * IRGenModule::getObjCRuntimeBaseForSwiftRootClass(ClassDecl *theClass) { assert(!theClass->hasSuperclass() && "must pass a root class"); Identifier name; // If the class declares its own ObjC runtime base, use it. if (auto baseAttr = theClass->getAttrs() .getAttribute()) { name = baseAttr->BaseClassName; } else { // Otherwise, use the standard SwiftObject class. name = Context.Id_SwiftObject; } return getObjCRuntimeBaseClass(name, name); } ClassDecl *irgen::getRootClassForMetaclass(IRGenModule &IGM, ClassDecl *C) { while (auto superclass = C->getSuperclass()) C = superclass->getClassOrBoundGenericClass(); // If the formal root class is imported from Objective-C, then // we should use that. For a class that's really implemented in // Objective-C, this is obviously right. For a class that's // really implemented in Swift, but that we're importing via an // Objective-C interface, this would be wrong --- except such a // class can never be a formal root class, because a Swift class // without a formal superclass will actually be parented by // SwiftObject (or maybe eventually something else like it), // which will be visible in the Objective-C type system. if (C->hasClangNode()) return C; // FIXME: If the root class specifies its own runtime ObjC base class, // assume that that base class ultimately inherits NSObject. if (C->getAttrs().hasAttribute()) return IGM.getObjCRuntimeBaseClass( IGM.Context.getSwiftId(KnownFoundationEntity::NSObject), IGM.Context.getIdentifier("NSObject")); return IGM.getObjCRuntimeBaseClass(IGM.Context.Id_SwiftObject, IGM.Context.Id_SwiftObject); } /// If the superclass came from another module, we may have dropped /// stored properties due to the Swift language version availability of /// their types. In these cases we can't precisely lay out the ivars in /// the class object at compile time so we need to do runtime layout. bool irgen::classHasIncompleteLayout(IRGenModule &IGM, ClassDecl *theClass) { do { if (theClass->getParentModule() != IGM.getSwiftModule()) { for (auto field : theClass->getStoredPropertiesAndMissingMemberPlaceholders()){ if (isa(field)) { return true; } } return false; } } while ((theClass = theClass->getSuperclassDecl())); return false; } bool irgen::doesClassMetadataRequireDynamicInitialization(IRGenModule &IGM, ClassDecl *theClass) { // Classes imported from Objective-C never requires dynamic initialization. if (theClass->hasClangNode()) return false; SILType selfType = getSelfType(theClass); auto &selfTI = IGM.getTypeInfo(selfType).as(); auto &layout = selfTI.getClassLayout(IGM, selfType); return layout.MetadataRequiresDynamicInitialization; } bool irgen::hasKnownSwiftMetadata(IRGenModule &IGM, CanType type) { // This needs to be kept up-to-date with getIsaEncodingForType. if (ClassDecl *theClass = type.getClassOrBoundGenericClass()) { return hasKnownSwiftMetadata(IGM, theClass); } if (auto archetype = dyn_cast(type)) { if (auto superclass = archetype->getSuperclass()) { return hasKnownSwiftMetadata(IGM, superclass->getCanonicalType()); } } // Class existentials, etc. return false; } /// Is the given class known to have Swift-compatible metadata? bool irgen::hasKnownSwiftMetadata(IRGenModule &IGM, ClassDecl *theClass) { // For now, the fact that a declaration was not implemented in Swift // is enough to conclusively force us into a slower path. // Eventually we might have an attribute here or something based on // the deployment target. return theClass->hasKnownSwiftImplementation(); } /// Given a reference to class metadata of the given type, /// load the fragile instance size and alignment of the class. std::pair irgen::emitClassFragileInstanceSizeAndAlignMask(IRGenFunction &IGF, ClassDecl *theClass, llvm::Value *metadata) { // FIXME: The below checks should capture this property already, but // resilient class metadata layout is not fully implemented yet. auto superClass = theClass; do { if (superClass->getParentModule() != IGF.IGM.getSwiftModule()) { return emitClassResilientInstanceSizeAndAlignMask(IGF, theClass, metadata); } } while ((superClass = superClass->getSuperclassDecl())); // If the class has fragile fixed layout, return the constant size and // alignment. if (llvm::Constant *size = tryEmitClassConstantFragileInstanceSize(IGF.IGM, theClass)) { llvm::Constant *alignMask = tryEmitClassConstantFragileInstanceAlignMask(IGF.IGM, theClass); assert(alignMask && "static size without static align"); return {size, alignMask}; } // Otherwise, load it from the metadata. return emitClassResilientInstanceSizeAndAlignMask(IGF, theClass, metadata); } std::pair irgen::emitClassResilientInstanceSizeAndAlignMask(IRGenFunction &IGF, ClassDecl *theClass, llvm::Value *metadata) { auto &layout = IGF.IGM.getClassMetadataLayout(theClass); Address metadataAsBytes(IGF.Builder.CreateBitCast(metadata, IGF.IGM.Int8PtrTy), IGF.IGM.getPointerAlignment()); Address slot = IGF.Builder.CreateConstByteArrayGEP( metadataAsBytes, layout.getInstanceSizeOffset()); slot = IGF.Builder.CreateBitCast(slot, IGF.IGM.Int32Ty->getPointerTo()); llvm::Value *size = IGF.Builder.CreateLoad(slot); if (IGF.IGM.SizeTy != IGF.IGM.Int32Ty) size = IGF.Builder.CreateZExt(size, IGF.IGM.SizeTy); slot = IGF.Builder.CreateConstByteArrayGEP( metadataAsBytes, layout.getInstanceAlignMaskOffset()); slot = IGF.Builder.CreateBitCast(slot, IGF.IGM.Int16Ty->getPointerTo()); llvm::Value *alignMask = IGF.Builder.CreateLoad(slot); alignMask = IGF.Builder.CreateZExt(alignMask, IGF.IGM.SizeTy); return {size, alignMask}; } FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, llvm::Value *metadata, SILDeclRef method, CanSILFunctionType methodType) { Signature signature = IGF.IGM.getSignature(methodType); auto classDecl = cast(method.getDecl()->getDeclContext()); // Find the vtable entry we're interested in. auto methodInfo = IGF.IGM.getClassMetadataLayout(classDecl).getMethodInfo(IGF, method); auto offset = methodInfo.getOffset(); auto slot = IGF.emitAddressAtOffset(metadata, offset, signature.getType()->getPointerTo(), IGF.IGM.getPointerAlignment()); auto fnPtr = IGF.emitInvariantLoad(slot); return FunctionPointer(fnPtr, signature); } FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF, llvm::Value *base, SILType baseType, SILDeclRef method, CanSILFunctionType methodType, bool useSuperVTable) { AbstractFunctionDecl *methodDecl = cast(method.getDecl()); // Find the vtable entry for this method. SILDeclRef overridden = method.getOverriddenVTableEntry(); // Find the metadata. llvm::Value *metadata; if (useSuperVTable) { auto instanceTy = baseType; if (auto metaTy = dyn_cast(baseType.getASTType())) instanceTy = SILType::getPrimitiveObjectType(metaTy.getInstanceType()); if (IGF.IGM.isResilient(instanceTy.getClassOrBoundGenericClass(), ResilienceExpansion::Maximal)) { // The derived type that is making the super call is resilient, // for example we may be in an extension of a class outside of our // resilience domain. So, we need to load the superclass metadata // dynamically. metadata = emitClassHeapMetadataRef(IGF, instanceTy.getASTType(), MetadataValueType::TypeMetadata, MetadataState::Complete); auto superField = emitAddressOfSuperclassRefInClassMetadata(IGF, metadata); metadata = IGF.Builder.CreateLoad(superField); } else { // Otherwise, we can directly load the statically known superclass's // metadata. auto superTy = instanceTy.getSuperclass(); metadata = emitClassHeapMetadataRef(IGF, superTy.getASTType(), MetadataValueType::TypeMetadata, MetadataState::Complete); } } else { if ((isa(methodDecl) && cast(methodDecl)->isStatic()) || (isa(methodDecl) && method.kind == SILDeclRef::Kind::Allocator)) { metadata = base; } else { metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType, /*suppress cast*/ true); } } return emitVirtualMethodValue(IGF, metadata, overridden, methodType); }