================ @@ -21,20 +20,526 @@ #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" +#include "clang/AST/CharUnits.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" -#include "clang/Basic/Specifiers.h" #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/MissingFeatures.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/Sequence.h" #include "llvm/Support/ErrorHandling.h" +#include <iterator> using namespace clang; using namespace clang::CIRGen; +//===----------------------------------------------------------------------===// +// ConstantAggregateBuilder +//===----------------------------------------------------------------------===// + +namespace { +class ConstExprEmitter; + +static mlir::TypedAttr computePadding(CIRGenModule &cgm, CharUnits size) { + mlir::Type eltTy = cgm.UCharTy; + clang::CharUnits::QuantityType arSize = size.getQuantity(); + CIRGenBuilderTy &bld = cgm.getBuilder(); + if (size > CharUnits::One()) { + SmallVector<mlir::Attribute, 4> elts(arSize, cir::ZeroAttr::get(eltTy)); + return bld.getConstArray(mlir::ArrayAttr::get(bld.getContext(), elts), + cir::ArrayType::get(eltTy, arSize)); + } + + return cir::ZeroAttr::get(eltTy); +} + +static mlir::Attribute +emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType, + mlir::Type commonElementType, unsigned arrayBound, + SmallVectorImpl<mlir::TypedAttr> &elements, + mlir::TypedAttr filler); + +struct ConstantAggregateBuilderUtils { + CIRGenModule &cgm; + cir::CIRDataLayout dataLayout; + + ConstantAggregateBuilderUtils(CIRGenModule &cgm) + : cgm(cgm), dataLayout{cgm.getModule()} {} + + CharUnits getAlignment(const mlir::TypedAttr c) const { + return CharUnits::fromQuantity( + dataLayout.getAlignment(c.getType(), /*abiOrPref=*/true)); + } + + CharUnits getSize(mlir::Type ty) const { + return CharUnits::fromQuantity(dataLayout.getTypeAllocSize(ty)); + } + + CharUnits getSize(const mlir::TypedAttr c) const { + return getSize(c.getType()); + } + + mlir::TypedAttr getPadding(CharUnits size) const { + return computePadding(cgm, size); + } +}; + +/// Incremental builder for an mlir::TypedAttr holding a record or array +/// constant. +class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils { + /// The elements of the constant. These two arrays must have the same size; + /// Offsets[i] describes the offset of Elems[i] within the constant. The + /// elements are kept in increasing offset order, and we ensure that there + /// is no overlap: Offsets[i+1] >= Offsets[i] + getSize(Elemes[i]). + /// + /// This may contain explicit padding elements (in order to create a + /// natural layout), but need not. Gaps between elements are implicitly + /// considered to be filled with undef. + llvm::SmallVector<mlir::TypedAttr, 32> elems; + llvm::SmallVector<CharUnits, 32> offsets; + + /// The size of the constant (the maximum end offset of any added element). + /// May be larger than the end of Elems.back() if we split the last element + /// and removed some trailing undefs. + CharUnits size = CharUnits::Zero(); + + /// This is true only if laying out Elems in order as the elements of a + /// non-packed LLVM struct will give the correct layout. + bool naturalLayout = true; + + static mlir::Attribute + buildFrom(CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems, + ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size, + bool naturalLayout, mlir::Type desiredTy, bool allowOversized); + +public: + ConstantAggregateBuilder(CIRGenModule &cgm) + : ConstantAggregateBuilderUtils(cgm) {} + + /// Update or overwrite the value starting at \p offset with \c c. + /// + /// \param allowOverwrite If \c true, this constant might overwrite (part of) + /// a constant that has already been added. This flag is only used to + /// detect bugs. + bool add(mlir::TypedAttr typedAttr, CharUnits offset, bool allowOverwrite); + + /// Update or overwrite the bits starting at \p offsetInBits with \p bits. + bool addBits(llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite); + + /// Produce a constant representing the entire accumulated value, ideally of + /// the specified type. If \p allowOversized, the constant might be larger + /// than implied by \p desiredTy (eg, if there is a flexible array member). + /// Otherwise, the constant will be of exactly the same size as \p desiredTy + /// even if we can't represent it as that type. + mlir::Attribute build(mlir::Type desiredTy, bool allowOversized) const { + return buildFrom(cgm, elems, offsets, CharUnits::Zero(), size, + naturalLayout, desiredTy, allowOversized); + } +}; + +template <typename Container, typename Range = std::initializer_list< + typename Container::value_type>> +static void replace(Container &c, size_t beginOff, size_t endOff, Range vals) { + assert(beginOff <= endOff && "invalid replacement range"); + llvm::replace(c, c.begin() + beginOff, c.begin() + endOff, vals); +} + +bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset, + bool allowOverwrite) { + // Common case: appending to a layout. + if (offset >= size) { + CharUnits align = getAlignment(typedAttr); + CharUnits alignedSize = size.alignTo(align); + if (alignedSize > offset || offset.alignTo(align) != offset) + naturalLayout = false; + else if (alignedSize < offset) { + elems.push_back(getPadding(offset - size)); + offsets.push_back(size); + } + elems.push_back(typedAttr); + offsets.push_back(offset); + size = offset + getSize(typedAttr); + return true; + } + + // Uncommon case: constant overlaps what we've already created. + cgm.errorNYI("overlapping constants"); + return false; +} + +mlir::Attribute ConstantAggregateBuilder::buildFrom( + CIRGenModule &cgm, ArrayRef<mlir::TypedAttr> elems, + ArrayRef<CharUnits> offsets, CharUnits startOffset, CharUnits size, + bool naturalLayout, mlir::Type desiredTy, bool allowOversized) { + ConstantAggregateBuilderUtils utils(cgm); + + if (elems.empty()) + return cir::UndefAttr::get(desiredTy); + + auto offset = [&](size_t i) { return offsets[i] - startOffset; }; + + // If we want an array type, see if all the elements are the same type and + // appropriately spaced. + if (mlir::isa<cir::ArrayType>(desiredTy)) { + cgm.errorNYI("array aggregate constants"); + return {}; + } + + // The size of the constant we plan to generate. This is usually just the size + // of the initialized type, but in AllowOversized mode (i.e. flexible array + // init), it can be larger. + CharUnits desiredSize = utils.getSize(desiredTy); + if (size > desiredSize) { + assert(allowOversized && "Elems are oversized"); + desiredSize = size; + } + + // The natural alignment of an unpacked CIR record with the given elements. + CharUnits align = CharUnits::One(); + for (mlir::TypedAttr e : elems) { + align = std::max(align, utils.getAlignment(e)); + } + + // The natural size of an unpacked LLVM struct with the given elements. + CharUnits alignedSize = size.alignTo(align); + + bool packed = false; + bool padded = false; + ArrayRef<mlir::TypedAttr> unpackedElems = elems; + + llvm::SmallVector<mlir::TypedAttr, 32> unpackedElemStorage; + if (desiredSize < alignedSize || desiredSize.alignTo(align) != desiredSize) { + naturalLayout = false; + packed = true; + } else if (desiredSize > alignedSize) { + // The natural layout would be too small. Add padding to fix it. (This + // is ignored if we choose a packed layout.) + unpackedElemStorage.assign(unpackedElems.begin(), unpackedElems.end()); + unpackedElemStorage.push_back(utils.getPadding(desiredSize - size)); + unpackedElems = unpackedElemStorage; + } + + // If we don't have a natural layout, insert padding as necessary. + // As we go, double-check to see if we can actually just emit Elems + // as a non-packed record and do so opportunistically if possible. + llvm::SmallVector<mlir::TypedAttr, 32> packedElems; + if (!naturalLayout) { + CharUnits sizeSoFar = CharUnits::Zero(); + for (auto [index, element] : llvm::enumerate(elems)) { + CharUnits align = utils.getAlignment(element); + CharUnits naturalOffset = sizeSoFar.alignTo(align); + CharUnits desiredOffset = offset(index); + assert(desiredOffset >= sizeSoFar && "elements out of order"); + + if (desiredOffset != naturalOffset) + packed = true; + if (desiredOffset != sizeSoFar) + packedElems.push_back(utils.getPadding(desiredOffset - sizeSoFar)); + packedElems.push_back(element); + sizeSoFar = desiredOffset + utils.getSize(element); + } + // If we're using the packed layout, pad it out to the desired size if + // necessary. + if (packed) { + assert(sizeSoFar <= desiredSize && + "requested size is too small for contents"); + + if (sizeSoFar < desiredSize) + packedElems.push_back(utils.getPadding(desiredSize - sizeSoFar)); + } + } + + CIRGenBuilderTy &builder = cgm.getBuilder(); + llvm::SmallVector<mlir::Attribute, 32> arrayElements; + arrayElements.reserve(elems.size()); + if (packed) + llvm::copy(packedElems, std::back_inserter(arrayElements)); + else + llvm::copy(unpackedElems, std::back_inserter(arrayElements)); + auto arrAttr = mlir::ArrayAttr::get(builder.getContext(), arrayElements); + + cir::RecordType strType = builder.getCompleteRecordType(arrAttr, packed); + if (auto desired = mlir::dyn_cast<cir::RecordType>(desiredTy)) + if (desired.isLayoutIdentical(strType)) + strType = desired; + + return builder.getConstRecordOrZeroAttr(arrAttr, packed, padded, strType); +} + +//===----------------------------------------------------------------------===// +// ConstRecordBuilder +//===----------------------------------------------------------------------===// + +class ConstRecordBuilder { + CIRGenModule &cgm; + ConstantEmitter &emitter; + ConstantAggregateBuilder &builder; + CharUnits startOffset; + +public: + static mlir::Attribute buildRecord(ConstantEmitter &emitter, + InitListExpr *ile, QualType valTy); + static mlir::Attribute buildRecord(ConstantEmitter &emitter, + const APValue &value, QualType valTy); + static bool updateRecord(ConstantEmitter &emitter, + ConstantAggregateBuilder &constant, CharUnits offset, + InitListExpr *updater); + +private: + ConstRecordBuilder(ConstantEmitter &emitter, + ConstantAggregateBuilder &builder, CharUnits startOffset) + : cgm(emitter.cgm), emitter(emitter), builder(builder), + startOffset(startOffset) {} + + bool appendField(const FieldDecl *field, uint64_t fieldOffset, + mlir::TypedAttr initCst, bool allowOverwrite = false); + + bool appendBytes(CharUnits fieldOffsetInChars, mlir::TypedAttr initCst, + bool allowOverwrite = false); + + bool build(InitListExpr *ile, bool allowOverwrite); + bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase, + const CXXRecordDecl *vTableClass, CharUnits baseOffset); + + mlir::Attribute finalize(QualType ty); +}; + +bool ConstRecordBuilder::appendField(const FieldDecl *field, + uint64_t fieldOffset, + mlir::TypedAttr initCst, + bool allowOverwrite) { + const ASTContext &astContext = cgm.getASTContext(); + + CharUnits fieldOffsetInChars = astContext.toCharUnitsFromBits(fieldOffset); + + return appendBytes(fieldOffsetInChars, initCst, allowOverwrite); +} + +bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars, + mlir::TypedAttr initCst, + bool allowOverwrite) { + return builder.add(initCst, startOffset + fieldOffsetInChars, allowOverwrite); +} + +bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { + RecordDecl *rd = ile->getType() + ->castAs<clang::RecordType>() + ->getOriginalDecl() + ->getDefinitionOrSelf(); + const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd); + + // Bail out if we have base classes. We could support these, but they only + // arise in C++1z where we will have already constant folded most interesting + // cases. FIXME: There are still a few more cases we can handle this way. + if (auto *cxxrd = dyn_cast<CXXRecordDecl>(rd)) + if (cxxrd->getNumBases()) + return false; + + if (cgm.shouldZeroInitPadding()) { + assert(!cir::MissingFeatures::recordZeroInitPadding()); + cgm.errorNYI(rd->getSourceRange(), "zero init padding"); + return false; + } + + unsigned elementNo = 0; + for (auto [index, field] : llvm::enumerate(rd->fields())) { + + // If this is a union, skip all the fields that aren't being initialized. + if (rd->isUnion() && + !declaresSameEntity(ile->getInitializedFieldInUnion(), field)) + continue; + + // Don't emit anonymous bitfields. + if (field->isUnnamedBitField()) + continue; + + // Get the initializer. A record can include fields without initializers, + // we just use explicit null values for them. + Expr *init = nullptr; + if (elementNo < ile->getNumInits()) + init = ile->getInit(elementNo++); + if (isa_and_nonnull<NoInitExpr>(init)) + continue; + + // Zero-sized fields are not emitted, but their initializers may still + // prevent emission of this record as a constant. + if (field->isZeroSize(cgm.getASTContext())) { + if (init->HasSideEffects(cgm.getASTContext())) + return false; + continue; + } + + assert(!cir::MissingFeatures::recordZeroInitPadding()); + + // When emitting a DesignatedInitUpdateExpr, a nested InitListExpr + // represents additional overwriting of our current constant value, and not + // a new constant to emit independently. + if (allowOverwrite && + (field->getType()->isArrayType() || field->getType()->isRecordType())) { + cgm.errorNYI(field->getSourceRange(), "designated init lists"); + return false; + } + + mlir::TypedAttr eltInit; + if (init) + eltInit = mlir::cast<mlir::TypedAttr>( + emitter.tryEmitPrivateForMemory(init, field->getType())); + else + eltInit = mlir::cast<mlir::TypedAttr>(emitter.emitNullForMemory( + cgm.getLoc(ile->getSourceRange()), field->getType())); + + if (!eltInit) + return false; + + if (!field->isBitField()) { + // Handle non-bitfield members. + if (!appendField(field, layout.getFieldOffset(index), eltInit, + allowOverwrite)) + return false; + // After emitting a non-empty field with [[no_unique_address]], we may + // need to overwrite its tail padding. + if (field->hasAttr<NoUniqueAddressAttr>()) + allowOverwrite = true; + } else { + // Otherwise we have a bitfield. + if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) { + assert(!cir::MissingFeatures::bitfields()); + cgm.errorNYI(field->getSourceRange(), "bitfields"); + } + // We are trying to initialize a bitfield with a non-trivial constant, + // this must require run-time code. + return false; + } + } + + assert(!cir::MissingFeatures::recordZeroInitPadding()); + return true; +} + +namespace { +struct BaseInfo { + BaseInfo(const CXXRecordDecl *decl, CharUnits offset, unsigned index) + : decl(decl), offset(offset), index(index) {} + + const CXXRecordDecl *decl; + CharUnits offset; + unsigned index; + + bool operator<(const BaseInfo &o) const { return offset < o.offset; } +}; +} // namespace + +bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd, + bool isPrimaryBase, + const CXXRecordDecl *vTableClass, + CharUnits offset) { + const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd); + if (const CXXRecordDecl *cd = dyn_cast<CXXRecordDecl>(rd)) { + assert(!cir::MissingFeatures::vtableInitialization()); + + // Accumulate and sort bases, in order to visit them in address order, which + // may not be the same as declaration order. + SmallVector<BaseInfo, 8> bases; + bases.reserve(cd->getNumBases()); + for (auto [index, base] : llvm::enumerate(cd->bases())) { + assert(!base.isVirtual() && "should not have virtual bases here"); ---------------- andykaylor wrote:
Where is this prevented? https://github.com/llvm/llvm-project/pull/155663 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits