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