1//===--- Block.cpp - Allocated blocks for the interpreter -------*- 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// Defines the classes describing allocated blocks.
10//
11//===----------------------------------------------------------------------===//
12
13#include "InterpBlock.h"
14#include "Pointer.h"
15
16using namespace clang;
17using namespace clang::interp;
18
19void Block::addPointer(Pointer *P) {
20 assert(P);
21
22#ifndef NDEBUG
23 assert(!hasPointer(P));
24#endif
25 if (Pointers)
26 Pointers->BS.Prev = P;
27 P->BS.Next = Pointers;
28 P->BS.Prev = nullptr;
29 Pointers = P;
30#ifndef NDEBUG
31 assert(hasPointer(P));
32#endif
33}
34
35void Block::removePointer(Pointer *P) {
36 assert(P->isBlockPointer());
37 assert(P);
38
39#ifndef NDEBUG
40 assert(hasPointer(P));
41#endif
42
43 BlockPointer &BP = P->BS;
44
45 if (Pointers == P)
46 Pointers = BP.Next;
47
48 if (BP.Prev)
49 BP.Prev->BS.Next = BP.Next;
50 if (BP.Next)
51 BP.Next->BS.Prev = BP.Prev;
52 P->BS.Pointee = nullptr;
53#ifndef NDEBUG
54 assert(!hasPointer(P));
55#endif
56}
57
58void Block::cleanup() {
59 if (Pointers == nullptr && !isDynamic() && isDead())
60 (reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
61}
62
63void Block::replacePointer(Pointer *Old, Pointer *New) {
64 assert(Old);
65 assert(Old->isBlockPointer());
66 assert(New);
67 assert(New->isBlockPointer());
68 assert(Old != New);
69#ifndef NDEBUG
70 assert(hasPointer(Old));
71#endif
72
73 BlockPointer &OldBP = Old->BS;
74 BlockPointer &NewBP = New->BS;
75
76 if (OldBP.Prev)
77 OldBP.Prev->BS.Next = New;
78 if (OldBP.Next)
79 OldBP.Next->BS.Prev = New;
80 NewBP.Prev = OldBP.Prev;
81 NewBP.Next = OldBP.Next;
82 if (Pointers == Old)
83 Pointers = New;
84
85 OldBP.Pointee = nullptr;
86 NewBP.Pointee = this;
87#ifndef NDEBUG
88 assert(!hasPointer(Old));
89 assert(hasPointer(New));
90#endif
91}
92
93#ifndef NDEBUG
94bool Block::hasPointer(const Pointer *P) const {
95 for (const Pointer *C = Pointers; C; C = C->asBlockPointer().Next) {
96 if (C == P)
97 return true;
98 }
99 return false;
100}
101#endif
102
103void Block::movePointersTo(Block *B) {
104 assert(B != this);
105 unsigned MDDiff = static_cast<int>(B->Desc->getMetadataSize()) -
106 static_cast<int>(Desc->getMetadataSize());
107
108 while (Pointers) {
109 Pointer *P = Pointers;
110
111 this->removePointer(P);
112 P->BS.Pointee = B;
113
114 // If the metadata size changed between the two blocks, move the pointer
115 // base/offset. Realistically, this should only happen when we move pointers
116 // from a dummy pointer to a global one.
117 P->BS.Base += MDDiff;
118 P->Offset += MDDiff;
119
120 B->addPointer(P);
121 }
122 assert(!this->hasPointers());
123}
124
125DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
126 : Root(Root), B(~0u, Blk->Desc, Blk->isExtern(), Blk->IsStatic,
127 Blk->isWeak(), Blk->isDummy(), /*IsDead=*/true) {
128 // Add the block to the chain of dead blocks.
129 if (Root)
130 Root->Prev = this;
131
132 Next = Root;
133 Prev = nullptr;
134 Root = this;
135
136 B.DynAllocId = Blk->DynAllocId;
137
138 // Transfer pointers.
139 B.Pointers = Blk->Pointers;
140 for (Pointer *P = Blk->Pointers; P; P = P->asBlockPointer().Next)
141 P->BS.Pointee = &B;
142 Blk->Pointers = nullptr;
143}
144
145void DeadBlock::free() {
146 assert(!B.isInitialized());
147
148 if (Prev)
149 Prev->Next = Next;
150 if (Next)
151 Next->Prev = Prev;
152 if (Root == this)
153 Root = Next;
154 std::free(ptr: this);
155}
156