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