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