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 20 using namespace clang; 21 using namespace clang::interp; 22 23 template <typename T> 24 static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool, 25 const Descriptor *) { 26 new (Ptr) T(); 27 } 28 29 template <typename T> 30 static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) { 31 reinterpret_cast<T *>(Ptr)->~T(); 32 } 33 34 template <typename T> 35 static void moveTy(Block *, std::byte *Src, std::byte *Dst, 36 const Descriptor *) { 37 auto *SrcPtr = reinterpret_cast<T *>(Src); 38 auto *DstPtr = reinterpret_cast<T *>(Dst); 39 new (DstPtr) T(std::move(*SrcPtr)); 40 } 41 42 template <typename T> 43 static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool, 44 const Descriptor *D) { 45 new (Ptr) InitMapPtr(std::nullopt); 46 47 Ptr += sizeof(InitMapPtr); 48 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 49 new (&reinterpret_cast<T *>(Ptr)[I]) T(); 50 } 51 } 52 53 template <typename T> 54 static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) { 55 InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr); 56 57 if (IMP) 58 IMP = std::nullopt; 59 Ptr += sizeof(InitMapPtr); 60 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 61 reinterpret_cast<T *>(Ptr)[I].~T(); 62 } 63 } 64 65 template <typename T> 66 static void moveArrayTy(Block *, std::byte *Src, std::byte *Dst, 67 const Descriptor *D) { 68 InitMapPtr &SrcIMP = *reinterpret_cast<InitMapPtr *>(Src); 69 if (SrcIMP) { 70 // We only ever invoke the moveFunc when moving block contents to a 71 // DeadBlock. DeadBlocks don't need InitMaps, so we destroy them here. 72 SrcIMP = std::nullopt; 73 } 74 Src += sizeof(InitMapPtr); 75 Dst += sizeof(InitMapPtr); 76 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 77 auto *SrcPtr = &reinterpret_cast<T *>(Src)[I]; 78 auto *DstPtr = &reinterpret_cast<T *>(Dst)[I]; 79 new (DstPtr) T(std::move(*SrcPtr)); 80 } 81 } 82 83 static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst, 84 bool IsMutable, bool IsActive, bool InUnion, 85 const Descriptor *D) { 86 const unsigned NumElems = D->getNumElems(); 87 const unsigned ElemSize = 88 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 89 90 unsigned ElemOffset = 0; 91 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 92 auto *ElemPtr = Ptr + ElemOffset; 93 auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); 94 auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1); 95 auto *SD = D->ElemDesc; 96 97 Desc->Offset = ElemOffset + sizeof(InlineDescriptor); 98 Desc->Desc = SD; 99 Desc->IsInitialized = true; 100 Desc->IsBase = false; 101 Desc->IsActive = IsActive; 102 Desc->IsConst = IsConst || D->IsConst; 103 Desc->IsFieldMutable = IsMutable || D->IsMutable; 104 Desc->InUnion = InUnion; 105 Desc->IsArrayElement = true; 106 107 if (auto Fn = D->ElemDesc->CtorFn) 108 Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, 109 Desc->InUnion || SD->isUnion(), D->ElemDesc); 110 } 111 } 112 113 static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) { 114 const unsigned NumElems = D->getNumElems(); 115 const unsigned ElemSize = 116 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 117 118 unsigned ElemOffset = 0; 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 if (auto Fn = D->ElemDesc->DtorFn) 124 Fn(B, ElemLoc, D->ElemDesc); 125 } 126 } 127 128 static void moveArrayDesc(Block *B, std::byte *Src, std::byte *Dst, 129 const Descriptor *D) { 130 const unsigned NumElems = D->getNumElems(); 131 const unsigned ElemSize = 132 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 133 134 unsigned ElemOffset = 0; 135 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 136 auto *SrcPtr = Src + ElemOffset; 137 auto *DstPtr = Dst + ElemOffset; 138 139 auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr); 140 auto *SrcElemLoc = reinterpret_cast<std::byte *>(SrcDesc + 1); 141 auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr); 142 auto *DstElemLoc = reinterpret_cast<std::byte *>(DstDesc + 1); 143 144 *DstDesc = *SrcDesc; 145 if (auto Fn = D->ElemDesc->MoveFn) 146 Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc); 147 } 148 } 149 150 static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 151 bool IsActive, bool IsUnionField, bool InUnion, 152 const Descriptor *D, unsigned FieldOffset) { 153 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1; 154 Desc->Offset = FieldOffset; 155 Desc->Desc = D; 156 Desc->IsInitialized = D->IsArray; 157 Desc->IsBase = false; 158 Desc->IsActive = IsActive && !IsUnionField; 159 Desc->InUnion = InUnion; 160 Desc->IsConst = IsConst || D->IsConst; 161 Desc->IsFieldMutable = IsMutable || D->IsMutable; 162 163 if (auto Fn = D->CtorFn) 164 Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable, 165 Desc->IsActive, InUnion || D->isUnion(), D); 166 } 167 168 static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 169 bool IsActive, bool InUnion, const Descriptor *D, 170 unsigned FieldOffset, bool IsVirtualBase) { 171 assert(D); 172 assert(D->ElemRecord); 173 assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes. 174 175 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1; 176 Desc->Offset = FieldOffset; 177 Desc->Desc = D; 178 Desc->IsInitialized = D->IsArray; 179 Desc->IsBase = true; 180 Desc->IsVirtualBase = IsVirtualBase; 181 Desc->IsActive = IsActive && !InUnion; 182 Desc->IsConst = IsConst || D->IsConst; 183 Desc->IsFieldMutable = IsMutable || D->IsMutable; 184 Desc->InUnion = InUnion; 185 186 for (const auto &V : D->ElemRecord->bases()) 187 initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion, 188 V.Desc, V.Offset, false); 189 for (const auto &F : D->ElemRecord->fields()) 190 initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion, 191 InUnion, F.Desc, F.Offset); 192 } 193 194 static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 195 bool IsActive, bool InUnion, const Descriptor *D) { 196 for (const auto &V : D->ElemRecord->bases()) 197 initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset, 198 false); 199 for (const auto &F : D->ElemRecord->fields()) { 200 bool IsUnionField = D->isUnion(); 201 initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField, 202 InUnion || IsUnionField, F.Desc, F.Offset); 203 } 204 for (const auto &V : D->ElemRecord->virtual_bases()) 205 initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset, 206 true); 207 } 208 209 static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D, 210 unsigned FieldOffset) { 211 if (auto Fn = D->DtorFn) 212 Fn(B, Ptr + FieldOffset, D); 213 } 214 215 static void destroyBase(Block *B, std::byte *Ptr, const Descriptor *D, 216 unsigned FieldOffset) { 217 assert(D); 218 assert(D->ElemRecord); 219 220 for (const auto &V : D->ElemRecord->bases()) 221 destroyBase(B, Ptr + FieldOffset, V.Desc, V.Offset); 222 for (const auto &F : D->ElemRecord->fields()) 223 destroyField(B, Ptr + FieldOffset, F.Desc, F.Offset); 224 } 225 226 static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) { 227 for (const auto &F : D->ElemRecord->bases()) 228 destroyBase(B, Ptr, F.Desc, F.Offset); 229 for (const auto &F : D->ElemRecord->fields()) 230 destroyField(B, Ptr, F.Desc, F.Offset); 231 for (const auto &F : D->ElemRecord->virtual_bases()) 232 destroyBase(B, Ptr, F.Desc, F.Offset); 233 } 234 235 static void moveRecord(Block *B, std::byte *Src, std::byte *Dst, 236 const Descriptor *D) { 237 assert(D); 238 assert(D->ElemRecord); 239 240 // FIXME: There might be cases where we need to move over the (v)bases as 241 // well. 242 for (const auto &F : D->ElemRecord->fields()) { 243 auto FieldOffset = F.Offset; 244 const auto *SrcDesc = 245 reinterpret_cast<const InlineDescriptor *>(Src + FieldOffset) - 1; 246 auto *DestDesc = 247 reinterpret_cast<InlineDescriptor *>(Dst + FieldOffset) - 1; 248 std::memcpy(DestDesc, SrcDesc, sizeof(InlineDescriptor)); 249 250 if (auto Fn = F.Desc->MoveFn) 251 Fn(B, Src + FieldOffset, Dst + FieldOffset, F.Desc); 252 } 253 } 254 255 static BlockCtorFn getCtorPrim(PrimType Type) { 256 // Floating types are special. They are primitives, but need their 257 // constructor called. 258 if (Type == PT_Float) 259 return ctorTy<PrimConv<PT_Float>::T>; 260 if (Type == PT_IntAP) 261 return ctorTy<PrimConv<PT_IntAP>::T>; 262 if (Type == PT_IntAPS) 263 return ctorTy<PrimConv<PT_IntAPS>::T>; 264 if (Type == PT_MemberPtr) 265 return ctorTy<PrimConv<PT_MemberPtr>::T>; 266 267 COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); 268 } 269 270 static BlockDtorFn getDtorPrim(PrimType Type) { 271 // Floating types are special. They are primitives, but need their 272 // destructor called, since they might allocate memory. 273 if (Type == PT_Float) 274 return dtorTy<PrimConv<PT_Float>::T>; 275 if (Type == PT_IntAP) 276 return dtorTy<PrimConv<PT_IntAP>::T>; 277 if (Type == PT_IntAPS) 278 return dtorTy<PrimConv<PT_IntAPS>::T>; 279 if (Type == PT_MemberPtr) 280 return dtorTy<PrimConv<PT_MemberPtr>::T>; 281 282 COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); 283 } 284 285 static BlockMoveFn getMovePrim(PrimType Type) { 286 if (Type == PT_Float) 287 return moveTy<PrimConv<PT_Float>::T>; 288 if (Type == PT_IntAP) 289 return moveTy<PrimConv<PT_IntAP>::T>; 290 if (Type == PT_IntAPS) 291 return moveTy<PrimConv<PT_IntAPS>::T>; 292 if (Type == PT_MemberPtr) 293 return moveTy<PrimConv<PT_MemberPtr>::T>; 294 COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr); 295 } 296 297 static BlockCtorFn getCtorArrayPrim(PrimType Type) { 298 TYPE_SWITCH(Type, return ctorArrayTy<T>); 299 llvm_unreachable("unknown Expr"); 300 } 301 302 static BlockDtorFn getDtorArrayPrim(PrimType Type) { 303 TYPE_SWITCH(Type, return dtorArrayTy<T>); 304 llvm_unreachable("unknown Expr"); 305 } 306 307 static BlockMoveFn getMoveArrayPrim(PrimType Type) { 308 TYPE_SWITCH(Type, return moveArrayTy<T>); 309 llvm_unreachable("unknown Expr"); 310 } 311 312 /// Primitives. 313 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 314 bool IsConst, bool IsTemporary, bool IsMutable) 315 : Source(D), ElemSize(primSize(Type)), Size(ElemSize), 316 MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type), 317 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), 318 CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), 319 MoveFn(getMovePrim(Type)) { 320 assert(AllocSize >= Size); 321 assert(Source && "Missing source"); 322 } 323 324 /// Primitive arrays. 325 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 326 size_t NumElems, bool IsConst, bool IsTemporary, 327 bool IsMutable) 328 : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), 329 MDSize(MD.value_or(0)), 330 AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type), 331 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), 332 IsArray(true), CtorFn(getCtorArrayPrim(Type)), 333 DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { 334 assert(Source && "Missing source"); 335 assert(NumElems <= (MaxArrayElemBytes / ElemSize)); 336 } 337 338 /// Primitive unknown-size arrays. 339 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 340 bool IsTemporary, UnknownSize) 341 : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), 342 MDSize(MD.value_or(0)), 343 AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true), 344 IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 345 CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), 346 MoveFn(getMoveArrayPrim(Type)) { 347 assert(Source && "Missing source"); 348 } 349 350 /// Arrays of composite elements. 351 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, 352 unsigned NumElems, bool IsConst, bool IsTemporary, 353 bool IsMutable) 354 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 355 Size(ElemSize * NumElems), MDSize(MD.value_or(0)), 356 AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), 357 ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), 358 IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), 359 DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 360 assert(Source && "Missing source"); 361 } 362 363 /// Unknown-size arrays of composite elements. 364 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, 365 bool IsTemporary, UnknownSize) 366 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 367 Size(UnknownSizeMark), MDSize(MD.value_or(0)), 368 AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true), 369 IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 370 CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 371 assert(Source && "Missing source"); 372 } 373 374 /// Composite records. 375 Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, 376 bool IsConst, bool IsTemporary, bool IsMutable) 377 : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), 378 Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), 379 ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), 380 IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord), 381 MoveFn(moveRecord) { 382 assert(Source && "Missing source"); 383 } 384 385 /// Dummy. 386 Descriptor::Descriptor(const DeclTy &D) 387 : Source(D), ElemSize(1), Size(1), MDSize(0), AllocSize(MDSize), 388 ElemRecord(nullptr), IsConst(true), IsMutable(false), IsTemporary(false), 389 IsDummy(true) { 390 assert(Source && "Missing source"); 391 } 392 393 QualType Descriptor::getType() const { 394 if (const auto *D = asValueDecl()) 395 return D->getType(); 396 if (const auto *T = dyn_cast_if_present<TypeDecl>(asDecl())) 397 return QualType(T->getTypeForDecl(), 0); 398 399 // The Source sometimes has a different type than the once 400 // we really save. Try to consult the Record first. 401 if (isRecord()) 402 return QualType(ElemRecord->getDecl()->getTypeForDecl(), 0); 403 if (const auto *E = asExpr()) 404 return E->getType(); 405 llvm_unreachable("Invalid descriptor type"); 406 } 407 408 QualType Descriptor::getElemQualType() const { 409 assert(isArray()); 410 QualType T = getType(); 411 if (T->isPointerOrReferenceType()) 412 T = T->getPointeeType(); 413 414 if (const auto *AT = T->getAsArrayTypeUnsafe()) { 415 // For primitive arrays, we don't save a QualType at all, 416 // just a PrimType. Try to figure out the QualType here. 417 if (isPrimitiveArray()) { 418 while (T->isArrayType()) 419 T = T->getAsArrayTypeUnsafe()->getElementType(); 420 return T; 421 } 422 return AT->getElementType(); 423 } 424 if (const auto *CT = T->getAs<ComplexType>()) 425 return CT->getElementType(); 426 if (const auto *CT = T->getAs<VectorType>()) 427 return CT->getElementType(); 428 429 return T; 430 } 431 432 SourceLocation Descriptor::getLocation() const { 433 if (auto *D = dyn_cast<const Decl *>(Source)) 434 return D->getLocation(); 435 if (auto *E = dyn_cast<const Expr *>(Source)) 436 return E->getExprLoc(); 437 llvm_unreachable("Invalid descriptor type"); 438 } 439 440 SourceInfo Descriptor::getLoc() const { 441 if (const auto *D = dyn_cast<const Decl *>(Source)) 442 return SourceInfo(D); 443 if (const auto *E = dyn_cast<const Expr *>(Source)) 444 return SourceInfo(E); 445 llvm_unreachable("Invalid descriptor type"); 446 } 447 448 bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); } 449 450 InitMap::InitMap(unsigned N) 451 : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) { 452 std::fill_n(data(), numFields(N), 0); 453 } 454 455 bool InitMap::initializeElement(unsigned I) { 456 unsigned Bucket = I / PER_FIELD; 457 T Mask = T(1) << (I % PER_FIELD); 458 if (!(data()[Bucket] & Mask)) { 459 data()[Bucket] |= Mask; 460 UninitFields -= 1; 461 } 462 return UninitFields == 0; 463 } 464 465 bool InitMap::isElementInitialized(unsigned I) const { 466 unsigned Bucket = I / PER_FIELD; 467 return data()[Bucket] & (T(1) << (I % PER_FIELD)); 468 } 469