//===--- GenEnum.h - Swift IR Generation For 'enum' Types -------*- 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 // //===----------------------------------------------------------------------===// #ifndef SWIFT_IRGEN_GENENUM_H #define SWIFT_IRGEN_GENENUM_H #include "TypeInfo.h" namespace llvm { class BasicBlock; class ConstantInt; class StructType; class Type; class Value; } namespace clang { namespace CodeGen { namespace swiftcall { class SwiftAggLowering; } } } namespace swift { class EnumElementDecl; namespace irgen { class EnumPayload; class EnumPayloadSchema; class IRGenFunction; class TypeConverter; using clang::CodeGen::swiftcall::SwiftAggLowering; /// \brief Emit the dispatch branch(es) for an address-only enum. void emitSwitchAddressOnlyEnumDispatch(IRGenFunction &IGF, SILType enumTy, Address enumAddr, ArrayRef> dests, llvm::BasicBlock *defaultDest); /// \brief Injects a case and its associated data, if any, into a loadable enum /// value. void emitInjectLoadableEnum(IRGenFunction &IGF, SILType enumTy, EnumElementDecl *theCase, Explosion &data, Explosion &out); /// \brief Extracts the associated data for an enum case. This is an unchecked /// operation; the input enum value must be of the given case. void emitProjectLoadableEnum(IRGenFunction &IGF, SILType enumTy, Explosion &inData, EnumElementDecl *theCase, Explosion &out); /// \brief Projects the address of the associated data for a case inside a /// enum, to which a new data value can be stored. Address emitProjectEnumAddressForStore(IRGenFunction &IGF, SILType enumTy, Address enumAddr, EnumElementDecl *theCase); /// \brief Projects the address of the associated data for a case inside a /// enum, clearing any tag bits interleaved into the data area, so that the /// value inside can be loaded. Does not check that the enum has a value of the /// given case. Address emitDestructiveProjectEnumAddressForLoad(IRGenFunction &IGF, SILType enumTy, Address enumAddr, EnumElementDecl *theCase); /// \brief Stores the tag bits for an enum case to the given address, overlaying /// the data (if any) stored there. void emitStoreEnumTagToAddress(IRGenFunction &IGF, SILType enumTy, Address enumAddr, EnumElementDecl *theCase); /// Interleave the occupiedValue and spareValue bits, taking a bit from one /// or the other at each position based on the spareBits mask. APInt interleaveSpareBits(IRGenModule &IGM, const SpareBitVector &spareBits, unsigned bits, unsigned spareValue, unsigned occupiedValue); /// A version of the above where the tag value is dynamic. EnumPayload interleaveSpareBits(IRGenFunction &IGF, const EnumPayloadSchema &schema, const SpareBitVector &spareBitVector, llvm::Value *value); /// Gather spare bits into the low bits of a smaller integer value. llvm::Value *emitGatherSpareBits(IRGenFunction &IGF, const SpareBitVector &spareBitMask, llvm::Value *spareBits, unsigned resultLowBit, unsigned resultBitWidth); /// Scatter spare bits from the low bits of a smaller integer value. llvm::Value *emitScatterSpareBits(IRGenFunction &IGF, const SpareBitVector &spareBitMask, llvm::Value *packedBits, unsigned packedLowBit); /// An implementation strategy for an enum, which handles how the enum is /// laid out and how to perform TypeInfo operations on values of the enum. class EnumImplStrategy { public: struct Element { EnumElementDecl *decl; const TypeInfo *ti; const TypeInfo *origTI; }; enum TypeInfoKind { Opaque, ///< The enum has a NonFixedTypeInfo. Fixed, ///< The enum has a FixedTypeInfo. Loadable, ///< The enum has a LoadableTypeInfo. }; protected: std::vector ElementsWithPayload; std::vector ElementsWithNoPayload; IRGenModule &IGM; const TypeInfo *TI = nullptr; TypeInfoKind TIK; IsFixedSize_t AlwaysFixedSize; unsigned NumElements; EnumImplStrategy(IRGenModule &IGM, TypeInfoKind tik, IsFixedSize_t alwaysFixedSize, unsigned NumElements, std::vector &&ElementsWithPayload, std::vector &&ElementsWithNoPayload) : ElementsWithPayload(std::move(ElementsWithPayload)), ElementsWithNoPayload(std::move(ElementsWithNoPayload)), IGM(IGM), TIK(tik), AlwaysFixedSize(alwaysFixedSize), NumElements(NumElements) {} /// Save the TypeInfo created for the enum. TypeInfo *registerEnumTypeInfo(TypeInfo *mutableTI) { TI = mutableTI; return mutableTI; } /// Constructs a TypeInfo for an enum of the best possible kind for its /// layout, FixedEnumTypeInfo or LoadableEnumTypeInfo. TypeInfo *getFixedEnumTypeInfo(llvm::StructType *T, Size S, SpareBitVector SB, Alignment A, IsPOD_t isPOD, IsBitwiseTakable_t isBT); public: virtual ~EnumImplStrategy() { } /// Construct a layout strategy appropriate to the enum type. static std::unique_ptr get(TypeConverter &TC, SILType Type, EnumDecl *theEnum); /// Given an incomplete StructType for the enum, completes layout of the /// storage type, calculates its size and alignment, and produces the /// TypeInfo for the enum. virtual TypeInfo *completeEnumTypeLayout(TypeConverter &TC, SILType Type, EnumDecl *theEnum, llvm::StructType *enumTy) = 0; const TypeInfo &getTypeInfo() const { assert(TI); return *TI; } llvm::StructType *getStorageType() const { return cast(getTypeInfo().getStorageType()); } IsPOD_t isPOD(ResilienceExpansion expansion) const { return getTypeInfo().isPOD(expansion); } /// \group Query enum layout /// /// These APIs assume a fixed layout; they will not work on generic /// enum types that have not been fully instantiated. /// Return the list of cases determined to have a payload. /// The payloads are discriminated using the bits in the mask returned by /// getTagBits(). ArrayRef getElementsWithPayload() const { return ElementsWithPayload; } /// Return the list of cases determined to have no payload. /// Each no-payload case is represented by a specific bit pattern which can /// be queried using getBitPatternForNoPayloadElement. ArrayRef getElementsWithNoPayload() const { return ElementsWithNoPayload; } /// Return a tag index in the range [0..NumElements]. unsigned getTagIndex(EnumElementDecl *Case) const; /// Return a tag index in the range /// [-ElementsWithPayload..ElementsWithNoPayload-1]. int getResilientTagIndex(EnumElementDecl *Case) const; /// Map the given element to the appropriate index in the /// discriminator type. /// Returns -1 if this is not supported by the enum implementation. virtual int64_t getDiscriminatorIndex(EnumElementDecl *target) const { return -1; } /// Emit field names for enum reflection. virtual llvm::Constant *emitCaseNames() const; /// \brief Return the bits used for discriminators for payload cases. /// /// These bits are populated in increasing value according to the order of /// the getElementsWithPayload() array, starting from zero for the first /// element with payload. virtual ClusteredBitVector getTagBitsForPayloads() const = 0; /// Return the bit pattern used for the given no-payload case. virtual ClusteredBitVector getBitPatternForNoPayloadElement(EnumElementDecl *theCase) const = 0; /// Return the bit mask used to test for no-payload cases. virtual ClusteredBitVector getBitMaskForNoPayloadElements() const = 0; /// \group Indirect enum operations /// Return the enum case tag for the given value. Payload cases come first, /// followed by non-payload cases. Used for the getEnumTag value witness. virtual llvm::Value *emitGetEnumTag(IRGenFunction &IGF, SILType T, Address enumAddr) const = 0; /// Project the address of the data for a case. Does not check or modify /// the referenced enum value. /// Corresponds to the SIL 'init_enum_data_addr' instruction. Address projectDataForStore(IRGenFunction &IGF, EnumElementDecl *elt, Address enumAddr) const; /// Overlay the tag value for a case onto a data value in memory. /// Corresponds to the SIL 'inject_enum_addr' instruction. virtual void storeTag(IRGenFunction &IGF, SILType T, Address enumAddr, EnumElementDecl *elt) const = 0; /// Overlay a dynamic case tag onto a data value in memory. Used when /// generating the destructiveInjectEnumTag value witness. virtual void emitStoreTag(IRGenFunction &IGF, SILType T, Address enumAddr, llvm::Value *tag) const = 0; /// Clears tag bits from within the payload of an enum in memory and /// projects the address of the data for a case. Does not check /// the referenced enum value. /// Performs the block argument binding for a SIL /// 'switch_enum_addr' instruction. /// Also used when generating the destructiveProjectEnumData value /// witness. Address destructiveProjectDataForLoad(IRGenFunction &IGF, SILType T, Address enumAddr, EnumElementDecl *Case) const; /// Clears tag bits from within the payload of an enum in memory and /// projects the address of the data for a case. Does not check /// the referenced enum value. virtual void destructiveProjectDataForLoad(IRGenFunction &IGF, SILType T, Address enumAddr) const = 0; /// Return an i1 value that indicates whether the specified indirect enum /// value holds the specified case. This is a light-weight form of a switch. virtual llvm::Value *emitIndirectCaseTest(IRGenFunction &IGF, SILType T, Address enumAddr, EnumElementDecl *Case) const = 0; /// Emit a branch on the case contained by an enum explosion. /// Performs the branching for a SIL 'switch_enum_addr' /// instruction. virtual void emitIndirectSwitch(IRGenFunction &IGF, SILType T, Address enumAddr, ArrayRef> dests, llvm::BasicBlock *defaultDest) const = 0; /// Emit code to extract the discriminator as an integer value. /// Returns null if this is not supported by the enum implementation. /// /// FIXME: is this the same as emitGetEnumTag()? Seems like it could be, /// except that CCompatibleEnumImplStrategy maps cases to their raw /// values rather than indices. virtual llvm::Value *emitExtractDiscriminator(IRGenFunction &IGF, Explosion &value) const { return nullptr; } /// \group Loadable enum operations /// Emit the construction sequence for an enum case into an explosion. /// Corresponds to the SIL 'enum' instruction. virtual void emitValueInjection(IRGenFunction &IGF, EnumElementDecl *elt, Explosion ¶ms, Explosion &out) const = 0; /// Return an i1 value that indicates whether the specified loadable enum /// value holds the specified case. This is a light-weight form of a switch. virtual llvm::Value *emitValueCaseTest(IRGenFunction &IGF, Explosion &value, EnumElementDecl *Case) const = 0; /// Emit a branch on the case contained by an enum explosion. /// Performs the branching for a SIL 'switch_enum' instruction. virtual void emitValueSwitch(IRGenFunction &IGF, Explosion &value, ArrayRef> dests, llvm::BasicBlock *defaultDest) const = 0; /// Project a case value out of an enum explosion. This does not check that /// the explosion actually contains a value of the given case. /// Performs the block argument binding for a SIL 'switch_enum' /// instruction. virtual void emitValueProject(IRGenFunction &IGF, Explosion &inEnum, EnumElementDecl *theCase, Explosion &out) const = 0; /// \group Delegated TypeInfo operations virtual void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering, Size offset) const = 0; virtual void getSchema(ExplosionSchema &schema) const = 0; virtual void destroy(IRGenFunction &IGF, Address addr, SILType T, bool isOutlined) const = 0; virtual void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, Address dest, SILType T, bool isOutlined) const; virtual void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const = 0; virtual void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const = 0; virtual void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const = 0; virtual void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined) const = 0; virtual void initializeMetadata(IRGenFunction &IGF, llvm::Value *metadata, llvm::Value *vwtable, SILType T) const = 0; virtual bool mayHaveExtraInhabitants(IRGenModule &IGM) const = 0; virtual llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, SILType T) const = 0; virtual void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, Address dest, SILType T) const = 0; /// \group Delegated FixedTypeInfo operations virtual unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const = 0; virtual APInt getFixedExtraInhabitantValue(IRGenModule &IGM, unsigned bits, unsigned index) const = 0; virtual APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const = 0; /// \group Delegated LoadableTypeInfo operations virtual unsigned getExplosionSize() const = 0; virtual void loadAsCopy(IRGenFunction &IGF, Address addr, Explosion &e) const = 0; virtual void loadAsTake(IRGenFunction &IGF, Address addr, Explosion &e) const = 0; virtual void assign(IRGenFunction &IGF, Explosion &e, Address addr, bool isOutlined) const = 0; virtual void initialize(IRGenFunction &IGF, Explosion &e, Address addr, bool isOutlined) const = 0; virtual void reexplode(IRGenFunction &IGF, Explosion &src, Explosion &dest) const = 0; virtual void copy(IRGenFunction &IGF, Explosion &src, Explosion &dest, Atomicity atomicity) const = 0; virtual void consume(IRGenFunction &IGF, Explosion &src, Atomicity atomicity) const = 0; virtual void fixLifetime(IRGenFunction &IGF, Explosion &src) const = 0; virtual void packIntoEnumPayload(IRGenFunction &IGF, EnumPayload &payload, Explosion &in, unsigned offset) const = 0; virtual void unpackFromEnumPayload(IRGenFunction &IGF, const EnumPayload &payload, Explosion &dest, unsigned offset) const = 0; virtual bool needsPayloadSizeInMetadata() const = 0; virtual unsigned getPayloadSizeForMetadata() const; virtual llvm::Value *loadRefcountedPtr(IRGenFunction &IGF, SourceLoc loc, Address addr) const; virtual void collectArchetypeMetadata( IRGenFunction &IGF, llvm::MapVector &typeToMetadataVec, SILType T) const = 0; private: EnumImplStrategy(const EnumImplStrategy &) = delete; EnumImplStrategy &operator=(const EnumImplStrategy &) = delete; }; /// Get the EnumImplStrategy for an enum type. const EnumImplStrategy &getEnumImplStrategy(IRGenModule &IGM, CanType ty); const EnumImplStrategy &getEnumImplStrategy(IRGenModule &IGM, SILType ty); } } #endif