================
@@ -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");
+      const CXXRecordDecl *bd = base.getType()->getAsCXXRecordDecl();
+      CharUnits baseOffset = layout.getBaseClassOffset(bd);
+      bases.push_back(BaseInfo(bd, baseOffset, index));
+    }
+    llvm::stable_sort(bases);
----------------
andykaylor wrote:

I'd be a bit surprised if these aren't already correctly sorted. Have you seen 
otherwise?

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

Reply via email to