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