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
13using namespace clang;
14using namespace clang::interp;
15
16DynamicAllocator::~DynamicAllocator() { cleanup(); }
17
18void 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
42Block *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 Args&: Source, Args&: T, Args: Descriptor::InlineDescMD, Args&: NumElements, /*IsConst=*/Args: false,
48 /*IsTemporary=*/Args: false, /*IsMutable=*/Args: false);
49
50 return allocate(D, EvalID);
51}
52
53Block *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 Args: ElementDesc->asExpr(), Args&: ElementDesc, Args: Descriptor::InlineDescMD, Args&: NumElements,
59 /*IsConst=*/Args: false, /*IsTemporary=*/Args: false, /*IsMutable=*/Args: false);
60 return allocate(D, EvalID);
61}
62
63Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID) {
64 assert(D);
65 assert(D->asExpr());
66
67 auto Memory =
68 std::make_unique<std::byte[]>(num: 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(Val: D->asExpr()); It != AllocationSites.end())
84 It->second.Allocations.emplace_back(Args: std::move(Memory));
85 else
86 AllocationSites.insert(
87 KV: {D->asExpr(), AllocationSite(std::move(Memory), D->isArray())});
88 return B;
89}
90
91bool DynamicAllocator::deallocate(const Expr *Source,
92 const Block *BlockToDelete, InterpState &S) {
93 auto It = AllocationSites.find(Val: 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(Range&: Site.Allocations, P: [&](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(CI: AllocIt);
113
114 if (Site.size() == 0)
115 AllocationSites.erase(I: It);
116
117 return true;
118}
119