1 //===--- Descriptor.cpp - Types 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 #include "Descriptor.h"
10 #include "Boolean.h"
11 #include "Floating.h"
12 #include "FunctionPointer.h"
13 #include "IntegralAP.h"
14 #include "Pointer.h"
15 #include "PrimType.h"
16 #include "Record.h"
17 
18 using namespace clang;
19 using namespace clang::interp;
20 
21 template <typename T>
ctorTy(Block *,std::byte * Ptr,bool,bool,bool,const Descriptor *)22 static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool,
23                    const Descriptor *) {
24   new (Ptr) T();
25 }
26 
27 template <typename T>
dtorTy(Block *,std::byte * Ptr,const Descriptor *)28 static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) {
29   reinterpret_cast<T *>(Ptr)->~T();
30 }
31 
32 template <typename T>
moveTy(Block *,const std::byte * Src,std::byte * Dst,const Descriptor *)33 static void moveTy(Block *, const std::byte *Src, std::byte *Dst,
34                    const Descriptor *) {
35   const auto *SrcPtr = reinterpret_cast<const T *>(Src);
36   auto *DstPtr = reinterpret_cast<T *>(Dst);
37   new (DstPtr) T(std::move(*SrcPtr));
38 }
39 
40 template <typename T>
ctorArrayTy(Block *,std::byte * Ptr,bool,bool,bool,const Descriptor * D)41 static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool,
42                         const Descriptor *D) {
43   new (Ptr) InitMapPtr(std::nullopt);
44 
45   Ptr += sizeof(InitMapPtr);
46   for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
47     new (&reinterpret_cast<T *>(Ptr)[I]) T();
48   }
49 }
50 
51 template <typename T>
dtorArrayTy(Block *,std::byte * Ptr,const Descriptor * D)52 static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
53   InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr);
54 
55   if (IMP)
56     IMP = std::nullopt;
57   Ptr += sizeof(InitMapPtr);
58   for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
59     reinterpret_cast<T *>(Ptr)[I].~T();
60   }
61 }
62 
63 template <typename T>
moveArrayTy(Block *,const std::byte * Src,std::byte * Dst,const Descriptor * D)64 static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
65                         const Descriptor *D) {
66   for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
67     const auto *SrcPtr = &reinterpret_cast<const T *>(Src)[I];
68     auto *DstPtr = &reinterpret_cast<T *>(Dst)[I];
69     new (DstPtr) T(std::move(*SrcPtr));
70   }
71 }
72 
ctorArrayDesc(Block * B,std::byte * Ptr,bool IsConst,bool IsMutable,bool IsActive,const Descriptor * D)73 static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
74                           bool IsMutable, bool IsActive, const Descriptor *D) {
75   const unsigned NumElems = D->getNumElems();
76   const unsigned ElemSize =
77       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
78 
79   unsigned ElemOffset = 0;
80   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
81     auto *ElemPtr = Ptr + ElemOffset;
82     auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
83     auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1);
84     auto *SD = D->ElemDesc;
85 
86     Desc->Offset = ElemOffset + sizeof(InlineDescriptor);
87     Desc->Desc = SD;
88     Desc->IsInitialized = true;
89     Desc->IsBase = false;
90     Desc->IsActive = IsActive;
91     Desc->IsConst = IsConst || D->IsConst;
92     Desc->IsFieldMutable = IsMutable || D->IsMutable;
93     if (auto Fn = D->ElemDesc->CtorFn)
94       Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
95          D->ElemDesc);
96   }
97 }
98 
dtorArrayDesc(Block * B,std::byte * Ptr,const Descriptor * D)99 static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) {
100   const unsigned NumElems = D->getNumElems();
101   const unsigned ElemSize =
102       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
103 
104   unsigned ElemOffset = 0;
105   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
106     auto *ElemPtr = Ptr + ElemOffset;
107     auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
108     auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1);
109     if (auto Fn = D->ElemDesc->DtorFn)
110       Fn(B, ElemLoc, D->ElemDesc);
111   }
112 }
113 
moveArrayDesc(Block * B,const std::byte * Src,std::byte * Dst,const Descriptor * D)114 static void moveArrayDesc(Block *B, const std::byte *Src, std::byte *Dst,
115                           const Descriptor *D) {
116   const unsigned NumElems = D->getNumElems();
117   const unsigned ElemSize =
118       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
119 
120   unsigned ElemOffset = 0;
121   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
122     const auto *SrcPtr = Src + ElemOffset;
123     auto *DstPtr = Dst + ElemOffset;
124 
125     const auto *SrcDesc = reinterpret_cast<const InlineDescriptor *>(SrcPtr);
126     const auto *SrcElemLoc = reinterpret_cast<const std::byte *>(SrcDesc + 1);
127     auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr);
128     auto *DstElemLoc = reinterpret_cast<std::byte *>(DstDesc + 1);
129 
130     *DstDesc = *SrcDesc;
131     if (auto Fn = D->ElemDesc->MoveFn)
132       Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc);
133   }
134 }
135 
ctorRecord(Block * B,std::byte * Ptr,bool IsConst,bool IsMutable,bool IsActive,const Descriptor * D)136 static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
137                        bool IsActive, const Descriptor *D) {
138   const bool IsUnion = D->ElemRecord->isUnion();
139   auto CtorSub = [=](unsigned SubOff, Descriptor *F, bool IsBase) {
140     auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1;
141     Desc->Offset = SubOff;
142     Desc->Desc = F;
143     Desc->IsInitialized = F->IsArray && !IsBase;
144     Desc->IsBase = IsBase;
145     Desc->IsActive = IsActive && !IsUnion;
146     Desc->IsConst = IsConst || F->IsConst;
147     Desc->IsFieldMutable = IsMutable || F->IsMutable;
148     if (auto Fn = F->CtorFn)
149       Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsFieldMutable, Desc->IsActive,
150          F);
151   };
152   for (const auto &B : D->ElemRecord->bases())
153     CtorSub(B.Offset, B.Desc, /*isBase=*/true);
154   for (const auto &F : D->ElemRecord->fields())
155     CtorSub(F.Offset, F.Desc, /*isBase=*/false);
156   for (const auto &V : D->ElemRecord->virtual_bases())
157     CtorSub(V.Offset, V.Desc, /*isBase=*/true);
158 }
159 
dtorRecord(Block * B,std::byte * Ptr,const Descriptor * D)160 static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) {
161   auto DtorSub = [=](unsigned SubOff, Descriptor *F) {
162     if (auto Fn = F->DtorFn)
163       Fn(B, Ptr + SubOff, F);
164   };
165   for (const auto &F : D->ElemRecord->bases())
166     DtorSub(F.Offset, F.Desc);
167   for (const auto &F : D->ElemRecord->fields())
168     DtorSub(F.Offset, F.Desc);
169   for (const auto &F : D->ElemRecord->virtual_bases())
170     DtorSub(F.Offset, F.Desc);
171 }
172 
moveRecord(Block * B,const std::byte * Src,std::byte * Dst,const Descriptor * D)173 static void moveRecord(Block *B, const std::byte *Src, std::byte *Dst,
174                        const Descriptor *D) {
175   for (const auto &F : D->ElemRecord->fields()) {
176     auto FieldOff = F.Offset;
177     auto *FieldDesc = F.Desc;
178 
179     if (auto Fn = FieldDesc->MoveFn)
180       Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc);
181   }
182 }
183 
getCtorPrim(PrimType Type)184 static BlockCtorFn getCtorPrim(PrimType Type) {
185   // Floating types are special. They are primitives, but need their
186   // constructor called.
187   if (Type == PT_Float)
188     return ctorTy<PrimConv<PT_Float>::T>;
189   if (Type == PT_IntAP)
190     return ctorTy<PrimConv<PT_IntAP>::T>;
191   if (Type == PT_IntAPS)
192     return ctorTy<PrimConv<PT_IntAPS>::T>;
193 
194   COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
195 }
196 
getDtorPrim(PrimType Type)197 static BlockDtorFn getDtorPrim(PrimType Type) {
198   // Floating types are special. They are primitives, but need their
199   // destructor called, since they might allocate memory.
200   if (Type == PT_Float)
201     return dtorTy<PrimConv<PT_Float>::T>;
202   if (Type == PT_IntAP)
203     return dtorTy<PrimConv<PT_IntAP>::T>;
204   if (Type == PT_IntAPS)
205     return dtorTy<PrimConv<PT_IntAPS>::T>;
206 
207   COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
208 }
209 
getMovePrim(PrimType Type)210 static BlockMoveFn getMovePrim(PrimType Type) {
211   COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr);
212 }
213 
getCtorArrayPrim(PrimType Type)214 static BlockCtorFn getCtorArrayPrim(PrimType Type) {
215   TYPE_SWITCH(Type, return ctorArrayTy<T>);
216   llvm_unreachable("unknown Expr");
217 }
218 
getDtorArrayPrim(PrimType Type)219 static BlockDtorFn getDtorArrayPrim(PrimType Type) {
220   TYPE_SWITCH(Type, return dtorArrayTy<T>);
221   llvm_unreachable("unknown Expr");
222 }
223 
getMoveArrayPrim(PrimType Type)224 static BlockMoveFn getMoveArrayPrim(PrimType Type) {
225   TYPE_SWITCH(Type, return moveArrayTy<T>);
226   llvm_unreachable("unknown Expr");
227 }
228 
229 /// Primitives.
Descriptor(const DeclTy & D,PrimType Type,MetadataSize MD,bool IsConst,bool IsTemporary,bool IsMutable)230 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
231                        bool IsConst, bool IsTemporary, bool IsMutable)
232     : Source(D), ElemSize(primSize(Type)), Size(ElemSize),
233       MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst),
234       IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)),
235       DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
236   assert(AllocSize >= Size);
237   assert(Source && "Missing source");
238 }
239 
240 /// Primitive arrays.
Descriptor(const DeclTy & D,PrimType Type,MetadataSize MD,size_t NumElems,bool IsConst,bool IsTemporary,bool IsMutable)241 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
242                        size_t NumElems, bool IsConst, bool IsTemporary,
243                        bool IsMutable)
244     : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
245       MDSize(MD.value_or(0)),
246       AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst),
247       IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
248       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
249       MoveFn(getMoveArrayPrim(Type)) {
250   assert(Source && "Missing source");
251 }
252 
253 /// Primitive unknown-size arrays.
Descriptor(const DeclTy & D,PrimType Type,bool IsTemporary,UnknownSize)254 Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
255                        UnknownSize)
256     : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
257       AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true),
258       IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
259       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
260       MoveFn(getMoveArrayPrim(Type)) {
261   assert(Source && "Missing source");
262 }
263 
264 /// Arrays of composite elements.
Descriptor(const DeclTy & D,const Descriptor * Elem,MetadataSize MD,unsigned NumElems,bool IsConst,bool IsTemporary,bool IsMutable)265 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
266                        unsigned NumElems, bool IsConst, bool IsTemporary,
267                        bool IsMutable)
268     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
269       Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
270       AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
271       ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
272       IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
273       DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
274   assert(Source && "Missing source");
275 }
276 
277 /// Unknown-size arrays of composite elements.
Descriptor(const DeclTy & D,const Descriptor * Elem,bool IsTemporary,UnknownSize)278 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem,
279                        bool IsTemporary, UnknownSize)
280     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
281       Size(UnknownSizeMark), MDSize(0),
282       AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
283       IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
284       CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
285   assert(Source && "Missing source");
286 }
287 
288 /// Composite records.
Descriptor(const DeclTy & D,const Record * R,MetadataSize MD,bool IsConst,bool IsTemporary,bool IsMutable)289 Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD,
290                        bool IsConst, bool IsTemporary, bool IsMutable)
291     : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
292       Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
293       ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
294       IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
295       MoveFn(moveRecord) {
296   assert(Source && "Missing source");
297 }
298 
Descriptor(const DeclTy & D,MetadataSize MD)299 Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
300     : Source(D), ElemSize(1), Size(ElemSize), MDSize(MD.value_or(0)),
301       AllocSize(Size + MDSize), ElemRecord(nullptr), IsConst(true),
302       IsMutable(false), IsTemporary(false), IsDummy(true) {
303   assert(Source && "Missing source");
304 }
305 
getType() const306 QualType Descriptor::getType() const {
307   if (auto *E = asExpr())
308     return E->getType();
309   if (auto *D = asValueDecl())
310     return D->getType();
311   if (auto *T = dyn_cast<TypeDecl>(asDecl()))
312     return QualType(T->getTypeForDecl(), 0);
313   llvm_unreachable("Invalid descriptor type");
314 }
315 
getElemQualType() const316 QualType Descriptor::getElemQualType() const {
317   assert(isArray());
318   const auto *AT = cast<ArrayType>(getType());
319   return AT->getElementType();
320 }
321 
getLocation() const322 SourceLocation Descriptor::getLocation() const {
323   if (auto *D = Source.dyn_cast<const Decl *>())
324     return D->getLocation();
325   if (auto *E = Source.dyn_cast<const Expr *>())
326     return E->getExprLoc();
327   llvm_unreachable("Invalid descriptor type");
328 }
329 
InitMap(unsigned N)330 InitMap::InitMap(unsigned N)
331     : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
332   std::fill_n(data(), numFields(N), 0);
333 }
334 
initializeElement(unsigned I)335 bool InitMap::initializeElement(unsigned I) {
336   unsigned Bucket = I / PER_FIELD;
337   T Mask = T(1) << (I % PER_FIELD);
338   if (!(data()[Bucket] & Mask)) {
339     data()[Bucket] |= Mask;
340     UninitFields -= 1;
341   }
342   return UninitFields == 0;
343 }
344 
isElementInitialized(unsigned I) const345 bool InitMap::isElementInitialized(unsigned I) const {
346   unsigned Bucket = I / PER_FIELD;
347   return data()[Bucket] & (T(1) << (I % PER_FIELD));
348 }
349