//===--- 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 "LoadableTypeInfo.h" #include "TypeInfo.h" #include "StructLayout.h" #include "llvm/Support/TrailingObjects.h" namespace swift { namespace irgen { template class RecordTypeBuilder; /// A field of a record type. template class RecordField { ElementLayout Layout; template 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(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(); } Address projectAddress(IRGenFunction &IGF, Address seq, NonFixedOffsets offsets) const { return Layout.project(IGF, seq, offsets, "." + asImpl()->getFieldName()); } ElementLayout::Kind getKind() const { return Layout.getKind(); } Size getFixedByteOffset() const { return Layout.getByteOffset(); } unsigned getStructIndex() const { return Layout.getStructIndex(); } unsigned getNonFixedElementIndex() const { return Layout.getNonFixedElementIndex(); } std::pair getProjectionRange() const { return {Begin, End}; } }; /// A metaprogrammed TypeInfo implementation for record types. template ::value> class RecordTypeInfoImpl : public Base, private llvm::TrailingObjects { friend class llvm::TrailingObjects; public: typedef FieldImpl_ FieldImpl; private: const unsigned NumFields; protected: const Impl &asImpl() const { return *static_cast(this); } template RecordTypeInfoImpl(ArrayRef fields, As&&...args) : Base(std::forward(args)...), NumFields(fields.size()) { std::uninitialized_copy(fields.begin(), fields.end(), this->template getTrailingObjects()); } public: /// Allocate and initialize a type info of this type. template static Impl *create(ArrayRef fields, As &&...args) { size_t size = Impl::template totalSizeToAlloc(fields.size()); void *buffer = ::operator new(size); return new(buffer) Impl(fields, std::forward(args)...); } ArrayRef getFields() const { return {this->template getTrailingObjects(), 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) const override { 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)); } } void assignWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { 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)); } } void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { // If we're POD, use the generic routine. if (this->isPOD(ResilienceExpansion::Maximal) && isa(this)) { return cast(this)-> LoadableTypeInfo::initializeWithCopy(IGF, dest, src, T); } 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)); } } void initializeWithTake(IRGenFunction &IGF, Address dest, Address src, SILType T) const override { // If we're bitwise-takable, use memcpy. if (this->isBitwiseTakable(ResilienceExpansion::Maximal)) { IGF.Builder.CreateMemCpy(dest.getAddress(), src.getAddress(), asImpl().Impl::getSize(IGF, T), std::min(dest.getAlignment(), src.getAlignment()).getValue()); return; } 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)); } } void destroy(IRGenFunction &IGF, Address addr, SILType T) const override { 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)); } } }; template ::value, bool IsLoadable = std::is_base_of::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 RecordTypeInfo : public RecordTypeInfoImpl { typedef RecordTypeInfoImpl super; /// The index+1 of the unique non-empty field, or zero if there is none. unsigned UniqueNonEmptyFieldIndexPlusOne; protected: template RecordTypeInfo(ArrayRef fields, As&&...args) : super(fields, std::forward(args)...) { // Look for a unique non-empty field. UniqueNonEmptyFieldIndexPlusOne = findUniqueNonEmptyField(fields); } public: using super::getStorageType; Address allocateBuffer(IRGenFunction &IGF, Address buffer, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { Address address = field->getTypeInfo().allocateBuffer(IGF, buffer, field->getType(IGF.IGM, type)); return IGF.Builder.CreateElementBitCast(address, getStorageType()); } else { return super::allocateBuffer(IGF, buffer, type); } } Address projectBuffer(IRGenFunction &IGF, Address buffer, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { Address address = field->getTypeInfo().projectBuffer(IGF, buffer, field->getType(IGF.IGM, type)); return IGF.Builder.CreateElementBitCast(address, getStorageType()); } else { return super::projectBuffer(IGF, buffer, type); } } void destroyBuffer(IRGenFunction &IGF, Address buffer, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { field->getTypeInfo().destroyBuffer(IGF, buffer, field->getType(IGF.IGM, type)); } else { super::destroyBuffer(IGF, buffer, type); } } void deallocateBuffer(IRGenFunction &IGF, Address buffer, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { field->getTypeInfo().deallocateBuffer(IGF, buffer, field->getType(IGF.IGM, type)); } else { super::deallocateBuffer(IGF, buffer, type); } } Address initializeBufferWithTake(IRGenFunction &IGF, Address destBuffer, Address srcAddr, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { auto &fieldTI = field->getTypeInfo(); Address srcFieldAddr = IGF.Builder.CreateElementBitCast(srcAddr, fieldTI.getStorageType()); Address fieldResult = fieldTI.initializeBufferWithTake(IGF, destBuffer, srcFieldAddr, field->getType(IGF.IGM, type)); return IGF.Builder.CreateElementBitCast(fieldResult, getStorageType()); } else { return super::initializeBufferWithTake(IGF, destBuffer, srcAddr, type); } } Address initializeBufferWithCopy(IRGenFunction &IGF, Address destBuffer, Address srcAddr, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { auto &fieldTI = field->getTypeInfo(); Address srcFieldAddr = IGF.Builder.CreateElementBitCast(srcAddr, fieldTI.getStorageType()); Address fieldResult = fieldTI.initializeBufferWithCopy(IGF, destBuffer, srcFieldAddr, field->getType(IGF.IGM, type)); return IGF.Builder.CreateElementBitCast(fieldResult, getStorageType()); } else { return super::initializeBufferWithCopy(IGF, destBuffer, srcAddr, type); } } Address initializeBufferWithTakeOfBuffer(IRGenFunction &IGF, Address destBuffer, Address srcBuffer, SILType type) const override { if (auto field = getUniqueNonEmptyField()) { auto &fieldTI = field->getTypeInfo(); Address fieldResult = fieldTI.initializeBufferWithTakeOfBuffer(IGF, destBuffer, srcBuffer, field->getType(IGF.IGM, type)); return IGF.Builder.CreateElementBitCast(fieldResult, getStorageType()); } else { return super::initializeBufferWithTakeOfBuffer(IGF, destBuffer, srcBuffer, type); } } 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 fields) { unsigned result = 0; for (auto &field : fields) { // Ignore empty fields. if (field.isEmpty()) 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 non-loadable types. template class RecordTypeInfo : public RecordTypeInfoImpl { typedef RecordTypeInfoImpl super; protected: template RecordTypeInfo(As&&...args) : super(std::forward(args)...) {} }; /// An implementation of RecordTypeInfo for loadable types. template class RecordTypeInfo : public RecordTypeInfoImpl { typedef RecordTypeInfoImpl super; unsigned ExplosionSize : 16; protected: using super::asImpl; template RecordTypeInfo(ArrayRef fields, unsigned explosionSize, As &&...args) : super(fields, std::forward(args)...), ExplosionSize(explosionSize) {} private: template 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(field.getTypeInfo()).*Op)(IGF, fieldAddr, out); } } template 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(field.getTypeInfo()).*Op)(IGF, fieldAddr, out, atomicity); } } template void forAllFields(IRGenFunction &IGF, Explosion &in, Address addr) const { auto offsets = asImpl().getNonFixedOffsets(IGF); for (auto &field : getFields()) { if (field.isEmpty()) continue; Address fieldAddr = field.projectAddress(IGF, addr, offsets); (cast(field.getTypeInfo()).*Op)(IGF, in, fieldAddr); } } 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) const override { forAllFields<&LoadableTypeInfo::assign>(IGF, e, addr); } void initialize(IRGenFunction &IGF, Explosion &e, Address addr) const override { forAllFields<&LoadableTypeInfo::initialize>(IGF, e, addr); } unsigned getExplosionSize() const override { return ExplosionSize; } void reexplode(IRGenFunction &IGF, Explosion &src, Explosion &dest) const override { for (auto &field : getFields()) cast(field.getTypeInfo()).reexplode(IGF, src, dest); } void copy(IRGenFunction &IGF, Explosion &src, Explosion &dest, Atomicity atomicity) const override { for (auto &field : getFields()) cast(field.getTypeInfo()) .copy(IGF, src, dest, atomicity); } void consume(IRGenFunction &IGF, Explosion &src, Atomicity atomicity) const override { for (auto &field : getFields()) cast(field.getTypeInfo()) .consume(IGF, src, atomicity); } void fixLifetime(IRGenFunction &IGF, Explosion &src) const override { for (auto &field : getFields()) cast(field.getTypeInfo()).fixLifetime(IGF, src); } void packIntoEnumPayload(IRGenFunction &IGF, EnumPayload &payload, Explosion &src, unsigned startOffset) const override { for (auto &field : getFields()) { if (field.getKind() != ElementLayout::Kind::Empty) { unsigned offset = field.getFixedByteOffset().getValueInBits() + startOffset; cast(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.getKind() != ElementLayout::Kind::Empty) { unsigned offset = field.getFixedByteOffset().getValueInBits() + startOffset; cast(field.getTypeInfo()) .unpackFromEnumPayload(IGF, payload, dest, offset); } } } }; /// A builder of record types. /// /// Required for a full implementation: /// TypeInfoImpl *construct(void *buffer, ArrayRef fields); /// FieldImpl getFieldInfo(const ASTField &field, const TypeInfo &fieldTI); /// Type getType(const ASTField &field); /// void performLayout(ArrayRef fieldTypes); /// - should call recordLayout with the layout template class RecordTypeBuilder { protected: IRGenModule &IGM; RecordTypeBuilder(IRGenModule &IGM) : IGM(IGM) {} BuilderImpl *asImpl() { return static_cast(this); } public: TypeInfo *layout(ArrayRef astFields) { SmallVector fields; SmallVector fieldTypesForLayout; fields.reserve(astFields.size()); fieldTypesForLayout.reserve(astFields.size()); bool loadable = true; 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)); assert(fieldTI.isComplete()); fieldTypesForLayout.push_back(&fieldTI); fields.push_back(FieldImpl(asImpl()->getFieldInfo(i, astField, fieldTI))); auto loadableFieldTI = dyn_cast(&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()); return asImpl()->createLoadable(fields, std::move(layout), explosionSize); } else if (layout.isFixedLayout()) { return asImpl()->createFixed(fields, std::move(layout)); } else { return asImpl()->createNonFixed(fields, std::move(layout)); } } }; } // end namespace irgen } // end namespace swift #endif