//==-------- 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(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, unsigned EvalID) { // 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, EvalID); } Block *DynamicAllocator::allocate(const Descriptor *ElementDesc, size_t NumElements, unsigned EvalID) { // 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, EvalID); } Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID) { assert(D); assert(D->asExpr()); auto Memory = std::make_unique(sizeof(Block) + D->getAllocSize()); auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false); B->invokeCtor(); InlineDescriptor *ID = reinterpret_cast(B->rawData()); ID->Desc = D; ID->IsActive = true; ID->Offset = sizeof(InlineDescriptor); ID->IsBase = false; ID->IsFieldMutable = false; ID->IsConst = false; ID->IsInitialized = false; 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(A.Memory.get()); return BlockToDelete == B; }); assert(AllocIt != Site.Allocations.end()); Block *B = reinterpret_cast(AllocIt->Memory.get()); B->invokeDtor(); S.deallocate(B); Site.Allocations.erase(AllocIt); if (Site.size() == 0) AllocationSites.erase(It); return true; }