[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