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(rawData(), 0, 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 protected:
122   friend class Pointer;
123   friend class DeadBlock;
124   friend class InterpState;
125 
126   Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead)
127       : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {}
128 
129   /// Deletes a dead block at the end of its lifetime.
130   void cleanup();
131 
132   /// Pointer chain management.
133   void addPointer(Pointer *P);
134   void removePointer(Pointer *P);
135   void replacePointer(Pointer *Old, Pointer *New);
136 #ifndef NDEBUG
137   bool hasPointer(const Pointer *P) const;
138 #endif
139 
140   /// Start of the chain of pointers.
141   Pointer *Pointers = nullptr;
142   /// Unique identifier of the declaration.
143   std::optional<unsigned> DeclID;
144   /// Flag indicating if the block has static storage duration.
145   bool IsStatic = false;
146   /// Flag indicating if the block is an extern.
147   bool IsExtern = false;
148   /// Flag indicating if the pointer is dead. This is only ever
149   /// set once, when converting the Block to a DeadBlock.
150   bool IsDead = false;
151   /// Flag indicating if the block contents have been initialized
152   /// via invokeCtor.
153   bool IsInitialized = false;
154   /// Pointer to the stack slot descriptor.
155   const Descriptor *Desc;
156 };
157 
158 /// Descriptor for a dead block.
159 ///
160 /// Dead blocks are chained in a double-linked list to deallocate them
161 /// whenever pointers become dead.
162 class DeadBlock final {
163 public:
164   /// Copies the block.
165   DeadBlock(DeadBlock *&Root, Block *Blk);
166 
167   /// Returns a pointer to the stored data.
168   std::byte *data() { return B.data(); }
169   std::byte *rawData() { return B.rawData(); }
170 
171 private:
172   friend class Block;
173   friend class InterpState;
174 
175   void free();
176 
177   /// Root pointer of the list.
178   DeadBlock *&Root;
179   /// Previous block in the list.
180   DeadBlock *Prev;
181   /// Next block in the list.
182   DeadBlock *Next;
183 
184   /// Actual block storing data and tracking pointers.
185   Block B;
186 };
187 
188 } // namespace interp
189 } // namespace clang
190 
191 #endif
192