1 | //===-- InterpBlock.h - 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 | #ifndef LLVM_CLANG_AST_INTERP_BLOCK_H |
14 | #define LLVM_CLANG_AST_INTERP_BLOCK_H |
15 | |
16 | #include "Descriptor.h" |
17 | #include "clang/AST/Decl.h" |
18 | #include "clang/AST/DeclCXX.h" |
19 | #include "clang/AST/Expr.h" |
20 | #include "clang/AST/ComparisonCategories.h" |
21 | #include "llvm/ADT/PointerUnion.h" |
22 | #include "llvm/Support/raw_ostream.h" |
23 | |
24 | namespace clang { |
25 | namespace interp { |
26 | class Block; |
27 | class DeadBlock; |
28 | class InterpState; |
29 | class Pointer; |
30 | enum PrimType : unsigned; |
31 | |
32 | /// A memory block, either on the stack or in the heap. |
33 | /// |
34 | /// The storage described by the block is immediately followed by |
35 | /// optional metadata, which is followed by the actual data. |
36 | /// |
37 | /// Block* rawData() data() |
38 | /// │ │ │ |
39 | /// │ │ │ |
40 | /// ▼ ▼ ▼ |
41 | /// ┌───────────────┬─────────────────────────┬─────────────────┐ |
42 | /// │ Block │ Metadata │ Data │ |
43 | /// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │ |
44 | /// └───────────────┴─────────────────────────┴─────────────────┘ |
45 | /// |
46 | /// Desc->getAllocSize() describes the size after the Block, i.e. |
47 | /// the data size and the metadata size. |
48 | /// |
49 | class Block final { |
50 | public: |
51 | /// Creates a new block. |
52 | Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc, |
53 | bool IsStatic = false, bool IsExtern = false) |
54 | : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {} |
55 | |
56 | Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) |
57 | : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern), |
58 | Desc(Desc) {} |
59 | |
60 | /// Returns the block's descriptor. |
61 | const Descriptor *getDescriptor() const { return Desc; } |
62 | /// Checks if the block has any live pointers. |
63 | bool hasPointers() const { return Pointers; } |
64 | /// Checks if the block is extern. |
65 | bool isExtern() const { return IsExtern; } |
66 | /// Checks if the block has static storage duration. |
67 | bool isStatic() const { return IsStatic; } |
68 | /// Checks if the block is temporary. |
69 | bool isTemporary() const { return Desc->IsTemporary; } |
70 | /// Returns the size of the block. |
71 | unsigned getSize() const { return Desc->getAllocSize(); } |
72 | /// Returns the declaration ID. |
73 | std::optional<unsigned> getDeclID() const { return DeclID; } |
74 | bool isInitialized() const { return IsInitialized; } |
75 | |
76 | /// Returns a pointer to the stored data. |
77 | /// You are allowed to read Desc->getSize() bytes from this address. |
78 | std::byte *data() { |
79 | // rawData might contain metadata as well. |
80 | size_t DataOffset = Desc->getMetadataSize(); |
81 | return rawData() + DataOffset; |
82 | } |
83 | const std::byte *data() const { |
84 | // rawData might contain metadata as well. |
85 | size_t DataOffset = Desc->getMetadataSize(); |
86 | return rawData() + DataOffset; |
87 | } |
88 | |
89 | /// Returns a pointer to the raw data, including metadata. |
90 | /// You are allowed to read Desc->getAllocSize() bytes from this address. |
91 | std::byte *rawData() { |
92 | return reinterpret_cast<std::byte *>(this) + sizeof(Block); |
93 | } |
94 | const std::byte *rawData() const { |
95 | return reinterpret_cast<const std::byte *>(this) + sizeof(Block); |
96 | } |
97 | |
98 | /// Returns a view over the data. |
99 | template <typename T> |
100 | T &deref() { return *reinterpret_cast<T *>(data()); } |
101 | template <typename T> const T &deref() const { |
102 | return *reinterpret_cast<const T *>(data()); |
103 | } |
104 | |
105 | /// Invokes the constructor. |
106 | void invokeCtor() { |
107 | std::memset(s: rawData(), c: 0, n: Desc->getAllocSize()); |
108 | if (Desc->CtorFn) |
109 | Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, |
110 | /*isActive=*/true, Desc); |
111 | IsInitialized = true; |
112 | } |
113 | |
114 | /// Invokes the Destructor. |
115 | void invokeDtor() { |
116 | if (Desc->DtorFn) |
117 | Desc->DtorFn(this, data(), Desc); |
118 | IsInitialized = false; |
119 | } |
120 | |
121 | void dump() const { dump(OS&: llvm::errs()); } |
122 | void dump(llvm::raw_ostream &OS) const; |
123 | |
124 | protected: |
125 | friend class Pointer; |
126 | friend class DeadBlock; |
127 | friend class InterpState; |
128 | |
129 | Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead) |
130 | : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {} |
131 | |
132 | /// Deletes a dead block at the end of its lifetime. |
133 | void cleanup(); |
134 | |
135 | /// Pointer chain management. |
136 | void addPointer(Pointer *P); |
137 | void removePointer(Pointer *P); |
138 | void replacePointer(Pointer *Old, Pointer *New); |
139 | #ifndef NDEBUG |
140 | bool hasPointer(const Pointer *P) const; |
141 | #endif |
142 | |
143 | /// Start of the chain of pointers. |
144 | Pointer *Pointers = nullptr; |
145 | /// Unique identifier of the declaration. |
146 | std::optional<unsigned> DeclID; |
147 | /// Flag indicating if the block has static storage duration. |
148 | bool IsStatic = false; |
149 | /// Flag indicating if the block is an extern. |
150 | bool IsExtern = false; |
151 | /// Flag indicating if the pointer is dead. This is only ever |
152 | /// set once, when converting the Block to a DeadBlock. |
153 | bool IsDead = false; |
154 | /// Flag indicating if the block contents have been initialized |
155 | /// via invokeCtor. |
156 | bool IsInitialized = false; |
157 | /// Pointer to the stack slot descriptor. |
158 | const Descriptor *Desc; |
159 | }; |
160 | |
161 | /// Descriptor for a dead block. |
162 | /// |
163 | /// Dead blocks are chained in a double-linked list to deallocate them |
164 | /// whenever pointers become dead. |
165 | class DeadBlock final { |
166 | public: |
167 | /// Copies the block. |
168 | DeadBlock(DeadBlock *&Root, Block *Blk); |
169 | |
170 | /// Returns a pointer to the stored data. |
171 | std::byte *data() { return B.data(); } |
172 | std::byte *rawData() { return B.rawData(); } |
173 | |
174 | private: |
175 | friend class Block; |
176 | friend class InterpState; |
177 | |
178 | void free(); |
179 | |
180 | /// Root pointer of the list. |
181 | DeadBlock *&Root; |
182 | /// Previous block in the list. |
183 | DeadBlock *Prev; |
184 | /// Next block in the list. |
185 | DeadBlock *Next; |
186 | |
187 | /// Actual block storing data and tracking pointers. |
188 | Block B; |
189 | }; |
190 | |
191 | } // namespace interp |
192 | } // namespace clang |
193 | |
194 | #endif |
195 | |