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