Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/clang/lib/AST/Interp/InterpBlock.h
Line
Count
Source (jump to first uncovered line)
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
24
namespace clang {
25
namespace interp {
26
class Block;
27
class DeadBlock;
28
class InterpState;
29
class Pointer;
30
enum 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
///
49
class Block final {
50
public:
51
  /// Creates a new block.
52
  Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc,
53
        bool IsStatic = false, bool IsExtern = false)
54
0
      : 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
0
        Desc(Desc) {}
59
60
  /// Returns the block's descriptor.
61
0
  const Descriptor *getDescriptor() const { return Desc; }
62
  /// Checks if the block has any live pointers.
63
0
  bool hasPointers() const { return Pointers; }
64
  /// Checks if the block is extern.
65
0
  bool isExtern() const { return IsExtern; }
66
  /// Checks if the block has static storage duration.
67
0
  bool isStatic() const { return IsStatic; }
68
  /// Checks if the block is temporary.
69
0
  bool isTemporary() const { return Desc->IsTemporary; }
70
  /// Returns the size of the block.
71
0
  unsigned getSize() const { return Desc->getAllocSize(); }
72
  /// Returns the declaration ID.
73
0
  std::optional<unsigned> getDeclID() const { return DeclID; }
74
0
  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
0
  std::byte *data() {
79
    // rawData might contain metadata as well.
80
0
    size_t DataOffset = Desc->getMetadataSize();
81
0
    return rawData() + DataOffset;
82
0
  }
83
0
  const std::byte *data() const {
84
    // rawData might contain metadata as well.
85
0
    size_t DataOffset = Desc->getMetadataSize();
86
0
    return rawData() + DataOffset;
87
0
  }
88
89
  /// Returns a pointer to the raw data, including metadata.
90
  /// You are allowed to read Desc->getAllocSize() bytes from this address.
91
0
  std::byte *rawData() {
92
0
    return reinterpret_cast<std::byte *>(this) + sizeof(Block);
93
0
  }
94
0
  const std::byte *rawData() const {
95
0
    return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
96
0
  }
97
98
  /// Returns a view over the data.
99
  template <typename T>
100
0
  T &deref() { return *reinterpret_cast<T *>(data()); }
Unexecuted instantiation: clang::interp::Integral<8u, true>& clang::interp::Block::deref<clang::interp::Integral<8u, true> >()
Unexecuted instantiation: clang::interp::Integral<8u, false>& clang::interp::Block::deref<clang::interp::Integral<8u, false> >()
Unexecuted instantiation: clang::interp::Integral<16u, true>& clang::interp::Block::deref<clang::interp::Integral<16u, true> >()
Unexecuted instantiation: clang::interp::Integral<16u, false>& clang::interp::Block::deref<clang::interp::Integral<16u, false> >()
Unexecuted instantiation: clang::interp::Integral<32u, true>& clang::interp::Block::deref<clang::interp::Integral<32u, true> >()
Unexecuted instantiation: clang::interp::Integral<32u, false>& clang::interp::Block::deref<clang::interp::Integral<32u, false> >()
Unexecuted instantiation: clang::interp::Integral<64u, true>& clang::interp::Block::deref<clang::interp::Integral<64u, true> >()
Unexecuted instantiation: clang::interp::Integral<64u, false>& clang::interp::Block::deref<clang::interp::Integral<64u, false> >()
Unexecuted instantiation: clang::interp::IntegralAP<false>& clang::interp::Block::deref<clang::interp::IntegralAP<false> >()
Unexecuted instantiation: clang::interp::IntegralAP<true>& clang::interp::Block::deref<clang::interp::IntegralAP<true> >()
Unexecuted instantiation: clang::interp::Boolean& clang::interp::Block::deref<clang::interp::Boolean>()
Unexecuted instantiation: clang::interp::Pointer& clang::interp::Block::deref<clang::interp::Pointer>()
Unexecuted instantiation: clang::interp::FunctionPointer& clang::interp::Block::deref<clang::interp::FunctionPointer>()
Unexecuted instantiation: clang::interp::Floating& clang::interp::Block::deref<clang::interp::Floating>()
101
0
  template <typename T> const T &deref() const {
102
0
    return *reinterpret_cast<const T *>(data());
103
0
  }
Unexecuted instantiation: clang::interp::Integral<8u, true> const& clang::interp::Block::deref<clang::interp::Integral<8u, true> >() const
Unexecuted instantiation: clang::interp::Integral<8u, false> const& clang::interp::Block::deref<clang::interp::Integral<8u, false> >() const
Unexecuted instantiation: clang::interp::Integral<16u, true> const& clang::interp::Block::deref<clang::interp::Integral<16u, true> >() const
Unexecuted instantiation: clang::interp::Integral<16u, false> const& clang::interp::Block::deref<clang::interp::Integral<16u, false> >() const
Unexecuted instantiation: clang::interp::Integral<32u, true> const& clang::interp::Block::deref<clang::interp::Integral<32u, true> >() const
Unexecuted instantiation: clang::interp::Integral<32u, false> const& clang::interp::Block::deref<clang::interp::Integral<32u, false> >() const
Unexecuted instantiation: clang::interp::Integral<64u, true> const& clang::interp::Block::deref<clang::interp::Integral<64u, true> >() const
Unexecuted instantiation: clang::interp::Integral<64u, false> const& clang::interp::Block::deref<clang::interp::Integral<64u, false> >() const
Unexecuted instantiation: clang::interp::IntegralAP<false> const& clang::interp::Block::deref<clang::interp::IntegralAP<false> >() const
Unexecuted instantiation: clang::interp::IntegralAP<true> const& clang::interp::Block::deref<clang::interp::IntegralAP<true> >() const
Unexecuted instantiation: clang::interp::Boolean const& clang::interp::Block::deref<clang::interp::Boolean>() const
Unexecuted instantiation: clang::interp::Pointer const& clang::interp::Block::deref<clang::interp::Pointer>() const
Unexecuted instantiation: clang::interp::FunctionPointer const& clang::interp::Block::deref<clang::interp::FunctionPointer>() const
Unexecuted instantiation: clang::interp::Floating const& clang::interp::Block::deref<clang::interp::Floating>() const
104
105
  /// Invokes the constructor.
106
0
  void invokeCtor() {
107
0
    std::memset(rawData(), 0, Desc->getAllocSize());
108
0
    if (Desc->CtorFn)
109
0
      Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
110
0
                   /*isActive=*/true, Desc);
111
0
    IsInitialized = true;
112
0
  }
113
114
  /// Invokes the Destructor.
115
0
  void invokeDtor() {
116
0
    if (Desc->DtorFn)
117
0
      Desc->DtorFn(this, data(), Desc);
118
0
    IsInitialized = false;
119
0
  }
120
121
protected:
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
0
      : 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.
162
class DeadBlock final {
163
public:
164
  /// Copies the block.
165
  DeadBlock(DeadBlock *&Root, Block *Blk);
166
167
  /// Returns a pointer to the stored data.
168
0
  std::byte *data() { return B.data(); }
169
0
  std::byte *rawData() { return B.rawData(); }
170
171
private:
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