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 "Floating.h" 12 #include "FunctionPointer.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 106 if (auto Fn = D->ElemDesc->CtorFn) 107 Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, 108 Desc->InUnion || SD->isUnion(), D->ElemDesc); 109 } 110 } 111 112 static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) { 113 const unsigned NumElems = D->getNumElems(); 114 const unsigned ElemSize = 115 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 116 117 unsigned ElemOffset = 0; 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 if (auto Fn = D->ElemDesc->DtorFn) 123 Fn(B, ElemLoc, D->ElemDesc); 124 } 125 } 126 127 static void moveArrayDesc(Block *B, std::byte *Src, std::byte *Dst, 128 const Descriptor *D) { 129 const unsigned NumElems = D->getNumElems(); 130 const unsigned ElemSize = 131 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 132 133 unsigned ElemOffset = 0; 134 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 135 auto *SrcPtr = Src + ElemOffset; 136 auto *DstPtr = Dst + ElemOffset; 137 138 auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr); 139 auto *SrcElemLoc = reinterpret_cast<std::byte *>(SrcDesc + 1); 140 auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr); 141 auto *DstElemLoc = reinterpret_cast<std::byte *>(DstDesc + 1); 142 143 *DstDesc = *SrcDesc; 144 if (auto Fn = D->ElemDesc->MoveFn) 145 Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc); 146 } 147 } 148 149 static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 150 bool IsActive, bool IsUnionField, bool InUnion, 151 const Descriptor *D, unsigned FieldOffset) { 152 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1; 153 Desc->Offset = FieldOffset; 154 Desc->Desc = D; 155 Desc->IsInitialized = D->IsArray; 156 Desc->IsBase = false; 157 Desc->IsActive = IsActive && !IsUnionField; 158 Desc->InUnion = InUnion; 159 Desc->IsConst = IsConst || D->IsConst; 160 Desc->IsFieldMutable = IsMutable || D->IsMutable; 161 162 if (auto Fn = D->CtorFn) 163 Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable, 164 Desc->IsActive, InUnion || D->isUnion(), D); 165 } 166 167 static void initBase(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 168 bool IsActive, bool InUnion, const Descriptor *D, 169 unsigned FieldOffset, bool IsVirtualBase) { 170 assert(D); 171 assert(D->ElemRecord); 172 assert(!D->ElemRecord->isUnion()); // Unions cannot be base classes. 173 174 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + FieldOffset) - 1; 175 Desc->Offset = FieldOffset; 176 Desc->Desc = D; 177 Desc->IsInitialized = D->IsArray; 178 Desc->IsBase = true; 179 Desc->IsVirtualBase = IsVirtualBase; 180 Desc->IsActive = IsActive && !InUnion; 181 Desc->IsConst = IsConst || D->IsConst; 182 Desc->IsFieldMutable = IsMutable || D->IsMutable; 183 Desc->InUnion = InUnion; 184 185 for (const auto &V : D->ElemRecord->bases()) 186 initBase(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion, 187 V.Desc, V.Offset, false); 188 for (const auto &F : D->ElemRecord->fields()) 189 initField(B, Ptr + FieldOffset, IsConst, IsMutable, IsActive, InUnion, 190 InUnion, F.Desc, F.Offset); 191 } 192 193 static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 194 bool IsActive, bool InUnion, const Descriptor *D) { 195 for (const auto &V : D->ElemRecord->bases()) 196 initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset, 197 false); 198 for (const auto &F : D->ElemRecord->fields()) { 199 bool IsUnionField = D->isUnion(); 200 initField(B, Ptr, IsConst, IsMutable, IsActive, IsUnionField, 201 InUnion || IsUnionField, F.Desc, F.Offset); 202 } 203 for (const auto &V : D->ElemRecord->virtual_bases()) 204 initBase(B, Ptr, IsConst, IsMutable, IsActive, InUnion, V.Desc, V.Offset, 205 true); 206 } 207 208 static void destroyField(Block *B, std::byte *Ptr, const Descriptor *D, 209 unsigned FieldOffset) { 210 if (auto Fn = D->DtorFn) 211 Fn(B, Ptr + FieldOffset, D); 212 } 213 214 static void destroyBase(Block *B, std::byte *Ptr, const Descriptor *D, 215 unsigned FieldOffset) { 216 assert(D); 217 assert(D->ElemRecord); 218 219 for (const auto &V : D->ElemRecord->bases()) 220 destroyBase(B, Ptr + FieldOffset, V.Desc, V.Offset); 221 for (const auto &F : D->ElemRecord->fields()) 222 destroyField(B, Ptr + FieldOffset, F.Desc, F.Offset); 223 } 224 225 static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) { 226 for (const auto &F : D->ElemRecord->bases()) 227 destroyBase(B, Ptr, F.Desc, F.Offset); 228 for (const auto &F : D->ElemRecord->fields()) 229 destroyField(B, Ptr, F.Desc, F.Offset); 230 for (const auto &F : D->ElemRecord->virtual_bases()) 231 destroyBase(B, Ptr, F.Desc, F.Offset); 232 } 233 234 static void moveRecord(Block *B, std::byte *Src, std::byte *Dst, 235 const Descriptor *D) { 236 assert(D); 237 assert(D->ElemRecord); 238 239 // FIXME: There might be cases where we need to move over the (v)bases as 240 // well. 241 for (const auto &F : D->ElemRecord->fields()) { 242 auto FieldOffset = F.Offset; 243 const auto *SrcDesc = 244 reinterpret_cast<const InlineDescriptor *>(Src + FieldOffset) - 1; 245 auto *DestDesc = 246 reinterpret_cast<InlineDescriptor *>(Dst + FieldOffset) - 1; 247 std::memcpy(DestDesc, SrcDesc, sizeof(InlineDescriptor)); 248 249 if (auto Fn = F.Desc->MoveFn) 250 Fn(B, Src + FieldOffset, Dst + FieldOffset, F.Desc); 251 } 252 } 253 254 static BlockCtorFn getCtorPrim(PrimType Type) { 255 // Floating types are special. They are primitives, but need their 256 // constructor called. 257 if (Type == PT_Float) 258 return ctorTy<PrimConv<PT_Float>::T>; 259 if (Type == PT_IntAP) 260 return ctorTy<PrimConv<PT_IntAP>::T>; 261 if (Type == PT_IntAPS) 262 return ctorTy<PrimConv<PT_IntAPS>::T>; 263 if (Type == PT_MemberPtr) 264 return ctorTy<PrimConv<PT_MemberPtr>::T>; 265 266 COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); 267 } 268 269 static BlockDtorFn getDtorPrim(PrimType Type) { 270 // Floating types are special. They are primitives, but need their 271 // destructor called, since they might allocate memory. 272 if (Type == PT_Float) 273 return dtorTy<PrimConv<PT_Float>::T>; 274 if (Type == PT_IntAP) 275 return dtorTy<PrimConv<PT_IntAP>::T>; 276 if (Type == PT_IntAPS) 277 return dtorTy<PrimConv<PT_IntAPS>::T>; 278 if (Type == PT_MemberPtr) 279 return dtorTy<PrimConv<PT_MemberPtr>::T>; 280 281 COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); 282 } 283 284 static BlockMoveFn getMovePrim(PrimType Type) { 285 if (Type == PT_Float) 286 return moveTy<PrimConv<PT_Float>::T>; 287 if (Type == PT_IntAP) 288 return moveTy<PrimConv<PT_IntAP>::T>; 289 if (Type == PT_IntAPS) 290 return moveTy<PrimConv<PT_IntAPS>::T>; 291 if (Type == PT_MemberPtr) 292 return moveTy<PrimConv<PT_MemberPtr>::T>; 293 COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr); 294 } 295 296 static BlockCtorFn getCtorArrayPrim(PrimType Type) { 297 TYPE_SWITCH(Type, return ctorArrayTy<T>); 298 llvm_unreachable("unknown Expr"); 299 } 300 301 static BlockDtorFn getDtorArrayPrim(PrimType Type) { 302 TYPE_SWITCH(Type, return dtorArrayTy<T>); 303 llvm_unreachable("unknown Expr"); 304 } 305 306 static BlockMoveFn getMoveArrayPrim(PrimType Type) { 307 TYPE_SWITCH(Type, return moveArrayTy<T>); 308 llvm_unreachable("unknown Expr"); 309 } 310 311 /// Primitives. 312 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 313 bool IsConst, bool IsTemporary, bool IsMutable) 314 : Source(D), ElemSize(primSize(Type)), Size(ElemSize), 315 MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type), 316 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), 317 CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), 318 MoveFn(getMovePrim(Type)) { 319 assert(AllocSize >= Size); 320 assert(Source && "Missing source"); 321 } 322 323 /// Primitive arrays. 324 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 325 size_t NumElems, bool IsConst, bool IsTemporary, 326 bool IsMutable) 327 : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), 328 MDSize(MD.value_or(0)), 329 AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type), 330 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), 331 IsArray(true), CtorFn(getCtorArrayPrim(Type)), 332 DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { 333 assert(Source && "Missing source"); 334 assert(NumElems <= (MaxArrayElemBytes / ElemSize)); 335 } 336 337 /// Primitive unknown-size arrays. 338 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 339 bool IsTemporary, UnknownSize) 340 : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), 341 MDSize(MD.value_or(0)), 342 AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true), 343 IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 344 CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), 345 MoveFn(getMoveArrayPrim(Type)) { 346 assert(Source && "Missing source"); 347 } 348 349 /// Arrays of composite elements. 350 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, 351 unsigned NumElems, bool IsConst, bool IsTemporary, 352 bool IsMutable) 353 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 354 Size(ElemSize * NumElems), MDSize(MD.value_or(0)), 355 AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), 356 ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), 357 IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), 358 DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 359 assert(Source && "Missing source"); 360 } 361 362 /// Unknown-size arrays of composite elements. 363 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, 364 bool IsTemporary, UnknownSize) 365 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 366 Size(UnknownSizeMark), MDSize(MD.value_or(0)), 367 AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true), 368 IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 369 CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 370 assert(Source && "Missing source"); 371 } 372 373 /// Composite records. 374 Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, 375 bool IsConst, bool IsTemporary, bool IsMutable) 376 : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), 377 Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), 378 ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), 379 IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord), 380 MoveFn(moveRecord) { 381 assert(Source && "Missing source"); 382 } 383 384 /// Dummy. 385 Descriptor::Descriptor(const DeclTy &D) 386 : Source(D), ElemSize(1), Size(1), MDSize(0), AllocSize(MDSize), 387 ElemRecord(nullptr), IsConst(true), IsMutable(false), IsTemporary(false), 388 IsDummy(true) { 389 assert(Source && "Missing source"); 390 } 391 392 QualType Descriptor::getType() const { 393 if (const auto *D = asValueDecl()) 394 return D->getType(); 395 if (const auto *T = dyn_cast_if_present<TypeDecl>(asDecl())) 396 return QualType(T->getTypeForDecl(), 0); 397 398 // The Source sometimes has a different type than the once 399 // we really save. Try to consult the Record first. 400 if (isRecord()) 401 return QualType(ElemRecord->getDecl()->getTypeForDecl(), 0); 402 if (const auto *E = asExpr()) 403 return E->getType(); 404 llvm_unreachable("Invalid descriptor type"); 405 } 406 407 QualType Descriptor::getElemQualType() const { 408 assert(isArray()); 409 QualType T = getType(); 410 if (const auto *AT = T->getAsArrayTypeUnsafe()) 411 return AT->getElementType(); 412 if (const auto *CT = T->getAs<ComplexType>()) 413 return CT->getElementType(); 414 if (const auto *CT = T->getAs<VectorType>()) 415 return CT->getElementType(); 416 llvm_unreachable("Array that's not an array/complex/vector type?"); 417 } 418 419 SourceLocation Descriptor::getLocation() const { 420 if (auto *D = Source.dyn_cast<const Decl *>()) 421 return D->getLocation(); 422 if (auto *E = Source.dyn_cast<const Expr *>()) 423 return E->getExprLoc(); 424 llvm_unreachable("Invalid descriptor type"); 425 } 426 427 SourceInfo Descriptor::getLoc() const { 428 if (const auto *D = Source.dyn_cast<const Decl *>()) 429 return SourceInfo(D); 430 if (const auto *E = Source.dyn_cast<const Expr *>()) 431 return SourceInfo(E); 432 llvm_unreachable("Invalid descriptor type"); 433 } 434 435 bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); } 436 437 InitMap::InitMap(unsigned N) 438 : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) { 439 std::fill_n(data(), numFields(N), 0); 440 } 441 442 bool InitMap::initializeElement(unsigned I) { 443 unsigned Bucket = I / PER_FIELD; 444 T Mask = T(1) << (I % PER_FIELD); 445 if (!(data()[Bucket] & Mask)) { 446 data()[Bucket] |= Mask; 447 UninitFields -= 1; 448 } 449 return UninitFields == 0; 450 } 451 452 bool InitMap::isElementInitialized(unsigned I) const { 453 unsigned Bucket = I / PER_FIELD; 454 return data()[Bucket] & (T(1) << (I % PER_FIELD)); 455 } 456