//===--- MetadataLayout.h - Type metadata layout ----------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // // Information recording the layout of type metadata objects. // //===----------------------------------------------------------------------===// #ifndef SWIFT_IRGEN_METADATALAYOUT_H #define SWIFT_IRGEN_METADATALAYOUT_H #include "IRGen.h" #include "swift/SIL/SILDeclRef.h" namespace swift { class ClassDecl; class EnumDecl; class StructDecl; class VarDecl; namespace irgen { class Address; class IRGenFunction; class IRGenModule; /// The total size and address point of a metadata object. struct MetadataSize { Size FullSize; Size AddressPoint; /// Return the offset from the address point to the end of the /// metadata object. Size getOffsetToEnd() const { return FullSize - AddressPoint; } }; /// A base class for various kinds of metadata layout. class MetadataLayout { public: enum class Kind { Class, Struct, Enum, ForeignClass, }; class StoredOffset { public: enum Kind { /// The high bits are an integer displacement. Static = 0, /// The high bits are an integer displacement relative to a offset stored /// in a class metadata base offset global variable. This is used to /// access members of class metadata where the superclass is resilient to /// us, and therefore has an unknown size. Dynamic, }; private: enum : uint64_t { KindBits = 1, KindMask = (1 << KindBits) - 1, PayloadMask = ~uint64_t(KindMask) }; mutable uintptr_t Data; public: StoredOffset() : Data(0) {} explicit StoredOffset(Size offset, Kind kind) : Data((static_cast(offset.getValue()) << KindBits) | kind) { assert(kind == Kind::Dynamic || !offset.isZero() && "cannot store a zero static offset"); if (kind == Kind::Static) assert(getStaticOffset() == offset && "overflow"); if (kind == Kind::Dynamic) assert(getRelativeOffset() == offset && "overflow"); } bool isValid() const { return Data != 0; } bool isStatic() const { return isValid() && (Data & KindMask) == Static; } bool isDynamic() const { return (Data & KindMask) == Dynamic; } /// If this is a metadata offset into a resilient class, returns the offset /// relative to the size of the superclass metadata. Size getRelativeOffset() const { assert(isDynamic()); return Size(static_cast(Data) >> KindBits); } /// Returns the offset relative to start of metadata. Only used for /// metadata fields whose offset is completely known at compile time. Size getStaticOffset() const { assert(isStatic()); return Size(static_cast(Data) >> KindBits); } }; private: Kind TheKind; protected: MetadataSize TheSize; MetadataLayout(Kind theKind) : TheKind(theKind) {} MetadataLayout(const MetadataLayout &other) = delete; MetadataLayout &operator=(const MetadataLayout &other) = delete; public: /// Destruct and deallocate this layout object. void destroy() const; Kind getKind() const { return TheKind; } MetadataSize getSize() const { return TheSize; } }; /// Base class for nominal type metadata layouts. class NominalMetadataLayout : public MetadataLayout { protected: NominalTypeDecl *Nominal; StoredOffset GenericRequirements; NominalMetadataLayout(Kind kind, NominalTypeDecl *nominal) : MetadataLayout(kind), Nominal(nominal) {} Offset emitOffset(IRGenFunction &IGF, StoredOffset offset) const; public: NominalTypeDecl *getDecl() const { return Nominal; } bool hasGenericRequirements() const { return GenericRequirements.isValid(); } /// Should only be used when emitting the nominal type descriptor. Size getStaticGenericRequirementsOffset() const; Offset getGenericRequirementsOffset(IRGenFunction &IGF) const; static bool classof(const MetadataLayout *layout) { switch (layout->getKind()) { case MetadataLayout::Kind::Class: case MetadataLayout::Kind::Enum: case MetadataLayout::Kind::Struct: return true; case MetadataLayout::Kind::ForeignClass: return false; } llvm_unreachable("unhandled kind"); } }; /// Layout for class type metadata. class ClassMetadataLayout : public NominalMetadataLayout { public: class MethodInfo { public: enum class Kind { Offset, DirectImpl, }; private: Kind TheKind; union { Offset TheOffset; llvm::Function *TheImpl; }; public: MethodInfo(Offset offset) : TheKind(Kind::Offset), TheOffset(offset) {} MethodInfo(llvm::Function *impl) : TheKind(Kind::DirectImpl), TheImpl(impl) {} Kind getKind() const { return TheKind; } Offset getOffset() const { assert(getKind() == Kind::Offset); return TheOffset; } llvm::Function *getDirectImpl() const { assert(getKind() == Kind::DirectImpl); return TheImpl; } }; private: bool HasResilientSuperclass = false; StoredOffset StartOfImmediateMembers; StoredOffset MetadataSize; StoredOffset MetadataAddressPoint; StoredOffset InstanceSize; StoredOffset InstanceAlignMask; struct StoredMethodInfo { MethodInfo::Kind TheKind; union { StoredOffset TheOffset; llvm::Function *TheImpl; }; StoredMethodInfo(StoredOffset offset) : TheKind(MethodInfo::Kind::Offset), TheOffset(offset) {} StoredMethodInfo(llvm::Function *impl) : TheKind(MethodInfo::Kind::DirectImpl), TheImpl(impl) {} }; llvm::DenseMap MethodInfos; /// Field offsets for various fields. llvm::DenseMap FieldOffsets; /// The start of the vtable. StoredOffset VTableOffset; /// The start of the field-offset vector. StoredOffset FieldOffsetVector; /// The number of members to add after superclass metadata. unsigned NumImmediateMembers; const StoredMethodInfo &getStoredMethodInfo(SILDeclRef method) const { auto it = MethodInfos.find(method); assert(it != MethodInfos.end()); return it->second; } const StoredOffset &getStoredFieldOffset(VarDecl *field) const { auto it = FieldOffsets.find(field); assert(it != FieldOffsets.end()); return it->second; } friend class IRGenModule; ClassMetadataLayout(IRGenModule &IGM, ClassDecl *theClass); public: ClassDecl *getDecl() const { return cast(Nominal); } bool hasResilientSuperclass() const { return HasResilientSuperclass; } constexpr static bool areImmediateMembersNegative() { return false; } Size getMetadataSizeOffset() const; Size getMetadataAddressPointOffset() const; Size getInstanceSizeOffset() const; Size getInstanceAlignMaskOffset() const; MethodInfo getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const; const StoredMethodInfo * getStoredMethodInfoIfPresent(SILDeclRef method) const { auto it = MethodInfos.find(method); if (it != MethodInfos.end()) return &it->second; return nullptr; } Offset getFieldOffset(IRGenFunction &IGF, VarDecl *field) const; /// Assuming that the given field offset is at a static offset in /// the metadata, return that static offset. /// /// DEPRECATED: callers should be updated to handle this in a /// more arbitrary fashion. Size getStaticFieldOffset(VarDecl *field) const; /// Should only be used when emitting the nominal type descriptor. Size getRelativeGenericRequirementsOffset() const; Size getStaticFieldOffsetVectorOffset() const; Size getRelativeFieldOffsetVectorOffset() const; Size getStaticVTableOffset() const; Size getRelativeVTableOffset() const; Offset getFieldOffsetVectorOffset(IRGenFunction &IGF) const; /// If the start of the immediate members is statically known, this /// method will return it. Otherwise, it will assert. Size getStartOfImmediateMembers() const { return StartOfImmediateMembers.getStaticOffset(); } /// The number of members to add after superclass metadata. The size of /// this metadata is the superclass size plus the number of immediate /// members in the class itself. unsigned getNumImmediateMembers() const { return NumImmediateMembers; } static bool classof(const MetadataLayout *layout) { return layout->getKind() == Kind::Class; } }; /// Layout for enum type metadata. class EnumMetadataLayout : public NominalMetadataLayout { /// The offset of the payload size field, if there is one. StoredOffset PayloadSizeOffset; StoredOffset TrailingFlagsOffset; // TODO: presumably it would be useful to store *something* here // for resilience. friend class IRGenModule; EnumMetadataLayout(IRGenModule &IGM, EnumDecl *theEnum); public: EnumDecl *getDecl() const { return cast(Nominal); } bool hasPayloadSizeOffset() const { return PayloadSizeOffset.isValid(); } Offset getPayloadSizeOffset() const; Offset getTrailingFlagsOffset() const; static bool classof(const MetadataLayout *layout) { return layout->getKind() == Kind::Enum; } }; /// Layout for struct type metadata. class StructMetadataLayout : public NominalMetadataLayout { llvm::DenseMap FieldOffsets; StoredOffset TrailingFlagsOffset; /// The start of the field-offset vector. StoredOffset FieldOffsetVector; const StoredOffset &getStoredFieldOffset(VarDecl *field) const { auto it = FieldOffsets.find(field); assert(it != FieldOffsets.end()); return it->second; } friend class IRGenModule; StructMetadataLayout(IRGenModule &IGM, StructDecl *theStruct); public: StructDecl *getDecl() const { return cast(Nominal); } Offset getFieldOffset(IRGenFunction &IGF, VarDecl *field) const; /// Assuming that the given field offset is at a static offset in /// the metadata, return that static offset. /// /// DEPRECATED: callers should be updated to handle this in a /// more arbitrary fashion. Size getStaticFieldOffset(VarDecl *field) const; Offset getFieldOffsetVectorOffset() const; Offset getTrailingFlagsOffset() const; static bool classof(const MetadataLayout *layout) { return layout->getKind() == Kind::Struct; } }; /// Layout for foreign class type metadata. class ForeignClassMetadataLayout : public MetadataLayout { ClassDecl *Class; StoredOffset SuperClassOffset; friend class IRGenModule; ForeignClassMetadataLayout(IRGenModule &IGM, ClassDecl *theClass); public: StoredOffset getSuperClassOffset() const { return SuperClassOffset; } static bool classof(const MetadataLayout *layout) { return layout->getKind() == Kind::ForeignClass; } }; /// Emit the address of the field-offset slot in the given class metadata. Address emitAddressOfClassFieldOffset(IRGenFunction &IGF, llvm::Value *metadata, ClassDecl *theClass, VarDecl *field); /// Get the offset to a field offset in the class type metadata. /// /// DEPRECATED: callers should be updated to handle this in a more /// arbitrary fashion. Size getClassFieldOffsetOffset(IRGenModule &IGM, ClassDecl *theClass, VarDecl *field); /// Emit the address of the field-offset vector in the given class or struct /// metadata. Address emitAddressOfFieldOffsetVector(IRGenFunction &IGF, llvm::Value *metadata, NominalTypeDecl *theDecl); /// Given a reference to class type metadata of the given type, /// decide the offset to the given field. This assumes that the /// offset is stored in the metadata, i.e. its offset is potentially /// dependent on generic arguments. The result is a ptrdiff_t. llvm::Value *emitClassFieldOffset(IRGenFunction &IGF, ClassDecl *theClass, VarDecl *field, llvm::Value *metadata); /// Given a class metadata pointer, emit the address of its superclass field. Address emitAddressOfSuperclassRefInClassMetadata(IRGenFunction &IGF, llvm::Value *metadata); Size getStaticTupleElementOffset(IRGenModule &IGM, SILType tupleType, unsigned eltIdx); } // end namespace irgen } // end namespace swift #endif