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
14using namespace clang;
15using namespace clang::interp;
16
17template <typename T>
18static void ctorTy(Block *, char *Ptr, bool, bool, bool, Descriptor *) {
19  new (Ptr) T();
20}
21
22template <typename T> static void dtorTy(Block *, char *Ptr, Descriptor *) {
23  reinterpret_cast<T *>(Ptr)->~T();
24}
25
26template <typename T>
27static 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
33template <typename T>
34static 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
40template <typename T>
41static 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
47template <typename T>
48static 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
56static 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
81static 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
96static 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
117static 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
140static 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
153static 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
164static BlockCtorFn getCtorPrim(PrimType Type) {
165  COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
166}
167
168static BlockDtorFn getDtorPrim(PrimType Type) {
169  COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
170}
171
172static BlockMoveFn getMovePrim(PrimType Type) {
173  COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr);
174}
175
176static BlockCtorFn getCtorArrayPrim(PrimType Type) {
177  COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr);
178}
179
180static BlockDtorFn getDtorArrayPrim(PrimType Type) {
181  COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr);
182}
183
184static BlockMoveFn getMoveArrayPrim(PrimType Type) {
185  COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr);
186}
187
188Descriptor::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
197Descriptor::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
207Descriptor::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
216Descriptor::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
227Descriptor::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
236Descriptor::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
245QualType 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
253SourceLocation 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
261InitMap::InitMap(unsigned N) : UninitFields(N) {
262  for (unsigned I = 0; I < N / PER_FIELD; ++I) {
263    data()[I] = 0;
264  }
265}
266
267InitMap::T *InitMap::data() {
268  auto *Start = reinterpret_cast<char *>(this) + align(sizeof(InitMap));
269  return reinterpret_cast<T *>(Start);
270}
271
272bool 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
282bool 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
288InitMap *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