15ffd83dbSDimitry Andric //===-- InterpBlock.h - Allocated blocks for the interpreter -*- C++ ----*-===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric // Defines the classes describing allocated blocks.
105ffd83dbSDimitry Andric //
115ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
125ffd83dbSDimitry Andric 
135ffd83dbSDimitry Andric #ifndef LLVM_CLANG_AST_INTERP_BLOCK_H
145ffd83dbSDimitry Andric #define LLVM_CLANG_AST_INTERP_BLOCK_H
155ffd83dbSDimitry Andric 
165ffd83dbSDimitry Andric #include "Descriptor.h"
175ffd83dbSDimitry Andric #include "clang/AST/Decl.h"
185ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h"
195ffd83dbSDimitry Andric #include "clang/AST/Expr.h"
205ffd83dbSDimitry Andric #include "clang/AST/ComparisonCategories.h"
215ffd83dbSDimitry Andric #include "llvm/ADT/PointerUnion.h"
225ffd83dbSDimitry Andric #include "llvm/Support/raw_ostream.h"
235ffd83dbSDimitry Andric 
245ffd83dbSDimitry Andric namespace clang {
255ffd83dbSDimitry Andric namespace interp {
265ffd83dbSDimitry Andric class Block;
275ffd83dbSDimitry Andric class DeadBlock;
285ffd83dbSDimitry Andric class InterpState;
295ffd83dbSDimitry Andric class Pointer;
305ffd83dbSDimitry Andric enum PrimType : unsigned;
315ffd83dbSDimitry Andric 
325ffd83dbSDimitry Andric /// A memory block, either on the stack or in the heap.
335ffd83dbSDimitry Andric ///
34bdd1243dSDimitry Andric /// The storage described by the block is immediately followed by
35bdd1243dSDimitry Andric /// optional metadata, which is followed by the actual data.
36bdd1243dSDimitry Andric ///
37bdd1243dSDimitry Andric /// Block*        rawData()                  data()
38bdd1243dSDimitry Andric /// │               │                         │
39bdd1243dSDimitry Andric /// │               │                         │
40bdd1243dSDimitry Andric /// ▼               ▼                         ▼
41bdd1243dSDimitry Andric /// ┌───────────────┬─────────────────────────┬─────────────────┐
42bdd1243dSDimitry Andric /// │ Block         │ Metadata                │ Data            │
43bdd1243dSDimitry Andric /// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │
44bdd1243dSDimitry Andric /// └───────────────┴─────────────────────────┴─────────────────┘
45bdd1243dSDimitry Andric ///
46bdd1243dSDimitry Andric /// Desc->getAllocSize() describes the size after the Block, i.e.
47bdd1243dSDimitry Andric /// the data size and the metadata size.
48bdd1243dSDimitry Andric ///
49bdd1243dSDimitry Andric class Block final {
505ffd83dbSDimitry Andric public:
5106c3fb27SDimitry Andric   /// Creates a new block.
52*5f757f3fSDimitry Andric   Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc,
535ffd83dbSDimitry Andric         bool IsStatic = false, bool IsExtern = false)
DeclID(DeclID)545ffd83dbSDimitry Andric       : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {}
555ffd83dbSDimitry Andric 
56*5f757f3fSDimitry Andric   Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
575ffd83dbSDimitry Andric       : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern),
585ffd83dbSDimitry Andric         Desc(Desc) {}
595ffd83dbSDimitry Andric 
605ffd83dbSDimitry Andric   /// Returns the block's descriptor.
getDescriptor()6106c3fb27SDimitry Andric   const Descriptor *getDescriptor() const { return Desc; }
625ffd83dbSDimitry Andric   /// Checks if the block has any live pointers.
hasPointers()635ffd83dbSDimitry Andric   bool hasPointers() const { return Pointers; }
645ffd83dbSDimitry Andric   /// Checks if the block is extern.
isExtern()655ffd83dbSDimitry Andric   bool isExtern() const { return IsExtern; }
665ffd83dbSDimitry Andric   /// Checks if the block has static storage duration.
isStatic()675ffd83dbSDimitry Andric   bool isStatic() const { return IsStatic; }
685ffd83dbSDimitry Andric   /// Checks if the block is temporary.
isTemporary()695ffd83dbSDimitry Andric   bool isTemporary() const { return Desc->IsTemporary; }
705ffd83dbSDimitry Andric   /// Returns the size of the block.
getSize()7106c3fb27SDimitry Andric   unsigned getSize() const { return Desc->getAllocSize(); }
725ffd83dbSDimitry Andric   /// Returns the declaration ID.
getDeclID()73bdd1243dSDimitry Andric   std::optional<unsigned> getDeclID() const { return DeclID; }
isInitialized()74*5f757f3fSDimitry Andric   bool isInitialized() const { return IsInitialized; }
755ffd83dbSDimitry Andric 
765ffd83dbSDimitry Andric   /// Returns a pointer to the stored data.
77bdd1243dSDimitry Andric   /// You are allowed to read Desc->getSize() bytes from this address.
data()78*5f757f3fSDimitry Andric   std::byte *data() {
79bdd1243dSDimitry Andric     // rawData might contain metadata as well.
80bdd1243dSDimitry Andric     size_t DataOffset = Desc->getMetadataSize();
81bdd1243dSDimitry Andric     return rawData() + DataOffset;
82bdd1243dSDimitry Andric   }
data()83*5f757f3fSDimitry Andric   const std::byte *data() const {
84bdd1243dSDimitry Andric     // rawData might contain metadata as well.
85bdd1243dSDimitry Andric     size_t DataOffset = Desc->getMetadataSize();
86bdd1243dSDimitry Andric     return rawData() + DataOffset;
87bdd1243dSDimitry Andric   }
88bdd1243dSDimitry Andric 
89bdd1243dSDimitry Andric   /// Returns a pointer to the raw data, including metadata.
90bdd1243dSDimitry Andric   /// You are allowed to read Desc->getAllocSize() bytes from this address.
rawData()91*5f757f3fSDimitry Andric   std::byte *rawData() {
92*5f757f3fSDimitry Andric     return reinterpret_cast<std::byte *>(this) + sizeof(Block);
93*5f757f3fSDimitry Andric   }
rawData()94*5f757f3fSDimitry Andric   const std::byte *rawData() const {
95*5f757f3fSDimitry Andric     return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
96bdd1243dSDimitry Andric   }
975ffd83dbSDimitry Andric 
985ffd83dbSDimitry Andric   /// Returns a view over the data.
995ffd83dbSDimitry Andric   template <typename T>
deref()1005ffd83dbSDimitry Andric   T &deref() { return *reinterpret_cast<T *>(data()); }
deref()101*5f757f3fSDimitry Andric   template <typename T> const T &deref() const {
102*5f757f3fSDimitry Andric     return *reinterpret_cast<const T *>(data());
103*5f757f3fSDimitry Andric   }
1045ffd83dbSDimitry Andric 
1055ffd83dbSDimitry Andric   /// Invokes the constructor.
invokeCtor()1065ffd83dbSDimitry Andric   void invokeCtor() {
107bdd1243dSDimitry Andric     std::memset(rawData(), 0, Desc->getAllocSize());
1085ffd83dbSDimitry Andric     if (Desc->CtorFn)
1095ffd83dbSDimitry Andric       Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
1105ffd83dbSDimitry Andric                    /*isActive=*/true, Desc);
111*5f757f3fSDimitry Andric     IsInitialized = true;
1125ffd83dbSDimitry Andric   }
1135ffd83dbSDimitry Andric 
11406c3fb27SDimitry Andric   /// Invokes the Destructor.
invokeDtor()115bdd1243dSDimitry Andric   void invokeDtor() {
116bdd1243dSDimitry Andric     if (Desc->DtorFn)
117bdd1243dSDimitry Andric       Desc->DtorFn(this, data(), Desc);
118*5f757f3fSDimitry Andric     IsInitialized = false;
119bdd1243dSDimitry Andric   }
120bdd1243dSDimitry Andric 
1215ffd83dbSDimitry Andric protected:
1225ffd83dbSDimitry Andric   friend class Pointer;
1235ffd83dbSDimitry Andric   friend class DeadBlock;
1245ffd83dbSDimitry Andric   friend class InterpState;
1255ffd83dbSDimitry Andric 
Block(const Descriptor * Desc,bool IsExtern,bool IsStatic,bool IsDead)126*5f757f3fSDimitry Andric   Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead)
1275ffd83dbSDimitry Andric       : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {}
1285ffd83dbSDimitry Andric 
12906c3fb27SDimitry Andric   /// Deletes a dead block at the end of its lifetime.
1305ffd83dbSDimitry Andric   void cleanup();
1315ffd83dbSDimitry Andric 
13206c3fb27SDimitry Andric   /// Pointer chain management.
1335ffd83dbSDimitry Andric   void addPointer(Pointer *P);
1345ffd83dbSDimitry Andric   void removePointer(Pointer *P);
13506c3fb27SDimitry Andric   void replacePointer(Pointer *Old, Pointer *New);
13606c3fb27SDimitry Andric #ifndef NDEBUG
13706c3fb27SDimitry Andric   bool hasPointer(const Pointer *P) const;
13806c3fb27SDimitry Andric #endif
1395ffd83dbSDimitry Andric 
1405ffd83dbSDimitry Andric   /// Start of the chain of pointers.
1415ffd83dbSDimitry Andric   Pointer *Pointers = nullptr;
1425ffd83dbSDimitry Andric   /// Unique identifier of the declaration.
143bdd1243dSDimitry Andric   std::optional<unsigned> DeclID;
1445ffd83dbSDimitry Andric   /// Flag indicating if the block has static storage duration.
1455ffd83dbSDimitry Andric   bool IsStatic = false;
1465ffd83dbSDimitry Andric   /// Flag indicating if the block is an extern.
1475ffd83dbSDimitry Andric   bool IsExtern = false;
148*5f757f3fSDimitry Andric   /// Flag indicating if the pointer is dead. This is only ever
149*5f757f3fSDimitry Andric   /// set once, when converting the Block to a DeadBlock.
1505ffd83dbSDimitry Andric   bool IsDead = false;
151*5f757f3fSDimitry Andric   /// Flag indicating if the block contents have been initialized
152*5f757f3fSDimitry Andric   /// via invokeCtor.
153*5f757f3fSDimitry Andric   bool IsInitialized = false;
1545ffd83dbSDimitry Andric   /// Pointer to the stack slot descriptor.
155*5f757f3fSDimitry Andric   const Descriptor *Desc;
1565ffd83dbSDimitry Andric };
1575ffd83dbSDimitry Andric 
1585ffd83dbSDimitry Andric /// Descriptor for a dead block.
1595ffd83dbSDimitry Andric ///
1605ffd83dbSDimitry Andric /// Dead blocks are chained in a double-linked list to deallocate them
1615ffd83dbSDimitry Andric /// whenever pointers become dead.
162bdd1243dSDimitry Andric class DeadBlock final {
1635ffd83dbSDimitry Andric public:
1645ffd83dbSDimitry Andric   /// Copies the block.
1655ffd83dbSDimitry Andric   DeadBlock(DeadBlock *&Root, Block *Blk);
1665ffd83dbSDimitry Andric 
1675ffd83dbSDimitry Andric   /// Returns a pointer to the stored data.
data()168*5f757f3fSDimitry Andric   std::byte *data() { return B.data(); }
rawData()169*5f757f3fSDimitry Andric   std::byte *rawData() { return B.rawData(); }
1705ffd83dbSDimitry Andric 
1715ffd83dbSDimitry Andric private:
1725ffd83dbSDimitry Andric   friend class Block;
1735ffd83dbSDimitry Andric   friend class InterpState;
1745ffd83dbSDimitry Andric 
1755ffd83dbSDimitry Andric   void free();
1765ffd83dbSDimitry Andric 
1775ffd83dbSDimitry Andric   /// Root pointer of the list.
1785ffd83dbSDimitry Andric   DeadBlock *&Root;
1795ffd83dbSDimitry Andric   /// Previous block in the list.
1805ffd83dbSDimitry Andric   DeadBlock *Prev;
1815ffd83dbSDimitry Andric   /// Next block in the list.
1825ffd83dbSDimitry Andric   DeadBlock *Next;
1835ffd83dbSDimitry Andric 
1845ffd83dbSDimitry Andric   /// Actual block storing data and tracking pointers.
1855ffd83dbSDimitry Andric   Block B;
1865ffd83dbSDimitry Andric };
1875ffd83dbSDimitry Andric 
1885ffd83dbSDimitry Andric } // namespace interp
1895ffd83dbSDimitry Andric } // namespace clang
1905ffd83dbSDimitry Andric 
1915ffd83dbSDimitry Andric #endif
192