xref: /llvm-project/clang/lib/AST/ByteCode/Descriptor.cpp (revision 51c7338cc671c90ba9345b53c7ca01dc461341ed)
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 "FixedPoint.h"
12 #include "Floating.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     Desc->IsArrayElement = true;
106 
107     if (auto Fn = D->ElemDesc->CtorFn)
108       Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
109          Desc->InUnion || SD->isUnion(), D->ElemDesc);
110   }
111 }
112 
113 static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) {
114   const unsigned NumElems = D->getNumElems();
115   const unsigned ElemSize =
116       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
117 
118   unsigned ElemOffset = 0;
119   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
120     auto *ElemPtr = Ptr + ElemOffset;
121     auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
122     auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1);
123     if (auto Fn = D->ElemDesc->DtorFn)
124       Fn(B, ElemLoc, D->ElemDesc);
125   }
126 }
127 
128 static void moveArrayDesc(Block *B, std::byte *Src, std::byte *Dst,
129                           const Descriptor *D) {
130   const unsigned NumElems = D->getNumElems();
131   const unsigned ElemSize =
132       D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
133 
134   unsigned ElemOffset = 0;
135   for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
136     auto *SrcPtr = Src + ElemOffset;
137     auto *DstPtr = Dst + ElemOffset;
138 
139     auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr);
140     auto *SrcElemLoc = reinterpret_cast<std::byte *>(SrcDesc + 1);
141     auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr);
142     auto *DstElemLoc = reinterpret_cast<std::byte *>(DstDesc + 1);
143 
144     *DstDesc = *SrcDesc;
145     if (auto Fn = D->ElemDesc->MoveFn)
146       Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc);
147   }
148 }
149 
150 static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
151                       bool IsActive, bool IsUnionField, bool InUnion,
152                       const Descriptor *D, unsigned FieldOffset) {
153   auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
154   Desc->Offset = FieldOffset;
155   Desc->Desc = D;
156   Desc->IsInitialized = D->IsArray;
157   Desc->IsBase = false;
158   Desc->IsActive = IsActive && !IsUnionField;
159   Desc->InUnion = InUnion;
160   Desc->IsConst = IsConst || D->IsConst;
161   Desc->IsFieldMutable = IsMutable || D->IsMutable;
162 
163   if (auto Fn = D->CtorFn)
164     Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
165        Desc->IsActive, InUnion || D->isUnion(), D);
166 }
167 
168 static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
169                      bool IsActive, bool InUnion, const Descriptor *D,
170                      unsigned FieldOffset, bool IsVirtualBase) {
171   assert(D);
172   assert(D->ElemRecord);
173   assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes.
174 
175   auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
176   Desc->Offset = FieldOffset;
177   Desc->Desc = D;
178   Desc->IsInitialized = D->IsArray;
179   Desc->IsBase = true;
180   Desc->IsVirtualBase = IsVirtualBase;
181   Desc->IsActive = IsActive && !InUnion;
182   Desc->IsConst = IsConst || D->IsConst;
183   Desc->IsFieldMutable = IsMutable || D->IsMutable;
184   Desc->InUnion = InUnion;
185 
186   for (const auto &V : D->ElemRecord->bases())
187     initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
188              V.Desc, V.Offset, false);
189   for (const auto &F : D->ElemRecord->fields())
190     initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion,
191               InUnion, F.Desc, F.Offset);
192 }
193 
194 static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
195                        bool IsActive, bool InUnion, const Descriptor *D) {
196   for (const auto &V : D->ElemRecord->bases())
197     initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
198              false);
199   for (const auto &F : D->ElemRecord->fields()) {
200     bool IsUnionField = D->isUnion();
201     initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField,
202               InUnion || IsUnionField, F.Desc, F.Offset);
203   }
204   for (const auto &V : D->ElemRecord->virtual_bases())
205     initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset,
206              true);
207 }
208 
209 static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
210                          unsigned FieldOffset) {
211   if (auto Fn = D->DtorFn)
212     Fn(B, Ptr + FieldOffset, D);
213 }
214 
215 static void destroyBase(Block *B, std::byte *Ptr, const Descriptor *D,
216                         unsigned FieldOffset) {
217   assert(D);
218   assert(D->ElemRecord);
219 
220   for (const auto &V : D->ElemRecord->bases())
221     destroyBase(B, Ptr + FieldOffset, V.Desc, V.Offset);
222   for (const auto &F : D->ElemRecord->fields())
223     destroyField(B, Ptr + FieldOffset, F.Desc, F.Offset);
224 }
225 
226 static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) {
227   for (const auto &F : D->ElemRecord->bases())
228     destroyBase(B, Ptr, F.Desc, F.Offset);
229   for (const auto &F : D->ElemRecord->fields())
230     destroyField(B, Ptr, F.Desc, F.Offset);
231   for (const auto &F : D->ElemRecord->virtual_bases())
232     destroyBase(B, Ptr, F.Desc, F.Offset);
233 }
234 
235 static void moveRecord(Block *B, std::byte *Src, std::byte *Dst,
236                        const Descriptor *D) {
237   assert(D);
238   assert(D->ElemRecord);
239 
240   // FIXME: There might be cases where we need to move over the (v)bases as
241   // well.
242   for (const auto &F : D->ElemRecord->fields()) {
243     auto FieldOffset = F.Offset;
244     const auto *SrcDesc =
245         reinterpret_cast<const InlineDescriptor *>(Src + FieldOffset) - 1;
246     auto *DestDesc =
247         reinterpret_cast<InlineDescriptor *>(Dst + FieldOffset) - 1;
248     std::memcpy(DestDesc, SrcDesc, sizeof(InlineDescriptor));
249 
250     if (auto Fn = F.Desc->MoveFn)
251       Fn(B, Src + FieldOffset, Dst + FieldOffset, F.Desc);
252   }
253 }
254 
255 static BlockCtorFn getCtorPrim(PrimType Type) {
256   // Floating types are special. They are primitives, but need their
257   // constructor called.
258   if (Type == PT_Float)
259     return ctorTy<PrimConv<PT_Float>::T>;
260   if (Type == PT_IntAP)
261     return ctorTy<PrimConv<PT_IntAP>::T>;
262   if (Type == PT_IntAPS)
263     return ctorTy<PrimConv<PT_IntAPS>::T>;
264   if (Type == PT_MemberPtr)
265     return ctorTy<PrimConv<PT_MemberPtr>::T>;
266 
267   COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
268 }
269 
270 static BlockDtorFn getDtorPrim(PrimType Type) {
271   // Floating types are special. They are primitives, but need their
272   // destructor called, since they might allocate memory.
273   if (Type == PT_Float)
274     return dtorTy<PrimConv<PT_Float>::T>;
275   if (Type == PT_IntAP)
276     return dtorTy<PrimConv<PT_IntAP>::T>;
277   if (Type == PT_IntAPS)
278     return dtorTy<PrimConv<PT_IntAPS>::T>;
279   if (Type == PT_MemberPtr)
280     return dtorTy<PrimConv<PT_MemberPtr>::T>;
281 
282   COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
283 }
284 
285 static BlockMoveFn getMovePrim(PrimType Type) {
286   if (Type == PT_Float)
287     return moveTy<PrimConv<PT_Float>::T>;
288   if (Type == PT_IntAP)
289     return moveTy<PrimConv<PT_IntAP>::T>;
290   if (Type == PT_IntAPS)
291     return moveTy<PrimConv<PT_IntAPS>::T>;
292   if (Type == PT_MemberPtr)
293     return moveTy<PrimConv<PT_MemberPtr>::T>;
294   COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr);
295 }
296 
297 static BlockCtorFn getCtorArrayPrim(PrimType Type) {
298   TYPE_SWITCH(Type, return ctorArrayTy<T>);
299   llvm_unreachable("unknown Expr");
300 }
301 
302 static BlockDtorFn getDtorArrayPrim(PrimType Type) {
303   TYPE_SWITCH(Type, return dtorArrayTy<T>);
304   llvm_unreachable("unknown Expr");
305 }
306 
307 static BlockMoveFn getMoveArrayPrim(PrimType Type) {
308   TYPE_SWITCH(Type, return moveArrayTy<T>);
309   llvm_unreachable("unknown Expr");
310 }
311 
312 /// Primitives.
313 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
314                        bool IsConst, bool IsTemporary, bool IsMutable)
315     : Source(D), ElemSize(primSize(Type)), Size(ElemSize),
316       MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type),
317       IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
318       CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
319       MoveFn(getMovePrim(Type)) {
320   assert(AllocSize >= Size);
321   assert(Source && "Missing source");
322 }
323 
324 /// Primitive arrays.
325 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
326                        size_t NumElems, bool IsConst, bool IsTemporary,
327                        bool IsMutable)
328     : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
329       MDSize(MD.value_or(0)),
330       AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type),
331       IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
332       IsArray(true), CtorFn(getCtorArrayPrim(Type)),
333       DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
334   assert(Source && "Missing source");
335   assert(NumElems <= (MaxArrayElemBytes / ElemSize));
336 }
337 
338 /// Primitive unknown-size arrays.
339 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
340                        bool IsTemporary, UnknownSize)
341     : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
342       MDSize(MD.value_or(0)),
343       AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true),
344       IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
345       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
346       MoveFn(getMoveArrayPrim(Type)) {
347   assert(Source && "Missing source");
348 }
349 
350 /// Arrays of composite elements.
351 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
352                        unsigned NumElems, bool IsConst, bool IsTemporary,
353                        bool IsMutable)
354     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
355       Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
356       AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
357       ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
358       IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
359       DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
360   assert(Source && "Missing source");
361 }
362 
363 /// Unknown-size arrays of composite elements.
364 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
365                        bool IsTemporary, UnknownSize)
366     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
367       Size(UnknownSizeMark), MDSize(MD.value_or(0)),
368       AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true),
369       IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
370       CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
371   assert(Source && "Missing source");
372 }
373 
374 /// Composite records.
375 Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD,
376                        bool IsConst, bool IsTemporary, bool IsMutable)
377     : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
378       Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
379       ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
380       IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
381       MoveFn(moveRecord) {
382   assert(Source && "Missing source");
383 }
384 
385 /// Dummy.
386 Descriptor::Descriptor(const DeclTy &D)
387     : Source(D), ElemSize(1), Size(1), MDSize(0), AllocSize(MDSize),
388       ElemRecord(nullptr), IsConst(true), IsMutable(false), IsTemporary(false),
389       IsDummy(true) {
390   assert(Source && "Missing source");
391 }
392 
393 QualType Descriptor::getType() const {
394   if (const auto *D = asValueDecl())
395     return D->getType();
396   if (const auto *T = dyn_cast_if_present<TypeDecl>(asDecl()))
397     return QualType(T->getTypeForDecl(), 0);
398 
399   // The Source sometimes has a different type than the once
400   // we really save. Try to consult the Record first.
401   if (isRecord())
402     return QualType(ElemRecord->getDecl()->getTypeForDecl(), 0);
403   if (const auto *E = asExpr())
404     return E->getType();
405   llvm_unreachable("Invalid descriptor type");
406 }
407 
408 QualType Descriptor::getElemQualType() const {
409   assert(isArray());
410   QualType T = getType();
411   if (T->isPointerOrReferenceType())
412     T = T->getPointeeType();
413 
414   if (const auto *AT = T->getAsArrayTypeUnsafe()) {
415     // For primitive arrays, we don't save a QualType at all,
416     // just a PrimType. Try to figure out the QualType here.
417     if (isPrimitiveArray()) {
418       while (T->isArrayType())
419         T = T->getAsArrayTypeUnsafe()->getElementType();
420       return T;
421     }
422     return AT->getElementType();
423   }
424   if (const auto *CT = T->getAs<ComplexType>())
425     return CT->getElementType();
426   if (const auto *CT = T->getAs<VectorType>())
427     return CT->getElementType();
428 
429   return T;
430 }
431 
432 SourceLocation Descriptor::getLocation() const {
433   if (auto *D = dyn_cast<const Decl *>(Source))
434     return D->getLocation();
435   if (auto *E = dyn_cast<const Expr *>(Source))
436     return E->getExprLoc();
437   llvm_unreachable("Invalid descriptor type");
438 }
439 
440 SourceInfo Descriptor::getLoc() const {
441   if (const auto *D = dyn_cast<const Decl *>(Source))
442     return SourceInfo(D);
443   if (const auto *E = dyn_cast<const Expr *>(Source))
444     return SourceInfo(E);
445   llvm_unreachable("Invalid descriptor type");
446 }
447 
448 bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }
449 
450 InitMap::InitMap(unsigned N)
451     : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {
452   std::fill_n(data(), numFields(N), 0);
453 }
454 
455 bool InitMap::initializeElement(unsigned I) {
456   unsigned Bucket = I / PER_FIELD;
457   T Mask = T(1) << (I % PER_FIELD);
458   if (!(data()[Bucket] & Mask)) {
459     data()[Bucket] |= Mask;
460     UninitFields -= 1;
461   }
462   return UninitFields == 0;
463 }
464 
465 bool InitMap::isElementInitialized(unsigned I) const {
466   unsigned Bucket = I / PER_FIELD;
467   return data()[Bucket] & (T(1) << (I % PER_FIELD));
468 }
469