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#include "clang/AST/ExprCXX.h"
20
21using namespace clang;
22using namespace clang::interp;
23
24template <typename T> static constexpr bool needsCtor() {
25 if constexpr (std::is_same_v<T, Integral<8, true>> ||
26 std::is_same_v<T, Integral<8, false>> ||
27 std::is_same_v<T, Integral<16, true>> ||
28 std::is_same_v<T, Integral<16, false>> ||
29 std::is_same_v<T, Integral<32, true>> ||
30 std::is_same_v<T, Integral<32, false>> ||
31 std::is_same_v<T, Integral<64, true>> ||
32 std::is_same_v<T, Integral<64, false>> ||
33 std::is_same_v<T, Boolean>)
34 return false;
35
36 return true;
37}
38
39template <typename T>
40static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool,
41 const Descriptor *) {
42 static_assert(needsCtor<T>());
43 new (Ptr) T();
44}
45
46template <typename T>
47static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) {
48 static_assert(needsCtor<T>());
49 reinterpret_cast<T *>(Ptr)->~T();
50}
51
52template <typename T>
53static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool,
54 const Descriptor *D) {
55 new (Ptr) InitMapPtr();
56
57 if constexpr (needsCtor<T>()) {
58 Ptr += sizeof(InitMapPtr);
59 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
60 new (&reinterpret_cast<T *>(Ptr)[I]) T();
61 }
62 }
63}
64
65template <typename T>
66static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
67 InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr);
68 IMP.deleteInitMap();
69
70 if constexpr (needsCtor<T>()) {
71 Ptr += sizeof(InitMapPtr);
72 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
73 reinterpret_cast<T *>(Ptr)[I].~T();
74 }
75 }
76}
77
78static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst,
79 bool IsMutable, bool IsVolatile, bool IsActive,
80 bool InUnion, const Descriptor *D) {
81 const unsigned NumElems = D->getNumElems();
82 const unsigned ElemSize =
83 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
84
85 unsigned ElemOffset = 0;
86 for (unsigned I = 0; I != NumElems; ++I, ElemOffset += ElemSize) {
87 auto *ElemPtr = Ptr + ElemOffset;
88 auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
89 auto *SD = D->ElemDesc;
90
91 Desc->Offset = ElemOffset + sizeof(InlineDescriptor);
92 Desc->Desc = SD;
93 Desc->IsInitialized = true;
94 Desc->IsBase = false;
95 Desc->IsActive = IsActive;
96 Desc->IsConst = IsConst || D->IsConst;
97 Desc->IsFieldMutable = IsMutable || D->IsMutable;
98 Desc->InUnion = InUnion;
99 Desc->IsArrayElement = true;
100 Desc->IsVolatile = IsVolatile;
101
102 if (auto Fn = D->ElemDesc->CtorFn) {
103 auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1);
104 Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsVolatile, IsActive,
105 Desc->InUnion || SD->isUnion(), D->ElemDesc);
106 }
107 }
108}
109
110static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) {
111 const unsigned NumElems = D->getNumElems();
112 const unsigned ElemSize =
113 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
114
115 unsigned ElemOffset = 0;
116 auto Dtor = D->ElemDesc->DtorFn;
117 assert(Dtor &&
118 "a composite array without an elem dtor shouldn't have a dtor itself");
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 Dtor(B, ElemLoc, D->ElemDesc);
124 }
125}
126
127static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
128 bool IsVolatile, bool IsActive, bool IsUnionField,
129 bool InUnion, const Descriptor *D, unsigned FieldOffset) {
130 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
131 Desc->Offset = FieldOffset;
132 Desc->Desc = D;
133 Desc->IsInitialized = D->IsArray;
134 Desc->IsBase = false;
135 Desc->IsActive = IsActive && !IsUnionField;
136 Desc->InUnion = InUnion;
137 Desc->IsConst = IsConst || D->IsConst;
138 Desc->IsFieldMutable = IsMutable || D->IsMutable;
139 Desc->IsVolatile = IsVolatile || D->IsVolatile;
140 // True if this field is const AND the parent is mutable.
141 Desc->IsConstInMutable = Desc->IsConst && IsMutable;
142
143 if (auto Fn = D->CtorFn)
144 Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
145 Desc->IsVolatile, Desc->IsActive, InUnion || D->isUnion(), D);
146}
147
148static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
149 bool IsVolatile, bool IsActive, bool InUnion,
150 const Descriptor *D, unsigned FieldOffset,
151 bool IsVirtualBase) {
152 assert(D);
153 assert(D->ElemRecord);
154 assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes.
155
156 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1;
157 Desc->Offset = FieldOffset;
158 Desc->Desc = D;
159 Desc->IsInitialized = D->IsArray;
160 Desc->IsBase = true;
161 Desc->IsVirtualBase = IsVirtualBase;
162 Desc->IsActive = IsActive && !InUnion;
163 Desc->IsConst = IsConst || D->IsConst;
164 Desc->IsFieldMutable = IsMutable || D->IsMutable;
165 Desc->InUnion = InUnion;
166 Desc->IsVolatile = false;
167
168 for (const auto &V : D->ElemRecord->bases())
169 initBase(B, Ptr: Ptr + FieldOffset, IsConst, IsMutable, IsVolatile, IsActive,
170 InUnion, D: V.Desc, FieldOffset: V.Offset, IsVirtualBase: false);
171 for (const auto &F : D->ElemRecord->fields())
172 initField(B, Ptr: Ptr + FieldOffset, IsConst, IsMutable, IsVolatile, IsActive,
173 IsUnionField: InUnion, InUnion, D: F.Desc, FieldOffset: F.Offset);
174}
175
176static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
177 bool IsVolatile, bool IsActive, bool InUnion,
178 const Descriptor *D) {
179 for (const auto &V : D->ElemRecord->bases())
180 initBase(B, Ptr, IsConst, IsMutable, IsVolatile, IsActive, InUnion, D: V.Desc,
181 FieldOffset: V.Offset,
182 /*IsVirtualBase=*/false);
183 for (const auto &F : D->ElemRecord->fields()) {
184 bool IsUnionField = D->isUnion();
185 initField(B, Ptr, IsConst, IsMutable, IsVolatile, IsActive, IsUnionField,
186 InUnion: InUnion || IsUnionField, D: F.Desc, FieldOffset: F.Offset);
187 }
188 for (const auto &V : D->ElemRecord->virtual_bases())
189 initBase(B, Ptr, IsConst, IsMutable, IsVolatile, IsActive, InUnion, D: V.Desc,
190 FieldOffset: V.Offset,
191 /*IsVirtualBase=*/true);
192}
193
194static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D,
195 unsigned FieldOffset) {
196 if (auto Fn = D->DtorFn)
197 Fn(B, Ptr + FieldOffset, D);
198}
199
200static void destroyBase(Block *B, std::byte *Ptr, const Descriptor *D,
201 unsigned FieldOffset) {
202 assert(D);
203 assert(D->ElemRecord);
204
205 for (const auto &V : D->ElemRecord->bases())
206 destroyBase(B, Ptr: Ptr + FieldOffset, D: V.Desc, FieldOffset: V.Offset);
207 for (const auto &F : D->ElemRecord->fields())
208 destroyField(B, Ptr: Ptr + FieldOffset, D: F.Desc, FieldOffset: F.Offset);
209}
210
211static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) {
212 for (const auto &F : D->ElemRecord->bases())
213 destroyBase(B, Ptr, D: F.Desc, FieldOffset: F.Offset);
214 for (const auto &F : D->ElemRecord->fields())
215 destroyField(B, Ptr, D: F.Desc, FieldOffset: F.Offset);
216 for (const auto &F : D->ElemRecord->virtual_bases())
217 destroyBase(B, Ptr, D: F.Desc, FieldOffset: F.Offset);
218}
219
220/// Whether a record needs its descriptor dtor function called.
221static bool needsRecordDtor(const Record *R) {
222 for (const auto &B : R->bases()) {
223 if (B.Desc->DtorFn)
224 return true;
225 }
226
227 for (const auto &F : R->fields()) {
228 if (F.Desc->DtorFn)
229 return true;
230 }
231
232 for (const auto &V : R->virtual_bases()) {
233 if (V.Desc->DtorFn)
234 return true;
235 }
236 return false;
237}
238
239static BlockCtorFn getCtorPrim(PrimType T) {
240 switch (T) {
241 case PT_Float:
242 return ctorTy<PrimConv<PT_Float>::T>;
243 case PT_IntAP:
244 return ctorTy<PrimConv<PT_IntAP>::T>;
245 case PT_IntAPS:
246 return ctorTy<PrimConv<PT_IntAPS>::T>;
247 case PT_Ptr:
248 return ctorTy<PrimConv<PT_Ptr>::T>;
249 case PT_MemberPtr:
250 return ctorTy<PrimConv<PT_MemberPtr>::T>;
251 default:
252 return nullptr;
253 }
254 llvm_unreachable("Unhandled PrimType");
255}
256
257static BlockDtorFn getDtorPrim(PrimType T) {
258 switch (T) {
259 case PT_Float:
260 return dtorTy<PrimConv<PT_Float>::T>;
261 case PT_IntAP:
262 return dtorTy<PrimConv<PT_IntAP>::T>;
263 case PT_IntAPS:
264 return dtorTy<PrimConv<PT_IntAPS>::T>;
265 case PT_Ptr:
266 return dtorTy<PrimConv<PT_Ptr>::T>;
267 case PT_MemberPtr:
268 return dtorTy<PrimConv<PT_MemberPtr>::T>;
269 default:
270 return nullptr;
271 }
272 llvm_unreachable("Unhandled PrimType");
273}
274
275static BlockCtorFn getCtorArrayPrim(PrimType Type) {
276 TYPE_SWITCH(Type, return ctorArrayTy<T>);
277 llvm_unreachable("unknown Expr");
278}
279
280static BlockDtorFn getDtorArrayPrim(PrimType Type) {
281 TYPE_SWITCH(Type, return dtorArrayTy<T>);
282 llvm_unreachable("unknown Expr");
283}
284
285/// Primitives.
286Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type,
287 MetadataSize MD, bool IsConst, bool IsTemporary,
288 bool IsMutable, bool IsVolatile)
289 : Source(D), SourceType(SourceTy), ElemSize(primSize(Type)), Size(ElemSize),
290 MDSize(MD.value_or(u: 0)), AllocSize(align(Size: Size + MDSize)), PrimT(Type),
291 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
292 IsVolatile(IsVolatile), CtorFn(getCtorPrim(T: Type)),
293 DtorFn(getDtorPrim(T: Type)) {
294 assert(AllocSize >= Size);
295 assert(Source && "Missing source");
296}
297
298/// Primitive arrays.
299Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
300 size_t NumElems, bool IsConst, bool IsTemporary,
301 bool IsMutable)
302 : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
303 MDSize(MD.value_or(u: 0)),
304 AllocSize(align(Size: MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type),
305 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
306 IsArray(true), CtorFn(getCtorArrayPrim(Type)),
307 DtorFn(getDtorArrayPrim(Type)) {
308 assert(Source && "Missing source");
309 assert(NumElems <= (MaxArrayElemBytes / ElemSize));
310}
311
312/// Primitive unknown-size arrays.
313Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
314 bool IsTemporary, bool IsConst, UnknownSize)
315 : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
316 MDSize(MD.value_or(u: 0)),
317 AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), PrimT(Type),
318 IsConst(IsConst), IsMutable(false), IsTemporary(IsTemporary),
319 IsArray(true), CtorFn(getCtorArrayPrim(Type)),
320 DtorFn(getDtorArrayPrim(Type)) {
321 assert(Source && "Missing source");
322}
323
324/// Arrays of composite elements.
325Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy,
326 const Descriptor *Elem, MetadataSize MD,
327 unsigned NumElems, bool IsConst, bool IsTemporary,
328 bool IsMutable)
329 : Source(D), SourceType(SourceTy),
330 ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
331 Size(ElemSize * NumElems), MDSize(MD.value_or(u: 0)),
332 AllocSize(std::max<size_t>(a: alignof(void *), b: Size) + MDSize),
333 ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
334 IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
335 DtorFn(Elem->DtorFn ? dtorArrayDesc : nullptr) {
336 assert(Source && "Missing source");
337}
338
339/// Unknown-size arrays of composite elements.
340Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
341 bool IsTemporary, UnknownSize)
342 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
343 Size(UnknownSizeMark), MDSize(MD.value_or(u: 0)),
344 AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true),
345 IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
346 CtorFn(ctorArrayDesc), DtorFn(Elem->DtorFn ? dtorArrayDesc : nullptr) {
347 assert(Source && "Missing source");
348}
349
350/// Composite records.
351Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD,
352 bool IsConst, bool IsTemporary, bool IsMutable,
353 bool IsVolatile)
354 : Source(D), ElemSize(std::max<size_t>(a: alignof(void *), b: R->getFullSize())),
355 Size(ElemSize), MDSize(MD.value_or(u: 0)), AllocSize(Size + MDSize),
356 ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
357 IsTemporary(IsTemporary), IsVolatile(IsVolatile), CtorFn(ctorRecord),
358 DtorFn(needsRecordDtor(R) ? dtorRecord : nullptr) {
359 assert(Source && "Missing source");
360}
361
362/// Dummy.
363Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
364 : Source(D), ElemSize(1), Size(1), MDSize(MD.value_or(u: 0)),
365 AllocSize(MDSize), ElemRecord(nullptr), IsConst(true), IsMutable(false),
366 IsTemporary(false) {
367 assert(Source && "Missing source");
368}
369
370QualType Descriptor::getType() const {
371 if (SourceType)
372 return QualType(SourceType, 0);
373 if (const auto *D = asValueDecl())
374 return D->getType();
375 if (const auto *T = dyn_cast_if_present<TypeDecl>(Val: asDecl()))
376 return T->getASTContext().getTypeDeclType(Decl: T);
377
378 // The Source sometimes has a different type than the once
379 // we really save. Try to consult the Record first.
380 if (isRecord()) {
381 const RecordDecl *RD = ElemRecord->getDecl();
382 return RD->getASTContext().getCanonicalTagType(TD: RD);
383 }
384 if (const auto *E = asExpr())
385 return E->getType();
386 llvm_unreachable("Invalid descriptor type");
387}
388
389QualType Descriptor::getElemQualType() const {
390 assert(isArray());
391 QualType T = getType();
392 if (const auto *AT = T->getAs<AtomicType>())
393 T = AT->getValueType();
394 if (T->isPointerOrReferenceType())
395 T = T->getPointeeType();
396
397 if (const auto *AT = T->getAsArrayTypeUnsafe()) {
398 // For primitive arrays, we don't save a QualType at all,
399 // just a PrimType. Try to figure out the QualType here.
400 if (isPrimitiveArray()) {
401 while (T->isArrayType())
402 T = T->getAsArrayTypeUnsafe()->getElementType();
403 return T;
404 }
405 return AT->getElementType();
406 }
407 if (const auto *CT = T->getAs<ComplexType>())
408 return CT->getElementType();
409 if (const auto *CT = T->getAs<VectorType>())
410 return CT->getElementType();
411
412 return T;
413}
414
415QualType Descriptor::getDataType(const ASTContext &Ctx) const {
416 auto MakeArrayType = [&](QualType ElemType) -> QualType {
417 if (IsArray)
418 return Ctx.getConstantArrayType(
419 EltTy: ElemType, ArySize: APInt(64, static_cast<uint64_t>(getNumElems()), false),
420 SizeExpr: nullptr, ASM: ArraySizeModifier::Normal, IndexTypeQuals: 0);
421 return ElemType;
422 };
423
424 if (const auto *E = asExpr()) {
425 if (isa<CXXNewExpr>(Val: E))
426 return MakeArrayType(E->getType()->getPointeeType());
427
428 // std::allocator.allocate() call.
429 if (const auto *ME = dyn_cast<CXXMemberCallExpr>(Val: E);
430 ME && ME->getRecordDecl()->getName() == "allocator" &&
431 ME->getMethodDecl()->getName() == "allocate")
432 return MakeArrayType(E->getType()->getPointeeType());
433 return E->getType();
434 }
435
436 return getType();
437}
438
439QualType Descriptor::getDataElemType() const {
440 if (const auto *E = asExpr()) {
441 if (isa<CXXNewExpr>(Val: E))
442 return E->getType()->getPointeeType();
443
444 // std::allocator.allocate() call.
445 if (const auto *ME = dyn_cast<CXXMemberCallExpr>(Val: E);
446 ME && ME->getRecordDecl()->getName() == "allocator" &&
447 ME->getMethodDecl()->getName() == "allocate")
448 return E->getType()->getPointeeType();
449 return E->getType();
450 }
451
452 return getType();
453}
454
455SourceLocation Descriptor::getLocation() const {
456 if (auto *D = dyn_cast<const Decl *>(Val: Source))
457 return D->getLocation();
458 if (auto *E = dyn_cast<const Expr *>(Val: Source))
459 return E->getExprLoc();
460 llvm_unreachable("Invalid descriptor type");
461}
462
463SourceInfo Descriptor::getLoc() const {
464 if (const auto *D = dyn_cast<const Decl *>(Val: Source))
465 return SourceInfo(D);
466 if (const auto *E = dyn_cast<const Expr *>(Val: Source))
467 return SourceInfo(E);
468 llvm_unreachable("Invalid descriptor type");
469}
470
471bool Descriptor::hasTrivialDtor() const {
472 if (isPrimitive() || isPrimitiveArray())
473 return true;
474
475 if (isRecord()) {
476 assert(ElemRecord);
477 return ElemRecord->hasTrivialDtor();
478 }
479
480 if (!ElemDesc)
481 return true;
482 // Composite arrays.
483 return ElemDesc->hasTrivialDtor();
484}
485
486bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }
487