233 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8//9// Defines the classes describing allocated blocks.10//11//===----------------------------------------------------------------------===//12 13#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H14#define LLVM_CLANG_AST_INTERP_BLOCK_H15 16#include "Descriptor.h"17#include "llvm/Support/raw_ostream.h"18 19namespace clang {20namespace interp {21class Block;22class DeadBlock;23class InterpState;24class Pointer;25enum PrimType : uint8_t;26 27/// A memory block, either on the stack or in the heap.28///29/// The storage described by the block is immediately followed by30/// optional metadata, which is followed by the actual data.31///32/// Block* rawData() data()33/// │ │ │34/// │ │ │35/// ▼ ▼ ▼36/// ┌───────────────┬─────────────────────────┬─────────────────┐37/// │ Block │ Metadata │ Data │38/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │39/// └───────────────┴─────────────────────────┴─────────────────┘40///41/// Desc->getAllocSize() describes the size after the Block, i.e.42/// the data size and the metadata size.43///44class Block final {45private:46 static constexpr uint8_t ExternFlag = 1 << 0;47 static constexpr uint8_t DeadFlag = 1 << 1;48 static constexpr uint8_t WeakFlag = 1 << 2;49 static constexpr uint8_t DummyFlag = 1 << 3;50 51public:52 /// Creates a new block.53 Block(unsigned EvalID, UnsignedOrNone DeclID, const Descriptor *Desc,54 bool IsStatic = false, bool IsExtern = false, bool IsWeak = false,55 bool IsDummy = false)56 : Desc(Desc), DeclID(DeclID), EvalID(EvalID), IsStatic(IsStatic) {57 assert(Desc);58 AccessFlags |= (ExternFlag * IsExtern);59 AccessFlags |= (WeakFlag * IsWeak);60 AccessFlags |= (DummyFlag * IsDummy);61 }62 63 Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,64 bool IsExtern = false, bool IsWeak = false, bool IsDummy = false)65 : Desc(Desc), EvalID(EvalID), IsStatic(IsStatic) {66 assert(Desc);67 AccessFlags |= (ExternFlag * IsExtern);68 AccessFlags |= (WeakFlag * IsWeak);69 AccessFlags |= (DummyFlag * IsDummy);70 }71 72 /// Returns the block's descriptor.73 const Descriptor *getDescriptor() const { return Desc; }74 /// Checks if the block has any live pointers.75 bool hasPointers() const { return Pointers; }76 /// Checks if the block is extern.77 bool isExtern() const { return AccessFlags & ExternFlag; }78 /// Checks if the block has static storage duration.79 bool isStatic() const { return IsStatic; }80 /// Checks if the block is temporary.81 bool isTemporary() const { return Desc->IsTemporary; }82 bool isWeak() const { return AccessFlags & WeakFlag; }83 bool isDynamic() const { return (DynAllocId != std::nullopt); }84 bool isDummy() const { return AccessFlags & DummyFlag; }85 bool isDead() const { return AccessFlags & DeadFlag; }86 /// Returns the size of the block.87 unsigned getSize() const { return Desc->getAllocSize(); }88 /// Returns the declaration ID.89 UnsignedOrNone getDeclID() const { return DeclID; }90 /// Returns whether the data of this block has been initialized via91 /// invoking the Ctor func.92 bool isInitialized() const { return IsInitialized; }93 /// The Evaluation ID this block was created in.94 unsigned getEvalID() const { return EvalID; }95 /// Move all pointers from this block to \param B.96 void movePointersTo(Block *B);97 98 /// Returns a pointer to the stored data.99 /// You are allowed to read Desc->getSize() bytes from this address.100 std::byte *data() {101 // rawData might contain metadata as well.102 size_t DataOffset = Desc->getMetadataSize();103 return rawData() + DataOffset;104 }105 const std::byte *data() const {106 // rawData might contain metadata as well.107 size_t DataOffset = Desc->getMetadataSize();108 return rawData() + DataOffset;109 }110 111 /// Returns a pointer to the raw data, including metadata.112 /// You are allowed to read Desc->getAllocSize() bytes from this address.113 std::byte *rawData() {114 return reinterpret_cast<std::byte *>(this) + sizeof(Block);115 }116 const std::byte *rawData() const {117 return reinterpret_cast<const std::byte *>(this) + sizeof(Block);118 }119 120 template <typename T> const T &deref() const {121 return *reinterpret_cast<const T *>(data());122 }123 template <typename T> T &deref() { return *reinterpret_cast<T *>(data()); }124 125 /// Invokes the constructor.126 void invokeCtor() {127 assert(!IsInitialized);128 std::memset(rawData(), 0, Desc->getAllocSize());129 if (Desc->CtorFn) {130 Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,131 Desc->IsVolatile,132 /*isActive=*/true, /*InUnion=*/false, Desc);133 }134 IsInitialized = true;135 }136 137 /// Invokes the Destructor.138 void invokeDtor() {139 assert(IsInitialized);140 if (Desc->DtorFn)141 Desc->DtorFn(this, data(), Desc);142 IsInitialized = false;143 }144 145 void dump() const { dump(llvm::errs()); }146 void dump(llvm::raw_ostream &OS) const;147 148 bool isAccessible() const { return AccessFlags == 0; }149 150private:151 friend class Pointer;152 friend class DeadBlock;153 friend class InterpState;154 friend class DynamicAllocator;155 friend class Program;156 157 Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,158 bool IsWeak, bool IsDummy, bool IsDead)159 : Desc(Desc), EvalID(EvalID), IsStatic(IsStatic) {160 assert(Desc);161 AccessFlags |= (ExternFlag * IsExtern);162 AccessFlags |= (DeadFlag * IsDead);163 AccessFlags |= (WeakFlag * IsWeak);164 AccessFlags |= (DummyFlag * IsDummy);165 }166 167 /// To be called by DynamicAllocator.168 void setDynAllocId(unsigned ID) { DynAllocId = ID; }169 170 /// Deletes a dead block at the end of its lifetime.171 void cleanup();172 173 /// Pointer chain management.174 void addPointer(Pointer *P);175 void removePointer(Pointer *P);176 void replacePointer(Pointer *Old, Pointer *New);177#ifndef NDEBUG178 bool hasPointer(const Pointer *P) const;179#endif180 181 /// Pointer to the stack slot descriptor.182 const Descriptor *Desc;183 /// Start of the chain of pointers.184 Pointer *Pointers = nullptr;185 /// Unique identifier of the declaration.186 UnsignedOrNone DeclID = std::nullopt;187 const unsigned EvalID = ~0u;188 /// Flag indicating if the block has static storage duration.189 bool IsStatic = false;190 /// Flag indicating if the block contents have been initialized191 /// via invokeCtor.192 bool IsInitialized = false;193 /// Allocation ID for this dynamic allocation, if it is one.194 UnsignedOrNone DynAllocId = std::nullopt;195 /// AccessFlags containing IsExtern, IsDead, IsWeak, and IsDummy bits.196 uint8_t AccessFlags = 0;197};198 199/// Descriptor for a dead block.200///201/// Dead blocks are chained in a double-linked list to deallocate them202/// whenever pointers become dead.203class DeadBlock final {204public:205 /// Copies the block.206 DeadBlock(DeadBlock *&Root, Block *Blk);207 208 /// Returns a pointer to the stored data.209 std::byte *data() { return B.data(); }210 std::byte *rawData() { return B.rawData(); }211 212private:213 friend class Block;214 friend class InterpState;215 216 void free();217 218 /// Root pointer of the list.219 DeadBlock *&Root;220 /// Previous block in the list.221 DeadBlock *Prev;222 /// Next block in the list.223 DeadBlock *Next;224 225 /// Actual block storing data and tracking pointers.226 Block B;227};228 229} // namespace interp230} // namespace clang231 232#endif233