xref: /llvm-project/clang/lib/AST/ByteCode/DynamicAllocator.cpp (revision 610b85395d233b5cbc5a1f112ac3039b888c0a88)
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                                   Form AllocForm) {
45   // Create a new descriptor for an array of the specified size and
46   // element type.
47   const Descriptor *D = allocateDescriptor(
48       Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,
49       /*IsTemporary=*/false, /*IsMutable=*/false);
50 
51   return allocate(D, EvalID, AllocForm);
52 }
53 
54 Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
55                                   size_t NumElements, unsigned EvalID,
56                                   Form AllocForm) {
57   // Create a new descriptor for an array of the specified size and
58   // element type.
59   const Descriptor *D = allocateDescriptor(
60       ElementDesc->asExpr(), ElementDesc, Descriptor::InlineDescMD, NumElements,
61       /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
62   return allocate(D, EvalID, AllocForm);
63 }
64 
65 Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
66                                   Form AllocForm) {
67   assert(D);
68   assert(D->asExpr());
69 
70   auto Memory =
71       std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize());
72   auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false);
73   B->invokeCtor();
74 
75   InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData());
76   ID->Desc = D;
77   ID->IsActive = true;
78   ID->Offset = sizeof(InlineDescriptor);
79   ID->IsBase = false;
80   ID->IsFieldMutable = false;
81   ID->IsConst = false;
82   ID->IsInitialized = false;
83 
84   B->IsDynamic = true;
85 
86   if (auto It = AllocationSites.find(D->asExpr()); It != AllocationSites.end())
87     It->second.Allocations.emplace_back(std::move(Memory));
88   else
89     AllocationSites.insert(
90         {D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});
91   return B;
92 }
93 
94 bool DynamicAllocator::deallocate(const Expr *Source,
95                                   const Block *BlockToDelete, InterpState &S) {
96   auto It = AllocationSites.find(Source);
97   if (It == AllocationSites.end())
98     return false;
99 
100   auto &Site = It->second;
101   assert(Site.size() > 0);
102 
103   // Find the Block to delete.
104   auto AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {
105     const Block *B = reinterpret_cast<const Block *>(A.Memory.get());
106     return BlockToDelete == B;
107   });
108 
109   assert(AllocIt != Site.Allocations.end());
110 
111   Block *B = reinterpret_cast<Block *>(AllocIt->Memory.get());
112   B->invokeDtor();
113 
114   S.deallocate(B);
115   Site.Allocations.erase(AllocIt);
116 
117   if (Site.size() == 0)
118     AllocationSites.erase(It);
119 
120   return true;
121 }
122