1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
//==-------- DynamicAllocator.cpp - Dynamic allocations ----------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DynamicAllocator.h"
#include "InterpBlock.h"
#include "InterpState.h"
using namespace clang;
using namespace clang::interp;
DynamicAllocator::~DynamicAllocator() { cleanup(); }
void DynamicAllocator::cleanup() {
// Invoke destructors of all the blocks and as a last restort,
// reset all the pointers pointing to them to null pointees.
// This should never show up in diagnostics, but it's necessary
// for us to not cause use-after-free problems.
for (auto &Iter : AllocationSites) {
auto &AllocSite = Iter.second;
for (auto &Alloc : AllocSite.Allocations) {
Block *B = Alloc.block();
assert(!B->isDead());
assert(B->isInitialized());
B->invokeDtor();
if (B->hasPointers()) {
while (B->Pointers) {
Pointer *Next = B->Pointers->asBlockPointer().Next;
B->Pointers->PointeeStorage.BS.Pointee = nullptr;
B->Pointers = Next;
}
B->Pointers = nullptr;
}
}
}
AllocationSites.clear();
}
Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,
size_t NumElements, unsigned EvalID,
Form AllocForm) {
// Create a new descriptor for an array of the specified size and
// element type.
const Descriptor *D = allocateDescriptor(
Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false,
/*IsTemporary=*/false, /*IsMutable=*/false);
return allocate(D, EvalID, AllocForm);
}
Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
size_t NumElements, unsigned EvalID,
Form AllocForm) {
assert(ElementDesc->getMetadataSize() == 0);
// Create a new descriptor for an array of the specified size and
// element type.
// FIXME: Pass proper element type.
const Descriptor *D = allocateDescriptor(
ElementDesc->asExpr(), nullptr, ElementDesc, Descriptor::InlineDescMD,
NumElements,
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
return allocate(D, EvalID, AllocForm);
}
Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
Form AllocForm) {
assert(D);
assert(D->asExpr());
// Garbage collection. Remove all dead allocations that don't have pointers to
// them anymore.
llvm::erase_if(DeadAllocations, [](Allocation &Alloc) -> bool {
return !Alloc.block()->hasPointers();
});
auto Memory =
std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize());
auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false);
B->invokeCtor();
assert(D->getMetadataSize() == sizeof(InlineDescriptor));
InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData());
ID->Desc = D;
ID->IsActive = true;
ID->Offset = sizeof(InlineDescriptor);
ID->IsBase = false;
ID->IsFieldMutable = false;
ID->IsConst = false;
ID->IsInitialized = false;
ID->IsVolatile = false;
if (D->isCompositeArray())
ID->LifeState = Lifetime::Started;
else
ID->LifeState =
AllocForm == Form::Operator ? Lifetime::Ended : Lifetime::Started;
if (auto It = AllocationSites.find(D->asExpr());
It != AllocationSites.end()) {
It->second.Allocations.emplace_back(std::move(Memory));
B->setDynAllocId(It->second.NumAllocs);
++It->second.NumAllocs;
} else {
AllocationSites.insert(
{D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});
B->setDynAllocId(0);
}
assert(B->isDynamic());
return B;
}
bool DynamicAllocator::deallocate(const Expr *Source,
const Block *BlockToDelete, InterpState &S) {
auto It = AllocationSites.find(Source);
if (It == AllocationSites.end())
return false;
auto &Site = It->second;
assert(!Site.empty());
// Find the Block to delete.
auto *AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {
return BlockToDelete == A.block();
});
assert(AllocIt != Site.Allocations.end());
Block *B = AllocIt->block();
assert(B->isInitialized());
assert(!B->isDead());
B->invokeDtor();
// Almost all our dynamic allocations have a pointer pointing to them
// when we deallocate them, since otherwise we can't call delete() at all.
// This means that we would usually need to create DeadBlocks for all of them.
// To work around that, we instead mark them as dead without moving the data
// over to a DeadBlock and simply keep the block in a separate DeadAllocations
// list.
if (B->hasPointers()) {
B->AccessFlags |= Block::DeadFlag;
DeadAllocations.push_back(std::move(*AllocIt));
Site.Allocations.erase(AllocIt);
if (Site.size() == 0)
AllocationSites.erase(It);
return true;
}
// Get rid of the allocation altogether.
Site.Allocations.erase(AllocIt);
if (Site.empty())
AllocationSites.erase(It);
return true;
}
|