llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (keinflue)

<details>
<summary>Changes</summary>

Previously the old constant interpreter would crash on 
`__builtin_assume_aligned` applied to dynamic allocations and the new 
interpreter would incorrectly always assume an alignment of 8.

This implements computation of the alignments of dynamic allocations for the 
builtin by using the minimum alignment guarantees made in the standard for each 
kind of possible dynamic allocation during constant evaluation.

The kind of allocation depends on the source expression causing it to be 
created, but the alignment also depends on the size of the allocation which can 
be retrieved from the allocated type, but is decided dynamically.

For the old constant expression evaluator, the allocated type is always 
available from the LValue, but the source expression is only available from the 
`DynAlloc` representing the allocation.

The `DynAlloc` object does not continue to exist when the lifetime of the 
dynamic allocation ends. Because `__builtin_assume_aligned` should still be 
valid out-of-lifetime, it is therefore not possible to retrieve the source 
expression from the `DynAlloc` object.

Instead this calculates the alignment during creation of the allocation and 
stores it as part of the `LValueBase` by appropriating 5 bits of the allocation 
identifier, reducing the total number of possible allocations to 2**25-1.

The new constant expression interpreter does not need additional state to store 
the alignment, because it already has both the source expression and allocated 
size available.

Fixes #<!-- -->173767

---

I am not happy with the modifications to `DynamicAllocLValue` and I have doubts 
that it is ok. If anyone has suggestions for a better way to resolve the issue 
described above I'll try to implement it.

Also implements suggestion from 
https://github.com/llvm/llvm-project/pull/174132#discussion_r2658541020.

---

Patch is 29.58 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/174549.diff


14 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+3) 
- (modified) clang/include/clang/AST/APValue.h (+27-8) 
- (modified) clang/include/clang/AST/PropertiesBase.td (+5-1) 
- (modified) clang/lib/AST/ByteCode/Descriptor.cpp (+110-22) 
- (modified) clang/lib/AST/ByteCode/Descriptor.h (+20) 
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+3-13) 
- (modified) clang/lib/AST/ByteCode/Interp.h (+1-2) 
- (modified) clang/lib/AST/ByteCode/InterpBlock.h (+6-1) 
- (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+9-2) 
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+4-3) 
- (modified) clang/lib/AST/ExprConstShared.h (+3) 
- (modified) clang/lib/AST/ExprConstant.cpp (+86-2) 
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+1) 
- (modified) clang/test/SemaCXX/builtin-assume-aligned.cpp (+81-4) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 411cc348d4caf..fa97c382de764 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -557,6 +557,9 @@ Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fix an ambiguous reference to the builtin `type_info` (available when using
   `-fms-compatibility`) with modules. (#GH38400)
+- Fix a crash when using ``__builtin_assume_aligned`` with dynamic allocations 
during
+  constant evaluation. Also correct the behavior with the new constant 
interpreter, so
+  that both apply alignment guarantees made by the standard. (#GH173767)
 
 Bug Fixes to Attribute Support
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index 8a2d6d434792a..87a71a8bb3dc7 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -20,6 +20,7 @@
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/bit.h"
 #include "llvm/Support/AlignOf.h"
 
 namespace clang {
@@ -63,30 +64,48 @@ class TypeInfoLValue {
 
 /// Symbolic representation of a dynamic allocation.
 class DynamicAllocLValue {
-  unsigned Index;
+  // lower NumAlignmentBits: alignment exponent
+  // remaining bits: allocation index incremented by one
+  // value of zero indicates distinct empty state
+  unsigned AlignAndIndex;
 
 public:
-  DynamicAllocLValue() : Index(0) {}
-  explicit DynamicAllocLValue(unsigned Index) : Index(Index + 1) {}
-  unsigned getIndex() { return Index - 1; }
+  DynamicAllocLValue() : AlignAndIndex(0) {}
+  explicit DynamicAllocLValue(unsigned Index, uint64_t Align) {
+    assert(Align > 0 && "Invalid alignment for DynamicAllocLValue 
constructor");
+    AlignAndIndex =
+        ((Index + 1) << NumAlignmentBits) + llvm::countr_zero(Align);
+  }
+  unsigned getIndex() const { return (AlignAndIndex >> NumAlignmentBits) - 1; }
+  unsigned getAlignExponent() const {
+    return AlignAndIndex & ((1U << NumAlignmentBits) - 1);
+  }
+  uint64_t getAlign() const {
+    const unsigned AlignExponent = getAlignExponent();
+    assert(AlignExponent < 64 && "Invalid alignment in DynamicAllocLValue.");
+    return uint64_t{1} << AlignExponent;
+  }
 
-  explicit operator bool() const { return Index != 0; }
+  explicit operator bool() const { return AlignAndIndex != 0; }
 
   const void *getOpaqueValue() const {
-    return reinterpret_cast<const void *>(static_cast<uintptr_t>(Index)
+    return reinterpret_cast<const void *>(static_cast<uintptr_t>(AlignAndIndex)
                                           << NumLowBitsAvailable);
   }
   static DynamicAllocLValue getFromOpaqueValue(const void *Value) {
     DynamicAllocLValue V;
-    V.Index = reinterpret_cast<uintptr_t>(Value) >> NumLowBitsAvailable;
+    V.AlignAndIndex = reinterpret_cast<uintptr_t>(Value) >> 
NumLowBitsAvailable;
     return V;
   }
 
   static unsigned getMaxIndex() {
-    return (std::numeric_limits<unsigned>::max() >> NumLowBitsAvailable) - 1;
+    return (std::numeric_limits<unsigned>::max() >>
+            (NumLowBitsAvailable + NumAlignmentBits)) -
+           1;
   }
 
   static constexpr int NumLowBitsAvailable = 3;
+  static constexpr int NumAlignmentBits = 5;
 };
 }
 
diff --git a/clang/include/clang/AST/PropertiesBase.td 
b/clang/include/clang/AST/PropertiesBase.td
index 5b10127526e4e..3ffd8406ce838 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -502,6 +502,10 @@ let Class = PropertyTypeCase<APValue, "LValue"> in {
     let Conditional = [{ hasBase && isDynamicAlloc }];
     let Read = [{ node.getLValueBase().get<DynamicAllocLValue>().getIndex() }];
   }
+  def : Property<"dynamicAlign", UInt64> {
+    let Conditional = [{ hasBase && isDynamicAlloc }];
+    let Read = [{ node.getLValueBase().get<DynamicAllocLValue>().getAlign() }];
+  }
   def : Property<"type", QualType> {
     let Conditional = [{ hasBase && (isTypeInfo || isDynamicAlloc) }];
     let Read = [{
@@ -544,7 +548,7 @@ let Class = PropertyTypeCase<APValue, "LValue"> in {
             TypeInfoLValue(typeInfo->getTypePtr()), *type);
       } else if (isDynamicAlloc) {
         base = APValue::LValueBase::getDynamicAlloc(
-            DynamicAllocLValue(*dynamicAlloc), *type);
+            DynamicAllocLValue(*dynamicAlloc, *dynamicAlign), *type);
       } else if (isExpr) {
         base = APValue::LValueBase(cast<Expr>(*stmt),
                                    *callIndex, *version);
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp 
b/clang/lib/AST/ByteCode/Descriptor.cpp
index a3cee034191d2..4f677d361ccac 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -17,6 +17,8 @@
 #include "Record.h"
 #include "Source.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/Basic/TargetInfo.h"
+#include "llvm/Support/ErrorHandling.h"
 
 using namespace clang;
 using namespace clang::interp;
@@ -418,33 +420,15 @@ QualType Descriptor::getDataType(const ASTContext &Ctx) 
const {
     return ElemType;
   };
 
-  if (const auto *E = asExpr()) {
-    if (isa<CXXNewExpr>(E))
-      return MakeArrayType(E->getType()->getPointeeType());
-
-    // std::allocator.allocate() call.
-    if (const auto *ME = dyn_cast<CXXMemberCallExpr>(E);
-        ME && ME->getRecordDecl()->getName() == "allocator" &&
-        ME->getMethodDecl()->getName() == "allocate")
-      return MakeArrayType(E->getType()->getPointeeType());
-    return E->getType();
-  }
+  if (isDynAlloc())
+    return MakeArrayType(asExpr()->getType()->getPointeeType());
 
   return getType();
 }
 
 QualType Descriptor::getDataElemType() const {
-  if (const auto *E = asExpr()) {
-    if (isa<CXXNewExpr>(E))
-      return E->getType()->getPointeeType();
-
-    // std::allocator.allocate() call.
-    if (const auto *ME = dyn_cast<CXXMemberCallExpr>(E);
-        ME && ME->getRecordDecl()->getName() == "allocator" &&
-        ME->getMethodDecl()->getName() == "allocate")
-      return E->getType()->getPointeeType();
-    return E->getType();
-  }
+  if (isDynAlloc())
+    return asExpr()->getType()->getPointeeType();
 
   return getType();
 }
@@ -481,3 +465,107 @@ bool Descriptor::hasTrivialDtor() const {
 }
 
 bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); 
}
+
+Descriptor::DynAllocKind Descriptor::getDynAllocKindForExpr(const Expr *E) {
+  // new or new[] expression
+  if (const auto *NE = dyn_cast<CXXNewExpr>(E))
+    return NE->isArray() ? DynAllocKind::ArrayNew : DynAllocKind::New;
+  // std::allocator::allocate call
+  if (const auto *ME = dyn_cast<CXXMemberCallExpr>(E);
+      ME && ME->getRecordDecl()->getName() == "allocator" &&
+      ME->getMethodDecl()->getName() == "allocate")
+    return DynAllocKind::StdAllocator;
+  // __builtin_operator_new call
+  if (const auto *CE = dyn_cast<CallExpr>(E);
+      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_operator_new)
+    return DynAllocKind::BuiltinOperatorNew;
+  return DynAllocKind::None;
+}
+
+uint64_t Descriptor::computeAlignForDynamicAlloc(const ASTContext &Ctx) const {
+  const Expr *AllocExpr = asExpr();
+  const QualType AllocType = getDataType(Ctx);
+
+  const TargetInfo &TI = Ctx.getTargetInfo();
+
+  const uint64_t DefaultNewAlign = TI.getNewAlign();
+  const uint64_t MaxFundamentalAlign =
+      std::max(TI.getLongLongAlign(), TI.getLongDoubleAlign());
+
+  const DynAllocKind AllocKind = getDynAllocKindForExpr(AllocExpr);
+  assert((AllocKind != DynAllocKind::None) &&
+         "should only be called on dynamically allocated blocks");
+  assert((AllocKind != DynAllocKind::BuiltinOperatorNew) &&
+         "__builtin_operator_new should have been allowed only from "
+         "std::allocator::allocate");
+
+  const uint64_t AllocSize = Ctx.getTypeSize(AllocType);
+
+  // Allocating a zero-sized array is allowed, however it doesn't have
+  // any alignment guarantees.
+  if ((AllocKind == DynAllocKind::ArrayNew ||
+       AllocKind == DynAllocKind::StdAllocator) &&
+      AllocSize == 0)
+    return TI.getCharWidth();
+
+  // For the non-array form, the size of the allocated object should be
+  // positive.
+  assert(AllocSize > 0 && "Unknown size for allocated type!");
+
+  const uint64_t TypeAlignment = Ctx.getTypeAlign(AllocType);
+  assert(TypeAlignment > 0 && "Unknown alignment for allocated type!");
+
+  // For new-extended alignment the ::operator new overload with
+  // std::align_val_t parameter is used. According to C++
+  // [basic.stc.dynamic.allocation]p3.1 this overload returns memory
+  // according to the requested alignment. No stricter guarantees are
+  // made.
+  if (TypeAlignment > DefaultNewAlign)
+    return TypeAlignment;
+
+  switch (AllocKind) {
+  // According to C++ [allocator.members]p5 it is unspecified how the
+  // memory obtained from ::operator new is used by
+  // std::allocator::allocate, therefore be conservative here.
+  case DynAllocKind::StdAllocator:
+    return TypeAlignment;
+
+  // The non-array form of new does not permit allocation overhead and
+  // therefore provides alignment as guaranteed by ::operator new.
+  // According to C++ [basic.stc.dynamic.allocation]p3.3 the allocation
+  // is suitably aligned for all objects without new-extended alignment
+  // with the exact size of the allocation. An object of the exact size
+  // AllocSize can have alignment of at most the lowest bit set in
+  // AllocSize.
+  case DynAllocKind::New:
+    return std::min(DefaultNewAlign,
+                    uint64_t{1} << llvm::countr_zero(AllocSize));
+
+  case DynAllocKind::ArrayNew: {
+    const Type *ET = AllocType.getTypePtr()
+                         ->getArrayElementTypeNoTypeQual()
+                         ->getCanonicalTypeUnqualified()
+                         .getTypePtr();
+    // According to C++ [expr.new]p17, unless the element type of an
+    // array new expression is char, unsigned char or std::byte, the
+    // allocation may be offset into the allocation returned by
+    // ::operator new[]. Therefore no stricter alignment than the type's
+    // alignment is guaranteed. For char, unsigned char and std::byte
+    if (!ET->isSpecificBuiltinType(BuiltinType::UChar) &&
+        !ET->isSpecificBuiltinType(BuiltinType::Char_U) &&
+        !ET->isSpecificBuiltinType(BuiltinType::Char_S) && 
!ET->isStdByteType())
+      return TypeAlignment;
+
+    // Otherwise, the allocation is offset from the result of ::operator
+    // new[] by a multiple of the strictest fundamental alignment.
+    // According C++ [basic.stc.dynamic.allocation]p3.2 the allocation
+    // returned by ::operator new[] is suitably aligned for all objects
+    // without new-extended alignment and size up to the allocated size.
+    return std::min(
+        {DefaultNewAlign, MaxFundamentalAlign, llvm::bit_floor(AllocSize)});
+  }
+
+  default:
+    llvm_unreachable("Unhandled DynAllocKind");
+  }
+}
diff --git a/clang/lib/AST/ByteCode/Descriptor.h 
b/clang/lib/AST/ByteCode/Descriptor.h
index 5bf550ffe1172..906f028d3cfe2 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -272,6 +272,26 @@ struct Descriptor final {
   /// Whether variables of this descriptor need their destructor called or not.
   bool hasTrivialDtor() const;
 
+  /// Kind of source for a dynamic allocation.
+  enum class DynAllocKind {
+    None,              // not a dynamic allocation
+    New,               // new expression
+    ArrayNew,          // new[] expression
+    StdAllocator,      // std::allocator::allocate call
+    BuiltinOperatorNew // __operator_builtin_new call
+  };
+  /// Returns the kind of dynamic allocation source of this block.
+  static DynAllocKind getDynAllocKindForExpr(const Expr *E);
+  /// Returns the kind of dynamic allocation source of this block.
+  DynAllocKind getDynAllocKind() const {
+    return asExpr() ? getDynAllocKindForExpr(asExpr()) : DynAllocKind::None;
+  }
+  /// Checks if the descriptor is of a dynamic allocation.
+  bool isDynAlloc() const { return getDynAllocKind() != DynAllocKind::None; }
+
+  /// Compute the alignment for a dynamic allocation.
+  uint64_t computeAlignForDynamicAlloc(const ASTContext &Ctx) const;
+
   void dump() const;
   void dump(llvm::raw_ostream &OS) const;
   void dumpFull(unsigned Offset = 0, unsigned Indent = 0) const;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index fe3069a4d8ef8..772701fc6c5d9 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1136,18 +1136,8 @@ bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
   return false;
 }
 
-bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
-                       const Pointer &Ptr) {
-  // Regular new type(...) call.
-  if (isa_and_nonnull<CXXNewExpr>(Source))
-    return true;
-  // operator new.
-  if (const auto *CE = dyn_cast_if_present<CallExpr>(Source);
-      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_operator_new)
-    return true;
-  // std::allocator.allocate() call
-  if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(Source);
-      MCE && MCE->getMethodDecl()->getIdentifier()->isStr("allocate"))
+bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+  if (Ptr.block()->isDynamic())
     return true;
 
   // Whatever this is, we didn't heap allocate it.
@@ -1329,7 +1319,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool 
DeleteIsArrayForm,
       return false;
     }
 
-    if (!CheckDeleteSource(S, OpPC, Source, Ptr))
+    if (!CheckDeleteSource(S, OpPC, Ptr))
       return false;
 
     // For a class type with a virtual destructor, the selected operator delete
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 155d96fc1652b..3c50e9d580778 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -90,8 +90,7 @@ bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr 
OpPC);
 
 /// Check the source of the pointer passed to delete/delete[] has actually
 /// been heap allocated by us.
-bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
-                       const Pointer &Ptr);
+bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
 bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                  AccessKinds AK);
diff --git a/clang/lib/AST/ByteCode/InterpBlock.h 
b/clang/lib/AST/ByteCode/InterpBlock.h
index 57f9e7ec3714d..33cb801358540 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.h
+++ b/clang/lib/AST/ByteCode/InterpBlock.h
@@ -80,7 +80,12 @@ class Block final {
   /// Checks if the block is temporary.
   bool isTemporary() const { return Desc->IsTemporary; }
   bool isWeak() const { return AccessFlags & WeakFlag; }
-  bool isDynamic() const { return (DynAllocId != std::nullopt); }
+  bool isDynamic() const {
+    const bool Result = (DynAllocId != std::nullopt);
+    assert((Result == Desc->isDynAlloc()) &&
+           "Inconsistent block/descriptor dynamic alloc state");
+    return Result;
+  }
   bool isDummy() const { return AccessFlags & DummyFlag; }
   bool isDead() const { return AccessFlags & DeadFlag; }
   /// Returns the size of the block.
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index b4acb786fa90c..50936e8f5f62f 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -12,6 +12,8 @@
 #include "InterpHelpers.h"
 #include "PrimType.h"
 #include "Program.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/InferAlloc.h"
 #include "clang/AST/OSLog.h"
 #include "clang/AST/RecordLayout.h"
@@ -1250,6 +1252,8 @@ static bool interp__builtin_assume_aligned(InterpState 
&S, CodePtr OpPC,
                                            const CallExpr *Call) {
   assert(Call->getNumArgs() == 2 || Call->getNumArgs() == 3);
 
+  ASTContext &ASTCtx = S.getASTContext();
+
   std::optional<APSInt> ExtraOffset;
   if (Call->getNumArgs() == 3)
     ExtraOffset = popToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(2)));
@@ -1263,9 +1267,12 @@ static bool interp__builtin_assume_aligned(InterpState 
&S, CodePtr OpPC,
   if (Ptr.isBlockPointer()) {
     CharUnits BaseAlignment;
     if (const auto *VD = Ptr.getDeclDesc()->asValueDecl())
-      BaseAlignment = S.getASTContext().getDeclAlign(VD);
+      BaseAlignment = ASTCtx.getDeclAlign(VD);
+    else if (Ptr.block()->isDynamic())
+      BaseAlignment = ASTCtx.toCharUnitsFromBits(
+          Ptr.getDeclDesc()->computeAlignForDynamicAlloc(ASTCtx));
     else if (const auto *E = Ptr.getDeclDesc()->asExpr())
-      BaseAlignment = GetAlignOfExpr(S.getASTContext(), E, UETT_AlignOf);
+      BaseAlignment = GetAlignOfExpr(ASTCtx, E, UETT_AlignOf);
 
     if (BaseAlignment < Align) {
       S.CCEDiag(Call->getArg(0),
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index 5d11321d09079..7a1f80b48600b 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -205,9 +205,10 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const 
{
     Base = VD;
   else if (const auto *E = Desc->asExpr()) {
     if (block()->isDynamic()) {
-      QualType AllocatedType = 
getDeclPtr().getFieldDesc()->getDataType(ASTCtx);
-      DynamicAllocLValue DA(*block()->DynAllocId);
-      Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);
+      DynamicAllocLValue DA(*block()->DynAllocId,
+                            Desc->computeAlignForDynamicAlloc(ASTCtx));
+      Base =
+          APValue::LValueBase::getDynamicAlloc(DA, Desc->getDataType(ASTCtx));
     } else {
       Base = E;
     }
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index def57665e0998..ac85b62d435a9 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -77,6 +77,9 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat 
B, llvm::APFloat C,
 CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
                          UnaryExprOrTypeTrait ExprKind);
 
+uint64_t GetAlignOfDynamicAlloc(const ASTContext &Ctx, QualType AllocType,
+                                const Expr *AllocExpr);
+
 uint8_t GFNIMultiplicativeInverse(uint8_t Byte);
 uint8_t GFNIMul(uint8_t AByte, uint8_t BByte);
 uint8_t GFNIAffine(uint8_t XByte, const llvm::APInt &AQword,
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 8618979d1eba0..34324ba5bd135 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -59,6 +59,7 @@
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/bit.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/SaveAndRestore.h"
@@ -766,12 +767,14 @@ namespace {
 
     /// Get the kind of the allocation. This must match between allocation
     /// and deallocation.
-    Kind getKind() const {
+    static Kind kindOfExpr(const Expr *AllocExpr) {
       if (auto *NE = dyn_cast<CXXNewExpr>(AllocExpr))
         return NE->isArray() ? ArrayNew : New;
       assert(isa<CallExpr>(AllocExpr));
       return StdAllocator;
     }
+
+    Kind getKind() const { return kindOfExpr(AllocExpr); }
   };
 
   struct DynAllocOrder {
@@ -1956,13 +1959,92 @@ APValue 
&CallStackFrame::createLocal(APValue::LValueBase Base, const void *Key,
   return Result;
 }
 
+uint64_t GetAlignOfDynamicAlloc(const ASTContext &Ctx, QualType AllocType,
+                                const Expr *AllocExpr) {
+  const TargetInfo &TI = Ctx.getTargetInfo();
+  const uint64_t DefaultNewAlign = TI.getNewAlign();
+  const uint64_t MaxFundamentalAlign =
+      std::max(TI.getLongLongAlign(), TI.getLongDoubleAlign());
+  const DynAlloc::Kind AllocKind = DynAlloc::kindOfExpr(AllocExpr);
+
+  const uint64_t AllocSize = Ctx.getTypeSize(AllocType);
+
+  // Allocating a zero-sized array is allowed, however it doesn't have
+  // any alignment guarantees.
+  if ((AllocKind == DynAlloc::Kind::ArrayNew ||
+       AllocKind == DynAlloc::Kind::StdAllocator) &&
+      AllocSize == 0)
+    return TI.getCharWidth();
+
+  // For the non-array form, the size of the allocated object should be
+  // p...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/174549
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to