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 "Pointer.h" 15 #include "PrimType.h" 16 #include "Record.h" 17 18 using namespace clang; 19 using namespace clang::interp; 20 21 template <typename T> 22 static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, 23 const Descriptor *) { 24 new (Ptr) T(); 25 } 26 27 template <typename T> 28 static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) { 29 reinterpret_cast<T *>(Ptr)->~T(); 30 } 31 32 template <typename T> 33 static void moveTy(Block *, const std::byte *Src, std::byte *Dst, 34 const Descriptor *) { 35 const auto *SrcPtr = reinterpret_cast<const T *>(Src); 36 auto *DstPtr = reinterpret_cast<T *>(Dst); 37 new (DstPtr) T(std::move(*SrcPtr)); 38 } 39 40 template <typename T> 41 static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, 42 const Descriptor *D) { 43 new (Ptr) InitMapPtr(std::nullopt); 44 45 Ptr += sizeof(InitMapPtr); 46 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 47 new (&reinterpret_cast<T *>(Ptr)[I]) T(); 48 } 49 } 50 51 template <typename T> 52 static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) { 53 InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr); 54 55 if (IMP) 56 IMP = std::nullopt; 57 Ptr += sizeof(InitMapPtr); 58 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 59 reinterpret_cast<T *>(Ptr)[I].~T(); 60 } 61 } 62 63 template <typename T> 64 static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst, 65 const Descriptor *D) { 66 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 67 const auto *SrcPtr = &reinterpret_cast<const T *>(Src)[I]; 68 auto *DstPtr = &reinterpret_cast<T *>(Dst)[I]; 69 new (DstPtr) T(std::move(*SrcPtr)); 70 } 71 } 72 73 static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst, 74 bool IsMutable, bool IsActive, const Descriptor *D) { 75 const unsigned NumElems = D->getNumElems(); 76 const unsigned ElemSize = 77 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 78 79 unsigned ElemOffset = 0; 80 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 81 auto *ElemPtr = Ptr + ElemOffset; 82 auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); 83 auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1); 84 auto *SD = D->ElemDesc; 85 86 Desc->Offset = ElemOffset + sizeof(InlineDescriptor); 87 Desc->Desc = SD; 88 Desc->IsInitialized = true; 89 Desc->IsBase = false; 90 Desc->IsActive = IsActive; 91 Desc->IsConst = IsConst || D->IsConst; 92 Desc->IsFieldMutable = IsMutable || D->IsMutable; 93 if (auto Fn = D->ElemDesc->CtorFn) 94 Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, 95 D->ElemDesc); 96 } 97 } 98 99 static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) { 100 const unsigned NumElems = D->getNumElems(); 101 const unsigned ElemSize = 102 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 103 104 unsigned ElemOffset = 0; 105 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 106 auto *ElemPtr = Ptr + ElemOffset; 107 auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); 108 auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1); 109 if (auto Fn = D->ElemDesc->DtorFn) 110 Fn(B, ElemLoc, D->ElemDesc); 111 } 112 } 113 114 static void moveArrayDesc(Block *B, const std::byte *Src, std::byte *Dst, 115 const Descriptor *D) { 116 const unsigned NumElems = D->getNumElems(); 117 const unsigned ElemSize = 118 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 119 120 unsigned ElemOffset = 0; 121 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 122 const auto *SrcPtr = Src + ElemOffset; 123 auto *DstPtr = Dst + ElemOffset; 124 125 const auto *SrcDesc = reinterpret_cast<const InlineDescriptor *>(SrcPtr); 126 const auto *SrcElemLoc = reinterpret_cast<const std::byte *>(SrcDesc + 1); 127 auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr); 128 auto *DstElemLoc = reinterpret_cast<std::byte *>(DstDesc + 1); 129 130 *DstDesc = *SrcDesc; 131 if (auto Fn = D->ElemDesc->MoveFn) 132 Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc); 133 } 134 } 135 136 static void ctorRecord(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, 137 bool IsActive, const Descriptor *D) { 138 const bool IsUnion = D->ElemRecord->isUnion(); 139 auto CtorSub = [=](unsigned SubOff, Descriptor *F, bool IsBase) { 140 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1; 141 Desc->Offset = SubOff; 142 Desc->Desc = F; 143 Desc->IsInitialized = F->IsArray && !IsBase; 144 Desc->IsBase = IsBase; 145 Desc->IsActive = IsActive && !IsUnion; 146 Desc->IsConst = IsConst || F->IsConst; 147 Desc->IsFieldMutable = IsMutable || F->IsMutable; 148 if (auto Fn = F->CtorFn) 149 Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsFieldMutable, Desc->IsActive, 150 F); 151 }; 152 for (const auto &B : D->ElemRecord->bases()) 153 CtorSub(B.Offset, B.Desc, /*isBase=*/true); 154 for (const auto &F : D->ElemRecord->fields()) 155 CtorSub(F.Offset, F.Desc, /*isBase=*/false); 156 for (const auto &V : D->ElemRecord->virtual_bases()) 157 CtorSub(V.Offset, V.Desc, /*isBase=*/true); 158 } 159 160 static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) { 161 auto DtorSub = [=](unsigned SubOff, Descriptor *F) { 162 if (auto Fn = F->DtorFn) 163 Fn(B, Ptr + SubOff, F); 164 }; 165 for (const auto &F : D->ElemRecord->bases()) 166 DtorSub(F.Offset, F.Desc); 167 for (const auto &F : D->ElemRecord->fields()) 168 DtorSub(F.Offset, F.Desc); 169 for (const auto &F : D->ElemRecord->virtual_bases()) 170 DtorSub(F.Offset, F.Desc); 171 } 172 173 static void moveRecord(Block *B, const std::byte *Src, std::byte *Dst, 174 const Descriptor *D) { 175 for (const auto &F : D->ElemRecord->fields()) { 176 auto FieldOff = F.Offset; 177 auto *FieldDesc = F.Desc; 178 179 if (auto Fn = FieldDesc->MoveFn) 180 Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc); 181 } 182 } 183 184 static BlockCtorFn getCtorPrim(PrimType Type) { 185 // Floating types are special. They are primitives, but need their 186 // constructor called. 187 if (Type == PT_Float) 188 return ctorTy<PrimConv<PT_Float>::T>; 189 if (Type == PT_IntAP) 190 return ctorTy<PrimConv<PT_IntAP>::T>; 191 if (Type == PT_IntAPS) 192 return ctorTy<PrimConv<PT_IntAPS>::T>; 193 194 COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); 195 } 196 197 static BlockDtorFn getDtorPrim(PrimType Type) { 198 // Floating types are special. They are primitives, but need their 199 // destructor called, since they might allocate memory. 200 if (Type == PT_Float) 201 return dtorTy<PrimConv<PT_Float>::T>; 202 if (Type == PT_IntAP) 203 return dtorTy<PrimConv<PT_IntAP>::T>; 204 if (Type == PT_IntAPS) 205 return dtorTy<PrimConv<PT_IntAPS>::T>; 206 207 COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); 208 } 209 210 static BlockMoveFn getMovePrim(PrimType Type) { 211 COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr); 212 } 213 214 static BlockCtorFn getCtorArrayPrim(PrimType Type) { 215 TYPE_SWITCH(Type, return ctorArrayTy<T>); 216 llvm_unreachable("unknown Expr"); 217 } 218 219 static BlockDtorFn getDtorArrayPrim(PrimType Type) { 220 TYPE_SWITCH(Type, return dtorArrayTy<T>); 221 llvm_unreachable("unknown Expr"); 222 } 223 224 static BlockMoveFn getMoveArrayPrim(PrimType Type) { 225 TYPE_SWITCH(Type, return moveArrayTy<T>); 226 llvm_unreachable("unknown Expr"); 227 } 228 229 /// Primitives. 230 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 231 bool IsConst, bool IsTemporary, bool IsMutable) 232 : Source(D), ElemSize(primSize(Type)), Size(ElemSize), 233 MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst), 234 IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)), 235 DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) { 236 assert(AllocSize >= Size); 237 assert(Source && "Missing source"); 238 } 239 240 /// Primitive arrays. 241 Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, 242 size_t NumElems, bool IsConst, bool IsTemporary, 243 bool IsMutable) 244 : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), 245 MDSize(MD.value_or(0)), 246 AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst), 247 IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), 248 CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), 249 MoveFn(getMoveArrayPrim(Type)) { 250 assert(Source && "Missing source"); 251 } 252 253 /// Primitive unknown-size arrays. 254 Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, 255 UnknownSize) 256 : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0), 257 AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true), 258 IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 259 CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), 260 MoveFn(getMoveArrayPrim(Type)) { 261 assert(Source && "Missing source"); 262 } 263 264 /// Arrays of composite elements. 265 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, 266 unsigned NumElems, bool IsConst, bool IsTemporary, 267 bool IsMutable) 268 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 269 Size(ElemSize * NumElems), MDSize(MD.value_or(0)), 270 AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), 271 ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), 272 IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), 273 DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 274 assert(Source && "Missing source"); 275 } 276 277 /// Unknown-size arrays of composite elements. 278 Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, 279 bool IsTemporary, UnknownSize) 280 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 281 Size(UnknownSizeMark), MDSize(0), 282 AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem), 283 IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 284 CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 285 assert(Source && "Missing source"); 286 } 287 288 /// Composite records. 289 Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, 290 bool IsConst, bool IsTemporary, bool IsMutable) 291 : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), 292 Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), 293 ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), 294 IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord), 295 MoveFn(moveRecord) { 296 assert(Source && "Missing source"); 297 } 298 299 Descriptor::Descriptor(const DeclTy &D, MetadataSize MD) 300 : Source(D), ElemSize(1), Size(ElemSize), MDSize(MD.value_or(0)), 301 AllocSize(Size + MDSize), ElemRecord(nullptr), IsConst(true), 302 IsMutable(false), IsTemporary(false), IsDummy(true) { 303 assert(Source && "Missing source"); 304 } 305 306 QualType Descriptor::getType() const { 307 if (auto *E = asExpr()) 308 return E->getType(); 309 if (auto *D = asValueDecl()) 310 return D->getType(); 311 if (auto *T = dyn_cast<TypeDecl>(asDecl())) 312 return QualType(T->getTypeForDecl(), 0); 313 llvm_unreachable("Invalid descriptor type"); 314 } 315 316 QualType Descriptor::getElemQualType() const { 317 assert(isArray()); 318 const auto *AT = cast<ArrayType>(getType()); 319 return AT->getElementType(); 320 } 321 322 SourceLocation Descriptor::getLocation() const { 323 if (auto *D = Source.dyn_cast<const Decl *>()) 324 return D->getLocation(); 325 if (auto *E = Source.dyn_cast<const Expr *>()) 326 return E->getExprLoc(); 327 llvm_unreachable("Invalid descriptor type"); 328 } 329 330 InitMap::InitMap(unsigned N) 331 : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) { 332 std::fill_n(data(), numFields(N), 0); 333 } 334 335 bool InitMap::initializeElement(unsigned I) { 336 unsigned Bucket = I / PER_FIELD; 337 T Mask = T(1) << (I % PER_FIELD); 338 if (!(data()[Bucket] & Mask)) { 339 data()[Bucket] |= Mask; 340 UninitFields -= 1; 341 } 342 return UninitFields == 0; 343 } 344 345 bool InitMap::isElementInitialized(unsigned I) const { 346 unsigned Bucket = I / PER_FIELD; 347 return data()[Bucket] & (T(1) << (I % PER_FIELD)); 348 } 349