clang 24.0.0git
DynamicAllocator.cpp
Go to the documentation of this file.
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
11using namespace clang;
12using namespace clang::interp;
13
15
17 // Invoke destructors of all the blocks and as a last restort,
18 // reset all the pointers pointing to them to null pointees.
19 // This should never show up in diagnostics, but it's necessary
20 // for us to not cause use-after-free problems.
21 for (auto &Iter : AllocationSites) {
22 auto &AllocSite = Iter.second;
23 for (auto &Alloc : AllocSite.Allocations) {
24 Block *B = Alloc.block();
25 assert(!B->isDead());
26 assert(B->isInitialized());
27 B->invokeDtor();
28 B->removePointers();
29 }
30 }
31
32 AllocationSites.clear();
33}
34
36 size_t NumElements, unsigned EvalID,
37 Form AllocForm) {
38 // Create a new descriptor for an array of the specified size and
39 // element type.
40 const Descriptor *D =
41 allocateDescriptor(Source, nullptr, T, NumElements, /*IsConst=*/false,
42 /*IsTemporary=*/false, /*IsMutable=*/false,
43 /*IsVolatile=*/false);
44
45 return allocate(D, EvalID, AllocForm);
46}
47
49 size_t NumElements, unsigned EvalID,
50 Form AllocForm) {
51 // Create a new descriptor for an array of the specified size and
52 // element type.
53 // FIXME: Pass proper element type.
54 const Descriptor *D = allocateDescriptor(
55 ElementDesc->asExpr(), nullptr, ElementDesc, NumElements,
56 /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
57 return allocate(D, EvalID, AllocForm);
58}
59
60Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
61 Form AllocForm) {
62 assert(D);
63 assert(D->asExpr());
64
65 // Garbage collection. Remove all dead allocations that don't have pointers to
66 // them anymore.
67 llvm::erase_if(DeadAllocations, [](Allocation &Alloc) -> bool {
68 return !Alloc.block()->hasPointers();
69 });
70
71 auto Memory =
72 std::make_unique<std::byte[]>(Block::allocSize(D, Block::Local));
73 auto *B = new (Memory.get()) Block(EvalID, D, Block::Local);
74 B->invokeCtorNoMemset();
75 auto &ID = B->getBlockDesc<LocalInlineDescriptor>();
76
77 if (D->isCompositeArray())
79 else
80 ID.setLifetime(AllocForm == Form::Operator ? Lifetime::Ended
82
83 if (auto It = AllocationSites.find(D->asExpr());
84 It != AllocationSites.end()) {
85 It->second.Allocations.emplace_back(std::move(Memory));
86 B->setDynAllocId(It->second.NumAllocs);
87 ++It->second.NumAllocs;
88 } else {
89 AllocationSites.insert(
90 {D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});
91 B->setDynAllocId(0);
92 }
93 assert(B->isDynamic());
94 return B;
95}
96
98 const Block *BlockToDelete) {
99 auto It = AllocationSites.find(Source);
100 if (It == AllocationSites.end())
101 return false;
102
103 auto &Site = It->second;
104 assert(!Site.empty());
105
106 // Find the Block to delete.
107 auto *AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {
108 return BlockToDelete == A.block();
109 });
110
111 // The allocation site it fine, but this block doesn't belong to it. Must've
112 // already been deleted.
113 if (AllocIt == Site.Allocations.end())
114 return false;
115
116 Block *B = AllocIt->block();
117 assert(B->isInitialized());
118 assert(!B->isDead());
119 B->invokeDtor();
120
121 // Almost all our dynamic allocations have a pointer pointing to them
122 // when we deallocate them, since otherwise we can't call delete() at all.
123 // This means that we would usually need to create DeadBlocks for all of them.
124 // To work around that, we instead mark them as dead without moving the data
125 // over to a DeadBlock and simply keep the block in a separate DeadAllocations
126 // list.
127 if (B->hasPointers()) {
128 B->markDead();
129 DeadAllocations.push_back(std::move(*AllocIt));
130 Site.Allocations.erase(AllocIt);
131
132 if (Site.size() == 0)
133 AllocationSites.erase(It);
134 return true;
135 }
136
137 // Get rid of the allocation altogether.
138 Site.Allocations.erase(AllocIt);
139 if (Site.empty())
140 AllocationSites.erase(It);
141
142 return true;
143}
This represents one expression.
Definition Expr.h:114
A memory block, either on the stack or in the heap.
Definition Block.h:68
static unsigned allocSize(const Descriptor *D, BlockKind K)
Number of bytes needed to allocate the block.
Definition Block.h:83
void invokeDtor()
Invokes the Destructor.
Definition Block.h:181
bool isDead() const
Definition Block.h:117
bool isInitialized() const
Returns whether the data of this block has been initialized via invoking the Ctor func.
Definition Block.h:130
bool hasPointers() const
Checks if the block has any live pointers.
Definition Block.h:105
void removePointers()
Make all pointers that currently point to this block point to nullptr.
Definition Block.cpp:115
Block * allocate(const Descriptor *D, unsigned EvalID, Form AllocForm)
Allocate ONE element of the given descriptor.
bool deallocate(const Expr *Source, const Block *BlockToDelete)
Deallocate the given source+block combination.
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:35
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
Definition Interp.h:3927
Top level wrappers for InstallAPI frontend operations.
const FunctionProtoType * T
Describes a memory block created by an allocation site.
Definition Descriptor.h:126
bool isCompositeArray() const
Checks if the descriptor is of an array of composites.
Definition Descriptor.h:256
const Expr * asExpr() const
Definition Descriptor.h:205