1353942Sdim//===--- Descriptor.h - Types for the constexpr VM --------------*- C++ -*-===//
2353942Sdim//
3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353942Sdim// See https://llvm.org/LICENSE.txt for license information.
5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353942Sdim//
7353942Sdim//===----------------------------------------------------------------------===//
8353942Sdim//
9353942Sdim// Defines descriptors which characterise allocations.
10353942Sdim//
11353942Sdim//===----------------------------------------------------------------------===//
12353942Sdim
13353942Sdim#ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
14353942Sdim#define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
15353942Sdim
16353942Sdim#include "clang/AST/Decl.h"
17353942Sdim#include "clang/AST/Expr.h"
18353942Sdim
19353942Sdimnamespace clang {
20353942Sdimnamespace interp {
21353942Sdimclass Block;
22353942Sdimclass Record;
23353942Sdimstruct Descriptor;
24353942Sdimenum PrimType : unsigned;
25353942Sdim
26353942Sdimusing DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
27353942Sdim
28353942Sdim/// Invoked whenever a block is created. The constructor method fills in the
29353942Sdim/// inline descriptors of all fields and array elements. It also initializes
30353942Sdim/// all the fields which contain non-trivial types.
31353942Sdimusing BlockCtorFn = void (*)(Block *Storage, char *FieldPtr, bool IsConst,
32353942Sdim                             bool IsMutable, bool IsActive,
33353942Sdim                             Descriptor *FieldDesc);
34353942Sdim
35353942Sdim/// Invoked when a block is destroyed. Invokes the destructors of all
36353942Sdim/// non-trivial nested fields of arrays and records.
37353942Sdimusing BlockDtorFn = void (*)(Block *Storage, char *FieldPtr,
38353942Sdim                             Descriptor *FieldDesc);
39353942Sdim
40353942Sdim/// Invoked when a block with pointers referencing it goes out of scope. Such
41353942Sdim/// blocks are persisted: the move function copies all inline descriptors and
42353942Sdim/// non-trivial fields, as existing pointers might need to reference those
43353942Sdim/// descriptors. Data is not copied since it cannot be legally read.
44353942Sdimusing BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr,
45353942Sdim                             char *DstFieldPtr, Descriptor *FieldDesc);
46353942Sdim
47353942Sdim/// Object size as used by the interpreter.
48353942Sdimusing InterpSize = unsigned;
49353942Sdim
50353942Sdim/// Describes a memory block created by an allocation site.
51353942Sdimstruct Descriptor {
52353942Sdimprivate:
53353942Sdim  /// Original declaration, used to emit the error message.
54353942Sdim  const DeclTy Source;
55353942Sdim  /// Size of an element, in host bytes.
56353942Sdim  const InterpSize ElemSize;
57353942Sdim  /// Size of the storage, in host bytes.
58353942Sdim  const InterpSize Size;
59353942Sdim  /// Size of the allocation (storage + metadata), in host bytes.
60353942Sdim  const InterpSize AllocSize;
61353942Sdim
62353942Sdim  /// Value to denote arrays of unknown size.
63353942Sdim  static constexpr unsigned UnknownSizeMark = (unsigned)-1;
64353942Sdim
65353942Sdimpublic:
66353942Sdim  /// Token to denote structures of unknown size.
67353942Sdim  struct UnknownSize {};
68353942Sdim
69353942Sdim  /// Pointer to the record, if block contains records.
70353942Sdim  Record *const ElemRecord = nullptr;
71353942Sdim  /// Descriptor of the array element.
72353942Sdim  Descriptor *const ElemDesc = nullptr;
73353942Sdim  /// Flag indicating if the block is mutable.
74353942Sdim  const bool IsConst = false;
75353942Sdim  /// Flag indicating if a field is mutable.
76353942Sdim  const bool IsMutable = false;
77353942Sdim  /// Flag indicating if the block is a temporary.
78353942Sdim  const bool IsTemporary = false;
79353942Sdim  /// Flag indicating if the block is an array.
80353942Sdim  const bool IsArray = false;
81353942Sdim
82353942Sdim  /// Storage management methods.
83353942Sdim  const BlockCtorFn CtorFn = nullptr;
84353942Sdim  const BlockDtorFn DtorFn = nullptr;
85353942Sdim  const BlockMoveFn MoveFn = nullptr;
86353942Sdim
87353942Sdim  /// Allocates a descriptor for a primitive.
88353942Sdim  Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary,
89353942Sdim             bool IsMutable);
90353942Sdim
91353942Sdim  /// Allocates a descriptor for an array of primitives.
92353942Sdim  Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst,
93353942Sdim             bool IsTemporary, bool IsMutable);
94353942Sdim
95353942Sdim  /// Allocates a descriptor for an array of primitives of unknown size.
96353942Sdim  Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
97353942Sdim
98353942Sdim  /// Allocates a descriptor for an array of composites.
99353942Sdim  Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst,
100353942Sdim             bool IsTemporary, bool IsMutable);
101353942Sdim
102353942Sdim  /// Allocates a descriptor for an array of composites of unknown size.
103353942Sdim  Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize);
104353942Sdim
105353942Sdim  /// Allocates a descriptor for a record.
106353942Sdim  Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary,
107353942Sdim             bool IsMutable);
108353942Sdim
109353942Sdim  QualType getType() const;
110353942Sdim  SourceLocation getLocation() const;
111353942Sdim
112353942Sdim  const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
113353942Sdim  const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
114353942Sdim
115353942Sdim  const ValueDecl *asValueDecl() const {
116353942Sdim    return dyn_cast_or_null<ValueDecl>(asDecl());
117353942Sdim  }
118353942Sdim
119353942Sdim  const FieldDecl *asFieldDecl() const {
120353942Sdim    return dyn_cast_or_null<FieldDecl>(asDecl());
121353942Sdim  }
122353942Sdim
123353942Sdim  const RecordDecl *asRecordDecl() const {
124353942Sdim    return dyn_cast_or_null<RecordDecl>(asDecl());
125353942Sdim  }
126353942Sdim
127353942Sdim  /// Returns the size of the object without metadata.
128353942Sdim  unsigned getSize() const {
129353942Sdim    assert(!isUnknownSizeArray() && "Array of unknown size");
130353942Sdim    return Size;
131353942Sdim  }
132353942Sdim
133353942Sdim  /// Returns the allocated size, including metadata.
134353942Sdim  unsigned getAllocSize() const { return AllocSize; }
135353942Sdim  /// returns the size of an element when the structure is viewed as an array.
136353942Sdim  unsigned getElemSize()  const { return ElemSize; }
137353942Sdim
138353942Sdim  /// Returns the number of elements stored in the block.
139353942Sdim  unsigned getNumElems() const {
140353942Sdim    return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize());
141353942Sdim  }
142353942Sdim
143353942Sdim  /// Checks if the descriptor is of an array of primitives.
144353942Sdim  bool isPrimitiveArray() const { return IsArray && !ElemDesc; }
145353942Sdim  /// Checks if the descriptor is of an array of zero size.
146353942Sdim  bool isZeroSizeArray() const { return Size == 0; }
147353942Sdim  /// Checks if the descriptor is of an array of unknown size.
148353942Sdim  bool isUnknownSizeArray() const { return Size == UnknownSizeMark; }
149353942Sdim
150353942Sdim  /// Checks if the descriptor is of a primitive.
151353942Sdim  bool isPrimitive() const { return !IsArray && !ElemRecord; }
152353942Sdim
153353942Sdim  /// Checks if the descriptor is of an array.
154353942Sdim  bool isArray() const { return IsArray; }
155353942Sdim};
156353942Sdim
157353942Sdim/// Inline descriptor embedded in structures and arrays.
158353942Sdim///
159353942Sdim/// Such descriptors precede all composite array elements and structure fields.
160353942Sdim/// If the base of a pointer is not zero, the base points to the end of this
161353942Sdim/// structure. The offset field is used to traverse the pointer chain up
162353942Sdim/// to the root structure which allocated the object.
163353942Sdimstruct InlineDescriptor {
164353942Sdim  /// Offset inside the structure/array.
165353942Sdim  unsigned Offset;
166353942Sdim
167353942Sdim  /// Flag indicating if the storage is constant or not.
168353942Sdim  /// Relevant for primitive fields.
169353942Sdim  unsigned IsConst : 1;
170353942Sdim  /// For primitive fields, it indicates if the field was initialized.
171353942Sdim  /// Primitive fields in static storage are always initialized.
172353942Sdim  /// Arrays are always initialized, even though their elements might not be.
173353942Sdim  /// Base classes are initialized after the constructor is invoked.
174353942Sdim  unsigned IsInitialized : 1;
175353942Sdim  /// Flag indicating if the field is an embedded base class.
176353942Sdim  unsigned IsBase : 1;
177353942Sdim  /// Flag indicating if the field is the active member of a union.
178353942Sdim  unsigned IsActive : 1;
179353942Sdim  /// Flag indicating if the field is mutable (if in a record).
180353942Sdim  unsigned IsMutable : 1;
181353942Sdim
182353942Sdim  Descriptor *Desc;
183353942Sdim};
184353942Sdim
185353942Sdim/// Bitfield tracking the initialisation status of elements of primitive arrays.
186353942Sdim/// A pointer to this is embedded at the end of all primitive arrays.
187353942Sdim/// If the map was not yet created and nothing was initialied, the pointer to
188353942Sdim/// this structure is 0. If the object was fully initialized, the pointer is -1.
189353942Sdimstruct InitMap {
190353942Sdimprivate:
191353942Sdim  /// Type packing bits.
192353942Sdim  using T = uint64_t;
193353942Sdim  /// Bits stored in a single field.
194353942Sdim  static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
195353942Sdim
196353942Sdim  /// Initializes the map with no fields set.
197353942Sdim  InitMap(unsigned N);
198353942Sdim
199353942Sdim  /// Returns a pointer to storage.
200353942Sdim  T *data();
201353942Sdim
202353942Sdimpublic:
203353942Sdim  /// Initializes an element. Returns true when object if fully initialized.
204353942Sdim  bool initialize(unsigned I);
205353942Sdim
206353942Sdim  /// Checks if an element was initialized.
207353942Sdim  bool isInitialized(unsigned I);
208353942Sdim
209353942Sdim  /// Allocates a map holding N elements.
210353942Sdim  static InitMap *allocate(unsigned N);
211353942Sdim
212353942Sdimprivate:
213353942Sdim  /// Number of fields initialized.
214353942Sdim  unsigned UninitFields;
215353942Sdim};
216353942Sdim
217353942Sdim} // namespace interp
218353942Sdim} // namespace clang
219353942Sdim
220353942Sdim#endif
221