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