xref: /freebsd/contrib/llvm-project/clang/lib/AST/ByteCode/InterpBlock.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===-- InterpBlock.h - Allocated blocks for the interpreter -*- C++ ----*-===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric //
9*700637cbSDimitry Andric // Defines the classes describing allocated blocks.
10*700637cbSDimitry Andric //
11*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
12*700637cbSDimitry Andric 
13*700637cbSDimitry Andric #ifndef LLVM_CLANG_AST_INTERP_BLOCK_H
14*700637cbSDimitry Andric #define LLVM_CLANG_AST_INTERP_BLOCK_H
15*700637cbSDimitry Andric 
16*700637cbSDimitry Andric #include "Descriptor.h"
17*700637cbSDimitry Andric #include "clang/AST/ComparisonCategories.h"
18*700637cbSDimitry Andric #include "clang/AST/Decl.h"
19*700637cbSDimitry Andric #include "clang/AST/DeclCXX.h"
20*700637cbSDimitry Andric #include "clang/AST/Expr.h"
21*700637cbSDimitry Andric #include "llvm/ADT/PointerUnion.h"
22*700637cbSDimitry Andric #include "llvm/Support/raw_ostream.h"
23*700637cbSDimitry Andric 
24*700637cbSDimitry Andric namespace clang {
25*700637cbSDimitry Andric namespace interp {
26*700637cbSDimitry Andric class Block;
27*700637cbSDimitry Andric class DeadBlock;
28*700637cbSDimitry Andric class InterpState;
29*700637cbSDimitry Andric class Pointer;
30*700637cbSDimitry Andric enum PrimType : unsigned;
31*700637cbSDimitry Andric 
32*700637cbSDimitry Andric /// A memory block, either on the stack or in the heap.
33*700637cbSDimitry Andric ///
34*700637cbSDimitry Andric /// The storage described by the block is immediately followed by
35*700637cbSDimitry Andric /// optional metadata, which is followed by the actual data.
36*700637cbSDimitry Andric ///
37*700637cbSDimitry Andric /// Block*        rawData()                  data()
38*700637cbSDimitry Andric /// │               │                         │
39*700637cbSDimitry Andric /// │               │                         │
40*700637cbSDimitry Andric /// ▼               ▼                         ▼
41*700637cbSDimitry Andric /// ┌───────────────┬─────────────────────────┬─────────────────┐
42*700637cbSDimitry Andric /// │ Block         │ Metadata                │ Data            │
43*700637cbSDimitry Andric /// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │
44*700637cbSDimitry Andric /// └───────────────┴─────────────────────────┴─────────────────┘
45*700637cbSDimitry Andric ///
46*700637cbSDimitry Andric /// Desc->getAllocSize() describes the size after the Block, i.e.
47*700637cbSDimitry Andric /// the data size and the metadata size.
48*700637cbSDimitry Andric ///
49*700637cbSDimitry Andric class Block final {
50*700637cbSDimitry Andric public:
51*700637cbSDimitry Andric   /// Creates a new block.
52*700637cbSDimitry Andric   Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
53*700637cbSDimitry Andric         const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false,
54*700637cbSDimitry Andric         bool IsWeak = false)
EvalID(EvalID)55*700637cbSDimitry Andric       : EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern),
56*700637cbSDimitry Andric         IsDynamic(false), IsWeak(IsWeak), Desc(Desc) {
57*700637cbSDimitry Andric     assert(Desc);
58*700637cbSDimitry Andric   }
59*700637cbSDimitry Andric 
60*700637cbSDimitry Andric   Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
61*700637cbSDimitry Andric         bool IsExtern = false, bool IsWeak = false)
EvalID(EvalID)62*700637cbSDimitry Andric       : EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic),
63*700637cbSDimitry Andric         IsExtern(IsExtern), IsDynamic(false), IsWeak(IsWeak), Desc(Desc) {
64*700637cbSDimitry Andric     assert(Desc);
65*700637cbSDimitry Andric   }
66*700637cbSDimitry Andric 
67*700637cbSDimitry Andric   /// Returns the block's descriptor.
getDescriptor()68*700637cbSDimitry Andric   const Descriptor *getDescriptor() const { return Desc; }
69*700637cbSDimitry Andric   /// Checks if the block has any live pointers.
hasPointers()70*700637cbSDimitry Andric   bool hasPointers() const { return Pointers; }
71*700637cbSDimitry Andric   /// Checks if the block is extern.
isExtern()72*700637cbSDimitry Andric   bool isExtern() const { return IsExtern; }
73*700637cbSDimitry Andric   /// Checks if the block has static storage duration.
isStatic()74*700637cbSDimitry Andric   bool isStatic() const { return IsStatic; }
75*700637cbSDimitry Andric   /// Checks if the block is temporary.
isTemporary()76*700637cbSDimitry Andric   bool isTemporary() const { return Desc->IsTemporary; }
isWeak()77*700637cbSDimitry Andric   bool isWeak() const { return IsWeak; }
isDynamic()78*700637cbSDimitry Andric   bool isDynamic() const { return IsDynamic; }
79*700637cbSDimitry Andric   /// Returns the size of the block.
getSize()80*700637cbSDimitry Andric   unsigned getSize() const { return Desc->getAllocSize(); }
81*700637cbSDimitry Andric   /// Returns the declaration ID.
getDeclID()82*700637cbSDimitry Andric   std::optional<unsigned> getDeclID() const { return DeclID; }
83*700637cbSDimitry Andric   /// Returns whether the data of this block has been initialized via
84*700637cbSDimitry Andric   /// invoking the Ctor func.
isInitialized()85*700637cbSDimitry Andric   bool isInitialized() const { return IsInitialized; }
86*700637cbSDimitry Andric   /// The Evaluation ID this block was created in.
getEvalID()87*700637cbSDimitry Andric   unsigned getEvalID() const { return EvalID; }
88*700637cbSDimitry Andric 
89*700637cbSDimitry Andric   /// Returns a pointer to the stored data.
90*700637cbSDimitry Andric   /// You are allowed to read Desc->getSize() bytes from this address.
data()91*700637cbSDimitry Andric   std::byte *data() {
92*700637cbSDimitry Andric     // rawData might contain metadata as well.
93*700637cbSDimitry Andric     size_t DataOffset = Desc->getMetadataSize();
94*700637cbSDimitry Andric     return rawData() + DataOffset;
95*700637cbSDimitry Andric   }
data()96*700637cbSDimitry Andric   const std::byte *data() const {
97*700637cbSDimitry Andric     // rawData might contain metadata as well.
98*700637cbSDimitry Andric     size_t DataOffset = Desc->getMetadataSize();
99*700637cbSDimitry Andric     return rawData() + DataOffset;
100*700637cbSDimitry Andric   }
101*700637cbSDimitry Andric 
102*700637cbSDimitry Andric   /// Returns a pointer to the raw data, including metadata.
103*700637cbSDimitry Andric   /// You are allowed to read Desc->getAllocSize() bytes from this address.
rawData()104*700637cbSDimitry Andric   std::byte *rawData() {
105*700637cbSDimitry Andric     return reinterpret_cast<std::byte *>(this) + sizeof(Block);
106*700637cbSDimitry Andric   }
rawData()107*700637cbSDimitry Andric   const std::byte *rawData() const {
108*700637cbSDimitry Andric     return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
109*700637cbSDimitry Andric   }
110*700637cbSDimitry Andric 
111*700637cbSDimitry Andric   /// Invokes the constructor.
invokeCtor()112*700637cbSDimitry Andric   void invokeCtor() {
113*700637cbSDimitry Andric     assert(!IsInitialized);
114*700637cbSDimitry Andric     std::memset(rawData(), 0, Desc->getAllocSize());
115*700637cbSDimitry Andric     if (Desc->CtorFn) {
116*700637cbSDimitry Andric       Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
117*700637cbSDimitry Andric                    Desc->IsVolatile,
118*700637cbSDimitry Andric                    /*isActive=*/true, /*InUnion=*/false, Desc);
119*700637cbSDimitry Andric     }
120*700637cbSDimitry Andric     IsInitialized = true;
121*700637cbSDimitry Andric   }
122*700637cbSDimitry Andric 
123*700637cbSDimitry Andric   /// Invokes the Destructor.
invokeDtor()124*700637cbSDimitry Andric   void invokeDtor() {
125*700637cbSDimitry Andric     assert(IsInitialized);
126*700637cbSDimitry Andric     if (Desc->DtorFn)
127*700637cbSDimitry Andric       Desc->DtorFn(this, data(), Desc);
128*700637cbSDimitry Andric     IsInitialized = false;
129*700637cbSDimitry Andric   }
130*700637cbSDimitry Andric 
dump()131*700637cbSDimitry Andric   void dump() const { dump(llvm::errs()); }
132*700637cbSDimitry Andric   void dump(llvm::raw_ostream &OS) const;
133*700637cbSDimitry Andric 
134*700637cbSDimitry Andric private:
135*700637cbSDimitry Andric   friend class Pointer;
136*700637cbSDimitry Andric   friend class DeadBlock;
137*700637cbSDimitry Andric   friend class InterpState;
138*700637cbSDimitry Andric   friend class DynamicAllocator;
139*700637cbSDimitry Andric 
Block(unsigned EvalID,const Descriptor * Desc,bool IsExtern,bool IsStatic,bool IsWeak,bool IsDead)140*700637cbSDimitry Andric   Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
141*700637cbSDimitry Andric         bool IsWeak, bool IsDead)
142*700637cbSDimitry Andric       : EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true),
143*700637cbSDimitry Andric         IsDynamic(false), IsWeak(IsWeak), Desc(Desc) {
144*700637cbSDimitry Andric     assert(Desc);
145*700637cbSDimitry Andric   }
146*700637cbSDimitry Andric 
147*700637cbSDimitry Andric   /// Deletes a dead block at the end of its lifetime.
148*700637cbSDimitry Andric   void cleanup();
149*700637cbSDimitry Andric 
150*700637cbSDimitry Andric   /// Pointer chain management.
151*700637cbSDimitry Andric   void addPointer(Pointer *P);
152*700637cbSDimitry Andric   void removePointer(Pointer *P);
153*700637cbSDimitry Andric   void replacePointer(Pointer *Old, Pointer *New);
154*700637cbSDimitry Andric #ifndef NDEBUG
155*700637cbSDimitry Andric   bool hasPointer(const Pointer *P) const;
156*700637cbSDimitry Andric #endif
157*700637cbSDimitry Andric 
158*700637cbSDimitry Andric   const unsigned EvalID = ~0u;
159*700637cbSDimitry Andric   /// Start of the chain of pointers.
160*700637cbSDimitry Andric   Pointer *Pointers = nullptr;
161*700637cbSDimitry Andric   /// Unique identifier of the declaration.
162*700637cbSDimitry Andric   std::optional<unsigned> DeclID;
163*700637cbSDimitry Andric   /// Flag indicating if the block has static storage duration.
164*700637cbSDimitry Andric   bool IsStatic = false;
165*700637cbSDimitry Andric   /// Flag indicating if the block is an extern.
166*700637cbSDimitry Andric   bool IsExtern = false;
167*700637cbSDimitry Andric   /// Flag indicating if the pointer is dead. This is only ever
168*700637cbSDimitry Andric   /// set once, when converting the Block to a DeadBlock.
169*700637cbSDimitry Andric   bool IsDead = false;
170*700637cbSDimitry Andric   /// Flag indicating if the block contents have been initialized
171*700637cbSDimitry Andric   /// via invokeCtor.
172*700637cbSDimitry Andric   bool IsInitialized = false;
173*700637cbSDimitry Andric   /// Flag indicating if this block has been allocated via dynamic
174*700637cbSDimitry Andric   /// memory allocation (e.g. malloc).
175*700637cbSDimitry Andric   bool IsDynamic = false;
176*700637cbSDimitry Andric   bool IsWeak = false;
177*700637cbSDimitry Andric   /// Pointer to the stack slot descriptor.
178*700637cbSDimitry Andric   const Descriptor *Desc;
179*700637cbSDimitry Andric };
180*700637cbSDimitry Andric 
181*700637cbSDimitry Andric /// Descriptor for a dead block.
182*700637cbSDimitry Andric ///
183*700637cbSDimitry Andric /// Dead blocks are chained in a double-linked list to deallocate them
184*700637cbSDimitry Andric /// whenever pointers become dead.
185*700637cbSDimitry Andric class DeadBlock final {
186*700637cbSDimitry Andric public:
187*700637cbSDimitry Andric   /// Copies the block.
188*700637cbSDimitry Andric   DeadBlock(DeadBlock *&Root, Block *Blk);
189*700637cbSDimitry Andric 
190*700637cbSDimitry Andric   /// Returns a pointer to the stored data.
data()191*700637cbSDimitry Andric   std::byte *data() { return B.data(); }
rawData()192*700637cbSDimitry Andric   std::byte *rawData() { return B.rawData(); }
193*700637cbSDimitry Andric 
194*700637cbSDimitry Andric private:
195*700637cbSDimitry Andric   friend class Block;
196*700637cbSDimitry Andric   friend class InterpState;
197*700637cbSDimitry Andric 
198*700637cbSDimitry Andric   void free();
199*700637cbSDimitry Andric 
200*700637cbSDimitry Andric   /// Root pointer of the list.
201*700637cbSDimitry Andric   DeadBlock *&Root;
202*700637cbSDimitry Andric   /// Previous block in the list.
203*700637cbSDimitry Andric   DeadBlock *Prev;
204*700637cbSDimitry Andric   /// Next block in the list.
205*700637cbSDimitry Andric   DeadBlock *Next;
206*700637cbSDimitry Andric 
207*700637cbSDimitry Andric   /// Actual block storing data and tracking pointers.
208*700637cbSDimitry Andric   Block B;
209*700637cbSDimitry Andric };
210*700637cbSDimitry Andric 
211*700637cbSDimitry Andric } // namespace interp
212*700637cbSDimitry Andric } // namespace clang
213*700637cbSDimitry Andric 
214*700637cbSDimitry Andric #endif
215