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