[clang] [clang][Interp] Implement dynamic memory allocation handling (PR #70306)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 18 08:11:16 PDT 2024
================
@@ -0,0 +1,119 @@
+//==-------- DynamicAllocator.cpp - Dynamic allocations ----------*- 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;
+
+DynamicAllocator::~DynamicAllocator() { cleanup(); }
+
+void DynamicAllocator::cleanup() {
+ // Invoke destructors of all the blocks and as a last restort,
+ // reset all the pointers pointing to them to null pointees.
+ // This should never show up in diagnostics, but it's necessary
+ // for us to not cause use-after-free problems.
+ for (auto &Iter : AllocationSites) {
+ auto &AllocSite = Iter.second;
+ for (auto &Alloc : AllocSite.Allocations) {
+ Block *B = reinterpret_cast<Block *>(Alloc.Memory.get());
+ B->invokeDtor();
+ if (B->hasPointers()) {
+ while (B->Pointers) {
+ Pointer *Next = B->Pointers->Next;
+ B->Pointers->PointeeStorage.BS.Pointee = nullptr;
+ B->Pointers = Next;
+ }
+ B->Pointers = nullptr;
+ }
+ }
+ }
+
+ AllocationSites.clear();
+}
+
+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 = D;
+ ID->IsActive = true;
+ ID->Offset = sizeof(InlineDescriptor);
+ ID->IsBase = false;
+ ID->IsFieldMutable = false;
+ ID->IsConst = false;
+ ID->IsInitialized = false;
+ assert(ID->Desc);
+
+ B->IsDynamic = true;
+
+ 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;
+}
+
+bool DynamicAllocator::deallocate(const Expr *Source,
+ const Block *BlockToDelete, InterpState &S) {
+ auto It = AllocationSites.find(Source);
+ if (It == AllocationSites.end())
+ return false;
+
+ auto &Site = It->second;
+ assert(Site.size() > 0);
+
+ // Find the Block to delete.
+ auto AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {
+ const Block *B = reinterpret_cast<const Block *>(A.Memory.get());
+ return BlockToDelete == B;
+ });
+
+ assert(AllocIt != Site.Allocations.end());
+
+ Block *B = reinterpret_cast<Block *>(AllocIt->Memory.get());
+ B->invokeDtor();
+
+ S.deallocate(B);
+ Site.Allocations.erase(AllocIt);
+
+ if (Site.size() == 0)
----------------
tbaederr wrote:
`Size.size()` just returns `Size.Allocations.size()`, so this is affected by the `erase()` call above.
https://github.com/llvm/llvm-project/pull/70306
More information about the cfe-commits
mailing list