xref: /openbsd-src/gnu/llvm/clang/lib/AST/Interp/Descriptor.h (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- Descriptor.h - Types for the constexpr VM --------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // Defines descriptors which characterise allocations.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick 
13e5dd7070Spatrick #ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
14e5dd7070Spatrick #define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
15e5dd7070Spatrick 
16e5dd7070Spatrick #include "clang/AST/Decl.h"
17e5dd7070Spatrick #include "clang/AST/Expr.h"
18e5dd7070Spatrick 
19e5dd7070Spatrick namespace clang {
20e5dd7070Spatrick namespace interp {
21e5dd7070Spatrick class Block;
22e5dd7070Spatrick class Record;
23e5dd7070Spatrick struct Descriptor;
24e5dd7070Spatrick enum PrimType : unsigned;
25e5dd7070Spatrick 
26e5dd7070Spatrick using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
27e5dd7070Spatrick 
28e5dd7070Spatrick /// Invoked whenever a block is created. The constructor method fills in the
29e5dd7070Spatrick /// inline descriptors of all fields and array elements. It also initializes
30e5dd7070Spatrick /// all the fields which contain non-trivial types.
31e5dd7070Spatrick using BlockCtorFn = void (*)(Block *Storage, char *FieldPtr, bool IsConst,
32e5dd7070Spatrick                              bool IsMutable, bool IsActive,
33e5dd7070Spatrick                              Descriptor *FieldDesc);
34e5dd7070Spatrick 
35e5dd7070Spatrick /// Invoked when a block is destroyed. Invokes the destructors of all
36e5dd7070Spatrick /// non-trivial nested fields of arrays and records.
37e5dd7070Spatrick using BlockDtorFn = void (*)(Block *Storage, char *FieldPtr,
38e5dd7070Spatrick                              Descriptor *FieldDesc);
39e5dd7070Spatrick 
40e5dd7070Spatrick /// Invoked when a block with pointers referencing it goes out of scope. Such
41e5dd7070Spatrick /// blocks are persisted: the move function copies all inline descriptors and
42e5dd7070Spatrick /// non-trivial fields, as existing pointers might need to reference those
43e5dd7070Spatrick /// descriptors. Data is not copied since it cannot be legally read.
44e5dd7070Spatrick using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr,
45e5dd7070Spatrick                              char *DstFieldPtr, Descriptor *FieldDesc);
46e5dd7070Spatrick 
47e5dd7070Spatrick /// Object size as used by the interpreter.
48e5dd7070Spatrick using InterpSize = unsigned;
49e5dd7070Spatrick 
50e5dd7070Spatrick /// Inline descriptor embedded in structures and arrays.
51e5dd7070Spatrick ///
52e5dd7070Spatrick /// Such descriptors precede all composite array elements and structure fields.
53e5dd7070Spatrick /// If the base of a pointer is not zero, the base points to the end of this
54e5dd7070Spatrick /// structure. The offset field is used to traverse the pointer chain up
55e5dd7070Spatrick /// to the root structure which allocated the object.
56e5dd7070Spatrick struct InlineDescriptor {
57e5dd7070Spatrick   /// Offset inside the structure/array.
58e5dd7070Spatrick   unsigned Offset;
59e5dd7070Spatrick 
60e5dd7070Spatrick   /// Flag indicating if the storage is constant or not.
61e5dd7070Spatrick   /// Relevant for primitive fields.
62e5dd7070Spatrick   unsigned IsConst : 1;
63e5dd7070Spatrick   /// For primitive fields, it indicates if the field was initialized.
64e5dd7070Spatrick   /// Primitive fields in static storage are always initialized.
65e5dd7070Spatrick   /// Arrays are always initialized, even though their elements might not be.
66e5dd7070Spatrick   /// Base classes are initialized after the constructor is invoked.
67e5dd7070Spatrick   unsigned IsInitialized : 1;
68e5dd7070Spatrick   /// Flag indicating if the field is an embedded base class.
69e5dd7070Spatrick   unsigned IsBase : 1;
70e5dd7070Spatrick   /// Flag indicating if the field is the active member of a union.
71e5dd7070Spatrick   unsigned IsActive : 1;
72e5dd7070Spatrick   /// Flag indicating if the field is mutable (if in a record).
73*12c85518Srobert   unsigned IsFieldMutable : 1;
74e5dd7070Spatrick 
75e5dd7070Spatrick   Descriptor *Desc;
76e5dd7070Spatrick };
77e5dd7070Spatrick 
78*12c85518Srobert /// Describes a memory block created by an allocation site.
79*12c85518Srobert struct Descriptor final {
80*12c85518Srobert private:
81*12c85518Srobert   /// Original declaration, used to emit the error message.
82*12c85518Srobert   const DeclTy Source;
83*12c85518Srobert   /// Size of an element, in host bytes.
84*12c85518Srobert   const InterpSize ElemSize;
85*12c85518Srobert   /// Size of the storage, in host bytes.
86*12c85518Srobert   const InterpSize Size;
87*12c85518Srobert   // Size of the metadata.
88*12c85518Srobert   const InterpSize MDSize;
89*12c85518Srobert   /// Size of the allocation (storage + metadata), in host bytes.
90*12c85518Srobert   const InterpSize AllocSize;
91*12c85518Srobert 
92*12c85518Srobert   /// Value to denote arrays of unknown size.
93*12c85518Srobert   static constexpr unsigned UnknownSizeMark = (unsigned)-1;
94*12c85518Srobert 
95*12c85518Srobert public:
96*12c85518Srobert   /// Token to denote structures of unknown size.
97*12c85518Srobert   struct UnknownSize {};
98*12c85518Srobert 
99*12c85518Srobert   using MetadataSize = std::optional<InterpSize>;
100*12c85518Srobert   static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor);
101*12c85518Srobert 
102*12c85518Srobert   /// Pointer to the record, if block contains records.
103*12c85518Srobert   Record *const ElemRecord = nullptr;
104*12c85518Srobert   /// Descriptor of the array element.
105*12c85518Srobert   Descriptor *const ElemDesc = nullptr;
106*12c85518Srobert   /// Flag indicating if the block is mutable.
107*12c85518Srobert   const bool IsConst = false;
108*12c85518Srobert   /// Flag indicating if a field is mutable.
109*12c85518Srobert   const bool IsMutable = false;
110*12c85518Srobert   /// Flag indicating if the block is a temporary.
111*12c85518Srobert   const bool IsTemporary = false;
112*12c85518Srobert   /// Flag indicating if the block is an array.
113*12c85518Srobert   const bool IsArray = false;
114*12c85518Srobert 
115*12c85518Srobert   /// Storage management methods.
116*12c85518Srobert   const BlockCtorFn CtorFn = nullptr;
117*12c85518Srobert   const BlockDtorFn DtorFn = nullptr;
118*12c85518Srobert   const BlockMoveFn MoveFn = nullptr;
119*12c85518Srobert 
120*12c85518Srobert   /// Allocates a descriptor for a primitive.
121*12c85518Srobert   Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst,
122*12c85518Srobert              bool IsTemporary, bool IsMutable);
123*12c85518Srobert 
124*12c85518Srobert   /// Allocates a descriptor for an array of primitives.
125*12c85518Srobert   Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems,
126*12c85518Srobert              bool IsConst, bool IsTemporary, bool IsMutable);
127*12c85518Srobert 
128*12c85518Srobert   /// Allocates a descriptor for an array of primitives of unknown size.
129*12c85518Srobert   Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
130*12c85518Srobert 
131*12c85518Srobert   /// Allocates a descriptor for an array of composites.
132*12c85518Srobert   Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
133*12c85518Srobert              unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);
134*12c85518Srobert 
135*12c85518Srobert   /// Allocates a descriptor for an array of composites of unknown size.
136*12c85518Srobert   Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize);
137*12c85518Srobert 
138*12c85518Srobert   /// Allocates a descriptor for a record.
139*12c85518Srobert   Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
140*12c85518Srobert              bool IsTemporary, bool IsMutable);
141*12c85518Srobert 
142*12c85518Srobert   QualType getType() const;
143*12c85518Srobert   SourceLocation getLocation() const;
144*12c85518Srobert 
asDeclfinal145*12c85518Srobert   const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
asExprfinal146*12c85518Srobert   const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
147*12c85518Srobert 
asValueDeclfinal148*12c85518Srobert   const ValueDecl *asValueDecl() const {
149*12c85518Srobert     return dyn_cast_if_present<ValueDecl>(asDecl());
150*12c85518Srobert   }
151*12c85518Srobert 
asFieldDeclfinal152*12c85518Srobert   const FieldDecl *asFieldDecl() const {
153*12c85518Srobert     return dyn_cast_if_present<FieldDecl>(asDecl());
154*12c85518Srobert   }
155*12c85518Srobert 
asRecordDeclfinal156*12c85518Srobert   const RecordDecl *asRecordDecl() const {
157*12c85518Srobert     return dyn_cast_if_present<RecordDecl>(asDecl());
158*12c85518Srobert   }
159*12c85518Srobert 
160*12c85518Srobert   /// Returns the size of the object without metadata.
getSizefinal161*12c85518Srobert   unsigned getSize() const {
162*12c85518Srobert     assert(!isUnknownSizeArray() && "Array of unknown size");
163*12c85518Srobert     return Size;
164*12c85518Srobert   }
165*12c85518Srobert 
166*12c85518Srobert   /// Returns the allocated size, including metadata.
getAllocSizefinal167*12c85518Srobert   unsigned getAllocSize() const { return AllocSize; }
168*12c85518Srobert   /// returns the size of an element when the structure is viewed as an array.
getElemSizefinal169*12c85518Srobert   unsigned getElemSize()  const { return ElemSize; }
170*12c85518Srobert   /// Returns the size of the metadata.
getMetadataSizefinal171*12c85518Srobert   unsigned getMetadataSize() const { return MDSize; }
172*12c85518Srobert 
173*12c85518Srobert   /// Returns the number of elements stored in the block.
getNumElemsfinal174*12c85518Srobert   unsigned getNumElems() const {
175*12c85518Srobert     return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize());
176*12c85518Srobert   }
177*12c85518Srobert 
178*12c85518Srobert   /// Checks if the descriptor is of an array of primitives.
isPrimitiveArrayfinal179*12c85518Srobert   bool isPrimitiveArray() const { return IsArray && !ElemDesc; }
180*12c85518Srobert   /// Checks if the descriptor is of an array of zero size.
isZeroSizeArrayfinal181*12c85518Srobert   bool isZeroSizeArray() const { return Size == 0; }
182*12c85518Srobert   /// Checks if the descriptor is of an array of unknown size.
isUnknownSizeArrayfinal183*12c85518Srobert   bool isUnknownSizeArray() const { return Size == UnknownSizeMark; }
184*12c85518Srobert 
185*12c85518Srobert   /// Checks if the descriptor is of a primitive.
isPrimitivefinal186*12c85518Srobert   bool isPrimitive() const { return !IsArray && !ElemRecord; }
187*12c85518Srobert 
188*12c85518Srobert   /// Checks if the descriptor is of an array.
isArrayfinal189*12c85518Srobert   bool isArray() const { return IsArray; }
190*12c85518Srobert };
191*12c85518Srobert 
192e5dd7070Spatrick /// Bitfield tracking the initialisation status of elements of primitive arrays.
193e5dd7070Spatrick /// A pointer to this is embedded at the end of all primitive arrays.
194*12c85518Srobert /// If the map was not yet created and nothing was initialized, the pointer to
195e5dd7070Spatrick /// this structure is 0. If the object was fully initialized, the pointer is -1.
196*12c85518Srobert struct InitMap final {
197e5dd7070Spatrick private:
198e5dd7070Spatrick   /// Type packing bits.
199e5dd7070Spatrick   using T = uint64_t;
200e5dd7070Spatrick   /// Bits stored in a single field.
201e5dd7070Spatrick   static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
202e5dd7070Spatrick 
203e5dd7070Spatrick   /// Initializes the map with no fields set.
204e5dd7070Spatrick   InitMap(unsigned N);
205e5dd7070Spatrick 
206e5dd7070Spatrick   /// Returns a pointer to storage.
207e5dd7070Spatrick   T *data();
208*12c85518Srobert   const T *data() const;
209e5dd7070Spatrick 
210e5dd7070Spatrick public:
211e5dd7070Spatrick   /// Initializes an element. Returns true when object if fully initialized.
212e5dd7070Spatrick   bool initialize(unsigned I);
213e5dd7070Spatrick 
214e5dd7070Spatrick   /// Checks if an element was initialized.
215*12c85518Srobert   bool isInitialized(unsigned I) const;
216e5dd7070Spatrick 
217e5dd7070Spatrick   /// Allocates a map holding N elements.
218e5dd7070Spatrick   static InitMap *allocate(unsigned N);
219e5dd7070Spatrick 
220e5dd7070Spatrick private:
221e5dd7070Spatrick   /// Number of fields initialized.
222e5dd7070Spatrick   unsigned UninitFields;
223e5dd7070Spatrick };
224e5dd7070Spatrick 
225e5dd7070Spatrick } // namespace interp
226e5dd7070Spatrick } // namespace clang
227e5dd7070Spatrick 
228e5dd7070Spatrick #endif
229