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