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