blob: 283462202fce1dc65e3308b1c4e20ee76da40bd7 [file] [log] [blame]
//===--- 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 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 = SpecialTypeInfoKind::Fixed)
: TypeInfo(type, align, pod, bt, alwaysFixedSize, IsABIAccessible, stik),
SpareBits(spareBits) {
assert(SpareBits.size() == size.getValueInBits());
assert(isFixedSize());
Bits.FixedTypeInfo.Size = size.getValue();
assert(Bits.FixedTypeInfo.Size == size.getValue() && "truncation");
}
FixedTypeInfo(llvm::Type *type, Size size,
SpareBitVector &&spareBits,
Alignment align, IsPOD_t pod, IsBitwiseTakable_t bt,
IsFixedSize_t alwaysFixedSize,
SpecialTypeInfoKind stik = SpecialTypeInfoKind::Fixed)
: TypeInfo(type, align, pod, bt, alwaysFixedSize, IsABIAccessible, stik),
SpareBits(std::move(spareBits)) {
assert(SpareBits.size() == size.getValueInBits());
assert(isFixedSize());
Bits.FixedTypeInfo.Size = size.getValue();
assert(Bits.FixedTypeInfo.Size == size.getValue() && "truncation");
}
public:
// This is useful for metaprogramming.
static bool isFixed() { return true; }
static IsABIAccessible_t isABIAccessible() { return IsABIAccessible; }
/// Whether this type is known to be empty.
bool isKnownEmpty(ResilienceExpansion expansion) const {
return (isFixedSize(expansion) && getFixedSize().isZero());
}
StackAddress allocateStack(IRGenFunction &IGF, SILType T,
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 *getIsBitwiseTakable(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) {
Bits.FixedTypeInfo.Size = size.getValue();
assert(Bits.FixedTypeInfo.Size == size.getValue() && "truncation");
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 Size(Bits.FixedTypeInfo.Size);
}
/// 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 = getFixedSize().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.
virtual llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF,
Address src, SILType T,
bool isOutlined) const {
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.
virtual void storeExtraInhabitant(IRGenFunction &IGF,
llvm::Value *index,
Address dest, SILType T,
bool isOutlined) const {
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);
void collectMetadataForOutlining(OutliningMetadataCollector &collector,
SILType T) const override {
// We assume that fixed type infos generally do not require type
// metadata in order to perform value operations.
}
llvm::Value *getEnumTagSinglePayload(IRGenFunction &IGF,
llvm::Value *numEmptyCases,
Address enumAddr,
SILType T,
bool isOutlined) const override;
void storeEnumTagSinglePayload(IRGenFunction &IGF, llvm::Value *whichCase,
llvm::Value *numEmptyCases, Address enumAddr,
SILType T, bool isOutlined) const override;
static bool classof(const FixedTypeInfo *type) { return true; }
static bool classof(const TypeInfo *type) { return type->isFixedSize(); }
};
llvm::Value *getFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
const FixedTypeInfo &fixedTI,
llvm::Value *numEmptyCases,
Address enumAddr,
SILType T, bool isOutlined);
void storeFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
const FixedTypeInfo &fixedTI,
llvm::Value *index,
llvm::Value *numEmptyCases,
Address enumAddr,
SILType T, bool isOutlined);
}
}
#endif