xref: /llvm-project/clang/lib/AST/ByteCode/Descriptor.cpp (revision 3ea55d3cb0655c7863596321e132b59158325433)
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 "MemberPointer.h"
15 #include "Pointer.h"
16 #include "PrimType.h"
17 #include "Record.h"
18 #include "Source.h"
19 
20 using namespace clang;
21 using namespace clang::interp;
22 
23 template <typename T>
24 static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
25                    const Descriptor *) {
26   new (Ptr) T();
27 }
28 
29 template <typename T>
30 static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) {
31   reinterpret_cast<T *>(Ptr)->~T();
32 }
33 
34 template <typename T>
35 static void moveTy(Block *, std::byte *Src, std::byte *Dst,
36                    const Descriptor *) {
37   auto *SrcPtr = reinterpret_cast<T *>(Src);
38   auto *DstPtr = reinterpret_cast<T *>(Dst);
39   new (DstPtr) T(std::move(*SrcPtr));
40 }
41 
42 template <typename T>
43 static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool,
44                         const Descriptor *D) {
45   new (Ptr) InitMapPtr(std::nullopt);
46 
47   Ptr += sizeof(InitMapPtr);
48   for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
49     new (&reinterpret_cast<T *>(Ptr)[I]) T();
50   }
51 }
52 
53 template <typename T>
54 static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
55   InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr);
56 
57   if (IMP)
58     IMP = std::nullopt;
59   Ptr += sizeof(InitMapPtr);
60   for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
61     reinterpret_cast<T *>(Ptr)[I].~T();
62   }
63 }
64 
65 template <typename T>
66 static void moveArrayTy(Block *, std::byte *Src, std::byte *Dst,
67                         const Descriptor *D) {
68   InitMapPtr &SrcIMP = *reinterpret_cast<InitMapPtr *>(Src);
69   if (SrcIMP) {
70     // We only ever invoke the moveFunc when moving block contents to a
71     // DeadBlock. DeadBlocks don't need InitMaps, so we destroy them here.
72     SrcIMP = std::nullopt;
73   }
74   Src += sizeof(InitMapPtr);
75   Dst += sizeof(InitMapPtr);
76   for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
77     auto *SrcPtr = &reinterpret_cast<T *>(Src)[I];
78     auto *DstPtr = &reinterpret_cast<T *>(Dst)[I];
79     new (DstPtr) T(std::move(*SrcPtr));
80   }
81 }
82 
83 static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
84                           bool IsMutable, bool IsActive, bool InUnion,
85                           const Descriptor *D) {
86   const unsigned NumElems = D->getNumElems();
87   const unsigned ElemSize =
88       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
89 
90   unsigned ElemOffset = 0;
91   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
92     auto *ElemPtr = Ptr + ElemOffset;
93     auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
94     auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1);
95     auto *SD = D->ElemDesc;
96 
97     Desc->Offset = ElemOffset + sizeof(InlineDescriptor);
98     Desc->Desc = SD;
99     Desc->IsInitialized = true;
100     Desc->IsBase = false;
101     Desc->IsActive = IsActive;
102     Desc->IsConst = IsConst || D->IsConst;
103     Desc->IsFieldMutable = IsMutable || D->IsMutable;
104     Desc->InUnion = InUnion;
105 
106     if (auto Fn = D->ElemDesc->CtorFn)
107       Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
108          Desc->InUnion || SD->isUnion(), D->ElemDesc);
109   }
110 }
111 
112 static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) {
113   const unsigned NumElems = D->getNumElems();
114   const unsigned ElemSize =
115       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
116 
117   unsigned ElemOffset = 0;
118   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
119     auto *ElemPtr = Ptr + ElemOffset;
120     auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
121     auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1);
122     if (auto Fn = D->ElemDesc->DtorFn)
123       Fn(B, ElemLoc, D->ElemDesc);
124   }
125 }
126 
127 static void moveArrayDesc(Block *B, std::byte *Src, std::byte *Dst,
128                           const Descriptor *D) {
129   const unsigned NumElems = D->getNumElems();
130   const unsigned ElemSize =
131       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
132 
133   unsigned ElemOffset = 0;
134   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
135     auto *SrcPtr = Src + ElemOffset;
136     auto *DstPtr = Dst + ElemOffset;
137 
138     auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr);
139     auto *SrcElemLoc = reinterpret_cast<std::byte *>(SrcDesc + 1);
140     auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr);
141     auto *DstElemLoc = reinterpret_cast<std::byte *>(DstDesc + 1);
142 
143     *DstDesc = *SrcDesc;
144     if (auto Fn = D->ElemDesc->MoveFn)
145       Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc);
146   }
147 }
148 
149 static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
150                       bool IsActive, bool IsUnionField, bool InUnion,
151                       const Descriptor *D, unsigned FieldOffset) {
152   auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
153   Desc->Offset = FieldOffset;
154   Desc->Desc = D;
155   Desc->IsInitialized = D->IsArray;
156   Desc->IsBase = false;
157   Desc->IsActive = IsActive && !IsUnionField;
158   Desc->InUnion = InUnion;
159   Desc->IsConst = IsConst || D->IsConst;
160   Desc->IsFieldMutable = IsMutable || D->IsMutable;
161 
162   if (auto Fn = D->CtorFn)
163     Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
164        Desc->IsActive, InUnion || D->isUnion(), D);
165 }
166 
167 static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
168                      bool IsActive, bool InUnion, const Descriptor *D,
169                      unsigned FieldOffset, bool IsVirtualBase) {
170   assert(D);
171   assert(D->ElemRecord);
172   assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes.
173 
174   auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
175   Desc->Offset = FieldOffset;
176   Desc->Desc = D;
177   Desc->IsInitialized = D->IsArray;
178   Desc->IsBase = true;
179   Desc->IsVirtualBase = IsVirtualBase;
180   Desc->IsActive = IsActive && !InUnion;
181   Desc->IsConst = IsConst || D->IsConst;
182   Desc->IsFieldMutable = IsMutable || D->IsMutable;
183   Desc->InUnion = InUnion;
184 
185   for (const auto &V : D->ElemRecord->bases())
186     initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
187              V.Desc, V.Offset, false);
188   for (const auto &F : D->ElemRecord->fields())
189     initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
190               InUnion, F.Desc, F.Offset);
191 }
192 
193 static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
194                        bool IsActive, bool InUnion, const Descriptor *D) {
195   for (const auto &V : D->ElemRecord->bases())
196     initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
197              false);
198   for (const auto &F : D->ElemRecord->fields()) {
199     bool IsUnionField = D->isUnion();
200     initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField,
201               InUnion || IsUnionField, F.Desc, F.Offset);
202   }
203   for (const auto &V : D->ElemRecord->virtual_bases())
204     initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
205              true);
206 }
207 
208 static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
209                          unsigned FieldOffset) {
210   if (auto Fn = D->DtorFn)
211     Fn(B, Ptr + FieldOffset, D);
212 }
213 
214 static void destroyBase(Block *B, std::byte *Ptr, const Descriptor *D,
215                         unsigned FieldOffset) {
216   assert(D);
217   assert(D->ElemRecord);
218 
219   for (const auto &V : D->ElemRecord->bases())
220     destroyBase(B, Ptr + FieldOffset, V.Desc, V.Offset);
221   for (const auto &F : D->ElemRecord->fields())
222     destroyField(B, Ptr + FieldOffset, F.Desc, F.Offset);
223 }
224 
225 static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) {
226   for (const auto &F : D->ElemRecord->bases())
227     destroyBase(B, Ptr, F.Desc, F.Offset);
228   for (const auto &F : D->ElemRecord->fields())
229     destroyField(B, Ptr, F.Desc, F.Offset);
230   for (const auto &F : D->ElemRecord->virtual_bases())
231     destroyBase(B, Ptr, F.Desc, F.Offset);
232 }
233 
234 static void moveRecord(Block *B, std::byte *Src, std::byte *Dst,
235                        const Descriptor *D) {
236   assert(D);
237   assert(D->ElemRecord);
238 
239   // FIXME: There might be cases where we need to move over the (v)bases as
240   // well.
241   for (const auto &F : D->ElemRecord->fields()) {
242     auto FieldOffset = F.Offset;
243     const auto *SrcDesc =
244         reinterpret_cast<const InlineDescriptor *>(Src + FieldOffset) - 1;
245     auto *DestDesc =
246         reinterpret_cast<InlineDescriptor *>(Dst + FieldOffset) - 1;
247     std::memcpy(DestDesc, SrcDesc, sizeof(InlineDescriptor));
248 
249     if (auto Fn = F.Desc->MoveFn)
250       Fn(B, Src + FieldOffset, Dst + FieldOffset, F.Desc);
251   }
252 }
253 
254 static BlockCtorFn getCtorPrim(PrimType Type) {
255   // Floating types are special. They are primitives, but need their
256   // constructor called.
257   if (Type == PT_Float)
258     return ctorTy<PrimConv<PT_Float>::T>;
259   if (Type == PT_IntAP)
260     return ctorTy<PrimConv<PT_IntAP>::T>;
261   if (Type == PT_IntAPS)
262     return ctorTy<PrimConv<PT_IntAPS>::T>;
263   if (Type == PT_MemberPtr)
264     return ctorTy<PrimConv<PT_MemberPtr>::T>;
265 
266   COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
267 }
268 
269 static BlockDtorFn getDtorPrim(PrimType Type) {
270   // Floating types are special. They are primitives, but need their
271   // destructor called, since they might allocate memory.
272   if (Type == PT_Float)
273     return dtorTy<PrimConv<PT_Float>::T>;
274   if (Type == PT_IntAP)
275     return dtorTy<PrimConv<PT_IntAP>::T>;
276   if (Type == PT_IntAPS)
277     return dtorTy<PrimConv<PT_IntAPS>::T>;
278   if (Type == PT_MemberPtr)
279     return dtorTy<PrimConv<PT_MemberPtr>::T>;
280 
281   COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
282 }
283 
284 static BlockMoveFn getMovePrim(PrimType Type) {
285   if (Type == PT_Float)
286     return moveTy<PrimConv<PT_Float>::T>;
287   if (Type == PT_IntAP)
288     return moveTy<PrimConv<PT_IntAP>::T>;
289   if (Type == PT_IntAPS)
290     return moveTy<PrimConv<PT_IntAPS>::T>;
291   if (Type == PT_MemberPtr)
292     return moveTy<PrimConv<PT_MemberPtr>::T>;
293   COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr);
294 }
295 
296 static BlockCtorFn getCtorArrayPrim(PrimType Type) {
297   TYPE_SWITCH(Type, return ctorArrayTy<T>);
298   llvm_unreachable("unknown Expr");
299 }
300 
301 static BlockDtorFn getDtorArrayPrim(PrimType Type) {
302   TYPE_SWITCH(Type, return dtorArrayTy<T>);
303   llvm_unreachable("unknown Expr");
304 }
305 
306 static BlockMoveFn getMoveArrayPrim(PrimType Type) {
307   TYPE_SWITCH(Type, return moveArrayTy<T>);
308   llvm_unreachable("unknown Expr");
309 }
310 
311 /// Primitives.
312 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
313                        bool IsConst, bool IsTemporary, bool IsMutable)
314     : Source(D), ElemSize(primSize(Type)), Size(ElemSize),
315       MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type),
316       IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
317       CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
318       MoveFn(getMovePrim(Type)) {
319   assert(AllocSize >= Size);
320   assert(Source && "Missing source");
321 }
322 
323 /// Primitive arrays.
324 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
325                        size_t NumElems, bool IsConst, bool IsTemporary,
326                        bool IsMutable)
327     : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
328       MDSize(MD.value_or(0)),
329       AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type),
330       IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
331       IsArray(true), CtorFn(getCtorArrayPrim(Type)),
332       DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
333   assert(Source && "Missing source");
334   assert(NumElems <= (MaxArrayElemBytes / ElemSize));
335 }
336 
337 /// Primitive unknown-size arrays.
338 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
339                        bool IsTemporary, UnknownSize)
340     : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
341       MDSize(MD.value_or(0)),
342       AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true),
343       IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
344       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
345       MoveFn(getMoveArrayPrim(Type)) {
346   assert(Source && "Missing source");
347 }
348 
349 /// Arrays of composite elements.
350 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
351                        unsigned NumElems, bool IsConst, bool IsTemporary,
352                        bool IsMutable)
353     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
354       Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
355       AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
356       ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
357       IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
358       DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
359   assert(Source && "Missing source");
360 }
361 
362 /// Unknown-size arrays of composite elements.
363 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
364                        bool IsTemporary, UnknownSize)
365     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
366       Size(UnknownSizeMark), MDSize(MD.value_or(0)),
367       AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true),
368       IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
369       CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
370   assert(Source && "Missing source");
371 }
372 
373 /// Composite records.
374 Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD,
375                        bool IsConst, bool IsTemporary, bool IsMutable)
376     : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
377       Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
378       ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
379       IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
380       MoveFn(moveRecord) {
381   assert(Source && "Missing source");
382 }
383 
384 /// Dummy.
385 Descriptor::Descriptor(const DeclTy &D)
386     : Source(D), ElemSize(1), Size(1), MDSize(0), AllocSize(MDSize),
387       ElemRecord(nullptr), IsConst(true), IsMutable(false), IsTemporary(false),
388       IsDummy(true) {
389   assert(Source && "Missing source");
390 }
391 
392 QualType Descriptor::getType() const {
393   if (const auto *D = asValueDecl())
394     return D->getType();
395   if (const auto *T = dyn_cast_if_present<TypeDecl>(asDecl()))
396     return QualType(T->getTypeForDecl(), 0);
397 
398   // The Source sometimes has a different type than the once
399   // we really save. Try to consult the Record first.
400   if (isRecord())
401     return QualType(ElemRecord->getDecl()->getTypeForDecl(), 0);
402   if (const auto *E = asExpr())
403     return E->getType();
404   llvm_unreachable("Invalid descriptor type");
405 }
406 
407 QualType Descriptor::getElemQualType() const {
408   assert(isArray());
409   QualType T = getType();
410   if (const auto *AT = T->getAsArrayTypeUnsafe())
411     return AT->getElementType();
412   if (const auto *CT = T->getAs<ComplexType>())
413     return CT->getElementType();
414   if (const auto *CT = T->getAs<VectorType>())
415     return CT->getElementType();
416   llvm_unreachable("Array that's not an array/complex/vector type?");
417 }
418 
419 SourceLocation Descriptor::getLocation() const {
420   if (auto *D = Source.dyn_cast<const Decl *>())
421     return D->getLocation();
422   if (auto *E = Source.dyn_cast<const Expr *>())
423     return E->getExprLoc();
424   llvm_unreachable("Invalid descriptor type");
425 }
426 
427 SourceInfo Descriptor::getLoc() const {
428   if (const auto *D = Source.dyn_cast<const Decl *>())
429     return SourceInfo(D);
430   if (const auto *E = Source.dyn_cast<const Expr *>())
431     return SourceInfo(E);
432   llvm_unreachable("Invalid descriptor type");
433 }
434 
435 bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }
436 
437 InitMap::InitMap(unsigned N)
438     : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
439   std::fill_n(data(), numFields(N), 0);
440 }
441 
442 bool InitMap::initializeElement(unsigned I) {
443   unsigned Bucket = I / PER_FIELD;
444   T Mask = T(1) << (I % PER_FIELD);
445   if (!(data()[Bucket] & Mask)) {
446     data()[Bucket] |= Mask;
447     UninitFields -= 1;
448   }
449   return UninitFields == 0;
450 }
451 
452 bool InitMap::isElementInitialized(unsigned I) const {
453   unsigned Bucket = I / PER_FIELD;
454   return data()[Bucket] & (T(1) << (I % PER_FIELD));
455 }
456