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