162 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "DynamicAllocator.h"10#include "InterpBlock.h"11#include "InterpState.h"12 13using namespace clang;14using namespace clang::interp;15 16DynamicAllocator::~DynamicAllocator() { cleanup(); }17 18void 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 necessary22 // 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 = Alloc.block();27 assert(!B->isDead());28 assert(B->isInitialized());29 B->invokeDtor();30 31 if (B->hasPointers()) {32 while (B->Pointers) {33 Pointer *Next = B->Pointers->asBlockPointer().Next;34 B->Pointers->BS.Pointee = nullptr;35 B->Pointers = Next;36 }37 B->Pointers = nullptr;38 }39 }40 }41 42 AllocationSites.clear();43}44 45Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,46 size_t NumElements, unsigned EvalID,47 Form AllocForm) {48 // Create a new descriptor for an array of the specified size and49 // element type.50 const Descriptor *D = allocateDescriptor(51 Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,52 /*IsTemporary=*/false, /*IsMutable=*/false);53 54 return allocate(D, EvalID, AllocForm);55}56 57Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,58 size_t NumElements, unsigned EvalID,59 Form AllocForm) {60 assert(ElementDesc->getMetadataSize() == 0);61 // Create a new descriptor for an array of the specified size and62 // element type.63 // FIXME: Pass proper element type.64 const Descriptor *D = allocateDescriptor(65 ElementDesc->asExpr(), nullptr, ElementDesc, Descriptor::InlineDescMD,66 NumElements,67 /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);68 return allocate(D, EvalID, AllocForm);69}70 71Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,72 Form AllocForm) {73 assert(D);74 assert(D->asExpr());75 76 // Garbage collection. Remove all dead allocations that don't have pointers to77 // them anymore.78 llvm::erase_if(DeadAllocations, [](Allocation &Alloc) -> bool {79 return !Alloc.block()->hasPointers();80 });81 82 auto Memory =83 std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize());84 auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false);85 B->invokeCtor();86 87 assert(D->getMetadataSize() == sizeof(InlineDescriptor));88 InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData());89 ID->Desc = D;90 ID->IsActive = true;91 ID->Offset = sizeof(InlineDescriptor);92 ID->IsBase = false;93 ID->IsFieldMutable = false;94 ID->IsConst = false;95 ID->IsInitialized = false;96 ID->IsVolatile = false;97 98 if (D->isCompositeArray())99 ID->LifeState = Lifetime::Started;100 else101 ID->LifeState =102 AllocForm == Form::Operator ? Lifetime::Ended : Lifetime::Started;103 104 if (auto It = AllocationSites.find(D->asExpr());105 It != AllocationSites.end()) {106 It->second.Allocations.emplace_back(std::move(Memory));107 B->setDynAllocId(It->second.NumAllocs);108 ++It->second.NumAllocs;109 } else {110 AllocationSites.insert(111 {D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});112 B->setDynAllocId(0);113 }114 assert(B->isDynamic());115 return B;116}117 118bool DynamicAllocator::deallocate(const Expr *Source,119 const Block *BlockToDelete, InterpState &S) {120 auto It = AllocationSites.find(Source);121 if (It == AllocationSites.end())122 return false;123 124 auto &Site = It->second;125 assert(!Site.empty());126 127 // Find the Block to delete.128 auto *AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {129 return BlockToDelete == A.block();130 });131 132 assert(AllocIt != Site.Allocations.end());133 134 Block *B = AllocIt->block();135 assert(B->isInitialized());136 assert(!B->isDead());137 B->invokeDtor();138 139 // Almost all our dynamic allocations have a pointer pointing to them140 // when we deallocate them, since otherwise we can't call delete() at all.141 // This means that we would usually need to create DeadBlocks for all of them.142 // To work around that, we instead mark them as dead without moving the data143 // over to a DeadBlock and simply keep the block in a separate DeadAllocations144 // list.145 if (B->hasPointers()) {146 B->AccessFlags |= Block::DeadFlag;147 DeadAllocations.push_back(std::move(*AllocIt));148 Site.Allocations.erase(AllocIt);149 150 if (Site.size() == 0)151 AllocationSites.erase(It);152 return true;153 }154 155 // Get rid of the allocation altogether.156 Site.Allocations.erase(AllocIt);157 if (Site.empty())158 AllocationSites.erase(It);159 160 return true;161}162