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*0fca6ea1SDimitry Andric Block(unsigned EvalID, const std::optional<unsigned> &DeclID, 53*0fca6ea1SDimitry Andric const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) 54*0fca6ea1SDimitry Andric : EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), 55*0fca6ea1SDimitry Andric IsDynamic(false), Desc(Desc) { 56*0fca6ea1SDimitry Andric assert(Desc); 57*0fca6ea1SDimitry Andric } 585ffd83dbSDimitry Andric 59*0fca6ea1SDimitry Andric Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false, 60*0fca6ea1SDimitry Andric bool IsExtern = false) 61*0fca6ea1SDimitry Andric : EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic), 62*0fca6ea1SDimitry Andric IsExtern(IsExtern), IsDynamic(false), Desc(Desc) { 63*0fca6ea1SDimitry Andric assert(Desc); 64*0fca6ea1SDimitry Andric } 655ffd83dbSDimitry Andric 665ffd83dbSDimitry Andric /// Returns the block's descriptor. 6706c3fb27SDimitry Andric const Descriptor *getDescriptor() const { return Desc; } 685ffd83dbSDimitry Andric /// Checks if the block has any live pointers. 695ffd83dbSDimitry Andric bool hasPointers() const { return Pointers; } 705ffd83dbSDimitry Andric /// Checks if the block is extern. 715ffd83dbSDimitry Andric bool isExtern() const { return IsExtern; } 725ffd83dbSDimitry Andric /// Checks if the block has static storage duration. 735ffd83dbSDimitry Andric bool isStatic() const { return IsStatic; } 745ffd83dbSDimitry Andric /// Checks if the block is temporary. 755ffd83dbSDimitry Andric bool isTemporary() const { return Desc->IsTemporary; } 76*0fca6ea1SDimitry Andric bool isDynamic() const { return IsDynamic; } 775ffd83dbSDimitry Andric /// Returns the size of the block. 7806c3fb27SDimitry Andric unsigned getSize() const { return Desc->getAllocSize(); } 795ffd83dbSDimitry Andric /// Returns the declaration ID. 80bdd1243dSDimitry Andric std::optional<unsigned> getDeclID() const { return DeclID; } 81*0fca6ea1SDimitry Andric /// Returns whether the data of this block has been initialized via 82*0fca6ea1SDimitry Andric /// invoking the Ctor func. 835f757f3fSDimitry Andric bool isInitialized() const { return IsInitialized; } 84*0fca6ea1SDimitry Andric /// The Evaluation ID this block was created in. 85*0fca6ea1SDimitry Andric unsigned getEvalID() const { return EvalID; } 865ffd83dbSDimitry Andric 875ffd83dbSDimitry Andric /// Returns a pointer to the stored data. 88bdd1243dSDimitry Andric /// You are allowed to read Desc->getSize() bytes from this address. 895f757f3fSDimitry Andric std::byte *data() { 90bdd1243dSDimitry Andric // rawData might contain metadata as well. 91bdd1243dSDimitry Andric size_t DataOffset = Desc->getMetadataSize(); 92bdd1243dSDimitry Andric return rawData() + DataOffset; 93bdd1243dSDimitry Andric } 945f757f3fSDimitry Andric const std::byte *data() const { 95bdd1243dSDimitry Andric // rawData might contain metadata as well. 96bdd1243dSDimitry Andric size_t DataOffset = Desc->getMetadataSize(); 97bdd1243dSDimitry Andric return rawData() + DataOffset; 98bdd1243dSDimitry Andric } 99bdd1243dSDimitry Andric 100bdd1243dSDimitry Andric /// Returns a pointer to the raw data, including metadata. 101bdd1243dSDimitry Andric /// You are allowed to read Desc->getAllocSize() bytes from this address. 1025f757f3fSDimitry Andric std::byte *rawData() { 1035f757f3fSDimitry Andric return reinterpret_cast<std::byte *>(this) + sizeof(Block); 1045f757f3fSDimitry Andric } 1055f757f3fSDimitry Andric const std::byte *rawData() const { 1065f757f3fSDimitry Andric return reinterpret_cast<const std::byte *>(this) + sizeof(Block); 107bdd1243dSDimitry Andric } 1085ffd83dbSDimitry Andric 1095ffd83dbSDimitry Andric /// Invokes the constructor. 1105ffd83dbSDimitry Andric void invokeCtor() { 111*0fca6ea1SDimitry Andric assert(!IsInitialized); 112bdd1243dSDimitry Andric std::memset(rawData(), 0, Desc->getAllocSize()); 1135ffd83dbSDimitry Andric if (Desc->CtorFn) 1145ffd83dbSDimitry Andric Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, 1155ffd83dbSDimitry Andric /*isActive=*/true, Desc); 1165f757f3fSDimitry Andric IsInitialized = true; 1175ffd83dbSDimitry Andric } 1185ffd83dbSDimitry Andric 11906c3fb27SDimitry Andric /// Invokes the Destructor. 120bdd1243dSDimitry Andric void invokeDtor() { 121*0fca6ea1SDimitry Andric assert(IsInitialized); 122bdd1243dSDimitry Andric if (Desc->DtorFn) 123bdd1243dSDimitry Andric Desc->DtorFn(this, data(), Desc); 1245f757f3fSDimitry Andric IsInitialized = false; 125bdd1243dSDimitry Andric } 126bdd1243dSDimitry Andric 127*0fca6ea1SDimitry Andric void dump() const { dump(llvm::errs()); } 128*0fca6ea1SDimitry Andric void dump(llvm::raw_ostream &OS) const; 129*0fca6ea1SDimitry Andric 130*0fca6ea1SDimitry Andric private: 1315ffd83dbSDimitry Andric friend class Pointer; 1325ffd83dbSDimitry Andric friend class DeadBlock; 1335ffd83dbSDimitry Andric friend class InterpState; 134*0fca6ea1SDimitry Andric friend class DynamicAllocator; 1355ffd83dbSDimitry Andric 136*0fca6ea1SDimitry Andric Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic, 137*0fca6ea1SDimitry Andric bool IsDead) 138*0fca6ea1SDimitry Andric : EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), 139*0fca6ea1SDimitry Andric IsDynamic(false), Desc(Desc) { 140*0fca6ea1SDimitry Andric assert(Desc); 141*0fca6ea1SDimitry Andric } 1425ffd83dbSDimitry Andric 14306c3fb27SDimitry Andric /// Deletes a dead block at the end of its lifetime. 1445ffd83dbSDimitry Andric void cleanup(); 1455ffd83dbSDimitry Andric 14606c3fb27SDimitry Andric /// Pointer chain management. 1475ffd83dbSDimitry Andric void addPointer(Pointer *P); 1485ffd83dbSDimitry Andric void removePointer(Pointer *P); 14906c3fb27SDimitry Andric void replacePointer(Pointer *Old, Pointer *New); 15006c3fb27SDimitry Andric #ifndef NDEBUG 15106c3fb27SDimitry Andric bool hasPointer(const Pointer *P) const; 15206c3fb27SDimitry Andric #endif 1535ffd83dbSDimitry Andric 154*0fca6ea1SDimitry Andric const unsigned EvalID = ~0u; 1555ffd83dbSDimitry Andric /// Start of the chain of pointers. 1565ffd83dbSDimitry Andric Pointer *Pointers = nullptr; 1575ffd83dbSDimitry Andric /// Unique identifier of the declaration. 158bdd1243dSDimitry Andric std::optional<unsigned> DeclID; 1595ffd83dbSDimitry Andric /// Flag indicating if the block has static storage duration. 1605ffd83dbSDimitry Andric bool IsStatic = false; 1615ffd83dbSDimitry Andric /// Flag indicating if the block is an extern. 1625ffd83dbSDimitry Andric bool IsExtern = false; 1635f757f3fSDimitry Andric /// Flag indicating if the pointer is dead. This is only ever 1645f757f3fSDimitry Andric /// set once, when converting the Block to a DeadBlock. 1655ffd83dbSDimitry Andric bool IsDead = false; 1665f757f3fSDimitry Andric /// Flag indicating if the block contents have been initialized 1675f757f3fSDimitry Andric /// via invokeCtor. 1685f757f3fSDimitry Andric bool IsInitialized = false; 169*0fca6ea1SDimitry Andric /// Flag indicating if this block has been allocated via dynamic 170*0fca6ea1SDimitry Andric /// memory allocation (e.g. malloc). 171*0fca6ea1SDimitry Andric bool IsDynamic = false; 1725ffd83dbSDimitry Andric /// Pointer to the stack slot descriptor. 1735f757f3fSDimitry Andric const Descriptor *Desc; 1745ffd83dbSDimitry Andric }; 1755ffd83dbSDimitry Andric 1765ffd83dbSDimitry Andric /// Descriptor for a dead block. 1775ffd83dbSDimitry Andric /// 1785ffd83dbSDimitry Andric /// Dead blocks are chained in a double-linked list to deallocate them 1795ffd83dbSDimitry Andric /// whenever pointers become dead. 180bdd1243dSDimitry Andric class DeadBlock final { 1815ffd83dbSDimitry Andric public: 1825ffd83dbSDimitry Andric /// Copies the block. 1835ffd83dbSDimitry Andric DeadBlock(DeadBlock *&Root, Block *Blk); 1845ffd83dbSDimitry Andric 1855ffd83dbSDimitry Andric /// Returns a pointer to the stored data. 1865f757f3fSDimitry Andric std::byte *data() { return B.data(); } 1875f757f3fSDimitry Andric std::byte *rawData() { return B.rawData(); } 1885ffd83dbSDimitry Andric 1895ffd83dbSDimitry Andric private: 1905ffd83dbSDimitry Andric friend class Block; 1915ffd83dbSDimitry Andric friend class InterpState; 1925ffd83dbSDimitry Andric 1935ffd83dbSDimitry Andric void free(); 1945ffd83dbSDimitry Andric 1955ffd83dbSDimitry Andric /// Root pointer of the list. 1965ffd83dbSDimitry Andric DeadBlock *&Root; 1975ffd83dbSDimitry Andric /// Previous block in the list. 1985ffd83dbSDimitry Andric DeadBlock *Prev; 1995ffd83dbSDimitry Andric /// Next block in the list. 2005ffd83dbSDimitry Andric DeadBlock *Next; 2015ffd83dbSDimitry Andric 2025ffd83dbSDimitry Andric /// Actual block storing data and tracking pointers. 2035ffd83dbSDimitry Andric Block B; 2045ffd83dbSDimitry Andric }; 2055ffd83dbSDimitry Andric 2065ffd83dbSDimitry Andric } // namespace interp 2075ffd83dbSDimitry Andric } // namespace clang 2085ffd83dbSDimitry Andric 2095ffd83dbSDimitry Andric #endif 210