1//===--- Program.h - Bytecode for the constexpr VM --------------*- 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 a program which organises and links multiple bytecode functions.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_PROGRAM_H
14#define LLVM_CLANG_AST_INTERP_PROGRAM_H
15
16#include "Function.h"
17#include "Pointer.h"
18#include "PrimType.h"
19#include "Record.h"
20#include "Source.h"
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/Support/Allocator.h"
23#include <vector>
24
25namespace clang {
26class RecordDecl;
27class Expr;
28class FunctionDecl;
29class StringLiteral;
30class VarDecl;
31
32namespace interp {
33class Context;
34
35/// The program contains and links the bytecode for all functions.
36class Program final {
37public:
38 Program(Context &Ctx) : Ctx(Ctx) {}
39
40 ~Program() {
41 // Manually destroy all the blocks. They are almost all harmless,
42 // but primitive arrays might have an InitMap* heap allocated and
43 // that needs to be freed.
44 for (Global *G : Globals)
45 if (Block *B = G->block(); B->isInitialized())
46 B->invokeDtor();
47
48 // Records might actually allocate memory themselves, but they
49 // are allocated using a BumpPtrAllocator. Call their desctructors
50 // here manually so they are properly freeing their resources.
51 for (auto RecordPair : Records) {
52 if (Record *R = RecordPair.second)
53 R->~Record();
54 }
55 }
56
57 const Context &getContext() const { return Ctx; }
58
59 /// Marshals a native pointer to an ID for embedding in bytecode.
60 unsigned getOrCreateNativePointer(const void *Ptr);
61
62 /// Returns the value of a marshalled native pointer.
63 const void *getNativePointer(unsigned Idx) const;
64
65 /// Emits a string literal among global data.
66 unsigned createGlobalString(const StringLiteral *S,
67 const Expr *Base = nullptr);
68
69 /// Returns a pointer to a global.
70 Pointer getPtrGlobal(unsigned Idx) const;
71
72 /// Returns the value of a global.
73 Block *getGlobal(unsigned Idx) {
74 assert(Idx < Globals.size());
75 return Globals[Idx]->block();
76 }
77
78 bool isGlobalInitialized(unsigned Index) const {
79 return getPtrGlobal(Idx: Index).isInitialized();
80 }
81
82 /// Finds a global's index.
83 UnsignedOrNone getGlobal(const ValueDecl *VD);
84 UnsignedOrNone getGlobal(const Expr *E);
85
86 /// Returns or creates a global an creates an index to it.
87 UnsignedOrNone getOrCreateGlobal(const ValueDecl *VD,
88 const Expr *Init = nullptr);
89
90 /// Returns or creates a dummy value for unknown declarations.
91 unsigned getOrCreateDummy(const DeclTy &D, bool IsConstexprUnknown = false);
92
93 /// Creates a global and returns its index.
94 UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init,
95 bool IsConstexprUnknown = false);
96
97 /// Creates a global from a lifetime-extended temporary.
98 UnsignedOrNone createGlobal(const Expr *E, QualType ExprType);
99
100 /// Creates a new function from a code range.
101 template <typename... Ts>
102 Function *createFunction(const FunctionDecl *Def, Ts &&...Args) {
103 Def = Def->getCanonicalDecl();
104 auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...);
105 Funcs.insert(KV: {Def, std::unique_ptr<Function>(Func)});
106 return Func;
107 }
108 /// Creates an anonymous function.
109 template <typename... Ts> Function *createFunction(Ts &&...Args) {
110 auto *Func = new Function(*this, std::forward<Ts>(Args)...);
111 AnonFuncs.emplace_back(args&: Func);
112 return Func;
113 }
114
115 /// Returns a function.
116 Function *getFunction(const FunctionDecl *F);
117
118 /// Returns a record or creates one if it does not exist.
119 Record *getOrCreateRecord(const RecordDecl *RD);
120
121 /// Creates a descriptor for a primitive type.
122 Descriptor *createDescriptor(const DeclTy &D, PrimType T,
123 const Type *SourceTy = nullptr,
124 Descriptor::MetadataSize MDSize = std::nullopt,
125 bool IsConst = false, bool IsTemporary = false,
126 bool IsMutable = false,
127 bool IsVolatile = false) {
128 return allocateDescriptor(Args: D, Args&: SourceTy, Args&: T, Args&: MDSize, Args&: IsConst, Args&: IsTemporary,
129 Args&: IsMutable, Args&: IsVolatile);
130 }
131
132 /// Creates a descriptor for a composite type.
133 Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
134 Descriptor::MetadataSize MDSize = std::nullopt,
135 bool IsConst = false, bool IsTemporary = false,
136 bool IsMutable = false, bool IsVolatile = false,
137 const Expr *Init = nullptr);
138
139 void *Allocate(size_t Size, unsigned Align = 8) const {
140 return Allocator.Allocate(Size, Alignment: Align);
141 }
142 template <typename T> T *Allocate(size_t Num = 1) const {
143 return static_cast<T *>(Allocate(Size: Num * sizeof(T), Align: alignof(T)));
144 }
145 void Deallocate(void *Ptr) const {}
146
147 /// Context to manage declaration lifetimes.
148 class DeclScope {
149 public:
150 DeclScope(Program &P) : P(P), PrevDecl(P.CurrentDeclaration) {
151 ++P.LastDeclaration;
152 P.CurrentDeclaration = P.LastDeclaration;
153 }
154 ~DeclScope() { P.CurrentDeclaration = PrevDecl; }
155
156 private:
157 Program &P;
158 unsigned PrevDecl;
159 };
160
161 /// Returns the current declaration ID.
162 UnsignedOrNone getCurrentDecl() const {
163 if (CurrentDeclaration == NoDeclaration)
164 return std::nullopt;
165 return CurrentDeclaration;
166 }
167
168private:
169 friend class DeclScope;
170
171 UnsignedOrNone createGlobal(const DeclTy &D, QualType Ty, bool IsStatic,
172 bool IsExtern, bool IsWeak,
173 bool IsConstexprUnknown,
174 const Expr *Init = nullptr);
175
176 /// Reference to the VM context.
177 Context &Ctx;
178 /// Mapping from decls to cached bytecode functions.
179 llvm::DenseMap<const FunctionDecl *, std::unique_ptr<Function>> Funcs;
180 /// List of anonymous functions.
181 std::vector<std::unique_ptr<Function>> AnonFuncs;
182
183 /// Native pointers referenced by bytecode.
184 std::vector<const void *> NativePointers;
185 /// Cached native pointer indices.
186 llvm::DenseMap<const void *, unsigned> NativePointerIndices;
187
188 /// Custom allocator for global storage.
189 using PoolAllocTy = llvm::BumpPtrAllocator;
190
191 /// Descriptor + storage for a global object.
192 ///
193 /// Global objects never go out of scope, thus they do not track pointers.
194 class Global {
195 public:
196 /// Create a global descriptor for string literals.
197 template <typename... Tys>
198 Global(Tys... Args) : B(std::forward<Tys>(Args)...) {}
199
200 /// Allocates the global in the pool, reserving storate for data.
201 void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) {
202 return Alloc.Allocate(Size: Meta + Data, Alignment: alignof(void *));
203 }
204
205 /// Return a pointer to the data.
206 std::byte *data() { return B.data(); }
207 /// Return a pointer to the block.
208 Block *block() { return &B; }
209 const Block *block() const { return &B; }
210
211 private:
212 Block B;
213 };
214
215 /// Allocator for globals.
216 mutable PoolAllocTy Allocator;
217
218 /// Global objects.
219 std::vector<Global *> Globals;
220 /// Cached global indices.
221 llvm::DenseMap<const void *, unsigned> GlobalIndices;
222
223 /// Mapping from decls to record metadata.
224 llvm::DenseMap<const RecordDecl *, Record *> Records;
225
226 /// Dummy parameter to generate pointers from.
227 llvm::DenseMap<const void *, unsigned> DummyVariables;
228
229 /// Creates a new descriptor.
230 template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) {
231 return new (Allocator) Descriptor(std::forward<Ts>(Args)...);
232 }
233
234 /// No declaration ID.
235 static constexpr unsigned NoDeclaration = ~0u;
236 /// Last declaration ID.
237 unsigned LastDeclaration = 0;
238 /// Current declaration ID.
239 unsigned CurrentDeclaration = NoDeclaration;
240
241public:
242 /// Dumps the disassembled bytecode to \c llvm::errs().
243 void dump() const;
244 void dump(llvm::raw_ostream &OS) const;
245};
246
247} // namespace interp
248} // namespace clang
249
250inline void *operator new(size_t Bytes, const clang::interp::Program &C,
251 size_t Alignment = 8) {
252 return C.Allocate(Size: Bytes, Align: Alignment);
253}
254
255inline void operator delete(void *Ptr, const clang::interp::Program &C,
256 size_t) {
257 C.Deallocate(Ptr);
258}
259inline void *operator new[](size_t Bytes, const clang::interp::Program &C,
260 size_t Alignment = 8) {
261 return C.Allocate(Size: Bytes, Align: Alignment);
262}
263
264#endif
265