diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index 312d0a9422673..4eec34cb299ab 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -643,6 +643,8 @@ def CIR_RecordType : CIR_Type<"Record", "record", [ uint64_t getElementOffset(const mlir::DataLayout &dataLayout, unsigned idx) const; + bool isLayoutIdentical(const RecordType &other); + private: unsigned computeStructSize(const mlir::DataLayout &dataLayout) const; uint64_t computeStructAlignment(const mlir::DataLayout &dataLayout) const; diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index d7a2e49ec162a..f0a818b388d51 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -134,6 +134,7 @@ struct MissingFeatures { static bool astRecordDeclAttr() { return false; } static bool cxxSupport() { return false; } static bool recordZeroInit() { return false; } + static bool recordZeroInitPadding() { return false; } static bool zeroSizeRecordMembers() { return false; } static bool recordLayoutVirtualBases() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp index 755c76c89a645..670a431e47f40 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp @@ -7,6 +7,9 @@ //===----------------------------------------------------------------------===// #include "CIRGenBuilder.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "clang/CIR/MissingFeatures.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/TypeSwitch.h" using namespace clang::CIRGen; @@ -130,6 +133,39 @@ void CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset( computeGlobalViewIndicesFromFlatOffset(offset, subType, layout, indices); } +cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType( + mlir::ArrayAttr fields, bool packed, bool padded, llvm::StringRef name) { + assert(!cir::MissingFeatures::astRecordDeclAttr()); + llvm::SmallVector members; + members.reserve(fields.size()); + llvm::transform(fields, std::back_inserter(members), + [](mlir::Attribute attr) { + return mlir::cast(attr).getType(); + }); + + if (name.empty()) + return getAnonRecordTy(members, packed, padded); + + return getCompleteNamedRecordType(members, packed, padded, name); +} + +mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr( + mlir::ArrayAttr arrayAttr, bool packed, bool padded, mlir::Type type) { + auto recordTy = mlir::cast_or_null(type); + + // Record type not specified: create anon record type from members. + if (!recordTy) { + recordTy = getCompleteRecordType(arrayAttr, packed, padded); + } + + // Return zero or anonymous constant record. + const bool isZero = llvm::all_of( + arrayAttr, [&](mlir::Attribute a) { return isNullValue(a); }); + if (isZero) + return cir::ZeroAttr::get(recordTy); + return cir::ConstRecordAttr::get(recordTy, arrayAttr); +} + // This can't be defined in Address.h because that file is included by // CIRGenBuilder.h Address Address::withElementType(CIRGenBuilderTy &builder, diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index d5cb6d416bc9b..0a34b1d1715f3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -12,8 +12,10 @@ #include "Address.h" #include "CIRGenRecordLayout.h" #include "CIRGenTypeCache.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/Support/LLVM.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" -#include "clang/CIR/Interfaces/CIRTypeInterfaces.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" @@ -60,6 +62,16 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { trailingZerosNum); } + cir::ConstArrayAttr getConstArray(mlir::Attribute attrs, + cir::ArrayType arrayTy) const { + return cir::ConstArrayAttr::get(arrayTy, attrs); + } + + mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr, + bool packed = false, + bool padded = false, + mlir::Type type = {}); + cir::ConstRecordAttr getAnonConstRecord(mlir::ArrayAttr arrayAttr, bool packed = false, bool padded = false, @@ -126,9 +138,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { /// /// If a record already exists and is complete, but the client tries to fetch /// it with a different set of attributes, this method will crash. - cir::RecordType getCompleteRecordTy(llvm::ArrayRef members, - llvm::StringRef name, bool packed, - bool padded) { + cir::RecordType getCompleteNamedRecordType(llvm::ArrayRef members, + bool packed, bool padded, + llvm::StringRef name) { const auto nameAttr = getStringAttr(name); auto kind = cir::RecordType::RecordKind::Struct; assert(!cir::MissingFeatures::astRecordDeclAttr()); @@ -150,6 +162,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return type; } + cir::RecordType getCompleteRecordType(mlir::ArrayAttr fields, + bool packed = false, + bool padded = false, + llvm::StringRef name = ""); + /// Get an incomplete CIR struct type. If we have a complete record /// declaration, we may create an incomplete type and then add the /// members, so \p rd here may be complete. diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h index d455f6e283406..ccded3dacf031 100644 --- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h +++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h @@ -81,6 +81,14 @@ class ConstantEmitter { // side effects, or emitting an initialization that requires a // reference to its current location. mlir::Attribute emitForMemory(mlir::Attribute c, QualType destType); + static mlir::Attribute emitForMemory(CIRGenModule &cgm, mlir::Attribute c, + clang::QualType destTy); + + mlir::Attribute emitNullForMemory(mlir::Location loc, QualType t) { + return emitNullForMemory(loc, cgm, t); + } + static mlir::Attribute emitNullForMemory(mlir::Location loc, + CIRGenModule &cgm, QualType t); /// Try to emit the initializer of the given declaration as an abstract /// constant. @@ -104,7 +112,9 @@ class ConstantEmitter { mlir::TypedAttr tryEmitPrivate(const Expr *e, QualType destType); mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType); - mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t); + mlir::Attribute tryEmitPrivateForMemory(const Expr *e, QualType destTy); + mlir::Attribute tryEmitPrivateForMemory(const APValue &value, + QualType destTy); private: #ifndef NDEBUG diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 23132eae3214e..0218127c196d9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -12,7 +12,6 @@ #include "Address.h" #include "CIRGenConstantEmitter.h" -#include "CIRGenFunction.h" #include "CIRGenModule.h" #include "CIRGenRecordLayout.h" #include "mlir/IR/Attributes.h" @@ -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 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 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 &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 elems; + llvm::SmallVector 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 elems, + ArrayRef 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 > +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 elems, + ArrayRef 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(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 unpackedElems = elems; + + llvm::SmallVector 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 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 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(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() + ->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(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(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( + emitter.tryEmitPrivateForMemory(init, field->getType())); + else + eltInit = mlir::cast(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()) + allowOverwrite = true; + } else { + // Otherwise we have a bitfield. + if (auto constInt = dyn_cast(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(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 bases; + bases.reserve(cd->getNumBases()); + for (auto [index, base] : llvm::enumerate(cd->bases())) { + assert(!base.isVirtual() && "should not have virtual bases here"); + const CXXRecordDecl *bd = base.getType()->getAsCXXRecordDecl(); + CharUnits baseOffset = layout.getBaseClassOffset(bd); + bases.push_back(BaseInfo(bd, baseOffset, index)); + } + llvm::stable_sort(bases); + + for (BaseInfo &base : bases) { + bool isPrimaryBase = layout.getPrimaryBase() == base.decl; + build(val.getStructBase(base.index), base.decl, isPrimaryBase, + vTableClass, offset + base.offset); + } + } + + uint64_t offsetBits = cgm.getASTContext().toBits(offset); + + bool allowOverwrite = false; + 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(val.getUnionField(), field)) + continue; + + // Don't emit anonymous bitfields or zero-sized fields. + if (field->isUnnamedBitField() || field->isZeroSize(cgm.getASTContext())) + continue; + + // Emit the value of the initializer. + const APValue &fieldValue = + rd->isUnion() ? val.getUnionValue() : val.getStructField(index); + mlir::TypedAttr eltInit = mlir::cast( + emitter.tryEmitPrivateForMemory(fieldValue, field->getType())); + if (!eltInit) + return false; + + if (!field->isBitField()) { + // Handle non-bitfield members. + if (!appendField(field, layout.getFieldOffset(index) + offsetBits, + 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()) + allowOverwrite = true; + } else { + assert(!cir::MissingFeatures::bitfields()); + cgm.errorNYI(field->getSourceRange(), "bitfields"); + } + } + + return true; +} + +mlir::Attribute ConstRecordBuilder::finalize(QualType type) { + type = type.getNonReferenceType(); + RecordDecl *rd = type->castAs() + ->getOriginalDecl() + ->getDefinitionOrSelf(); + mlir::Type valTy = cgm.convertType(type); + return builder.build(valTy, rd->hasFlexibleArrayMember()); +} + +mlir::Attribute ConstRecordBuilder::buildRecord(ConstantEmitter &emitter, + InitListExpr *ile, + QualType valTy) { + ConstantAggregateBuilder constant(emitter.cgm); + ConstRecordBuilder builder(emitter, constant, CharUnits::Zero()); + + if (!builder.build(ile, /*allowOverwrite*/ false)) + return nullptr; + + return builder.finalize(valTy); +} + +mlir::Attribute ConstRecordBuilder::buildRecord(ConstantEmitter &emitter, + const APValue &val, + QualType valTy) { + ConstantAggregateBuilder constant(emitter.cgm); + ConstRecordBuilder builder(emitter, constant, CharUnits::Zero()); + + const RecordDecl *rd = valTy->castAs() + ->getOriginalDecl() + ->getDefinitionOrSelf(); + const CXXRecordDecl *cd = dyn_cast(rd); + if (!builder.build(val, rd, false, cd, CharUnits::Zero())) + return nullptr; + + return builder.finalize(valTy); +} + +bool ConstRecordBuilder::updateRecord(ConstantEmitter &emitter, + ConstantAggregateBuilder &constant, + CharUnits offset, InitListExpr *updater) { + return ConstRecordBuilder(emitter, constant, offset) + .build(updater, /*allowOverwrite*/ true); +} + //===----------------------------------------------------------------------===// // ConstExprEmitter //===----------------------------------------------------------------------===// @@ -59,7 +564,7 @@ class ConstExprEmitter // Visitor Methods //===--------------------------------------------------------------------===// - mlir::Attribute VisitStmt(Stmt *S, QualType T) { return {}; } + mlir::Attribute VisitStmt(Stmt *s, QualType t) { return {}; } mlir::Attribute VisitConstantExpr(ConstantExpr *ce, QualType t) { if (mlir::Attribute result = emitter.tryEmitConstantExpr(ce)) @@ -204,9 +709,9 @@ class ConstExprEmitter return Visit(e->getSubExpr(), t); } - mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E, - QualType T) { - cgm.errorNYI(E->getBeginLoc(), + mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *e, + QualType t) { + cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitImplicitValueInitExpr"); return {}; } @@ -223,8 +728,7 @@ class ConstExprEmitter } if (ile->getType()->isRecordType()) { - cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter: record ILE"); - return {}; + return ConstRecordBuilder::buildRecord(emitter, ile, t); } if (ile->getType()->isVectorType()) { @@ -358,9 +862,11 @@ emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType, eles.push_back(element); auto arrAttr = mlir::ArrayAttr::get(builder.getContext(), eles); - return builder.getAnonConstRecord(arrAttr, /*isPacked=*/true); + return builder.getAnonConstRecord(arrAttr, /*packed=*/true); } +} // namespace + //===----------------------------------------------------------------------===// // ConstantLValueEmitter //===----------------------------------------------------------------------===// @@ -734,6 +1240,17 @@ mlir::Attribute ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *ce) { return emitAbstract(ce->getBeginLoc(), ce->getAPValueResult(), retType); } +mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const Expr *e, + QualType destType) { + QualType nonMemoryDestType = getNonMemoryType(cgm, destType); + mlir::TypedAttr c = tryEmitPrivate(e, nonMemoryDestType); + if (c) { + mlir::Attribute attr = emitForMemory(c, destType); + return mlir::cast(attr); + } + return nullptr; +} + mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value, QualType destType) { QualType nonMemoryDestType = getNonMemoryType(cgm, destType); @@ -761,6 +1278,15 @@ mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc, return c; } +mlir::Attribute ConstantEmitter::emitNullForMemory(mlir::Location loc, + CIRGenModule &cgm, + QualType t) { + cir::ConstantOp cstOp = + cgm.emitNullConstant(t, loc).getDefiningOp(); + assert(cstOp && "expected cir.const op"); + return emitForMemory(cgm, cstOp.getValue(), t); +} + mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c, QualType destType) { // For an _Atomic-qualified constant, we may need to add tail padding. @@ -772,6 +1298,17 @@ mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c, return c; } +mlir::Attribute ConstantEmitter::emitForMemory(CIRGenModule &cgm, + mlir::Attribute c, + QualType destType) { + // For an _Atomic-qualified constant, we may need to add tail padding. + if (destType->getAs()) { + cgm.errorNYI("atomic constants"); + } + + return c; +} + mlir::TypedAttr ConstantEmitter::tryEmitPrivate(const Expr *e, QualType destType) { assert(!destType->isVoidType() && "can't emit a void constant"); @@ -903,8 +1440,7 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value, return ConstantLValueEmitter(*this, value, destType).tryEmit(); case APValue::Struct: case APValue::Union: - cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union"); - return {}; + return ConstRecordBuilder::buildRecord(*this, value, destType); case APValue::ComplexInt: case APValue::ComplexFloat: { mlir::Type desiredType = cgm.convertType(destType); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 08b40e08c51de..d352128ea149a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -93,6 +93,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, astContext.getTargetInfo().getPointerAlign(LangAS::Default)) .getQuantity(); + const unsigned charSize = astContext.getTargetInfo().getCharWidth(); + UCharTy = cir::IntType::get(&getMLIRContext(), charSize, /*isSigned=*/false); + // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed const unsigned sizeTypeSize = astContext.getTypeSize(astContext.getSignedSizeType()); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 4f5c7f898af8c..1e72b8f7e9708 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -153,6 +153,56 @@ class CIRGenModule : public CIRGenTypeCache { bool isConstant = false, mlir::Operation *insertPoint = nullptr); + bool shouldZeroInitPadding() const { + // In C23 (N3096) $6.7.10: + // """ + // If any object is initialized with an empty initializer, then it is + // subject to default initialization: + // - if it is an aggregate, every member is initialized (recursively) + // according to these rules, and any padding is initialized to zero bits; + // - if it is a union, the first named member is initialized (recursively) + // according to these rules, and any padding is initialized to zero bits. + // + // If the aggregate or union contains elements or members that are + // aggregates or unions, these rules apply recursively to the subaggregates + // or contained unions. + // + // If there are fewer initializers in a brace-enclosed list than there are + // elements or members of an aggregate, or fewer characters in a string + // literal used to initialize an array of known size than there are elements + // in the array, the remainder of the aggregate is subject to default + // initialization. + // """ + // + // The standard seems ambiguous in the following two areas: + // 1. For a union type with empty initializer, if the first named member is + // not the largest member, then the bytes comes after the first named member + // but before padding are left unspecified. An example is: + // union U { int a; long long b;}; + // union U u = {}; // The first 4 bytes are 0, but 4-8 bytes are left + // unspecified. + // + // 2. It only mentions padding for empty initializer, but doesn't mention + // padding for a non empty initialization list. And if the aggregation or + // union contains elements or members that are aggregates or unions, and + // some are non empty initializers, while others are empty initializers, + // the padding initialization is unclear. An example is: + // struct S1 { int a; long long b; }; + // struct S2 { char c; struct S1 s1; }; + // // The values for paddings between s2.c and s2.s1.a, between s2.s1.a + // and s2.s1.b are unclear. + // struct S2 s2 = { 'c' }; + // + // Here we choose to zero initiailize left bytes of a union type because + // projects like the Linux kernel are relying on this behavior. If we don't + // explicitly zero initialize them, the undef values can be optimized to + // return garbage data. We also choose to zero initialize paddings for + // aggregates and unions, no matter they are initialized by empty + // initializers or non empty initializers. This can provide a consistent + // behavior. So projects like the Linux kernel can rely on it. + return !getLangOpts().CPlusPlus; + } + llvm::StringMap cgGlobalNames; std::string getUniqueGlobalName(const std::string &baseName); diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 1764967329969..79e7543bb98e3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -601,9 +601,9 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed); baseLowering.lower(); std::string baseIdentifier = getRecordTypeName(rd, ".base"); - baseTy = - builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier, - baseLowering.packed, baseLowering.padded); + baseTy = builder.getCompleteNamedRecordType( + baseLowering.fieldTypes, baseLowering.packed, baseLowering.padded, + baseIdentifier); // TODO(cir): add something like addRecordTypeName // BaseTy and Ty must agree on their packedness for getCIRFieldNo to work diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h index eb8dcd67a4a81..cc3ce09be4f95 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -49,6 +49,9 @@ struct CIRGenTypeCache { cir::FP80Type FP80Ty; cir::FP128Type FP128Ty; + /// ClangIR char + mlir::Type UCharTy; + /// intptr_t, size_t, and ptrdiff_t, which we assume are the same size. union { mlir::Type UIntPtrTy; diff --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp index 42d45819de0f3..b3858e1764f20 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp @@ -30,7 +30,8 @@ llvm::Align CIRDataLayout::getAlignment(mlir::Type ty, bool useABIAlign) const { return llvm::Align(1); // Get the layout annotation... which is lazily created on demand. - llvm_unreachable("getAlignment()) for record type is not implemented"); + assert(!cir::MissingFeatures::alignCXXRecordDecl()); + return llvm::Align(1); } // FIXME(cir): This does not account for differnt address spaces, and relies diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 4fecb0108e001..35b4513c5789f 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -283,6 +283,16 @@ Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const { }); } +bool RecordType::isLayoutIdentical(const RecordType &other) { + if (getImpl() == other.getImpl()) + return true; + + if (getPacked() != other.getPacked()) + return false; + + return getMembers() == other.getMembers(); +} + //===----------------------------------------------------------------------===// // Data Layout information for types //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/CodeGen/constant-inits.cpp b/clang/test/CIR/CodeGen/constant-inits.cpp new file mode 100644 index 0000000000000..c9153c91ebc22 --- /dev/null +++ b/clang/test/CIR/CodeGen/constant-inits.cpp @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +struct empty{}; + +struct Point { + int x; + int y; + char c[3]; + int z; + [[no_unique_address]] empty e; +}; + +struct [[gnu::packed]] packed { + char c; + int i; +}; + +struct [[gnu::packed]] alignas(8) packed_and_aligned { + short s; + char c; + float f; +}; + +struct simple { + int a, b; +}; + +void function() { + constexpr static empty e; + + constexpr static Point p1{10, 20, {99, 88, 77}, 40, e}; + + constexpr static Point array[] { + {123, 456, {11, 22, 33}, 789, {}}, + {10, 20, {0, 0 ,0}, 40} + }; + + constexpr static packed p2 {123, 456}; + constexpr static packed packed_array[] { + p2, p2 + }; + + constexpr static packed_and_aligned paa {1, 2, 3.f}; + constexpr static packed_and_aligned paa_array[2] { + {1, 2, 3.f} + }; + + constexpr static simple s{0, -1}; + constexpr static simple simple_array[] { + s, {1111, 2222}, s + }; +} + +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1e = #cir.zero : !rec_empty +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1s = #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s32i}> : !rec_simple +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p1 = #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, #cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : !s8i]> : !cir.array, #cir.int<40> : !s32i}> : !rec_Point +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p2 = #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3paa = #cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, #cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : !rec_packed_and_aligned + +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5array = #cir.const_array<[ +// CIR-DAG-SAME: #cir.const_record<{#cir.int<123> : !s32i, #cir.int<456> : !s32i, #cir.const_array<[#cir.int<11> : !s8i, #cir.int<22> : !s8i, #cir.int<33> : !s8i]> : !cir.array, #cir.int<789> : !s32i}> : !rec_Point +// CIR-DAG-SAME: #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, #cir.zero : !cir.array, #cir.int<40> : !s32i}> : !rec_Point +// CIR-DAG-SAME: ]> : !cir.array + +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE12simple_array = #cir.const_array<[ +// CIR-DAG-SAME: #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s32i}> : !rec_simple, +// CIR-DAG-SAME: #cir.const_record<{#cir.int<1111> : !s32i, #cir.int<2222> : !s32i}> : !rec_simple, +// CIR-DAG-SAME: #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s32i}> : !rec_simple +// CIR-DAG-SAME: ]> : !cir.array + +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE12packed_array = #cir.const_array<[ +// CIR-DAG-SAME: #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed, +// CIR-DAG-SAME: #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed +// CIR-DAG-SAME: ]> : !cir.array + +// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE9paa_array = #cir.const_array<[ +// CIR-DAG-SAME: #cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, #cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : !rec_packed_and_aligned, +// CIR-DAG-SAME: #cir.zero : !rec_packed_and_aligned +// CIR-DAG-SAME: ]> : !cir.array + +// CIR-LABEL: cir.func dso_local @_Z8functionv() +// CIR: cir.return + + +// LLVM-DAG: @_ZZ8functionvE12packed_array = internal global [2 x %struct.packed] [%struct.packed <{ i8 123, i32 456 }>, %struct.packed <{ i8 123, i32 456 }>] +// LLVM-DAG: @_ZZ8functionvE12simple_array = internal global [3 x %struct.simple] [%struct.simple { i32 0, i32 -1 }, %struct.simple { i32 1111, i32 2222 }, %struct.simple { i32 0, i32 -1 }] +// LLVM-DAG: @_ZZ8functionvE1e = internal global %struct.empty zeroinitializer +// LLVM-DAG: @_ZZ8functionvE1s = internal global %struct.simple { i32 0, i32 -1 } +// LLVM-DAG: @_ZZ8functionvE2p1 = internal global %struct.Point { i32 10, i32 20, [3 x i8] c"cXM", i32 40 } +// LLVM-DAG: @_ZZ8functionvE2p2 = internal global %struct.packed <{ i8 123, i32 456 }> +// LLVM-DAG: @_ZZ8functionvE3paa = internal global %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }> +// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }] +// LLVM-DAG: @_ZZ8functionvE9paa_array = internal global [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>, %struct.packed_and_aligned zeroinitializer] + +// LLVM-LABEL: define{{.*}} void @_Z8functionv +// LLVM: ret void + + +// OGCG-DAG: @_ZZ8functionvE12packed_array = internal constant [2 x %struct.packed] [%struct.packed <{ i8 123, i32 456 }>, %struct.packed <{ i8 123, i32 456 }>] +// OGCG-DAG: @_ZZ8functionvE12simple_array = internal constant [3 x %struct.simple] [%struct.simple { i32 0, i32 -1 }, %struct.simple { i32 1111, i32 2222 }, %struct.simple { i32 0, i32 -1 }] +// OGCG-DAG: @_ZZ8functionvE1e = internal constant %struct.empty zeroinitializer +// OGCG-DAG: @_ZZ8functionvE1s = internal constant %struct.simple { i32 0, i32 -1 } +// OGCG-DAG: @_ZZ8functionvE2p1 = internal constant %struct.Point { i32 10, i32 20, [3 x i8] c"cXM", i32 40 } +// OGCG-DAG: @_ZZ8functionvE2p2 = internal constant %struct.packed <{ i8 123, i32 456 }> +// OGCG-DAG: @_ZZ8functionvE3paa = internal constant %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }> +// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }] +// OGCG-DAG: @_ZZ8functionvE9paa_array = internal constant [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>, %struct.packed_and_aligned <{ i16 0, i8 0, float 0.000000e+00, i8 undef }>] + +// OGCG-LABEL: define{{.*}} void @_Z8functionv +// OGCG: ret void