xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/DynamicAllocator.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
~DynamicAllocator()16 DynamicAllocator::~DynamicAllocator() { cleanup(); }
17 
cleanup()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 
allocate(const Expr * Source,PrimType T,size_t NumElements,unsigned EvalID)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 
allocate(const Descriptor * ElementDesc,size_t NumElements,unsigned EvalID)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 
allocate(const Descriptor * D,unsigned EvalID)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 
deallocate(const Expr * Source,const Block * BlockToDelete,InterpState & S)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