1e5dd7070Spatrick //===--- Program.cpp - Bytecode for the constexpr VM ------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick
9e5dd7070Spatrick #include "Program.h"
10e5dd7070Spatrick #include "ByteCodeStmtGen.h"
11e5dd7070Spatrick #include "Context.h"
12e5dd7070Spatrick #include "Function.h"
13e5dd7070Spatrick #include "Opcode.h"
14e5dd7070Spatrick #include "PrimType.h"
15e5dd7070Spatrick #include "clang/AST/Decl.h"
16e5dd7070Spatrick #include "clang/AST/DeclCXX.h"
17e5dd7070Spatrick
18e5dd7070Spatrick using namespace clang;
19e5dd7070Spatrick using namespace clang::interp;
20e5dd7070Spatrick
getOrCreateNativePointer(const void * Ptr)21*12c85518Srobert unsigned Program::getOrCreateNativePointer(const void *Ptr) {
22*12c85518Srobert auto It = NativePointerIndices.find(Ptr);
23*12c85518Srobert if (It != NativePointerIndices.end())
24*12c85518Srobert return It->second;
25*12c85518Srobert
26*12c85518Srobert unsigned Idx = NativePointers.size();
27*12c85518Srobert NativePointers.push_back(Ptr);
28*12c85518Srobert NativePointerIndices[Ptr] = Idx;
29*12c85518Srobert return Idx;
30*12c85518Srobert }
31*12c85518Srobert
getNativePointer(unsigned Idx)32*12c85518Srobert const void *Program::getNativePointer(unsigned Idx) {
33*12c85518Srobert return NativePointers[Idx];
34*12c85518Srobert }
35*12c85518Srobert
createGlobalString(const StringLiteral * S)36e5dd7070Spatrick unsigned Program::createGlobalString(const StringLiteral *S) {
37e5dd7070Spatrick const size_t CharWidth = S->getCharByteWidth();
38e5dd7070Spatrick const size_t BitWidth = CharWidth * Ctx.getCharBit();
39e5dd7070Spatrick
40e5dd7070Spatrick PrimType CharType;
41e5dd7070Spatrick switch (CharWidth) {
42e5dd7070Spatrick case 1:
43e5dd7070Spatrick CharType = PT_Sint8;
44e5dd7070Spatrick break;
45e5dd7070Spatrick case 2:
46e5dd7070Spatrick CharType = PT_Uint16;
47e5dd7070Spatrick break;
48e5dd7070Spatrick case 4:
49e5dd7070Spatrick CharType = PT_Uint32;
50e5dd7070Spatrick break;
51e5dd7070Spatrick default:
52e5dd7070Spatrick llvm_unreachable("unsupported character width");
53e5dd7070Spatrick }
54e5dd7070Spatrick
55e5dd7070Spatrick // Create a descriptor for the string.
56*12c85518Srobert Descriptor *Desc =
57*12c85518Srobert allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1,
58e5dd7070Spatrick /*isConst=*/true,
59e5dd7070Spatrick /*isTemporary=*/false,
60e5dd7070Spatrick /*isMutable=*/false);
61e5dd7070Spatrick
62e5dd7070Spatrick // Allocate storage for the string.
63e5dd7070Spatrick // The byte length does not include the null terminator.
64e5dd7070Spatrick unsigned I = Globals.size();
65e5dd7070Spatrick unsigned Sz = Desc->getAllocSize();
66e5dd7070Spatrick auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
67e5dd7070Spatrick /*isExtern=*/false);
68*12c85518Srobert G->block()->invokeCtor();
69e5dd7070Spatrick Globals.push_back(G);
70e5dd7070Spatrick
71e5dd7070Spatrick // Construct the string in storage.
72e5dd7070Spatrick const Pointer Ptr(G->block());
73e5dd7070Spatrick for (unsigned I = 0, N = S->getLength(); I <= N; ++I) {
74e5dd7070Spatrick Pointer Field = Ptr.atIndex(I).narrow();
75e5dd7070Spatrick const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I);
76e5dd7070Spatrick switch (CharType) {
77e5dd7070Spatrick case PT_Sint8: {
78e5dd7070Spatrick using T = PrimConv<PT_Sint8>::T;
79e5dd7070Spatrick Field.deref<T>() = T::from(CodePoint, BitWidth);
80e5dd7070Spatrick break;
81e5dd7070Spatrick }
82e5dd7070Spatrick case PT_Uint16: {
83e5dd7070Spatrick using T = PrimConv<PT_Uint16>::T;
84e5dd7070Spatrick Field.deref<T>() = T::from(CodePoint, BitWidth);
85e5dd7070Spatrick break;
86e5dd7070Spatrick }
87e5dd7070Spatrick case PT_Uint32: {
88e5dd7070Spatrick using T = PrimConv<PT_Uint32>::T;
89e5dd7070Spatrick Field.deref<T>() = T::from(CodePoint, BitWidth);
90e5dd7070Spatrick break;
91e5dd7070Spatrick }
92e5dd7070Spatrick default:
93e5dd7070Spatrick llvm_unreachable("unsupported character type");
94e5dd7070Spatrick }
95e5dd7070Spatrick }
96e5dd7070Spatrick return I;
97e5dd7070Spatrick }
98e5dd7070Spatrick
getPtrGlobal(unsigned Idx)99e5dd7070Spatrick Pointer Program::getPtrGlobal(unsigned Idx) {
100e5dd7070Spatrick assert(Idx < Globals.size());
101e5dd7070Spatrick return Pointer(Globals[Idx]->block());
102e5dd7070Spatrick }
103e5dd7070Spatrick
getGlobal(const ValueDecl * VD)104*12c85518Srobert std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
105e5dd7070Spatrick auto It = GlobalIndices.find(VD);
106e5dd7070Spatrick if (It != GlobalIndices.end())
107e5dd7070Spatrick return It->second;
108e5dd7070Spatrick
109*12c85518Srobert // Find any previous declarations which were already evaluated.
110*12c85518Srobert std::optional<unsigned> Index;
111e5dd7070Spatrick for (const Decl *P = VD; P; P = P->getPreviousDecl()) {
112e5dd7070Spatrick auto It = GlobalIndices.find(P);
113e5dd7070Spatrick if (It != GlobalIndices.end()) {
114e5dd7070Spatrick Index = It->second;
115e5dd7070Spatrick break;
116e5dd7070Spatrick }
117e5dd7070Spatrick }
118e5dd7070Spatrick
119e5dd7070Spatrick // Map the decl to the existing index.
120e5dd7070Spatrick if (Index) {
121e5dd7070Spatrick GlobalIndices[VD] = *Index;
122e5dd7070Spatrick return {};
123e5dd7070Spatrick }
124e5dd7070Spatrick
125e5dd7070Spatrick return Index;
126e5dd7070Spatrick }
127e5dd7070Spatrick
getOrCreateGlobal(const ValueDecl * VD,const Expr * Init)128*12c85518Srobert std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD,
129*12c85518Srobert const Expr *Init) {
130e5dd7070Spatrick if (auto Idx = getGlobal(VD))
131e5dd7070Spatrick return Idx;
132e5dd7070Spatrick
133*12c85518Srobert if (auto Idx = createGlobal(VD, Init)) {
134e5dd7070Spatrick GlobalIndices[VD] = *Idx;
135e5dd7070Spatrick return Idx;
136e5dd7070Spatrick }
137e5dd7070Spatrick return {};
138e5dd7070Spatrick }
139e5dd7070Spatrick
getOrCreateDummy(const ParmVarDecl * PD)140*12c85518Srobert std::optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) {
141e5dd7070Spatrick auto &ASTCtx = Ctx.getASTContext();
142e5dd7070Spatrick
143e5dd7070Spatrick // Create a pointer to an incomplete array of the specified elements.
144e5dd7070Spatrick QualType ElemTy = PD->getType()->castAs<PointerType>()->getPointeeType();
145e5dd7070Spatrick QualType Ty = ASTCtx.getIncompleteArrayType(ElemTy, ArrayType::Normal, 0);
146e5dd7070Spatrick
147e5dd7070Spatrick // Dedup blocks since they are immutable and pointers cannot be compared.
148e5dd7070Spatrick auto It = DummyParams.find(PD);
149e5dd7070Spatrick if (It != DummyParams.end())
150e5dd7070Spatrick return It->second;
151e5dd7070Spatrick
152e5dd7070Spatrick if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
153e5dd7070Spatrick DummyParams[PD] = *Idx;
154e5dd7070Spatrick return Idx;
155e5dd7070Spatrick }
156e5dd7070Spatrick return {};
157e5dd7070Spatrick }
158e5dd7070Spatrick
createGlobal(const ValueDecl * VD,const Expr * Init)159*12c85518Srobert std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
160*12c85518Srobert const Expr *Init) {
161*12c85518Srobert assert(!getGlobal(VD));
162e5dd7070Spatrick bool IsStatic, IsExtern;
163e5dd7070Spatrick if (auto *Var = dyn_cast<VarDecl>(VD)) {
164e5dd7070Spatrick IsStatic = !Var->hasLocalStorage();
165e5dd7070Spatrick IsExtern = !Var->getAnyInitializer();
166e5dd7070Spatrick } else {
167e5dd7070Spatrick IsStatic = false;
168e5dd7070Spatrick IsExtern = true;
169e5dd7070Spatrick }
170*12c85518Srobert if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) {
171e5dd7070Spatrick for (const Decl *P = VD; P; P = P->getPreviousDecl())
172e5dd7070Spatrick GlobalIndices[P] = *Idx;
173e5dd7070Spatrick return *Idx;
174e5dd7070Spatrick }
175e5dd7070Spatrick return {};
176e5dd7070Spatrick }
177e5dd7070Spatrick
createGlobal(const Expr * E)178*12c85518Srobert std::optional<unsigned> Program::createGlobal(const Expr *E) {
179e5dd7070Spatrick return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false);
180e5dd7070Spatrick }
181e5dd7070Spatrick
createGlobal(const DeclTy & D,QualType Ty,bool IsStatic,bool IsExtern,const Expr * Init)182*12c85518Srobert std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
183*12c85518Srobert bool IsStatic, bool IsExtern,
184*12c85518Srobert const Expr *Init) {
185e5dd7070Spatrick // Create a descriptor for the global.
186e5dd7070Spatrick Descriptor *Desc;
187e5dd7070Spatrick const bool IsConst = Ty.isConstQualified();
188e5dd7070Spatrick const bool IsTemporary = D.dyn_cast<const Expr *>();
189e5dd7070Spatrick if (auto T = Ctx.classify(Ty)) {
190*12c85518Srobert Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary);
191e5dd7070Spatrick } else {
192*12c85518Srobert Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst,
193*12c85518Srobert IsTemporary);
194e5dd7070Spatrick }
195e5dd7070Spatrick if (!Desc)
196e5dd7070Spatrick return {};
197e5dd7070Spatrick
198e5dd7070Spatrick // Allocate a block for storage.
199e5dd7070Spatrick unsigned I = Globals.size();
200e5dd7070Spatrick
201e5dd7070Spatrick auto *G = new (Allocator, Desc->getAllocSize())
202e5dd7070Spatrick Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
203e5dd7070Spatrick G->block()->invokeCtor();
204e5dd7070Spatrick
205e5dd7070Spatrick Globals.push_back(G);
206e5dd7070Spatrick
207e5dd7070Spatrick return I;
208e5dd7070Spatrick }
209e5dd7070Spatrick
getFunction(const FunctionDecl * F)210e5dd7070Spatrick Function *Program::getFunction(const FunctionDecl *F) {
211*12c85518Srobert F = F->getCanonicalDecl();
212*12c85518Srobert assert(F);
213e5dd7070Spatrick auto It = Funcs.find(F);
214e5dd7070Spatrick return It == Funcs.end() ? nullptr : It->second.get();
215e5dd7070Spatrick }
216e5dd7070Spatrick
getOrCreateRecord(const RecordDecl * RD)217e5dd7070Spatrick Record *Program::getOrCreateRecord(const RecordDecl *RD) {
218e5dd7070Spatrick // Use the actual definition as a key.
219e5dd7070Spatrick RD = RD->getDefinition();
220e5dd7070Spatrick if (!RD)
221e5dd7070Spatrick return nullptr;
222e5dd7070Spatrick
223e5dd7070Spatrick // Deduplicate records.
224e5dd7070Spatrick auto It = Records.find(RD);
225e5dd7070Spatrick if (It != Records.end()) {
226e5dd7070Spatrick return It->second;
227e5dd7070Spatrick }
228e5dd7070Spatrick
229*12c85518Srobert // We insert nullptr now and replace that later, so recursive calls
230*12c85518Srobert // to this function with the same RecordDecl don't run into
231*12c85518Srobert // infinite recursion.
232*12c85518Srobert Records.insert({RD, nullptr});
233*12c85518Srobert
234e5dd7070Spatrick // Number of bytes required by fields and base classes.
235*12c85518Srobert unsigned BaseSize = 0;
236e5dd7070Spatrick // Number of bytes required by virtual base.
237e5dd7070Spatrick unsigned VirtSize = 0;
238e5dd7070Spatrick
239e5dd7070Spatrick // Helper to get a base descriptor.
240e5dd7070Spatrick auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * {
241e5dd7070Spatrick if (!BR)
242e5dd7070Spatrick return nullptr;
243*12c85518Srobert return allocateDescriptor(BD, BR, std::nullopt, /*isConst=*/false,
244e5dd7070Spatrick /*isTemporary=*/false,
245e5dd7070Spatrick /*isMutable=*/false);
246e5dd7070Spatrick };
247e5dd7070Spatrick
248e5dd7070Spatrick // Reserve space for base classes.
249e5dd7070Spatrick Record::BaseList Bases;
250e5dd7070Spatrick Record::VirtualBaseList VirtBases;
251e5dd7070Spatrick if (auto *CD = dyn_cast<CXXRecordDecl>(RD)) {
252e5dd7070Spatrick for (const CXXBaseSpecifier &Spec : CD->bases()) {
253e5dd7070Spatrick if (Spec.isVirtual())
254e5dd7070Spatrick continue;
255e5dd7070Spatrick
256e5dd7070Spatrick const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
257e5dd7070Spatrick Record *BR = getOrCreateRecord(BD);
258e5dd7070Spatrick if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
259*12c85518Srobert BaseSize += align(sizeof(InlineDescriptor));
260*12c85518Srobert Bases.push_back({BD, BaseSize, Desc, BR});
261*12c85518Srobert BaseSize += align(BR->getSize());
262e5dd7070Spatrick continue;
263e5dd7070Spatrick }
264e5dd7070Spatrick return nullptr;
265e5dd7070Spatrick }
266e5dd7070Spatrick
267e5dd7070Spatrick for (const CXXBaseSpecifier &Spec : CD->vbases()) {
268e5dd7070Spatrick const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
269e5dd7070Spatrick Record *BR = getOrCreateRecord(BD);
270e5dd7070Spatrick
271e5dd7070Spatrick if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
272e5dd7070Spatrick VirtSize += align(sizeof(InlineDescriptor));
273e5dd7070Spatrick VirtBases.push_back({BD, VirtSize, Desc, BR});
274e5dd7070Spatrick VirtSize += align(BR->getSize());
275e5dd7070Spatrick continue;
276e5dd7070Spatrick }
277e5dd7070Spatrick return nullptr;
278e5dd7070Spatrick }
279e5dd7070Spatrick }
280e5dd7070Spatrick
281e5dd7070Spatrick // Reserve space for fields.
282e5dd7070Spatrick Record::FieldList Fields;
283e5dd7070Spatrick for (const FieldDecl *FD : RD->fields()) {
284e5dd7070Spatrick // Reserve space for the field's descriptor and the offset.
285*12c85518Srobert BaseSize += align(sizeof(InlineDescriptor));
286e5dd7070Spatrick
287e5dd7070Spatrick // Classify the field and add its metadata.
288e5dd7070Spatrick QualType FT = FD->getType();
289e5dd7070Spatrick const bool IsConst = FT.isConstQualified();
290e5dd7070Spatrick const bool IsMutable = FD->isMutable();
291e5dd7070Spatrick Descriptor *Desc;
292*12c85518Srobert if (std::optional<PrimType> T = Ctx.classify(FT)) {
293*12c85518Srobert Desc = createDescriptor(FD, *T, std::nullopt, IsConst,
294*12c85518Srobert /*isTemporary=*/false, IsMutable);
295e5dd7070Spatrick } else {
296*12c85518Srobert Desc = createDescriptor(FD, FT.getTypePtr(), std::nullopt, IsConst,
297e5dd7070Spatrick /*isTemporary=*/false, IsMutable);
298e5dd7070Spatrick }
299e5dd7070Spatrick if (!Desc)
300e5dd7070Spatrick return nullptr;
301*12c85518Srobert Fields.push_back({FD, BaseSize, Desc});
302*12c85518Srobert BaseSize += align(Desc->getAllocSize());
303e5dd7070Spatrick }
304e5dd7070Spatrick
305e5dd7070Spatrick Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields),
306*12c85518Srobert std::move(VirtBases), VirtSize, BaseSize);
307*12c85518Srobert Records[RD] = R;
308e5dd7070Spatrick return R;
309e5dd7070Spatrick }
310e5dd7070Spatrick
createDescriptor(const DeclTy & D,const Type * Ty,Descriptor::MetadataSize MDSize,bool IsConst,bool IsTemporary,bool IsMutable,const Expr * Init)311e5dd7070Spatrick Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
312*12c85518Srobert Descriptor::MetadataSize MDSize,
313e5dd7070Spatrick bool IsConst, bool IsTemporary,
314*12c85518Srobert bool IsMutable, const Expr *Init) {
315e5dd7070Spatrick // Classes and structures.
316e5dd7070Spatrick if (auto *RT = Ty->getAs<RecordType>()) {
317e5dd7070Spatrick if (auto *Record = getOrCreateRecord(RT->getDecl()))
318*12c85518Srobert return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary,
319*12c85518Srobert IsMutable);
320e5dd7070Spatrick }
321e5dd7070Spatrick
322e5dd7070Spatrick // Arrays.
323e5dd7070Spatrick if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) {
324e5dd7070Spatrick QualType ElemTy = ArrayType->getElementType();
325e5dd7070Spatrick // Array of well-known bounds.
326e5dd7070Spatrick if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) {
327e5dd7070Spatrick size_t NumElems = CAT->getSize().getZExtValue();
328*12c85518Srobert if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
329e5dd7070Spatrick // Arrays of primitives.
330e5dd7070Spatrick unsigned ElemSize = primSize(*T);
331e5dd7070Spatrick if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
332e5dd7070Spatrick return {};
333e5dd7070Spatrick }
334*12c85518Srobert return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary,
335e5dd7070Spatrick IsMutable);
336e5dd7070Spatrick } else {
337e5dd7070Spatrick // Arrays of composites. In this case, the array is a list of pointers,
338e5dd7070Spatrick // followed by the actual elements.
339*12c85518Srobert Descriptor *ElemDesc = createDescriptor(
340*12c85518Srobert D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
341*12c85518Srobert if (!ElemDesc)
342e5dd7070Spatrick return nullptr;
343*12c85518Srobert InterpSize ElemSize =
344*12c85518Srobert ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
345e5dd7070Spatrick if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
346e5dd7070Spatrick return {};
347*12c85518Srobert return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst,
348*12c85518Srobert IsTemporary, IsMutable);
349e5dd7070Spatrick }
350e5dd7070Spatrick }
351e5dd7070Spatrick
352e5dd7070Spatrick // Array of unknown bounds - cannot be accessed and pointer arithmetic
353e5dd7070Spatrick // is forbidden on pointers to such objects.
354e5dd7070Spatrick if (isa<IncompleteArrayType>(ArrayType)) {
355*12c85518Srobert if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
356e5dd7070Spatrick return allocateDescriptor(D, *T, IsTemporary,
357e5dd7070Spatrick Descriptor::UnknownSize{});
358e5dd7070Spatrick } else {
359*12c85518Srobert Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
360*12c85518Srobert IsConst, IsTemporary);
361e5dd7070Spatrick if (!Desc)
362e5dd7070Spatrick return nullptr;
363e5dd7070Spatrick return allocateDescriptor(D, Desc, IsTemporary,
364e5dd7070Spatrick Descriptor::UnknownSize{});
365e5dd7070Spatrick }
366e5dd7070Spatrick }
367e5dd7070Spatrick }
368e5dd7070Spatrick
369e5dd7070Spatrick // Atomic types.
370e5dd7070Spatrick if (auto *AT = Ty->getAs<AtomicType>()) {
371e5dd7070Spatrick const Type *InnerTy = AT->getValueType().getTypePtr();
372*12c85518Srobert return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary,
373*12c85518Srobert IsMutable);
374e5dd7070Spatrick }
375e5dd7070Spatrick
376e5dd7070Spatrick // Complex types - represented as arrays of elements.
377e5dd7070Spatrick if (auto *CT = Ty->getAs<ComplexType>()) {
378e5dd7070Spatrick PrimType ElemTy = *Ctx.classify(CT->getElementType());
379*12c85518Srobert return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary,
380*12c85518Srobert IsMutable);
381e5dd7070Spatrick }
382e5dd7070Spatrick
383e5dd7070Spatrick return nullptr;
384e5dd7070Spatrick }
385