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
24namespace clang {
25namespace interp {
26class Block;
27class DeadBlock;
28class InterpState;
29class Pointer;
30enum 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///
49class Block final {
50public:
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 : 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)
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.
67 const Descriptor *getDescriptor() const { return Desc; }
68 /// Checks if the block has any live pointers.
69 bool hasPointers() const { return Pointers; }
70 /// Checks if the block is extern.
71 bool isExtern() const { return IsExtern; }
72 /// Checks if the block has static storage duration.
73 bool isStatic() const { return IsStatic; }
74 /// Checks if the block is temporary.
75 bool isTemporary() const { return Desc->IsTemporary; }
76 bool isDynamic() const { return IsDynamic; }
77 /// Returns the size of the block.
78 unsigned getSize() const { return Desc->getAllocSize(); }
79 /// Returns the declaration ID.
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.
83 bool isInitialized() const { return IsInitialized; }
84 /// The Evaluation ID this block was created in.
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.
89 std::byte *data() {
90 // rawData might contain metadata as well.
91 size_t DataOffset = Desc->getMetadataSize();
92 return rawData() + DataOffset;
93 }
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.
102 std::byte *rawData() {
103 return reinterpret_cast<std::byte *>(this) + sizeof(Block);
104 }
105 const std::byte *rawData() const {
106 return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
107 }
108
109 /// Invokes the constructor.
110 void invokeCtor() {
111 assert(!IsInitialized);
112 std::memset(s: rawData(), c: 0, n: 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.
120 void invokeDtor() {
121 assert(IsInitialized);
122 if (Desc->DtorFn)
123 Desc->DtorFn(this, data(), Desc);
124 IsInitialized = false;
125 }
126
127 void dump() const { dump(OS&: llvm::errs()); }
128 void dump(llvm::raw_ostream &OS) const;
129
130private:
131 friend class Pointer;
132 friend class DeadBlock;
133 friend class InterpState;
134 friend class DynamicAllocator;
135
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.
180class DeadBlock final {
181public:
182 /// Copies the block.
183 DeadBlock(DeadBlock *&Root, Block *Blk);
184
185 /// Returns a pointer to the stored data.
186 std::byte *data() { return B.data(); }
187 std::byte *rawData() { return B.rawData(); }
188
189private:
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