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