//===--- 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 "BitPatternBuilder.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/MathExtras.h" #include "llvm/Support/TrailingObjects.h" #include "swift/AST/DiagnosticsIRGen.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; unsigned End; 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(); } IsTriviallyDestroyable_t isTriviallyDestroyable() const { return Layout.isTriviallyDestroyable(); } 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 getProjectionRange() const { return {Begin, End}; } }; enum FieldsAreABIAccessible_t : bool { FieldsAreNotABIAccessible = false, FieldsAreABIAccessible = true, }; /// A metaprogrammed TypeInfo implementation for record types. template ::value> class RecordTypeInfoImpl : public Base, private llvm::TrailingObjects { friend class llvm::TrailingObjects; public: using FieldImpl = FieldImpl_; private: const unsigned NumFields; const unsigned AreFieldsABIAccessible : 1; mutable std::optional ExtraInhabitantProvidingField; mutable std::optional MayHaveExtraInhabitants; protected: const Impl &asImpl() const { return *static_cast(this); } template RecordTypeInfoImpl(ArrayRef fields, FieldsAreABIAccessible_t fieldsABIAccessible, As&&...args) : Base(std::forward(args)...), NumFields(fields.size()), AreFieldsABIAccessible(fieldsABIAccessible) { std::uninitialized_copy(fields.begin(), fields.end(), this->template getTrailingObjects()); } void fillWithZerosIfSensitive(IRGenFunction &IGF, Address address, SILType T) const { if (T.isSensitive()) { llvm::Value *size = asImpl().getSize(IGF, T); IGF.emitClearSensitive(address, size); } } 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)...); } bool areFieldsABIAccessible() const { return AreFieldsABIAccessible; } 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, 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.hasParameterizedExistential()) { 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 (auto rawLayout = T.getRawLayout()) { return handleRawLayout(IGF, dest, src, T, isOutlined, rawLayout, [&](const TypeInfo &ti, SILType type, Address dest, Address src) { ti.assignWithTake(IGF, dest, src, type, isOutlined); }); } if (isOutlined || T.hasParameterizedExistential()) { 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); } fillWithZerosIfSensitive(IGF, src, T); } 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->isTriviallyDestroyable(ResilienceExpansion::Maximal) && isa(this)) { return cast(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.hasParameterizedExistential()) { 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, bool zeroizeIfSensitive) const override { // If we're bitwise-takable, use memcpy. if (this->isBitwiseTakable(ResilienceExpansion::Maximal)) { IGF.Builder.CreateMemCpy( dest.getAddress(), llvm::MaybeAlign(dest.getAlignment().getValue()), src.getAddress(), llvm::MaybeAlign(src.getAlignment().getValue()), asImpl().Impl::getSize(IGF, T)); } else if (!AreFieldsABIAccessible) { // If the fields are not ABI-accessible, use the value witness table. return emitInitializeWithTakeCall(IGF, T, dest, src); } else if (auto rawLayout = T.getRawLayout()) { return handleRawLayout(IGF, dest, src, T, isOutlined, rawLayout, [&](const TypeInfo &ti, SILType type, Address dest, Address src) { ti.initializeWithTake(IGF, dest, src, type, isOutlined, zeroizeIfSensitive); }); } else if (isOutlined || T.hasParameterizedExistential()) { 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, zeroizeIfSensitive); } } else { this->callOutlinedCopy(IGF, dest, src, T, IsInitialization, IsTake); } if (zeroizeIfSensitive) fillWithZerosIfSensitive(IGF, src, T); } void handleRawLayout(IRGenFunction &IGF, Address dest, Address src, SILType T, bool isOutlined, RawLayoutAttr *rawLayout, std::function body) const { if (rawLayout->shouldMoveAsLikeType()) { auto likeType = T.getRawLayoutSubstitutedLikeType(); auto loweredLikeType = IGF.IGM.getLoweredType(likeType); auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType); // Fixup src/dest address element types because currently they are in // terms of the raw layout type's [n x i8] where we're at a point to use // the like type's concrete storage type. src = Address(src.getAddress(), likeTypeInfo.getStorageType(), src.getAlignment()); dest = Address(dest.getAddress(), likeTypeInfo.getStorageType(), dest.getAlignment()); // If we're a scalar, then we only need to run the body once. if (rawLayout->getScalarLikeType()) { body(likeTypeInfo, loweredLikeType, dest, src); } // Otherwise, emit a loop that calls body N times where N is the count // of the array variant. This could be generic in which case we need to // pull the value out of metadata or it could be a constant integer. if (rawLayout->getArrayLikeTypeAndCount()) { auto countType = T.getRawLayoutSubstitutedCountType()->getCanonicalType(); IGF.emitLoopOverElements(likeTypeInfo, loweredLikeType, countType, dest, src, [&](Address dest, Address src) { body(likeTypeInfo, loweredLikeType, dest, src); }); } } } 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 (auto rawLayout = T.getRawLayout()) { return handleRawLayout(IGF, Address(), addr, T, isOutlined, rawLayout, [&](const TypeInfo &ti, SILType type, Address dest, Address src) { ti.destroy(IGF, src, type, isOutlined); }); } if (isOutlined || T.hasParameterizedExistential()) { auto offsets = asImpl().getNonFixedOffsets(IGF, T); for (auto &field : getFields()) { SILType fieldType = field.getType(IGF.IGM, T); if (field.isTriviallyDestroyable() && !((bool)fieldType && fieldType.isSensitive())) { continue; } field.getTypeInfo().destroy(IGF, field.projectAddress(IGF, addr, offsets), fieldType, 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.has_value()) { 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 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(&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 visitedTypes; for (auto &field : asImpl().getFields()) { if (!field.getTypeInfo().mayHaveExtraInhabitants(IGF.IGM)) continue; ConditionalDominanceScope condition(IGF); llvm::Value *fieldCount; if (isa(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.has_value()) { 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(&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()) { auto fType = field.getType(collector.IGF.IGM, T); field.getTypeInfo().collectMetadataForOutlining(collector, fType); } // If we're a raw layout type, collect metadata from our like type and count // as well. if (auto likeType = T.getRawLayoutSubstitutedLikeType()) { auto loweredLikeType = collector.IGF.IGM.getLoweredType(likeType); collector.IGF.IGM.getTypeInfo(loweredLikeType) .collectMetadataForOutlining(collector, loweredLikeType); if (auto countType = T.getRawLayoutSubstitutedCountType()) { if (countType->isValueParameter()) { auto loweredCountType = collector.IGF.IGM.getLoweredType(countType); collector.IGF.IGM.getTypeInfo(loweredCountType) .collectMetadataForOutlining(collector, loweredCountType); } } } collector.collectTypeMetadata(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 { using super = RecordTypeInfoImpl; /// 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 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: static unsigned findUniqueNonEmptyField(ArrayRef 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()) return 0; // 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 RecordTypeInfo : public RecordTypeInfoImpl { using super = RecordTypeInfoImpl; protected: template RecordTypeInfo(ArrayRef fields, As &&...args) : super(fields, std::forward(args)...) {} using super::asImpl; public: unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { if (auto field = asImpl().getFixedExtraInhabitantProvidingField(IGM)) { auto &fieldTI = cast(field->getTypeInfo()); return fieldTI.getFixedExtraInhabitantCount(IGM); } return 0; } bool canValueWitnessExtraInhabitantsUpTo(IRGenModule &IGM, unsigned index) const override { if (auto field = asImpl().getFixedExtraInhabitantProvidingField(IGM)) { // The non-extra-inhabitant-providing fields of the type must be // trivial, because an enum may contain garbage values in those fields' // storage which the value witness operation won't handle. for (auto &otherField : asImpl().getFields()) { if (field == &otherField) continue; auto &ti = otherField.getTypeInfo(); if (!ti.isTriviallyDestroyable(ResilienceExpansion::Maximal)) { return false; } } return field->getTypeInfo() .canValueWitnessExtraInhabitantsUpTo(IGM, index); } return false; } 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(field.getTypeInfo()); auto fieldSize = fieldTI.getFixedExtraInhabitantMask(IGM).getBitWidth(); auto value = BitPatternBuilder(IGM.Triple.isLittleEndian()); value.appendClearBits(field.getFixedByteOffset().getValueInBits()); value.append(fieldTI.getFixedExtraInhabitantValue(IGM, fieldSize, index)); value.padWithClearBitsTo(bits); return value.build().value(); } APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override { auto field = asImpl().getFixedExtraInhabitantProvidingField(IGM); if (!field) return APInt(); const FixedTypeInfo &fieldTI = cast(field->getTypeInfo()); auto targetSize = asImpl().getFixedSize().getValueInBits(); if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) return APInt(targetSize, 0); auto mask = BitPatternBuilder(IGM.Triple.isLittleEndian()); mask.appendClearBits(field->getFixedByteOffset().getValueInBits()); mask.append(fieldTI.getFixedExtraInhabitantMask(IGM)); mask.padWithClearBitsTo(targetSize); return mask.build().value(); } 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(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(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 RecordTypeInfo : public RecordTypeInfo { using super = RecordTypeInfo; 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, bool isOutlined) 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, isOutlined); } } template void forAllFields(IRGenFunction &IGF, Explosion &in, Address addr, bool isOutlined, SILType T) 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, isOutlined, field.getType(IGF.IGM, T)); } } 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, SILType T) const override { forAllFields<&LoadableTypeInfo::assign>(IGF, e, addr, isOutlined, T); } 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(Explosion &src, Explosion &dest) const override { for (auto &field : getFields()) cast(field.getTypeInfo()).reexplode(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, SILType T) const override { for (auto &field : getFields()) { cast(field.getTypeInfo()) .consume(IGF, src, atomicity, field.getType(IGF.IGM, T)); } } void fixLifetime(IRGenFunction &IGF, Explosion &src) const override { for (auto &field : getFields()) cast(field.getTypeInfo()).fixLifetime(IGF, src); } void packIntoEnumPayload(IRGenModule &IGM, IRBuilder &builder, EnumPayload &payload, Explosion &src, unsigned startOffset) const override { for (auto &field : getFields()) { if (!field.isEmpty()) { unsigned offset = field.getFixedByteOffset().getValueInBits() + startOffset; cast(field.getTypeInfo()) .packIntoEnumPayload(IGM, builder, 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(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()); auto fieldsABIAccessible = FieldsAreABIAccessible; unsigned explosionSize = 0; for (unsigned i : indices(astFields)) { auto &astField = astFields[i]; // Compute the field's type info. auto fieldTy = asImpl()->getType(astField); auto &fieldTI = IGM.getTypeInfo(fieldTy); fieldTypesForLayout.push_back(&fieldTI); if (!fieldTI.isABIAccessible()) fieldsABIAccessible = FieldsAreNotABIAccessible; fields.push_back(FieldImpl(asImpl()->getFieldInfo(i, astField, fieldTI))); auto loadableFieldTI = dyn_cast(&fieldTI); if (!loadableFieldTI) { continue; } auto &fieldInfo = fields.back(); fieldInfo.Begin = explosionSize; bool overflow = false; explosionSize = llvm::SaturatingAdd(explosionSize, loadableFieldTI->getExplosionSize(), &overflow); if (overflow) { IGM.Context.Diags.diagnose(SourceLoc(), diag::explosion_size_oveflow); } 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 (layout.isLoadable()) { assert(layout.isFixedLayout()); return asImpl()->createLoadable(fields, fieldsABIAccessible, std::move(layout), explosionSize ); } else if (layout.isFixedLayout()) { return asImpl()->createFixed(fields, fieldsABIAccessible, std::move(layout)); } else { return asImpl()->createNonFixed(fields, fieldsABIAccessible, std::move(layout)); } } }; } // end namespace irgen } // end namespace swift #endif