xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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(unsigned EvalID, const std::optional<unsigned> &DeclID,
53         const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
EvalID(EvalID)54       : EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern),
55         IsDynamic(false), Desc(Desc) {
56     assert(Desc);
57   }
58 
59   Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
60         bool IsExtern = false)
EvalID(EvalID)61       : EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic),
62         IsExtern(IsExtern), IsDynamic(false), Desc(Desc) {
63     assert(Desc);
64   }
65 
66   /// Returns the block's descriptor.
getDescriptor()67   const Descriptor *getDescriptor() const { return Desc; }
68   /// Checks if the block has any live pointers.
hasPointers()69   bool hasPointers() const { return Pointers; }
70   /// Checks if the block is extern.
isExtern()71   bool isExtern() const { return IsExtern; }
72   /// Checks if the block has static storage duration.
isStatic()73   bool isStatic() const { return IsStatic; }
74   /// Checks if the block is temporary.
isTemporary()75   bool isTemporary() const { return Desc->IsTemporary; }
isDynamic()76   bool isDynamic() const { return IsDynamic; }
77   /// Returns the size of the block.
getSize()78   unsigned getSize() const { return Desc->getAllocSize(); }
79   /// Returns the declaration ID.
getDeclID()80   std::optional<unsigned> getDeclID() const { return DeclID; }
81   /// Returns whether the data of this block has been initialized via
82   /// invoking the Ctor func.
isInitialized()83   bool isInitialized() const { return IsInitialized; }
84   /// The Evaluation ID this block was created in.
getEvalID()85   unsigned getEvalID() const { return EvalID; }
86 
87   /// Returns a pointer to the stored data.
88   /// You are allowed to read Desc->getSize() bytes from this address.
data()89   std::byte *data() {
90     // rawData might contain metadata as well.
91     size_t DataOffset = Desc->getMetadataSize();
92     return rawData() + DataOffset;
93   }
data()94   const std::byte *data() const {
95     // rawData might contain metadata as well.
96     size_t DataOffset = Desc->getMetadataSize();
97     return rawData() + DataOffset;
98   }
99 
100   /// Returns a pointer to the raw data, including metadata.
101   /// You are allowed to read Desc->getAllocSize() bytes from this address.
rawData()102   std::byte *rawData() {
103     return reinterpret_cast<std::byte *>(this) + sizeof(Block);
104   }
rawData()105   const std::byte *rawData() const {
106     return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
107   }
108 
109   /// Invokes the constructor.
invokeCtor()110   void invokeCtor() {
111     assert(!IsInitialized);
112     std::memset(rawData(), 0, Desc->getAllocSize());
113     if (Desc->CtorFn)
114       Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
115                    /*isActive=*/true, Desc);
116     IsInitialized = true;
117   }
118 
119   /// Invokes the Destructor.
invokeDtor()120   void invokeDtor() {
121     assert(IsInitialized);
122     if (Desc->DtorFn)
123       Desc->DtorFn(this, data(), Desc);
124     IsInitialized = false;
125   }
126 
dump()127   void dump() const { dump(llvm::errs()); }
128   void dump(llvm::raw_ostream &OS) const;
129 
130 private:
131   friend class Pointer;
132   friend class DeadBlock;
133   friend class InterpState;
134   friend class DynamicAllocator;
135 
Block(unsigned EvalID,const Descriptor * Desc,bool IsExtern,bool IsStatic,bool IsDead)136   Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
137         bool IsDead)
138       : EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true),
139         IsDynamic(false), Desc(Desc) {
140     assert(Desc);
141   }
142 
143   /// Deletes a dead block at the end of its lifetime.
144   void cleanup();
145 
146   /// Pointer chain management.
147   void addPointer(Pointer *P);
148   void removePointer(Pointer *P);
149   void replacePointer(Pointer *Old, Pointer *New);
150 #ifndef NDEBUG
151   bool hasPointer(const Pointer *P) const;
152 #endif
153 
154   const unsigned EvalID = ~0u;
155   /// Start of the chain of pointers.
156   Pointer *Pointers = nullptr;
157   /// Unique identifier of the declaration.
158   std::optional<unsigned> DeclID;
159   /// Flag indicating if the block has static storage duration.
160   bool IsStatic = false;
161   /// Flag indicating if the block is an extern.
162   bool IsExtern = false;
163   /// Flag indicating if the pointer is dead. This is only ever
164   /// set once, when converting the Block to a DeadBlock.
165   bool IsDead = false;
166   /// Flag indicating if the block contents have been initialized
167   /// via invokeCtor.
168   bool IsInitialized = false;
169   /// Flag indicating if this block has been allocated via dynamic
170   /// memory allocation (e.g. malloc).
171   bool IsDynamic = false;
172   /// Pointer to the stack slot descriptor.
173   const Descriptor *Desc;
174 };
175 
176 /// Descriptor for a dead block.
177 ///
178 /// Dead blocks are chained in a double-linked list to deallocate them
179 /// whenever pointers become dead.
180 class DeadBlock final {
181 public:
182   /// Copies the block.
183   DeadBlock(DeadBlock *&Root, Block *Blk);
184 
185   /// Returns a pointer to the stored data.
data()186   std::byte *data() { return B.data(); }
rawData()187   std::byte *rawData() { return B.rawData(); }
188 
189 private:
190   friend class Block;
191   friend class InterpState;
192 
193   void free();
194 
195   /// Root pointer of the list.
196   DeadBlock *&Root;
197   /// Previous block in the list.
198   DeadBlock *Prev;
199   /// Next block in the list.
200   DeadBlock *Next;
201 
202   /// Actual block storing data and tracking pointers.
203   Block B;
204 };
205 
206 } // namespace interp
207 } // namespace clang
208 
209 #endif
210