| //===--- FixedTypeInfo.h - Supplement for fixed-layout 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines FixedTypeInfo, which supplements the TypeInfo |
| // interface for classes with (at least locally) fixed-layout type |
| // implementations. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_IRGEN_FIXEDTYPEINFO_H |
| #define SWIFT_IRGEN_FIXEDTYPEINFO_H |
| |
| #include "Address.h" |
| #include "TypeInfo.h" |
| #include "swift/Basic/ClusteredBitVector.h" |
| #include "swift/SIL/SILType.h" |
| |
| namespace llvm { |
| class ConstantInt; |
| } |
| |
| namespace swift { |
| namespace irgen { |
| |
| /// FixedTypeInfo - An abstract class designed for use when |
| /// implementing a type that has a statically known layout. |
| class FixedTypeInfo : public TypeInfo { |
| private: |
| /// The storage size of this type in bytes. This may be zero even |
| /// for well-formed and complete types, such as a trivial enum or |
| /// tuple. |
| Size StorageSize; |
| |
| /// The spare bit mask for this type. SpareBits[0] is the LSB of the first |
| /// byte. This may be empty if the type has no spare bits. |
| SpareBitVector SpareBits; |
| |
| protected: |
| FixedTypeInfo(llvm::Type *type, Size size, |
| const SpareBitVector &spareBits, |
| Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt, |
| IsFixedSize_t alwaysFixedSize, |
| SpecialTypeInfoKind stik = STIK_Fixed) |
| : TypeInfo(type, align, pod, bt, alwaysFixedSize, stik), |
| StorageSize(size), SpareBits(spareBits) { |
| assert(SpareBits.size() == size.getValueInBits()); |
| assert(isFixedSize()); |
| } |
| |
| FixedTypeInfo(llvm::Type *type, Size size, |
| SpareBitVector &&spareBits, |
| Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt, |
| IsFixedSize_t alwaysFixedSize, |
| SpecialTypeInfoKind stik = STIK_Fixed) |
| : TypeInfo(type, align, pod, bt, alwaysFixedSize, stik), |
| StorageSize(size), SpareBits(std::move(spareBits)) { |
| assert(SpareBits.size() == size.getValueInBits()); |
| assert(isFixedSize()); |
| } |
| |
| public: |
| // This is useful for metaprogramming. |
| static bool isFixed() { return true; } |
| |
| /// Whether this type is known to be empty. |
| bool isKnownEmpty(ResilienceExpansion expansion) const { |
| return (isFixedSize(expansion) && StorageSize.isZero()); |
| } |
| |
| StackAddress allocateStack(IRGenFunction &IGF, SILType T, bool isEntryBlock, |
| const llvm::Twine &name) const override; |
| void deallocateStack(IRGenFunction &IGF, StackAddress addr, SILType T) const override; |
| void destroyStack(IRGenFunction &IGF, StackAddress addr, SILType T, |
| bool isOutlined) const override; |
| |
| // We can give these reasonable default implementations. |
| |
| void initializeWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr, |
| SILType T, bool isOutlined) const override; |
| |
| llvm::Value *getSize(IRGenFunction &IGF, SILType T) const override; |
| llvm::Value *getAlignmentMask(IRGenFunction &IGF, SILType T) const override; |
| llvm::Value *getStride(IRGenFunction &IGF, SILType T) const override; |
| llvm::Value *getIsPOD(IRGenFunction &IGF, SILType T) const override; |
| llvm::Value *isDynamicallyPackedInline(IRGenFunction &IGF, |
| SILType T) const override; |
| |
| llvm::Constant *getStaticSize(IRGenModule &IGM) const override; |
| llvm::Constant *getStaticAlignmentMask(IRGenModule &IGM) const override; |
| llvm::Constant *getStaticStride(IRGenModule &IGM) const override; |
| |
| void completeFixed(Size size, Alignment alignment) { |
| StorageSize = size; |
| setStorageAlignment(alignment); |
| } |
| |
| /// Returns the known, fixed alignment of a stored value of this type. |
| Alignment getFixedAlignment() const { |
| return getBestKnownAlignment(); |
| } |
| |
| /// Returns the known, fixed size required to store a value of this type. |
| Size getFixedSize() const { |
| return StorageSize; |
| } |
| |
| /// Returns the (assumed fixed) stride of the storage for this |
| /// object. The stride is the storage size rounded up to the |
| /// alignment; its practical use is that, in an array, it is the |
| /// offset from the size of one element to the offset of the next. |
| /// The stride is at least one, even for zero-sized types, like the empty |
| /// tuple. |
| Size getFixedStride() const { |
| Size s = StorageSize.roundUpToAlignment(getFixedAlignment()); |
| if (s.isZero()) |
| s = Size(1); |
| return s; |
| } |
| |
| /// Returns the fixed number of "extra inhabitants" (that is, bit |
| /// patterns that don't represent valid values of the type) in the type |
| /// representation. |
| virtual unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const { |
| return getSpareBitExtraInhabitantCount(); |
| } |
| |
| /// Returns the number of extra inhabitants available by exercising spare |
| /// bits. |
| unsigned getSpareBitExtraInhabitantCount() const; |
| |
| /// We can statically determine the presence of extra inhabitants for fixed |
| /// types. |
| bool mayHaveExtraInhabitants(IRGenModule &IGM) const override { |
| return getFixedExtraInhabitantCount(IGM) > 0; |
| } |
| |
| /// Get the bit mask that must be applied before testing an extra inhabitant. |
| virtual APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const { |
| return APInt::getAllOnesValue(getFixedSize().getValueInBits()); |
| } |
| |
| /// Create a constant of the given bit width holding one of the extra |
| /// inhabitants of the type. |
| /// The index must be less than the value returned by |
| /// getFixedExtraInhabitantCount(). |
| virtual APInt getFixedExtraInhabitantValue(IRGenModule &IGM, |
| unsigned bits, |
| unsigned index) const { |
| return getSpareBitFixedExtraInhabitantValue(IGM, bits, index); |
| } |
| |
| /// Create an extra inhabitant constant using the spare bits of the type. |
| APInt getSpareBitFixedExtraInhabitantValue(IRGenModule &IGM, |
| unsigned bits, |
| unsigned index) const; |
| |
| /// Map an extra inhabitant representation in memory to a unique 31-bit |
| /// identifier, and map a valid representation of the type to -1. |
| llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, |
| Address src, SILType T) const override { |
| return getSpareBitExtraInhabitantIndex(IGF, src); |
| } |
| |
| /// Map an extra inhabitant representation derived from spare bits to an |
| /// index. |
| llvm::Value *getSpareBitExtraInhabitantIndex(IRGenFunction &IGF, |
| Address src) const; |
| |
| /// Store the extra inhabitant representation indexed by a 31-bit identifier |
| /// to memory. |
| void storeExtraInhabitant(IRGenFunction &IGF, |
| llvm::Value *index, |
| Address dest, SILType T) const override { |
| storeSpareBitExtraInhabitant(IGF, index, dest); |
| } |
| |
| /// Store the indexed spare-bit-derived extra inhabitant to memory. |
| void storeSpareBitExtraInhabitant(IRGenFunction &IGF, |
| llvm::Value *index, |
| Address dest) const; |
| |
| /// Get the spare bit mask for the type. |
| const SpareBitVector &getSpareBits() const { return SpareBits; } |
| |
| /// True if the type representation has statically "spare" unused bits. |
| bool hasFixedSpareBits() const { |
| return SpareBits.any(); |
| } |
| |
| /// Applies the fixed spare bits mask for this type to the given BitVector, |
| /// clearing any bits used by valid representations of the type. |
| /// |
| /// If the bitvector is empty or smaller than this type, it is grown and |
| /// filled with bits direct from the spare bits mask. If the bitvector is |
| /// larger than this type, the trailing bits are untouched. |
| /// |
| /// The intent is that, for all the data types of an enum, you should be able |
| /// to do this: |
| /// |
| /// SpareBitVector spareBits; |
| /// for (EnumElementDecl *elt : u->getAllElements()) |
| /// getFragileTypeInfo(elt->getArgumentType()) |
| /// .applyFixedSpareBitsMask(spareBits, 0); |
| /// |
| /// and end up with a spare bits mask for the entire enum. |
| void applyFixedSpareBitsMask(SpareBitVector &mask) const; |
| |
| /// Applies a fixed spare bits mask to the given BitVector, |
| /// clearing any bits used by valid representations of the type. |
| /// |
| /// If the bitvector is empty or smaller than this type, it is grown and |
| /// filled with bits direct from the spare bits mask. If the bitvector is |
| /// larger than this type, the trailing bits are untouched. |
| static void applyFixedSpareBitsMask(SpareBitVector &mask, |
| const SpareBitVector &spareBits); |
| |
| /// Fixed-size types never need dynamic value witness table instantiation. |
| void initializeMetadata(IRGenFunction &IGF, |
| llvm::Value *metadata, |
| llvm::Value *vwtable, |
| SILType T) const override {} |
| |
| void collectArchetypeMetadata( |
| IRGenFunction &IGF, |
| llvm::MapVector<CanType, llvm::Value *> &typeToMetadataVec, |
| SILType T) const override { |
| return; |
| } |
| |
| llvm::Value *getEnumTagSinglePayload(IRGenFunction &IGF, |
| llvm::Value *numEmptyCases, |
| Address enumAddr, |
| SILType T) const override; |
| |
| void storeEnumTagSinglePayload(IRGenFunction &IGF, llvm::Value *whichCase, |
| llvm::Value *numEmptyCases, Address enumAddr, |
| SILType T) const override; |
| |
| static bool classof(const FixedTypeInfo *type) { return true; } |
| static bool classof(const TypeInfo *type) { return type->isFixedSize(); } |
| }; |
| |
| } |
| } |
| |
| #endif |