1 //===--- Descriptor.cpp - Types for the constexpr VM ------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Descriptor.h"
12 #include "FunctionPointer.h"
13 #include "IntegralAP.h"
18 using namespace clang
;
19 using namespace clang::interp
;
22 static void ctorTy(Block
*, std::byte
*Ptr
, bool, bool, bool,
28 static void dtorTy(Block
*, std::byte
*Ptr
, const Descriptor
*) {
29 reinterpret_cast<T
*>(Ptr
)->~T();
33 static void moveTy(Block
*, const std::byte
*Src
, std::byte
*Dst
,
35 const auto *SrcPtr
= reinterpret_cast<const T
*>(Src
);
36 auto *DstPtr
= reinterpret_cast<T
*>(Dst
);
37 new (DstPtr
) T(std::move(*SrcPtr
));
41 static void ctorArrayTy(Block
*, std::byte
*Ptr
, bool, bool, bool,
42 const Descriptor
*D
) {
43 new (Ptr
) InitMapPtr(std::nullopt
);
45 Ptr
+= sizeof(InitMapPtr
);
46 for (unsigned I
= 0, NE
= D
->getNumElems(); I
< NE
; ++I
) {
47 new (&reinterpret_cast<T
*>(Ptr
)[I
]) T();
52 static void dtorArrayTy(Block
*, std::byte
*Ptr
, const Descriptor
*D
) {
53 InitMapPtr
&IMP
= *reinterpret_cast<InitMapPtr
*>(Ptr
);
57 Ptr
+= sizeof(InitMapPtr
);
58 for (unsigned I
= 0, NE
= D
->getNumElems(); I
< NE
; ++I
) {
59 reinterpret_cast<T
*>(Ptr
)[I
].~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
));
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
);
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
;
86 Desc
->Offset
= ElemOffset
+ sizeof(InlineDescriptor
);
88 Desc
->IsInitialized
= true;
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
,
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
);
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
);
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
);
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
;
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);
131 if (auto Fn
= D
->ElemDesc
->MoveFn
)
132 Fn(B
, SrcElemLoc
, DstElemLoc
, D
->ElemDesc
);
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
;
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
,
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);
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
);
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
);
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
;
179 if (auto Fn
= FieldDesc
->MoveFn
)
180 Fn(B
, Src
+ FieldOff
, Dst
+ FieldOff
, FieldDesc
);
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
>;
194 COMPOSITE_TYPE_SWITCH(Type
, return ctorTy
<T
>, return nullptr);
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
>;
207 COMPOSITE_TYPE_SWITCH(Type
, return dtorTy
<T
>, return nullptr);
210 static BlockMoveFn
getMovePrim(PrimType Type
) {
211 COMPOSITE_TYPE_SWITCH(Type
, return moveTy
<T
>, return nullptr);
214 static BlockCtorFn
getCtorArrayPrim(PrimType Type
) {
215 TYPE_SWITCH(Type
, return ctorArrayTy
<T
>);
216 llvm_unreachable("unknown Expr");
219 static BlockDtorFn
getDtorArrayPrim(PrimType Type
) {
220 TYPE_SWITCH(Type
, return dtorArrayTy
<T
>);
221 llvm_unreachable("unknown Expr");
224 static BlockMoveFn
getMoveArrayPrim(PrimType Type
) {
225 TYPE_SWITCH(Type
, return moveArrayTy
<T
>);
226 llvm_unreachable("unknown Expr");
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");
240 /// Primitive arrays.
241 Descriptor::Descriptor(const DeclTy
&D
, PrimType Type
, MetadataSize MD
,
242 size_t NumElems
, bool IsConst
, bool IsTemporary
,
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");
253 /// Primitive unknown-size arrays.
254 Descriptor::Descriptor(const DeclTy
&D
, PrimType Type
, bool IsTemporary
,
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");
264 /// Arrays of composite elements.
265 Descriptor::Descriptor(const DeclTy
&D
, const Descriptor
*Elem
, MetadataSize MD
,
266 unsigned NumElems
, bool IsConst
, bool IsTemporary
,
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");
277 /// Unknown-size arrays of composite elements.
278 Descriptor::Descriptor(const DeclTy
&D
, Descriptor
*Elem
, bool IsTemporary
,
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");
288 /// Composite records.
289 Descriptor::Descriptor(const DeclTy
&D
, 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
),
296 assert(Source
&& "Missing source");
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");
306 QualType
Descriptor::getType() const {
307 if (auto *E
= asExpr())
309 if (auto *D
= asValueDecl())
311 if (auto *T
= dyn_cast
<TypeDecl
>(asDecl()))
312 return QualType(T
->getTypeForDecl(), 0);
313 llvm_unreachable("Invalid descriptor type");
316 QualType
Descriptor::getElemQualType() const {
318 const auto *AT
= cast
<ArrayType
>(getType());
319 return AT
->getElementType();
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");
330 InitMap::InitMap(unsigned N
)
331 : UninitFields(N
), Data(std::make_unique
<T
[]>(numFields(N
))) {
332 std::fill_n(data(), numFields(N
), 0);
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
;
342 return UninitFields
== 0;
345 bool InitMap::isElementInitialized(unsigned I
) const {
346 unsigned Bucket
= I
/ PER_FIELD
;
347 return data()[Bucket
] & (T(1) << (I
% PER_FIELD
));