[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