1//===-- InterpBlock.h - Allocated blocks for the interpreter -*- 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// Defines the classes describing allocated blocks.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H
14#define LLVM_CLANG_AST_INTERP_BLOCK_H
15
16#include "Descriptor.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/DeclCXX.h"
19#include "clang/AST/Expr.h"
20#include "clang/AST/ComparisonCategories.h"
21#include "llvm/ADT/PointerUnion.h"
22#include "llvm/Support/raw_ostream.h"
23
24namespace clang {
25namespace interp {
26class Block;
27class DeadBlock;
28class InterpState;
29class Pointer;
30enum PrimType : unsigned;
31
32/// A memory block, either on the stack or in the heap.
33///
34/// The storage described by the block is immediately followed by
35/// optional metadata, which is followed by the actual data.
36///
37/// Block*        rawData()                  data()
38/// ���               ���                         ���
39/// ���               ���                         ���
40/// ���               ���                         ���
41/// ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
42/// ��� Block         ��� Metadata                ��� Data            ���
43/// ��� sizeof(Block) ��� Desc->getMetadataSize() ��� Desc->getSize() ���
44/// ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
45///
46/// Desc->getAllocSize() describes the size after the Block, i.e.
47/// the data size and the metadata size.
48///
49class Block final {
50public:
51  /// Creates a new block.
52  Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc,
53        bool IsStatic = false, bool IsExtern = false)
54      : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {}
55
56  Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
57      : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern),
58        Desc(Desc) {}
59
60  /// Returns the block's descriptor.
61  const Descriptor *getDescriptor() const { return Desc; }
62  /// Checks if the block has any live pointers.
63  bool hasPointers() const { return Pointers; }
64  /// Checks if the block is extern.
65  bool isExtern() const { return IsExtern; }
66  /// Checks if the block has static storage duration.
67  bool isStatic() const { return IsStatic; }
68  /// Checks if the block is temporary.
69  bool isTemporary() const { return Desc->IsTemporary; }
70  /// Returns the size of the block.
71  unsigned getSize() const { return Desc->getAllocSize(); }
72  /// Returns the declaration ID.
73  std::optional<unsigned> getDeclID() const { return DeclID; }
74  bool isInitialized() const { return IsInitialized; }
75
76  /// Returns a pointer to the stored data.
77  /// You are allowed to read Desc->getSize() bytes from this address.
78  std::byte *data() {
79    // rawData might contain metadata as well.
80    size_t DataOffset = Desc->getMetadataSize();
81    return rawData() + DataOffset;
82  }
83  const std::byte *data() const {
84    // rawData might contain metadata as well.
85    size_t DataOffset = Desc->getMetadataSize();
86    return rawData() + DataOffset;
87  }
88
89  /// Returns a pointer to the raw data, including metadata.
90  /// You are allowed to read Desc->getAllocSize() bytes from this address.
91  std::byte *rawData() {
92    return reinterpret_cast<std::byte *>(this) + sizeof(Block);
93  }
94  const std::byte *rawData() const {
95    return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
96  }
97
98  /// Returns a view over the data.
99  template <typename T>
100  T &deref() { return *reinterpret_cast<T *>(data()); }
101  template <typename T> const T &deref() const {
102    return *reinterpret_cast<const T *>(data());
103  }
104
105  /// Invokes the constructor.
106  void invokeCtor() {
107    std::memset(rawData(), 0, Desc->getAllocSize());
108    if (Desc->CtorFn)
109      Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
110                   /*isActive=*/true, Desc);
111    IsInitialized = true;
112  }
113
114  /// Invokes the Destructor.
115  void invokeDtor() {
116    if (Desc->DtorFn)
117      Desc->DtorFn(this, data(), Desc);
118    IsInitialized = false;
119  }
120
121protected:
122  friend class Pointer;
123  friend class DeadBlock;
124  friend class InterpState;
125
126  Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead)
127      : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {}
128
129  /// Deletes a dead block at the end of its lifetime.
130  void cleanup();
131
132  /// Pointer chain management.
133  void addPointer(Pointer *P);
134  void removePointer(Pointer *P);
135  void replacePointer(Pointer *Old, Pointer *New);
136#ifndef NDEBUG
137  bool hasPointer(const Pointer *P) const;
138#endif
139
140  /// Start of the chain of pointers.
141  Pointer *Pointers = nullptr;
142  /// Unique identifier of the declaration.
143  std::optional<unsigned> DeclID;
144  /// Flag indicating if the block has static storage duration.
145  bool IsStatic = false;
146  /// Flag indicating if the block is an extern.
147  bool IsExtern = false;
148  /// Flag indicating if the pointer is dead. This is only ever
149  /// set once, when converting the Block to a DeadBlock.
150  bool IsDead = false;
151  /// Flag indicating if the block contents have been initialized
152  /// via invokeCtor.
153  bool IsInitialized = false;
154  /// Pointer to the stack slot descriptor.
155  const Descriptor *Desc;
156};
157
158/// Descriptor for a dead block.
159///
160/// Dead blocks are chained in a double-linked list to deallocate them
161/// whenever pointers become dead.
162class DeadBlock final {
163public:
164  /// Copies the block.
165  DeadBlock(DeadBlock *&Root, Block *Blk);
166
167  /// Returns a pointer to the stored data.
168  std::byte *data() { return B.data(); }
169  std::byte *rawData() { return B.rawData(); }
170
171private:
172  friend class Block;
173  friend class InterpState;
174
175  void free();
176
177  /// Root pointer of the list.
178  DeadBlock *&Root;
179  /// Previous block in the list.
180  DeadBlock *Prev;
181  /// Next block in the list.
182  DeadBlock *Next;
183
184  /// Actual block storing data and tracking pointers.
185  Block B;
186};
187
188} // namespace interp
189} // namespace clang
190
191#endif
192