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,Form AllocForm)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
allocate(const Descriptor * ElementDesc,size_t NumElements,unsigned EvalID,Form AllocForm)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
allocate(const Descriptor * D,unsigned EvalID,Form AllocForm)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
deallocate(const Expr * Source,const Block * BlockToDelete,InterpState & S)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