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