13
13
using namespace clang ;
14
14
using namespace clang ::interp;
15
15
16
- // FIXME: There is a peculiar problem with the way we track pointers
17
- // to blocks and the way we allocate dynamic memory.
18
- //
19
- // When we have code like this:
20
- // while (true) {
21
- // char *buffer = new char[1024];
22
- // delete[] buffer;
23
- // }
24
- //
25
- // We have a local variable 'buffer' pointing to the heap allocated memory.
26
- // When deallocating the memory via delete[], that local variable still
27
- // points to the memory, which means we will create a DeadBlock for it and move
28
- // it over to that block, essentially duplicating the allocation. Moving
29
- // the data is also slow.
30
- //
31
- // However, when we actually try to access the allocation after it has been
32
- // freed, we need the block to still exist (alive or dead) so we can tell
33
- // that it's a dynamic allocation.
34
-
35
16
DynamicAllocator::~DynamicAllocator () { cleanup (); }
36
17
37
18
void DynamicAllocator::cleanup () {
@@ -42,8 +23,11 @@ void DynamicAllocator::cleanup() {
42
23
for (auto &Iter : AllocationSites) {
43
24
auto &AllocSite = Iter.second ;
44
25
for (auto &Alloc : AllocSite.Allocations ) {
45
- Block *B = reinterpret_cast <Block *>(Alloc.Memory .get ());
26
+ Block *B = Alloc.block ();
27
+ assert (!B->IsDead );
28
+ assert (B->isInitialized ());
46
29
B->invokeDtor ();
30
+
47
31
if (B->hasPointers ()) {
48
32
while (B->Pointers ) {
49
33
Pointer *Next = B->Pointers ->asBlockPointer ().Next ;
@@ -89,6 +73,12 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
89
73
assert (D);
90
74
assert (D->asExpr ());
91
75
76
+ // Garbage collection. Remove all dead allocations that don't have pointers to
77
+ // them anymore.
78
+ llvm::erase_if (DeadAllocations, [](Allocation &Alloc) -> bool {
79
+ return !Alloc.block ()->hasPointers ();
80
+ });
81
+
92
82
auto Memory =
93
83
std::make_unique<std::byte[]>(sizeof (Block) + D->getAllocSize ());
94
84
auto *B = new (Memory.get ()) Block (EvalID, D, /* isStatic=*/ false );
@@ -132,18 +122,34 @@ bool DynamicAllocator::deallocate(const Expr *Source,
132
122
133
123
// Find the Block to delete.
134
124
auto AllocIt = llvm::find_if (Site.Allocations , [&](const Allocation &A) {
135
- const Block *B = reinterpret_cast <const Block *>(A.Memory .get ());
136
- return BlockToDelete == B;
125
+ return BlockToDelete == A.block ();
137
126
});
138
127
139
128
assert (AllocIt != Site.Allocations .end ());
140
129
141
- Block *B = reinterpret_cast <Block *>(AllocIt->Memory .get ());
130
+ Block *B = AllocIt->block ();
131
+ assert (B->isInitialized ());
132
+ assert (!B->IsDead );
142
133
B->invokeDtor ();
143
134
144
- S.deallocate (B);
145
- Site.Allocations .erase (AllocIt);
135
+ // Almost all our dynamic allocations have a pointer pointing to them
136
+ // when we deallocate them, since otherwise we can't call delete() at all.
137
+ // This means that we would usually need to create DeadBlocks for all of them.
138
+ // To work around that, we instead mark them as dead without moving the data
139
+ // over to a DeadBlock and simply keep the block in a separate DeadAllocations
140
+ // list.
141
+ if (B->hasPointers ()) {
142
+ B->IsDead = true ;
143
+ DeadAllocations.push_back (std::move (*AllocIt));
144
+ Site.Allocations .erase (AllocIt);
145
+
146
+ if (Site.size () == 0 )
147
+ AllocationSites.erase (It);
148
+ return true ;
149
+ }
146
150
151
+ // Get rid of the allocation altogether.
152
+ Site.Allocations .erase (AllocIt);
147
153
if (Site.empty ())
148
154
AllocationSites.erase (It);
149
155
0 commit comments