1313using namespace clang ;
1414using namespace clang ::interp;
1515
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-
3516DynamicAllocator::~DynamicAllocator () { cleanup (); }
3617
3718void DynamicAllocator::cleanup () {
@@ -42,8 +23,11 @@ void DynamicAllocator::cleanup() {
4223 for (auto &Iter : AllocationSites) {
4324 auto &AllocSite = Iter.second ;
4425 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 ());
4629 B->invokeDtor ();
30+
4731 if (B->hasPointers ()) {
4832 while (B->Pointers ) {
4933 Pointer *Next = B->Pointers ->asBlockPointer ().Next ;
@@ -89,6 +73,12 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
8973 assert (D);
9074 assert (D->asExpr ());
9175
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+
9282 auto Memory =
9383 std::make_unique<std::byte[]>(sizeof (Block) + D->getAllocSize ());
9484 auto *B = new (Memory.get ()) Block (EvalID, D, /* isStatic=*/ false );
@@ -132,18 +122,34 @@ bool DynamicAllocator::deallocate(const Expr *Source,
132122
133123 // Find the Block to delete.
134124 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 ();
137126 });
138127
139128 assert (AllocIt != Site.Allocations .end ());
140129
141- Block *B = reinterpret_cast <Block *>(AllocIt->Memory .get ());
130+ Block *B = AllocIt->block ();
131+ assert (B->isInitialized ());
132+ assert (!B->IsDead );
142133 B->invokeDtor ();
143134
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+ }
146150
151+ // Get rid of the allocation altogether.
152+ Site.Allocations .erase (AllocIt);
147153 if (Site.empty ())
148154 AllocationSites.erase (It);
149155
0 commit comments