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