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, Descriptor *Desc, 53 bool IsStatic = false, bool IsExtern = false) 54 : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {} 55 56 Block(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 75 /// Returns a pointer to the stored data. 76 /// You are allowed to read Desc->getSize() bytes from this address. 77 char *data() { 78 // rawData might contain metadata as well. 79 size_t DataOffset = Desc->getMetadataSize(); 80 return rawData() + DataOffset; 81 } 82 const char *data() const { 83 // rawData might contain metadata as well. 84 size_t DataOffset = Desc->getMetadataSize(); 85 return rawData() + DataOffset; 86 } 87 88 /// Returns a pointer to the raw data, including metadata. 89 /// You are allowed to read Desc->getAllocSize() bytes from this address. 90 char *rawData() { return reinterpret_cast<char *>(this) + sizeof(Block); } 91 const char *rawData() const { 92 return reinterpret_cast<const char *>(this) + sizeof(Block); 93 } 94 95 /// Returns a view over the data. 96 template <typename T> 97 T &deref() { return *reinterpret_cast<T *>(data()); } 98 99 /// Invokes the constructor. 100 void invokeCtor() { 101 std::memset(rawData(), 0, Desc->getAllocSize()); 102 if (Desc->CtorFn) 103 Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, 104 /*isActive=*/true, Desc); 105 } 106 107 /// Invokes the Destructor. 108 void invokeDtor() { 109 if (Desc->DtorFn) 110 Desc->DtorFn(this, data(), Desc); 111 } 112 113 protected: 114 friend class Pointer; 115 friend class DeadBlock; 116 friend class InterpState; 117 118 Block(Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead) 119 : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {} 120 121 /// Deletes a dead block at the end of its lifetime. 122 void cleanup(); 123 124 /// Pointer chain management. 125 void addPointer(Pointer *P); 126 void removePointer(Pointer *P); 127 void replacePointer(Pointer *Old, Pointer *New); 128 #ifndef NDEBUG 129 bool hasPointer(const Pointer *P) const; 130 #endif 131 132 /// Start of the chain of pointers. 133 Pointer *Pointers = nullptr; 134 /// Unique identifier of the declaration. 135 std::optional<unsigned> DeclID; 136 /// Flag indicating if the block has static storage duration. 137 bool IsStatic = false; 138 /// Flag indicating if the block is an extern. 139 bool IsExtern = false; 140 /// Flag indicating if the pointer is dead. 141 bool IsDead = false; 142 /// Pointer to the stack slot descriptor. 143 Descriptor *Desc; 144 }; 145 146 /// Descriptor for a dead block. 147 /// 148 /// Dead blocks are chained in a double-linked list to deallocate them 149 /// whenever pointers become dead. 150 class DeadBlock final { 151 public: 152 /// Copies the block. 153 DeadBlock(DeadBlock *&Root, Block *Blk); 154 155 /// Returns a pointer to the stored data. 156 char *data() { return B.data(); } 157 158 private: 159 friend class Block; 160 friend class InterpState; 161 162 void free(); 163 164 /// Root pointer of the list. 165 DeadBlock *&Root; 166 /// Previous block in the list. 167 DeadBlock *Prev; 168 /// Next block in the list. 169 DeadBlock *Next; 170 171 /// Actual block storing data and tracking pointers. 172 Block B; 173 }; 174 175 } // namespace interp 176 } // namespace clang 177 178 #endif 179