1 //==-------- DynamicAllocator.cpp - Dynamic allocations ----------*- C++ -*-==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "DynamicAllocator.h" 10 #include "InterpBlock.h" 11 #include "InterpState.h" 12 13 using namespace clang; 14 using namespace clang::interp; 15 16 DynamicAllocator::~DynamicAllocator() { cleanup(); } 17 18 void DynamicAllocator::cleanup() { 19 // Invoke destructors of all the blocks and as a last restort, 20 // reset all the pointers pointing to them to null pointees. 21 // This should never show up in diagnostics, but it's necessary 22 // for us to not cause use-after-free problems. 23 for (auto &Iter : AllocationSites) { 24 auto &AllocSite = Iter.second; 25 for (auto &Alloc : AllocSite.Allocations) { 26 Block *B = reinterpret_cast<Block *>(Alloc.Memory.get()); 27 B->invokeDtor(); 28 if (B->hasPointers()) { 29 while (B->Pointers) { 30 Pointer *Next = B->Pointers->Next; 31 B->Pointers->PointeeStorage.BS.Pointee = nullptr; 32 B->Pointers = Next; 33 } 34 B->Pointers = nullptr; 35 } 36 } 37 } 38 39 AllocationSites.clear(); 40 } 41 42 Block *DynamicAllocator::allocate(const Expr *Source, PrimType T, 43 size_t NumElements, unsigned EvalID) { 44 // Create a new descriptor for an array of the specified size and 45 // element type. 46 const Descriptor *D = allocateDescriptor( 47 Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false, 48 /*IsTemporary=*/false, /*IsMutable=*/false); 49 50 return allocate(D, EvalID); 51 } 52 53 Block *DynamicAllocator::allocate(const Descriptor *ElementDesc, 54 size_t NumElements, unsigned EvalID) { 55 // Create a new descriptor for an array of the specified size and 56 // element type. 57 const Descriptor *D = allocateDescriptor( 58 ElementDesc->asExpr(), ElementDesc, Descriptor::InlineDescMD, NumElements, 59 /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false); 60 return allocate(D, EvalID); 61 } 62 63 Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID) { 64 assert(D); 65 assert(D->asExpr()); 66 67 auto Memory = 68 std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize()); 69 auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false); 70 B->invokeCtor(); 71 72 InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData()); 73 ID->Desc = D; 74 ID->IsActive = true; 75 ID->Offset = sizeof(InlineDescriptor); 76 ID->IsBase = false; 77 ID->IsFieldMutable = false; 78 ID->IsConst = false; 79 ID->IsInitialized = false; 80 81 B->IsDynamic = true; 82 83 if (auto It = AllocationSites.find(D->asExpr()); It != AllocationSites.end()) 84 It->second.Allocations.emplace_back(std::move(Memory)); 85 else 86 AllocationSites.insert( 87 {D->asExpr(), AllocationSite(std::move(Memory), D->isArray())}); 88 return B; 89 } 90 91 bool DynamicAllocator::deallocate(const Expr *Source, 92 const Block *BlockToDelete, InterpState &S) { 93 auto It = AllocationSites.find(Source); 94 if (It == AllocationSites.end()) 95 return false; 96 97 auto &Site = It->second; 98 assert(Site.size() > 0); 99 100 // Find the Block to delete. 101 auto AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) { 102 const Block *B = reinterpret_cast<const Block *>(A.Memory.get()); 103 return BlockToDelete == B; 104 }); 105 106 assert(AllocIt != Site.Allocations.end()); 107 108 Block *B = reinterpret_cast<Block *>(AllocIt->Memory.get()); 109 B->invokeDtor(); 110 111 S.deallocate(B); 112 Site.Allocations.erase(AllocIt); 113 114 if (Site.size() == 0) 115 AllocationSites.erase(It); 116 117 return true; 118 } 119