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 "Pointer.h" 11 #include "PrimType.h" 12 #include "Record.h" 13 14 using namespace clang; 15 using namespace clang::interp; 16 17 template <typename T> 18 static void ctorTy(Block *, char *Ptr, bool, bool, bool, Descriptor *) { 19 new (Ptr) T(); 20 } 21 22 template <typename T> static void dtorTy(Block *, char *Ptr, Descriptor *) { 23 reinterpret_cast<T *>(Ptr)->~T(); 24 } 25 26 template <typename T> 27 static void moveTy(Block *, char *Src, char *Dst, Descriptor *) { 28 auto *SrcPtr = reinterpret_cast<T *>(Src); 29 auto *DstPtr = reinterpret_cast<T *>(Dst); 30 new (DstPtr) T(std::move(*SrcPtr)); 31 } 32 33 template <typename T> 34 static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) { 35 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 36 new (&reinterpret_cast<T *>(Ptr)[I]) T(); 37 } 38 } 39 40 template <typename T> 41 static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) { 42 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 43 reinterpret_cast<T *>(Ptr)[I].~T(); 44 } 45 } 46 47 template <typename T> 48 static void moveArrayTy(Block *, char *Src, char *Dst, Descriptor *D) { 49 for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { 50 auto *SrcPtr = &reinterpret_cast<T *>(Src)[I]; 51 auto *DstPtr = &reinterpret_cast<T *>(Dst)[I]; 52 new (DstPtr) T(std::move(*SrcPtr)); 53 } 54 } 55 56 static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable, 57 bool IsActive, Descriptor *D) { 58 const unsigned NumElems = D->getNumElems(); 59 const unsigned ElemSize = 60 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 61 62 unsigned ElemOffset = 0; 63 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 64 auto *ElemPtr = Ptr + ElemOffset; 65 auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); 66 auto *ElemLoc = reinterpret_cast<char *>(Desc + 1); 67 auto *SD = D->ElemDesc; 68 69 Desc->Offset = ElemOffset + sizeof(InlineDescriptor); 70 Desc->Desc = SD; 71 Desc->IsInitialized = true; 72 Desc->IsBase = false; 73 Desc->IsActive = IsActive; 74 Desc->IsConst = IsConst || D->IsConst; 75 Desc->IsMutable = IsMutable || D->IsMutable; 76 if (auto Fn = D->ElemDesc->CtorFn) 77 Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc); 78 } 79 } 80 81 static void dtorArrayDesc(Block *B, char *Ptr, Descriptor *D) { 82 const unsigned NumElems = D->getNumElems(); 83 const unsigned ElemSize = 84 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 85 86 unsigned ElemOffset = 0; 87 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 88 auto *ElemPtr = Ptr + ElemOffset; 89 auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); 90 auto *ElemLoc = reinterpret_cast<char *>(Desc + 1); 91 if (auto Fn = D->ElemDesc->DtorFn) 92 Fn(B, ElemLoc, D->ElemDesc); 93 } 94 } 95 96 static void moveArrayDesc(Block *B, char *Src, char *Dst, Descriptor *D) { 97 const unsigned NumElems = D->getNumElems(); 98 const unsigned ElemSize = 99 D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); 100 101 unsigned ElemOffset = 0; 102 for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { 103 auto *SrcPtr = Src + ElemOffset; 104 auto *DstPtr = Dst + ElemOffset; 105 106 auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr); 107 auto *SrcElemLoc = reinterpret_cast<char *>(SrcDesc + 1); 108 auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr); 109 auto *DstElemLoc = reinterpret_cast<char *>(DstDesc + 1); 110 111 *DstDesc = *SrcDesc; 112 if (auto Fn = D->ElemDesc->MoveFn) 113 Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc); 114 } 115 } 116 117 static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable, 118 bool IsActive, Descriptor *D) { 119 const bool IsUnion = D->ElemRecord->isUnion(); 120 auto CtorSub = [=](unsigned SubOff, Descriptor *F, bool IsBase) { 121 auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1; 122 Desc->Offset = SubOff; 123 Desc->Desc = F; 124 Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase; 125 Desc->IsBase = IsBase; 126 Desc->IsActive = IsActive && !IsUnion; 127 Desc->IsConst = IsConst || F->IsConst; 128 Desc->IsMutable = IsMutable || F->IsMutable; 129 if (auto Fn = F->CtorFn) 130 Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F); 131 }; 132 for (const auto &B : D->ElemRecord->bases()) 133 CtorSub(B.Offset, B.Desc, /*isBase=*/true); 134 for (const auto &F : D->ElemRecord->fields()) 135 CtorSub(F.Offset, F.Desc, /*isBase=*/false); 136 for (const auto &V : D->ElemRecord->virtual_bases()) 137 CtorSub(V.Offset, V.Desc, /*isBase=*/true); 138 } 139 140 static void dtorRecord(Block *B, char *Ptr, Descriptor *D) { 141 auto DtorSub = [=](unsigned SubOff, Descriptor *F) { 142 if (auto Fn = F->DtorFn) 143 Fn(B, Ptr + SubOff, F); 144 }; 145 for (const auto &F : D->ElemRecord->bases()) 146 DtorSub(F.Offset, F.Desc); 147 for (const auto &F : D->ElemRecord->fields()) 148 DtorSub(F.Offset, F.Desc); 149 for (const auto &F : D->ElemRecord->virtual_bases()) 150 DtorSub(F.Offset, F.Desc); 151 } 152 153 static void moveRecord(Block *B, char *Src, char *Dst, Descriptor *D) { 154 for (const auto &F : D->ElemRecord->fields()) { 155 auto FieldOff = F.Offset; 156 auto FieldDesc = F.Desc; 157 158 *(reinterpret_cast<Descriptor **>(Dst + FieldOff) - 1) = FieldDesc; 159 if (auto Fn = FieldDesc->MoveFn) 160 Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc); 161 } 162 } 163 164 static BlockCtorFn getCtorPrim(PrimType Type) { 165 COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); 166 } 167 168 static BlockDtorFn getDtorPrim(PrimType Type) { 169 COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); 170 } 171 172 static BlockMoveFn getMovePrim(PrimType Type) { 173 COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr); 174 } 175 176 static BlockCtorFn getCtorArrayPrim(PrimType Type) { 177 COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr); 178 } 179 180 static BlockDtorFn getDtorArrayPrim(PrimType Type) { 181 COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr); 182 } 183 184 static BlockMoveFn getMoveArrayPrim(PrimType Type) { 185 COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr); 186 } 187 188 Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst, 189 bool IsTemporary, bool IsMutable) 190 : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size), 191 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), 192 CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), 193 MoveFn(getMovePrim(Type)) { 194 assert(Source && "Missing source"); 195 } 196 197 Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, 198 bool IsConst, bool IsTemporary, bool IsMutable) 199 : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), 200 AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst), 201 IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), 202 CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), 203 MoveFn(getMoveArrayPrim(Type)) { 204 assert(Source && "Missing source"); 205 } 206 207 Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, 208 UnknownSize) 209 : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), 210 AllocSize(alignof(void *)), IsConst(true), IsMutable(false), 211 IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), 212 DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { 213 assert(Source && "Missing source"); 214 } 215 216 Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, 217 bool IsConst, bool IsTemporary, bool IsMutable) 218 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 219 Size(ElemSize * NumElems), 220 AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem), 221 IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), 222 IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), 223 MoveFn(moveArrayDesc) { 224 assert(Source && "Missing source"); 225 } 226 227 Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, 228 UnknownSize) 229 : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), 230 Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem), 231 IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), 232 CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { 233 assert(Source && "Missing source"); 234 } 235 236 Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst, 237 bool IsTemporary, bool IsMutable) 238 : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), 239 Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst), 240 IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord), 241 DtorFn(dtorRecord), MoveFn(moveRecord) { 242 assert(Source && "Missing source"); 243 } 244 245 QualType Descriptor::getType() const { 246 if (auto *E = asExpr()) 247 return E->getType(); 248 if (auto *D = asValueDecl()) 249 return D->getType(); 250 llvm_unreachable("Invalid descriptor type"); 251 } 252 253 SourceLocation Descriptor::getLocation() const { 254 if (auto *D = Source.dyn_cast<const Decl *>()) 255 return D->getLocation(); 256 if (auto *E = Source.dyn_cast<const Expr *>()) 257 return E->getExprLoc(); 258 llvm_unreachable("Invalid descriptor type"); 259 } 260 261 InitMap::InitMap(unsigned N) : UninitFields(N) { 262 for (unsigned I = 0; I < N / PER_FIELD; ++I) { 263 data()[I] = 0; 264 } 265 } 266 267 InitMap::T *InitMap::data() { 268 auto *Start = reinterpret_cast<char *>(this) + align(sizeof(InitMap)); 269 return reinterpret_cast<T *>(Start); 270 } 271 272 bool InitMap::initialize(unsigned I) { 273 unsigned Bucket = I / PER_FIELD; 274 unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); 275 if (!(data()[Bucket] & Mask)) { 276 data()[Bucket] |= Mask; 277 UninitFields -= 1; 278 } 279 return UninitFields == 0; 280 } 281 282 bool InitMap::isInitialized(unsigned I) { 283 unsigned Bucket = I / PER_FIELD; 284 unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); 285 return data()[Bucket] & Mask; 286 } 287 288 InitMap *InitMap::allocate(unsigned N) { 289 const size_t NumFields = ((N + PER_FIELD - 1) / PER_FIELD); 290 const size_t Size = align(sizeof(InitMap)) + NumFields * PER_FIELD; 291 return new (malloc(Size)) InitMap(N); 292 } 293