blob: 697742a01b01832cea6a1f747628ccdff9310c28 [file] [log] [blame]
//===--- GenRecord.h - IR generation for record 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 provides some common code for emitting record types.
// A record type is something like a tuple or a struct.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_IRGEN_GENRECORD_H
#define SWIFT_IRGEN_GENRECORD_H
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "Explosion.h"
#include "GenEnum.h"
#include "GenOpaque.h"
#include "LoadableTypeInfo.h"
#include "Outlining.h"
#include "TypeInfo.h"
#include "StructLayout.h"
#include "llvm/Support/TrailingObjects.h"
namespace swift {
namespace irgen {
template <class, class, class> class RecordTypeBuilder;
/// A field of a record type.
template <class FieldImpl> class RecordField {
ElementLayout Layout;
template <class, class, class> friend class RecordTypeBuilder;
/// Begin/End - the range of explosion indexes for this element
unsigned Begin : 16;
unsigned End : 16;
protected:
explicit RecordField(const TypeInfo &elementTI)
: Layout(ElementLayout::getIncomplete(elementTI)) {}
explicit RecordField(const ElementLayout &layout,
unsigned begin, unsigned end)
: Layout(layout), Begin(begin), End(end) {}
const FieldImpl *asImpl() const {
return static_cast<const FieldImpl*>(this);
}
public:
const TypeInfo &getTypeInfo() const { return Layout.getType(); }
void completeFrom(const ElementLayout &layout) {
Layout.completeFrom(layout);
}
bool isEmpty() const {
return Layout.isEmpty();
}
IsPOD_t isPOD() const {
return Layout.isPOD();
}
IsABIAccessible_t isABIAccessible() const {
return Layout.getType().isABIAccessible();
}
Address projectAddress(IRGenFunction &IGF, Address seq,
NonFixedOffsets offsets) const {
return Layout.project(IGF, seq, offsets, "." + asImpl()->getFieldName());
}
ElementLayout::Kind getKind() const {
return Layout.getKind();
}
bool hasFixedByteOffset() const {
return Layout.hasByteOffset();
}
Size getFixedByteOffset() const {
return Layout.getByteOffset();
}
unsigned getStructIndex() const { return Layout.getStructIndex(); }
unsigned getNonFixedElementIndex() const {
return Layout.getNonFixedElementIndex();
}
std::pair<unsigned, unsigned> getProjectionRange() const {
return {Begin, End};
}
};
enum FieldsAreABIAccessible_t : bool {
FieldsAreNotABIAccessible = false,
FieldsAreABIAccessible = true,
};
/// A metaprogrammed TypeInfo implementation for record types.
template <class Impl, class Base, class FieldImpl_,
bool IsLoadable = std::is_base_of<LoadableTypeInfo, Base>::value>
class RecordTypeInfoImpl : public Base,
private llvm::TrailingObjects<Impl, FieldImpl_> {
friend class llvm::TrailingObjects<Impl, FieldImpl_>;
public:
using FieldImpl = FieldImpl_;
private:
const unsigned NumFields;
const unsigned AreFieldsABIAccessible : 1;
mutable Optional<const FieldImpl *> ExtraInhabitantProvidingField;
mutable Optional<bool> MayHaveExtraInhabitants;
protected:
const Impl &asImpl() const { return *static_cast<const Impl*>(this); }
template <class... As>
RecordTypeInfoImpl(ArrayRef<FieldImpl> fields,
FieldsAreABIAccessible_t fieldsABIAccessible,
As&&...args)
: Base(std::forward<As>(args)...),
NumFields(fields.size()),
AreFieldsABIAccessible(fieldsABIAccessible) {
std::uninitialized_copy(fields.begin(), fields.end(),
this->template getTrailingObjects<FieldImpl>());
}
public:
/// Allocate and initialize a type info of this type.
template <class... As>
static Impl *create(ArrayRef<FieldImpl> fields, As &&...args) {
size_t size = Impl::template totalSizeToAlloc<FieldImpl>(fields.size());
void *buffer = ::operator new(size);
return new(buffer) Impl(fields, std::forward<As>(args)...);
}
ArrayRef<FieldImpl> getFields() const {
return {this->template getTrailingObjects<FieldImpl>(), NumFields};
}
/// The standard schema is just all the fields jumbled together.
void getSchema(ExplosionSchema &schema) const override {
for (auto &field : getFields()) {
field.getTypeInfo().getSchema(schema);
}
}
void assignWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T,
bool isOutlined) const override {
// If the fields are not ABI-accessible, use the value witness table.
if (!AreFieldsABIAccessible) {
return emitAssignWithCopyCall(IGF, T, dest, src);
}
if (isOutlined || T.hasOpenedExistential()) {
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
for (auto &field : getFields()) {
if (field.isEmpty())
continue;
Address destField = field.projectAddress(IGF, dest, offsets);
Address srcField = field.projectAddress(IGF, src, offsets);
field.getTypeInfo().assignWithCopy(
IGF, destField, srcField, field.getType(IGF.IGM, T), isOutlined);
}
} else {
this->callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsNotTake);
}
}
void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T,
bool isOutlined) const override {
// If the fields are not ABI-accessible, use the value witness table.
if (!AreFieldsABIAccessible) {
return emitAssignWithTakeCall(IGF, T, dest, src);
}
if (isOutlined || T.hasOpenedExistential()) {
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
for (auto &field : getFields()) {
if (field.isEmpty())
continue;
Address destField = field.projectAddress(IGF, dest, offsets);
Address srcField = field.projectAddress(IGF, src, offsets);
field.getTypeInfo().assignWithTake(
IGF, destField, srcField, field.getType(IGF.IGM, T), isOutlined);
}
} else {
this->callOutlinedCopy(IGF, dest, src, T, IsNotInitialization, IsTake);
}
}
void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src,
SILType T, bool isOutlined) const override {
// If we're POD, use the generic routine.
if (this->isPOD(ResilienceExpansion::Maximal) &&
isa<LoadableTypeInfo>(this)) {
return cast<LoadableTypeInfo>(this)->LoadableTypeInfo::initializeWithCopy(
IGF, dest, src, T, isOutlined);
}
// If the fields are not ABI-accessible, use the value witness table.
if (!AreFieldsABIAccessible) {
return emitInitializeWithCopyCall(IGF, T, dest, src);
}
if (isOutlined || T.hasOpenedExistential()) {
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
for (auto &field : getFields()) {
if (field.isEmpty())
continue;
Address destField = field.projectAddress(IGF, dest, offsets);
Address srcField = field.projectAddress(IGF, src, offsets);
field.getTypeInfo().initializeWithCopy(
IGF, destField, srcField, field.getType(IGF.IGM, T), isOutlined);
}
} else {
this->callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsNotTake);
}
}
void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
SILType T, bool isOutlined) const override {
// If we're bitwise-takable, use memcpy.
if (this->isBitwiseTakable(ResilienceExpansion::Maximal)) {
IGF.Builder.CreateMemCpy(dest.getAddress(),
dest.getAlignment().getValue(),
src.getAddress(),
src.getAlignment().getValue(),
asImpl().Impl::getSize(IGF, T));
return;
}
// If the fields are not ABI-accessible, use the value witness table.
if (!AreFieldsABIAccessible) {
return emitInitializeWithTakeCall(IGF, T, dest, src);
}
if (isOutlined || T.hasOpenedExistential()) {
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
for (auto &field : getFields()) {
if (field.isEmpty())
continue;
Address destField = field.projectAddress(IGF, dest, offsets);
Address srcField = field.projectAddress(IGF, src, offsets);
field.getTypeInfo().initializeWithTake(
IGF, destField, srcField, field.getType(IGF.IGM, T), isOutlined);
}
} else {
this->callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsTake);
}
}
void destroy(IRGenFunction &IGF, Address addr, SILType T,
bool isOutlined) const override {
// If the fields are not ABI-accessible, use the value witness table.
if (!AreFieldsABIAccessible) {
return emitDestroyCall(IGF, T, addr);
}
if (isOutlined || T.hasOpenedExistential()) {
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
for (auto &field : getFields()) {
if (field.isPOD())
continue;
field.getTypeInfo().destroy(IGF,
field.projectAddress(IGF, addr, offsets),
field.getType(IGF.IGM, T), isOutlined);
}
} else {
this->callOutlinedDestroy(IGF, addr, T);
}
}
// The extra inhabitants of a record are determined from its fields.
bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
if (!MayHaveExtraInhabitants.hasValue()) {
MayHaveExtraInhabitants = false;
for (auto &field : asImpl().getFields())
if (field.getTypeInfo().mayHaveExtraInhabitants(IGM)) {
MayHaveExtraInhabitants = true;
break;
}
}
return *MayHaveExtraInhabitants;
}
// Perform an operation using the field that provides extra inhabitants for
// the aggregate, whether that field is known statically or dynamically.
llvm::Value *withExtraInhabitantProvidingField(IRGenFunction &IGF,
Address structAddr,
SILType structType,
llvm::Value *knownStructNumXI,
llvm::Type *resultTy,
llvm::function_ref<llvm::Value* (const FieldImpl &field,
llvm::Value *numXI)> body) const {
// If we know one field consistently provides extra inhabitants, delegate
// to that field.
if (auto field = asImpl().getFixedExtraInhabitantProvidingField(IGF.IGM)){
return body(*field, knownStructNumXI);
}
// Otherwise, we have to figure out which field at runtime.
// The number of extra inhabitants the instantiated type has can be used
// to figure out which field the runtime chose. The runtime uses the same
// algorithm as above--use the field with the most extra inhabitants,
// favoring the earliest field in a tie. If we test the number of extra
// inhabitants in the struct against each field type's, then the first
// match should indicate which field we chose.
//
// We can reduce the decision space somewhat if there are fixed-layout
// fields, since we know the only possible runtime choices are
// either the fixed field with the most extra inhabitants (if any), or
// one of the unknown-layout fields.
//
// See whether we have a fixed candidate.
const FieldImpl *fixedCandidate = nullptr;
unsigned fixedCount = 0;
for (auto &field : asImpl().getFields()) {
if (!field.getTypeInfo().mayHaveExtraInhabitants(IGF.IGM))
continue;
if (const FixedTypeInfo *fixed =
dyn_cast<FixedTypeInfo>(&field.getTypeInfo())) {
auto fieldCount = fixed->getFixedExtraInhabitantCount(IGF.IGM);
if (fieldCount > fixedCount) {
fixedCandidate = &field;
fixedCount = fieldCount;
}
}
}
// Loop through checking to see whether we picked the fixed candidate
// (if any) or one of the unknown-layout fields.
llvm::Value *instantiatedCount
= (knownStructNumXI
? knownStructNumXI
: emitLoadOfExtraInhabitantCount(IGF, structType));
auto contBB = IGF.createBasicBlock("chose_field_for_xi");
llvm::PHINode *contPhi = nullptr;
if (resultTy != IGF.IGM.VoidTy)
contPhi = llvm::PHINode::Create(resultTy,
asImpl().getFields().size());
// If two fields have the same type, they have the same extra inhabitant
// count, and we'll pick the first. We don't have to check both.
SmallPtrSet<SILType, 4> visitedTypes;
for (auto &field : asImpl().getFields()) {
if (!field.getTypeInfo().mayHaveExtraInhabitants(IGF.IGM))
continue;
ConditionalDominanceScope condition(IGF);
llvm::Value *fieldCount;
if (isa<FixedTypeInfo>(field.getTypeInfo())) {
// Skip fixed fields except for the candidate with the most known
// extra inhabitants we picked above.
if (&field != fixedCandidate)
continue;
fieldCount = IGF.IGM.getInt32(fixedCount);
} else {
auto fieldTy = field.getType(IGF.IGM, structType);
// If this field has the same type as a field we already tested,
// we'll never pick this one, since they both have the same count.
if (!visitedTypes.insert(fieldTy).second)
continue;
fieldCount = emitLoadOfExtraInhabitantCount(IGF, fieldTy);
}
auto equalsCount = IGF.Builder.CreateICmpEQ(instantiatedCount,
fieldCount);
auto yesBB = IGF.createBasicBlock("");
auto noBB = IGF.createBasicBlock("");
IGF.Builder.CreateCondBr(equalsCount, yesBB, noBB);
IGF.Builder.emitBlock(yesBB);
auto value = body(field, instantiatedCount);
if (contPhi)
contPhi->addIncoming(value, IGF.Builder.GetInsertBlock());
IGF.Builder.CreateBr(contBB);
IGF.Builder.emitBlock(noBB);
}
// We shouldn't have picked a number of extra inhabitants inconsistent
// with any individual field.
IGF.Builder.CreateUnreachable();
IGF.Builder.emitBlock(contBB);
if (contPhi)
IGF.Builder.Insert(contPhi);
return contPhi;
}
const FieldImpl *
getFixedExtraInhabitantProvidingField(IRGenModule &IGM) const {
if (!ExtraInhabitantProvidingField.hasValue()) {
unsigned mostExtraInhabitants = 0;
const FieldImpl *fieldWithMost = nullptr;
const FieldImpl *singleNonFixedField = nullptr;
// TODO: If two fields have the same type, they have the same extra
// inhabitant count, and we'll pick the first. We don't have to check
// both. However, we don't always have access to the substituted struct
// type from this context, which would be necessary to make that
// judgment reliably.
for (auto &field : asImpl().getFields()) {
auto &ti = field.getTypeInfo();
if (!ti.mayHaveExtraInhabitants(IGM))
continue;
auto *fixed = dyn_cast<FixedTypeInfo>(&field.getTypeInfo());
// If any field is non-fixed, we can't definitively pick a best one,
// unless it happens to be the only non-fixed field and none of the
// other fields have extra inhabitants.
if (!fixed) {
// If we already saw a non-fixed field, then we can't pick one
// at compile time.
if (singleNonFixedField) {
singleNonFixedField = fieldWithMost = nullptr;
break;
}
// Otherwise, note this field for later. If we have no fixed
// candidates, it may be the only choice for extra inhabitants.
singleNonFixedField = &field;
continue;
}
unsigned count = fixed->getFixedExtraInhabitantCount(IGM);
if (count > mostExtraInhabitants) {
mostExtraInhabitants = count;
fieldWithMost = &field;
}
}
if (fieldWithMost) {
if (singleNonFixedField) {
// If we have a non-fixed and fixed candidate, we can't know for
// sure now.
ExtraInhabitantProvidingField = nullptr;
} else {
// If we had all fixed fields, pick the one with the most extra
// inhabitants.
ExtraInhabitantProvidingField = fieldWithMost;
}
} else {
// If there were no fixed candidates, but we had a single non-fixed
// field with potential extra inhabitants, then it's our only choice.
ExtraInhabitantProvidingField = singleNonFixedField;
}
}
return *ExtraInhabitantProvidingField;
}
void collectMetadataForOutlining(OutliningMetadataCollector &collector,
SILType T) const override {
for (auto &field : getFields()) {
if (field.isEmpty())
continue;
auto fType = field.getType(collector.IGF.IGM, T);
field.getTypeInfo().collectMetadataForOutlining(collector, fType);
}
collector.collectTypeMetadataForLayout(T);
}
};
template <class Impl, class Base, class FieldImpl_,
bool IsFixedSize = std::is_base_of<FixedTypeInfo, Base>::value,
bool IsLoadable = std::is_base_of<LoadableTypeInfo, Base>::value>
class RecordTypeInfo;
/// An implementation of RecordTypeInfo for non-fixed-size types
/// (but not resilient ones where we don't know the complete set of
/// stored properties).
///
/// Override the buffer operations to just delegate to the unique
/// non-empty field, if there is one.
template <class Impl, class Base, class FieldImpl>
class RecordTypeInfo<Impl, Base, FieldImpl,
/*IsFixedSize*/ false, /*IsLoadable*/ false>
: public RecordTypeInfoImpl<Impl, Base, FieldImpl> {
using super = RecordTypeInfoImpl<Impl, Base, FieldImpl>;
/// The index+1 of the unique non-empty field, or zero if there is none.
unsigned UniqueNonEmptyFieldIndexPlusOne;
protected:
template <class... As>
RecordTypeInfo(ArrayRef<FieldImpl> fields, As&&...args)
: super(fields, std::forward<As>(args)...) {
// Look for a unique non-empty field.
UniqueNonEmptyFieldIndexPlusOne = findUniqueNonEmptyField(fields);
}
public:
using super::getStorageType;
Address initializeBufferWithCopyOfBuffer(IRGenFunction &IGF,
Address destBuffer,
Address srcBuffer,
SILType type) const override {
if (auto field = getUniqueNonEmptyField()) {
auto &fieldTI = field->getTypeInfo();
Address fieldResult =
fieldTI.initializeBufferWithCopyOfBuffer(IGF, destBuffer, srcBuffer,
field->getType(IGF.IGM, type));
return IGF.Builder.CreateElementBitCast(fieldResult, getStorageType());
} else {
return super::initializeBufferWithCopyOfBuffer(IGF, destBuffer,
srcBuffer, type);
}
}
private:
/// Scan the given field info
static unsigned findUniqueNonEmptyField(ArrayRef<FieldImpl> fields) {
unsigned result = 0;
for (auto &field : fields) {
// Ignore empty fields.
if (field.isEmpty()) continue;
// If the field is not ABI-accessible, suppress this.
if (!field.isABIAccessible()) continue;
// If we've already found an index, then there isn't a
// unique non-empty field.
if (result) return 0;
result = (&field - fields.data()) + 1;
}
return result;
}
const FieldImpl *getUniqueNonEmptyField() const {
if (UniqueNonEmptyFieldIndexPlusOne) {
return &this->getFields()[UniqueNonEmptyFieldIndexPlusOne - 1];
} else {
return nullptr;
}
}
};
/// An implementation of RecordTypeInfo for fixed-layout types that
/// aren't necessarily loadable.
template <class Impl, class Base, class FieldImpl>
class RecordTypeInfo<Impl, Base, FieldImpl,
/*IsFixedSize*/ true, /*IsLoadable*/ false>
: public RecordTypeInfoImpl<Impl, Base, FieldImpl> {
using super = RecordTypeInfoImpl<Impl, Base, FieldImpl>;
protected:
template <class... As>
RecordTypeInfo(ArrayRef<FieldImpl> fields, As &&...args)
: super(fields, FieldsAreABIAccessible, std::forward<As>(args)...) {}
using super::asImpl;
public:
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
if (auto field = asImpl().getFixedExtraInhabitantProvidingField(IGM)) {
auto &fieldTI = cast<FixedTypeInfo>(field->getTypeInfo());
return fieldTI.getFixedExtraInhabitantCount(IGM);
}
return 0;
}
APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
unsigned bits,
unsigned index) const override {
// We are only called if the type is known statically to have extra
// inhabitants.
auto &field = *asImpl().getFixedExtraInhabitantProvidingField(IGM);
auto &fieldTI = cast<FixedTypeInfo>(field.getTypeInfo());
APInt fieldValue = fieldTI.getFixedExtraInhabitantValue(IGM, bits, index);
return fieldValue.shl(field.getFixedByteOffset().getValueInBits());
}
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
auto field = asImpl().getFixedExtraInhabitantProvidingField(IGM);
if (!field)
return APInt();
const FixedTypeInfo &fieldTI
= cast<FixedTypeInfo>(field->getTypeInfo());
auto targetSize = asImpl().getFixedSize().getValueInBits();
if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal))
return APInt(targetSize, 0);
APInt fieldMask = fieldTI.getFixedExtraInhabitantMask(IGM);
if (targetSize > fieldMask.getBitWidth())
fieldMask = fieldMask.zext(targetSize);
fieldMask = fieldMask.shl(field->getFixedByteOffset().getValueInBits());
return fieldMask;
}
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF,
Address structAddr,
SILType structType,
bool isOutlined) const override {
auto field = *asImpl().getFixedExtraInhabitantProvidingField(IGF.IGM);
Address fieldAddr =
asImpl().projectFieldAddress(IGF, structAddr, structType, field);
auto &fieldTI = cast<FixedTypeInfo>(field.getTypeInfo());
return fieldTI.getExtraInhabitantIndex(IGF, fieldAddr,
field.getType(IGF.IGM, structType),
false /*not outlined for field*/);
}
void storeExtraInhabitant(IRGenFunction &IGF,
llvm::Value *index,
Address structAddr,
SILType structType,
bool isOutlined) const override {
auto field = *asImpl().getFixedExtraInhabitantProvidingField(IGF.IGM);
Address fieldAddr =
asImpl().projectFieldAddress(IGF, structAddr, structType, field);
auto &fieldTI = cast<FixedTypeInfo>(field.getTypeInfo());
fieldTI.storeExtraInhabitant(IGF, index, fieldAddr,
field.getType(IGF.IGM, structType),
false /*not outlined for field*/);
}
};
/// An implementation of RecordTypeInfo for loadable types.
template <class Impl, class Base, class FieldImpl>
class RecordTypeInfo<Impl, Base, FieldImpl,
/*IsFixedSize*/ true, /*IsLoadable*/ true>
: public RecordTypeInfo<Impl, Base, FieldImpl, true, false> {
using super = RecordTypeInfo<Impl, Base, FieldImpl, true, false>;
unsigned ExplosionSize : 16;
protected:
using super::asImpl;
template <class... As>
RecordTypeInfo(ArrayRef<FieldImpl> fields,
unsigned explosionSize,
As &&...args)
: super(fields, std::forward<As>(args)...),
ExplosionSize(explosionSize) {}
private:
template <void (LoadableTypeInfo::*Op)(IRGenFunction &IGF,
Address addr,
Explosion &out) const>
void forAllFields(IRGenFunction &IGF, Address addr, Explosion &out) const {
auto offsets = asImpl().getNonFixedOffsets(IGF);
for (auto &field : getFields()) {
if (field.isEmpty()) continue;
Address fieldAddr = field.projectAddress(IGF, addr, offsets);
(cast<LoadableTypeInfo>(field.getTypeInfo()).*Op)(IGF, fieldAddr, out);
}
}
template <void (LoadableTypeInfo::*Op)(IRGenFunction &IGF, Address addr,
Explosion &out, Atomicity atomicity) const>
void forAllFields(IRGenFunction &IGF, Address addr, Explosion &out,
Atomicity atomicity) const {
auto offsets = asImpl().getNonFixedOffsets(IGF);
for (auto &field : getFields()) {
if (field.isEmpty()) continue;
Address fieldAddr = field.projectAddress(IGF, addr, offsets);
(cast<LoadableTypeInfo>(field.getTypeInfo()).*Op)(IGF, fieldAddr, out,
atomicity);
}
}
template <void (LoadableTypeInfo::*Op)(IRGenFunction &IGF, Explosion &in,
Address addr, bool isOutlined) const>
void forAllFields(IRGenFunction &IGF, Explosion &in, Address addr,
bool isOutlined) const {
auto offsets = asImpl().getNonFixedOffsets(IGF);
for (auto &field : getFields()) {
if (field.isEmpty()) continue;
Address fieldAddr = field.projectAddress(IGF, addr, offsets);
(cast<LoadableTypeInfo>(field.getTypeInfo()).*Op)(IGF, in, fieldAddr,
isOutlined);
}
}
public:
using super::getFields;
void loadAsCopy(IRGenFunction &IGF, Address addr,
Explosion &out) const override {
forAllFields<&LoadableTypeInfo::loadAsCopy>(IGF, addr, out);
}
void loadAsTake(IRGenFunction &IGF, Address addr,
Explosion &out) const override {
forAllFields<&LoadableTypeInfo::loadAsTake>(IGF, addr, out);
}
void assign(IRGenFunction &IGF, Explosion &e, Address addr,
bool isOutlined) const override {
forAllFields<&LoadableTypeInfo::assign>(IGF, e, addr, isOutlined);
}
void initialize(IRGenFunction &IGF, Explosion &e, Address addr,
bool isOutlined) const override {
forAllFields<&LoadableTypeInfo::initialize>(IGF, e, addr, isOutlined);
}
unsigned getExplosionSize() const override {
return ExplosionSize;
}
void reexplode(IRGenFunction &IGF, Explosion &src,
Explosion &dest) const override {
for (auto &field : getFields())
cast<LoadableTypeInfo>(field.getTypeInfo()).reexplode(IGF, src, dest);
}
void copy(IRGenFunction &IGF, Explosion &src,
Explosion &dest, Atomicity atomicity) const override {
for (auto &field : getFields())
cast<LoadableTypeInfo>(field.getTypeInfo())
.copy(IGF, src, dest, atomicity);
}
void consume(IRGenFunction &IGF, Explosion &src,
Atomicity atomicity) const override {
for (auto &field : getFields())
cast<LoadableTypeInfo>(field.getTypeInfo())
.consume(IGF, src, atomicity);
}
void fixLifetime(IRGenFunction &IGF, Explosion &src) const override {
for (auto &field : getFields())
cast<LoadableTypeInfo>(field.getTypeInfo()).fixLifetime(IGF, src);
}
void packIntoEnumPayload(IRGenFunction &IGF,
EnumPayload &payload,
Explosion &src,
unsigned startOffset) const override {
for (auto &field : getFields()) {
if (!field.isEmpty()) {
unsigned offset = field.getFixedByteOffset().getValueInBits()
+ startOffset;
cast<LoadableTypeInfo>(field.getTypeInfo())
.packIntoEnumPayload(IGF, payload, src, offset);
}
}
}
void unpackFromEnumPayload(IRGenFunction &IGF, const EnumPayload &payload,
Explosion &dest, unsigned startOffset)
const override {
for (auto &field : getFields()) {
if (!field.isEmpty()) {
unsigned offset = field.getFixedByteOffset().getValueInBits()
+ startOffset;
cast<LoadableTypeInfo>(field.getTypeInfo())
.unpackFromEnumPayload(IGF, payload, dest, offset);
}
}
}
};
/// A builder of record types.
///
/// Required for a full implementation:
/// TypeInfoImpl *construct(void *buffer, ArrayRef<ASTField> fields);
/// FieldImpl getFieldInfo(const ASTField &field, const TypeInfo &fieldTI);
/// Type getType(const ASTField &field);
/// void performLayout(ArrayRef<const TypeInfo *> fieldTypes);
/// - should call recordLayout with the layout
template <class BuilderImpl, class FieldImpl, class ASTField>
class RecordTypeBuilder {
protected:
IRGenModule &IGM;
RecordTypeBuilder(IRGenModule &IGM) : IGM(IGM) {}
BuilderImpl *asImpl() { return static_cast<BuilderImpl*>(this); }
public:
TypeInfo *layout(ArrayRef<ASTField> astFields) {
SmallVector<FieldImpl, 8> fields;
SmallVector<const TypeInfo *, 8> fieldTypesForLayout;
fields.reserve(astFields.size());
fieldTypesForLayout.reserve(astFields.size());
bool loadable = true;
auto fieldsABIAccessible = FieldsAreABIAccessible;
unsigned explosionSize = 0;
for (unsigned i : indices(astFields)) {
auto &astField = astFields[i];
// Compute the field's type info.
auto &fieldTI = IGM.getTypeInfo(asImpl()->getType(astField));
fieldTypesForLayout.push_back(&fieldTI);
if (!fieldTI.isABIAccessible())
fieldsABIAccessible = FieldsAreNotABIAccessible;
fields.push_back(FieldImpl(asImpl()->getFieldInfo(i, astField, fieldTI)));
auto loadableFieldTI = dyn_cast<LoadableTypeInfo>(&fieldTI);
if (!loadableFieldTI) {
loadable = false;
continue;
}
auto &fieldInfo = fields.back();
fieldInfo.Begin = explosionSize;
explosionSize += loadableFieldTI->getExplosionSize();
fieldInfo.End = explosionSize;
}
// Perform layout and fill in the fields.
StructLayout layout = asImpl()->performLayout(fieldTypesForLayout);
for (unsigned i = 0, e = fields.size(); i != e; ++i) {
fields[i].completeFrom(layout.getElements()[i]);
}
// Create the type info.
if (loadable) {
assert(layout.isFixedLayout());
assert(fieldsABIAccessible);
return asImpl()->createLoadable(fields, std::move(layout), explosionSize);
} else if (layout.isFixedLayout()) {
assert(fieldsABIAccessible);
return asImpl()->createFixed(fields, std::move(layout));
} else {
return asImpl()->createNonFixed(fields, fieldsABIAccessible,
std::move(layout));
}
}
};
} // end namespace irgen
} // end namespace swift
#endif