xref: /freebsd-src/contrib/llvm-project/clang/lib/AST/Interp/DynamicAllocator.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
16*0fca6ea1SDimitry Andric DynamicAllocator::~DynamicAllocator() { cleanup(); }
17*0fca6ea1SDimitry Andric 
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 
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 
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 
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 
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