1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
//===-- InterpBlock.h - Allocated blocks for the interpreter -*- C++ ----*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Defines the classes describing allocated blocks.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H
#define LLVM_CLANG_AST_INTERP_BLOCK_H
#include "Descriptor.h"
#include "clang/AST/ComparisonCategories.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace interp {
class Block;
class DeadBlock;
class InterpState;
class Pointer;
enum PrimType : unsigned;
/// A memory block, either on the stack or in the heap.
///
/// The storage described by the block is immediately followed by
/// optional metadata, which is followed by the actual data.
///
/// Block* rawData() data()
/// │ │ │
/// │ │ │
/// ▼ ▼ ▼
/// ┌───────────────┬─────────────────────────┬─────────────────┐
/// │ Block │ Metadata │ Data │
/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │
/// └───────────────┴─────────────────────────┴─────────────────┘
///
/// Desc->getAllocSize() describes the size after the Block, i.e.
/// the data size and the metadata size.
///
class Block final {
public:
/// Creates a new block.
Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false,
bool IsWeak = false)
: EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern),
IsDynamic(false), IsWeak(IsWeak), Desc(Desc) {
assert(Desc);
}
Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
bool IsExtern = false, bool IsWeak = false)
: EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic),
IsExtern(IsExtern), IsDynamic(false), IsWeak(IsWeak), Desc(Desc) {
assert(Desc);
}
/// Returns the block's descriptor.
const Descriptor *getDescriptor() const { return Desc; }
/// Checks if the block has any live pointers.
bool hasPointers() const { return Pointers; }
/// Checks if the block is extern.
bool isExtern() const { return IsExtern; }
/// Checks if the block has static storage duration.
bool isStatic() const { return IsStatic; }
/// Checks if the block is temporary.
bool isTemporary() const { return Desc->IsTemporary; }
bool isWeak() const { return IsWeak; }
bool isDynamic() const { return IsDynamic; }
/// Returns the size of the block.
unsigned getSize() const { return Desc->getAllocSize(); }
/// Returns the declaration ID.
std::optional<unsigned> getDeclID() const { return DeclID; }
/// Returns whether the data of this block has been initialized via
/// invoking the Ctor func.
bool isInitialized() const { return IsInitialized; }
/// The Evaluation ID this block was created in.
unsigned getEvalID() const { return EvalID; }
/// Returns a pointer to the stored data.
/// You are allowed to read Desc->getSize() bytes from this address.
std::byte *data() {
// rawData might contain metadata as well.
size_t DataOffset = Desc->getMetadataSize();
return rawData() + DataOffset;
}
const std::byte *data() const {
// rawData might contain metadata as well.
size_t DataOffset = Desc->getMetadataSize();
return rawData() + DataOffset;
}
/// Returns a pointer to the raw data, including metadata.
/// You are allowed to read Desc->getAllocSize() bytes from this address.
std::byte *rawData() {
return reinterpret_cast<std::byte *>(this) + sizeof(Block);
}
const std::byte *rawData() const {
return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
}
/// Invokes the constructor.
void invokeCtor() {
assert(!IsInitialized);
std::memset(rawData(), 0, Desc->getAllocSize());
if (Desc->CtorFn) {
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
/*isActive=*/true, /*InUnion=*/false, Desc);
}
IsInitialized = true;
}
/// Invokes the Destructor.
void invokeDtor() {
assert(IsInitialized);
if (Desc->DtorFn)
Desc->DtorFn(this, data(), Desc);
IsInitialized = false;
}
void dump() const { dump(llvm::errs()); }
void dump(llvm::raw_ostream &OS) const;
private:
friend class Pointer;
friend class DeadBlock;
friend class InterpState;
friend class DynamicAllocator;
Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
bool IsWeak, bool IsDead)
: EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true),
IsDynamic(false), IsWeak(IsWeak), Desc(Desc) {
assert(Desc);
}
/// Deletes a dead block at the end of its lifetime.
void cleanup();
/// Pointer chain management.
void addPointer(Pointer *P);
void removePointer(Pointer *P);
void replacePointer(Pointer *Old, Pointer *New);
#ifndef NDEBUG
bool hasPointer(const Pointer *P) const;
#endif
const unsigned EvalID = ~0u;
/// Start of the chain of pointers.
Pointer *Pointers = nullptr;
/// Unique identifier of the declaration.
std::optional<unsigned> DeclID;
/// Flag indicating if the block has static storage duration.
bool IsStatic = false;
/// Flag indicating if the block is an extern.
bool IsExtern = false;
/// Flag indicating if the pointer is dead. This is only ever
/// set once, when converting the Block to a DeadBlock.
bool IsDead = false;
/// Flag indicating if the block contents have been initialized
/// via invokeCtor.
bool IsInitialized = false;
/// Flag indicating if this block has been allocated via dynamic
/// memory allocation (e.g. malloc).
bool IsDynamic = false;
bool IsWeak = false;
/// Pointer to the stack slot descriptor.
const Descriptor *Desc;
};
/// Descriptor for a dead block.
///
/// Dead blocks are chained in a double-linked list to deallocate them
/// whenever pointers become dead.
class DeadBlock final {
public:
/// Copies the block.
DeadBlock(DeadBlock *&Root, Block *Blk);
/// Returns a pointer to the stored data.
std::byte *data() { return B.data(); }
std::byte *rawData() { return B.rawData(); }
private:
friend class Block;
friend class InterpState;
void free();
/// Root pointer of the list.
DeadBlock *&Root;
/// Previous block in the list.
DeadBlock *Prev;
/// Next block in the list.
DeadBlock *Next;
/// Actual block storing data and tracking pointers.
Block B;
};
} // namespace interp
} // namespace clang
#endif
|