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