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