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 for (Function *F : Funcs.values())
58 F->~Function();
59 }
60
61 const Context &getContext() const { return Ctx; }
62
63 /// Returns a pointer to a global.
64 Pointer getPtrGlobal(unsigned Idx) const;
65
66 /// Returns the value of a global.
67 Block *getGlobal(unsigned Idx) {
68 assert(Idx < Globals.size());
69 return Globals[Idx]->block();
70 }
71
72 bool isGlobalInitialized(unsigned Index) const {
73 return getPtrGlobal(Idx: Index).isInitialized();
74 }
75
76 /// Finds a global's index.
77 UnsignedOrNone getGlobal(const ValueDecl *VD);
78 UnsignedOrNone getGlobal(const Expr *E);
79
80 /// Returns or creates a global an creates an index to it.
81 UnsignedOrNone getOrCreateGlobal(const ValueDecl *VD,
82 const Expr *Init = nullptr);
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->getFirstDecl();
95 auto *Func = new (Allocate(Size: sizeof(Function)))
96 Function(Def, std::forward<Ts>(Args)...);
97 Funcs.insert(KV: {Def, Func});
98 return Func;
99 }
100 /// Creates an anonymous function.
101 template <typename... Ts> Function *createFunction(Ts &&...Args) {
102 auto *Func = new Function(std::forward<Ts>(Args)...);
103 AnonFuncs.emplace_back(args&: Func);
104 return Func;
105 }
106
107 /// Returns a function.
108 Function *getFunction(const FunctionDecl *F);
109
110 /// Returns a record or creates one if it does not exist.
111 Record *getOrCreateRecord(const RecordDecl *RD);
112
113 /// Creates a descriptor for a primitive type.
114 Descriptor *createDescriptor(DeclOrExpr D, PrimType T,
115 const Type *SourceTy = nullptr,
116 bool IsConst = false, bool IsTemporary = false,
117 bool IsMutable = false,
118 bool IsVolatile = false) {
119 return allocateDescriptor(Args&: D, Args&: SourceTy, Args&: T, Args&: IsConst, Args&: IsTemporary, Args&: IsMutable,
120 Args&: IsVolatile);
121 }
122
123 /// Creates a descriptor for a composite type.
124 Descriptor *createDescriptor(DeclOrExpr D, const Type *Ty,
125 bool IsConst = false, bool IsTemporary = false,
126 bool IsMutable = false, bool IsVolatile = false,
127 const Expr *Init = nullptr);
128
129 void *Allocate(size_t Size, unsigned Align = 8) const {
130 return Allocator.Allocate(Size, Alignment: Align);
131 }
132 template <typename T> T *Allocate(size_t Num = 1) const {
133 return static_cast<T *>(Allocate(Size: Num * sizeof(T), Align: alignof(T)));
134 }
135 void Deallocate(void *Ptr) const {}
136
137 /// Context to manage declaration lifetimes.
138 class DeclScope {
139 public:
140 DeclScope(Program &P) : P(P), PrevDecl(P.CurrentDeclaration) {
141 ++P.LastDeclaration;
142 P.CurrentDeclaration = P.LastDeclaration;
143 }
144 ~DeclScope() { P.CurrentDeclaration = PrevDecl; }
145
146 private:
147 Program &P;
148 unsigned PrevDecl;
149 };
150
151 /// Returns the current declaration ID.
152 UnsignedOrNone getCurrentDecl() const {
153 if (CurrentDeclaration == NoDeclaration)
154 return std::nullopt;
155 return CurrentDeclaration;
156 }
157
158private:
159 friend class DeclScope;
160
161 UnsignedOrNone createGlobal(DeclOrExpr D, QualType Ty, bool IsStatic,
162 bool IsExtern, bool IsWeak,
163 bool IsConstexprUnknown,
164 const Expr *Init = nullptr);
165
166 /// Reference to the VM context.
167 Context &Ctx;
168 /// Mapping from decls to cached bytecode functions.
169 llvm::DenseMap<const FunctionDecl *, Function *> Funcs;
170 /// List of anonymous functions.
171 std::vector<std::unique_ptr<Function>> AnonFuncs;
172
173 /// Custom allocator for global storage.
174 using PoolAllocTy = llvm::BumpPtrAllocator;
175
176 /// Descriptor + storage for a global object.
177 ///
178 /// Global objects never go out of scope, thus they do not track pointers.
179 class Global {
180 public:
181 /// Create a global descriptor for string literals.
182 template <typename... Tys>
183 Global(Tys... Args) : B(std::forward<Tys>(Args)...) {}
184
185 /// Allocates the global in the pool, reserving storate for data.
186 void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) {
187 return Alloc.Allocate(Size: Meta + Data, Alignment: alignof(void *));
188 }
189
190 /// Return a pointer to the data.
191 std::byte *data() { return B.data(); }
192 /// Return a pointer to the block.
193 Block *block() { return &B; }
194 const Block *block() const { return &B; }
195
196 private:
197 Block B;
198 };
199
200 /// Allocator for globals.
201 mutable PoolAllocTy Allocator;
202
203 /// Global objects.
204 std::vector<Global *> Globals;
205 /// Cached global indices.
206 llvm::DenseMap<const void *, unsigned> GlobalIndices;
207
208 /// Mapping from decls to record metadata.
209 llvm::DenseMap<const RecordDecl *, Record *> Records;
210
211 /// Creates a new descriptor.
212 template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) {
213 return new (Allocator) Descriptor(std::forward<Ts>(Args)...);
214 }
215
216 /// No declaration ID.
217 static constexpr unsigned NoDeclaration = ~0u;
218 /// Last declaration ID.
219 unsigned LastDeclaration = 0;
220 /// Current declaration ID.
221 unsigned CurrentDeclaration = NoDeclaration;
222
223public:
224 /// Dumps the disassembled bytecode to \c llvm::errs().
225 void dump() const;
226 void dump(llvm::raw_ostream &OS) const;
227};
228
229} // namespace interp
230} // namespace clang
231
232inline void *operator new(size_t Bytes, const clang::interp::Program &C,
233 size_t Alignment = 8) {
234 return C.Allocate(Size: Bytes, Align: Alignment);
235}
236
237inline void operator delete(void *Ptr, const clang::interp::Program &C,
238 size_t) {
239 C.Deallocate(Ptr);
240}
241inline void *operator new[](size_t Bytes, const clang::interp::Program &C,
242 size_t Alignment = 8) {
243 return C.Allocate(Size: Bytes, Align: Alignment);
244}
245
246#endif
247