[clang] [clang][Interp] Implement dynamic memory allocation handling (PR #70306)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 26 01:49:33 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

Implement handling for new/delete/new[]/delete[] expressions via a new `DynamicAllocator` class.

This introduces four new opcodes:
 - `Alloc` - Allocates one element (`new int(14)`)
 - `AllocN` - Allocates N elements of the given primitive (`new int[100]`)
 - `AllocCN` - Allocates N elements of the given (composite) descriptor (`new S[100]`)
 - `Free` - de-allocates memory allocates using any of the above.


This patch currently has a few NFC changes (like adding `const` in some places) that I will push to `main` later today. Some other changes I just noticed are in `tests/`, which I will just remove.

---

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


20 Files Affected:

- (modified) clang/lib/AST/CMakeLists.txt (+1) 
- (modified) clang/lib/AST/ExprConstant.cpp (+1-1) 
- (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+76) 
- (modified) clang/lib/AST/Interp/ByteCodeExprGen.h (+2) 
- (modified) clang/lib/AST/Interp/Context.cpp (+2-1) 
- (modified) clang/lib/AST/Interp/Descriptor.cpp (+1-1) 
- (modified) clang/lib/AST/Interp/Descriptor.h (+3-3) 
- (added) clang/lib/AST/Interp/DynamicAllocator.cpp (+91) 
- (added) clang/lib/AST/Interp/DynamicAllocator.h (+90) 
- (modified) clang/lib/AST/Interp/EvalEmitter.cpp (+3-2) 
- (modified) clang/lib/AST/Interp/Interp.cpp (+37) 
- (modified) clang/lib/AST/Interp/Interp.h (+94) 
- (modified) clang/lib/AST/Interp/InterpBlock.h (+5-5) 
- (modified) clang/lib/AST/Interp/InterpState.cpp (+17) 
- (modified) clang/lib/AST/Interp/InterpState.h (+10) 
- (modified) clang/lib/AST/Interp/Opcodes.td (+25) 
- (modified) clang/lib/AST/Interp/Pointer.cpp (+1-1) 
- (modified) clang/lib/AST/Interp/Pointer.h (+4-3) 
- (added) clang/test/AST/Interp/new-delete.cpp (+247) 
- (modified) clang/test/SemaCXX/cxx2a-consteval.cpp (+2-41) 


``````````diff
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index fe3f8c485ec1c56..1423623fb038cab 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -75,6 +75,7 @@ add_clang_library(clangAST
   Interp/Function.cpp
   Interp/InterpBuiltin.cpp
   Interp/Floating.cpp
+  Interp/DynamicAllocator.cpp
   Interp/Interp.cpp
   Interp/InterpBlock.cpp
   Interp/InterpFrame.cpp
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 6b47b8a1256477d..0a95259821466e2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6860,8 +6860,8 @@ static std::optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E,
     return std::nullopt;
   }
 
-  QualType AllocType = Pointer.Base.getDynamicAllocType();
   if (DeallocKind != (*Alloc)->getKind()) {
+    QualType AllocType = Pointer.Base.getDynamicAllocType();
     Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
         << DeallocKind << (*Alloc)->getKind() << AllocType;
     NoteLValueLocation(Info, Pointer.Base);
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 1b33c69b93aa4b9..c42fcabe3b075ba 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1617,6 +1617,82 @@ bool ByteCodeExprGen<Emitter>::VisitCXXScalarValueInitExpr(
   return this->visitZeroInitializer(classifyPrim(Ty), Ty, E);
 }
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
+  assert(classifyPrim(E->getType()) == PT_Ptr);
+  const Expr *Init = E->getInitializer();
+  QualType ElementType = E->getAllocatedType();
+  std::optional<PrimType> ElemT = classify(ElementType);
+
+  const Descriptor *Desc;
+  if (ElemT) {
+    if (E->isArray())
+      Desc = nullptr; // We're not going to use it in this case.
+    else
+      Desc = P.createDescriptor(E, *ElemT, Descriptor::InlineDescMD,
+                                /*IsConst=*/false, /*IsTemporary=*/false,
+                                /*IsMutable=*/false);
+  } else {
+    Desc = P.createDescriptor(
+        E, ElementType.getTypePtr(),
+        E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
+        /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
+  }
+
+  if (E->isArray()) {
+    assert(E->getArraySize());
+    PrimType SizeT = classifyPrim((*E->getArraySize())->getType());
+
+    if (!this->visit(*E->getArraySize()))
+      return false;
+
+    if (ElemT) {
+      // N primitive elements.
+      if (!this->emitAllocN(SizeT, *ElemT, E, E))
+        return false;
+    } else {
+      // N Composite elements.
+      if (!this->emitAllocCN(SizeT, Desc, E))
+        return false;
+    }
+
+  } else {
+    // Allocate just one element.
+    if (!this->emitAlloc(Desc, E))
+      return false;
+
+    if (Init) {
+      if (ElemT) {
+        if (!this->visit(Init))
+          return false;
+
+        if (!this->emitInit(*ElemT, E))
+          return false;
+      } else {
+        // Composite.
+        if (!this->visitInitializer(Init))
+          return false;
+      }
+    }
+  }
+
+  if (DiscardResult)
+    return this->emitPopPtr(E);
+
+  return true;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
+  const Expr *Arg = E->getArgument();
+
+  // Arg must be an lvalue.
+  if (!this->visit(Arg))
+    return false;
+
+  return this->emitFree(E->isArrayForm(), E);
+}
+
 template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
   if (E->containsErrors())
     return false;
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index 83986d3dd579ed6..f65dc9494db36ef 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -107,6 +107,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
   bool VisitSourceLocExpr(const SourceLocExpr *E);
   bool VisitOffsetOfExpr(const OffsetOfExpr *E);
   bool VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E);
+  bool VisitCXXNewExpr(const CXXNewExpr *E);
+  bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index cb96e56fb5e1ad8..93da5267bc1e6f4 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -164,7 +164,7 @@ bool Context::Run(State &Parent, const Function *Func, APValue &Result) {
     State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
     if (Interpret(State, Result)) {
       assert(Stk.empty());
-      return true;
+      return !State.maybeDiagnoseDanglingAllocations();
     }
 
     // State gets destroyed here, so the Stk.clear() below doesn't accidentally
@@ -177,6 +177,7 @@ bool Context::Run(State &Parent, const Function *Func, APValue &Result) {
 
 bool Context::Check(State &Parent, llvm::Expected<bool> &&Flag) {
   if (Flag)
+    // return !Parent.maybeDiagnoseDanglingAllocations();
     return *Flag;
   handleAllErrors(Flag.takeError(), [&Parent](ByteCodeGenError &Err) {
     Parent.FFDiag(Err.getRange().getBegin(),
diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp
index abd75a8d67bbd3f..2a21f60588d46cd 100644
--- a/clang/lib/AST/Interp/Descriptor.cpp
+++ b/clang/lib/AST/Interp/Descriptor.cpp
@@ -262,7 +262,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
 }
 
 /// Arrays of composite elements.
-Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
                        unsigned NumElems, bool IsConst, bool IsTemporary,
                        bool IsMutable)
     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h
index be9a380138a7b11..2fd4e9208264525 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -72,7 +72,7 @@ struct InlineDescriptor {
   /// Flag indicating if the field is mutable (if in a record).
   unsigned IsFieldMutable : 1;
 
-  Descriptor *Desc;
+  const Descriptor *Desc;
 };
 
 /// Describes a memory block created by an allocation site.
@@ -102,7 +102,7 @@ struct Descriptor final {
   /// Pointer to the record, if block contains records.
   Record *const ElemRecord = nullptr;
   /// Descriptor of the array element.
-  Descriptor *const ElemDesc = nullptr;
+  const Descriptor *const ElemDesc = nullptr;
   /// Flag indicating if the block is mutable.
   const bool IsConst = false;
   /// Flag indicating if a field is mutable.
@@ -129,7 +129,7 @@ struct Descriptor final {
   Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
 
   /// Allocates a descriptor for an array of composites.
-  Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+  Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
              unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);
 
   /// Allocates a descriptor for an array of composites of unknown size.
diff --git a/clang/lib/AST/Interp/DynamicAllocator.cpp b/clang/lib/AST/Interp/DynamicAllocator.cpp
new file mode 100644
index 000000000000000..a353d09d1f960ea
--- /dev/null
+++ b/clang/lib/AST/Interp/DynamicAllocator.cpp
@@ -0,0 +1,91 @@
+//==---- DynamicAllocator.cpp - Types for the constexpr VM -------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DynamicAllocator.h"
+#include "InterpBlock.h"
+#include "InterpState.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,
+                                  size_t NumElements) {
+  assert(NumElements > 0);
+  // Create a new descriptor for an array of the specified size and
+  // element type.
+  const Descriptor *D = allocateDescriptor(
+      Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,
+      /*IsTemporary=*/false, /*IsMutable=*/false);
+  return allocate(D);
+}
+
+Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
+                                  size_t NumElements) {
+  assert(NumElements > 0);
+  // Create a new descriptor for an array of the specified size and
+  // element type.
+  const Descriptor *D = allocateDescriptor(
+      ElementDesc->asExpr(), ElementDesc, Descriptor::InlineDescMD, NumElements,
+      /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
+  return allocate(D);
+}
+
+Block *DynamicAllocator::allocate(const Descriptor *D) {
+  assert(D->asExpr());
+
+  auto Memory =
+      std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize());
+  auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
+  B->invokeCtor();
+
+  InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData());
+  ID->Desc = const_cast<Descriptor *>(D);
+  ID->IsActive = true;
+  ID->Offset = sizeof(InlineDescriptor);
+  ID->IsBase = false;
+  ID->IsFieldMutable = false;
+  ID->IsConst = false;
+  ID->IsInitialized = false;
+  assert(ID->Desc);
+
+  if (auto It = AllocationSites.find(D->asExpr()); It != AllocationSites.end())
+    It->second.Allocations.emplace_back(std::move(Memory));
+  else
+    AllocationSites.insert(
+        {D->asExpr(), AllocationSite(std::move(Memory), D->isArray())});
+  return B;
+}
+
+void DynamicAllocator::deallocate(const Expr *Source,
+                                  const Block *BlockToDelete, InterpState &S) {
+  assert(AllocationSites.contains(Source));
+
+  auto It = AllocationSites.find(Source);
+  assert(It != AllocationSites.end());
+
+  auto &Site = It->second;
+  assert(Site.size() > 0);
+
+  // Find the Block to delete.
+  size_t I = 0;
+  [[maybe_unused]] bool Found = false;
+  for (auto &A : Site.Allocations) {
+    Block *B = reinterpret_cast<Block *>(A.Memory.get());
+    if (B == BlockToDelete) {
+      S.deallocate(B);
+      B->invokeDtor();
+      Site.Allocations.erase(Site.Allocations.begin() + I);
+      Found = true;
+      break;
+    }
+  }
+  assert(Found);
+
+  if (Site.size() == 0)
+    AllocationSites.erase(It);
+}
diff --git a/clang/lib/AST/Interp/DynamicAllocator.h b/clang/lib/AST/Interp/DynamicAllocator.h
new file mode 100644
index 000000000000000..d2a1d96232b9730
--- /dev/null
+++ b/clang/lib/AST/Interp/DynamicAllocator.h
@@ -0,0 +1,90 @@
+//==- DynamicAllocator.h - Bytecode allocator for the constexpr VM -*- C++
+//-*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_DYNAMIC_ALLOCATOR_H
+#define LLVM_CLANG_AST_INTERP_DYNAMIC_ALLOCATOR_H
+
+#include "Descriptor.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Allocator.h"
+
+namespace clang {
+class Expr;
+namespace interp {
+class Block;
+class InterpState;
+
+/// Manages dynamic memory allocations done during bytecode interpretation.
+///
+/// We manage allocations as a map from their new-expression to a list
+/// of allocations. This is called an AllocationSite. For each site, we
+/// record whether it was allocated using new or new[], the
+/// IsArrayAllocation flag.
+///
+/// For all array allocations, we need to allocat new Descriptor instances,
+/// so the DynamicAllocator has a llvm::BumpPtrAllocator similar to Program.
+class DynamicAllocator final {
+  struct Allocation {
+    std::unique_ptr<std::byte[]> Memory;
+    Allocation(std::unique_ptr<std::byte[]> Memory)
+        : Memory(std::move(Memory)) {}
+  };
+
+  struct AllocationSite {
+    llvm::SmallVector<Allocation> Allocations;
+    bool IsArrayAllocation = false;
+
+    AllocationSite(std::unique_ptr<std::byte[]> Memory, bool Array)
+        : IsArrayAllocation(Array) {
+      Allocations.push_back({std::move(Memory)});
+    }
+
+    size_t size() const { return Allocations.size(); }
+  };
+
+public:
+  DynamicAllocator() = default;
+
+  unsigned getNumAllocations() const { return AllocationSites.size(); }
+
+  /// Allocate ONE element of the given descriptor.
+  Block *allocate(const Descriptor *D);
+  /// Allocate \p NumElements primitive elements of the given type.
+  Block *allocate(const Expr *Source, PrimType T, size_t NumElements);
+  /// Allocate \p NumElements elements of the given descriptor.
+  Block *allocate(const Descriptor *D, size_t NumElements);
+
+  /// Deallocate the given source+block combination.
+  void deallocate(const Expr *Source, const Block *BlockToDelete,
+                  InterpState &S);
+
+  /// Checks whether the allocation done at the given source is an array
+  /// allocation.
+  bool isArrayAllocation(const Expr *Source) const {
+    assert(AllocationSites.contains(Source));
+    return AllocationSites.at(Source).IsArrayAllocation;
+  }
+
+  // FIXME: Public because I'm not sure how to expose an iterator to it.
+  llvm::DenseMap<const Expr *, AllocationSite> AllocationSites;
+
+private:
+  using PoolAllocTy = llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator>;
+  PoolAllocTy DescAllocator;
+
+  /// Allocates a new descriptor.
+  template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) {
+    return new (DescAllocator) Descriptor(std::forward<Ts>(Args)...);
+  }
+};
+
+} // namespace interp
+} // namespace clang
+#endif
diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index f8942291b3b162d..14d5446f64324ee 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -35,7 +35,7 @@ EvalEmitter::~EvalEmitter() {
 
 llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
   if (this->visitExpr(E))
-    return true;
+    return S.maybeDiagnoseDanglingAllocations();
   if (BailLocation)
     return llvm::make_error<ByteCodeGenError>(*BailLocation);
   return false;
@@ -43,7 +43,7 @@ llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
 
 llvm::Expected<bool> EvalEmitter::interpretDecl(const VarDecl *VD) {
   if (this->visitDecl(VD))
-    return true;
+    return S.maybeDiagnoseDanglingAllocations();
   if (BailLocation)
     return llvm::make_error<ByteCodeGenError>(*BailLocation);
   return false;
@@ -122,6 +122,7 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; }
 
 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
+  llvm::errs() << __PRETTY_FUNCTION__ << "\n";
   // Method to recursively traverse composites.
   std::function<bool(QualType, const Pointer &, APValue &)> Composite;
   Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) {
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 8b0e7beb4a1acc1..3ec093b16c8f9c2 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -548,6 +548,43 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
   return true;
 }
 
+bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) {
+  if (S.getLangOpts().CPlusPlus20)
+    return true;
+
+  const SourceInfo &E = S.Current->getSource(OpPC);
+  S.FFDiag(E, diag::note_constexpr_new);
+  return false;
+}
+
+bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
+                         bool DeleteIsArray, const Block *B,
+                         const Expr *NewExpr) {
+  if (NewWasArray == DeleteIsArray)
+    return true;
+
+  const Descriptor *D = B->getDescriptor();
+
+  QualType TypeToDiagnose;
+  // We need to shuffle things around a bit here to get a better diagnostic,
+  // because the expression we allocated the block for was of type int*,
+  // but we want to get the array size right.
+  if (D->isArray()) {
+    QualType ElemQT = D->getType()->getPointeeType();
+    TypeToDiagnose = S.getCtx().getConstantArrayType(
+        ElemQT, APInt(64, D->getNumElems(), false), nullptr, ArrayType::Normal,
+        0);
+  } else
+    TypeToDiagnose = D->getType()->getPointeeType();
+
+  const SourceInfo &E = S.Current->getSource(OpPC);
+  S.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
+      << DeleteIsArray << 0 << TypeToDiagnose;
+  S.Note(NewExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here)
+      << NewExpr->getSourceRange();
+  return false;
+}
+
 /// We aleady know the given DeclRefExpr is invalid for some reason,
 /// now figure out why and print appropriate diagnostics.
 bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 7ef1e344224a3c3..9062e4a81772888 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -14,6 +14,7 @@
 #define LLVM_CLANG_AST_INTERP_INTERP_H
 
 #include "Boolean.h"
+#include "DynamicAllocator.h"
 #include "Floating.h"
 #include "Function.h"
 #include "FunctionPointer.h"
@@ -112,6 +113,15 @@ bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This);
 bool CheckPotentialReinterpretCast(InterpState &S, CodePtr OpPC,
                                    const Pointer &Ptr);
 
+/// Checks if dynamic memory allocation is available in the current
+/// language mode.
+bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC);
+
+/// Diagnose mismatched new[]/delete or new/delete[] pairs.
+bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray,
+                         bool DeleteIsArray, const Block *B,
+                         const Expr *NewExpr);
+
 /// Sets the given integral value to the pointer, which is of
 /// a std::{weak,partial,strong}_ordering type.
 bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
@@ -1369,6 +1379,17 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Init(InterpState &S, CodePtr OpPC) {
+  const T &Value = S.Stk.pop<T>();
+  const Pointer &Ptr = S.Stk.peek<Pointer>();
+  if (!CheckInit(S, OpPC, Ptr))
+    return false;
+  Ptr.initialize();
+  new (&Ptr.deref<T>()) T(Value);
+  return true;
+}
+
 /// 1) Pops the value from the stack
 /// 2) Peeks a pointer and gets its index \Idx
 /// 3) Sets the value on the pointer, leaving the pointer on the stack.
@@ -1990,6 +2011,79 @@ inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
   return true;
 }
 
+inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+  assert(Desc);
+
+  if (!CheckDynamicMemoryAllocation(S, OpPC))
+    return false;
+
+  DynamicAllocator &Allocator = S.getAllocator();
+  Block *B = Allocator.allocate(Desc);
+
+  S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
+
+  return true;
+}
+
+template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
+inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T,
+                   const Expr *Source) {
+  if (!CheckDynamicMemoryAllocation(S, OpPC))
+    return false;
+
+  SizeT NumElements = S.Stk.pop<SizeT>();
+
+  DynamicAllocator &Allocator = S.getAllocator();
+  Block *B = Allocator.allocate(Source, T, static_cast<size_t>(NumElements));
+
+  S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
+
+  return true;
+}
+
+template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
+inline bool AllocCN(InterpState &S, CodePtr OpPC,
+                    const Descriptor *ElementDesc) {
+  if (!CheckDynamicMemoryAllocation(S, OpPC))
+    return false;
+
+  SizeT NumElements = S.Stk.pop<SizeT>();
+
+  DynamicAllocator &Allocator = S.getAllocator();
+  Block *B = Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements));
+
+  S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
+
+  return true;
+}
+
+static inline bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArr...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/70306


More information about the cfe-commits mailing list