clang 24.0.0git
InterpState.h
Go to the documentation of this file.
1//===--- InterpState.h - Interpreter state for the constexpr VM -*- 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// Definition of the interpreter state and entry point.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_INTERPSTATE_H
14#define LLVM_CLANG_AST_INTERP_INTERPSTATE_H
15
16#include "Context.h"
17#include "DynamicAllocator.h"
18#include "Floating.h"
19#include "FrameAllocator.h"
20#include "Function.h"
21#include "InterpFrame.h"
22#include "InterpStack.h"
23#include "State.h"
24#include <limits>
25
26namespace clang {
27namespace interp {
28class Context;
29class SourceMapper;
30
32
33 const Expr *Call = nullptr;
35 explicit operator bool() { return Call; }
36};
37
38// FIXME: Create one for the "checking potential constant expression"
39// evaluation.
40enum class EvaluationKind : uint8_t {
42 Dtor, /// We're checking for constant destruction of a global variable.
43};
44
45/// Interpreter context.
46class InterpState final : public State {
47public:
48 InterpState(const State &Parent, Program &P, InterpStack &Stk,
49 FrameAllocator &FrameAlloc, Context &Ctx,
50 SourceMapper *M = nullptr);
51
52 InterpState(const State &Parent, Program &P, InterpStack &Stk,
53 FrameAllocator &FA, Context &Ctx, const Function *Func);
54
57
59
60 void cleanup();
61
62 InterpState(const InterpState &) = delete;
63 InterpState &operator=(const InterpState &) = delete;
64
65 bool diagnosing() const { return getEvalStatus().Diag != nullptr; }
66
67 // Stack frame accessors.
68 const Frame *getCurrentFrame() override;
69 unsigned getCallStackDepth() override {
70 return Current ? (Current->getDepth() + 1) : 1;
71 }
72 bool stepsLeft() const override { return true; }
73 bool inConstantContext() const;
74
75 /// Deallocates a pointer.
76 void deallocate(Block *B);
77
78 /// Delegates source mapping to the mapper.
79 SourceInfo getSource(CodePtr PC) const { return M->getSource(PC); }
80
81 Context &getContext() const { return Ctx; }
82
84
86 if (!Alloc) {
87 if (!Allocator)
88 Allocator.emplace();
89 Alloc = std::make_unique<DynamicAllocator>(*Allocator);
90 }
91
92 return *Alloc;
93 }
94
95 /// Diagnose any dynamic allocations that haven't been freed yet.
96 /// Will return \c false if there were any allocations to diagnose,
97 /// \c true otherwise.
99
100 StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const;
101
102 void *allocate(size_t Size, unsigned Align = 8) const {
103 if (!Allocator)
104 Allocator.emplace();
105 return Allocator->Allocate(Size, Align);
106 }
107 template <typename T> T *allocate(size_t Num = 1) const {
108 return static_cast<T *>(allocate(Num * sizeof(T), alignof(T)));
109 }
110
111 template <typename T> T allocAP(unsigned BitWidth) {
112 unsigned NumWords = APInt::getNumWords(BitWidth);
113 if (NumWords == 1)
114 return T(BitWidth);
115 uint64_t *Mem = (uint64_t *)this->allocate(NumWords * sizeof(uint64_t));
116 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
117 return T(Mem, BitWidth);
118 }
119
120 Floating allocFloat(const llvm::fltSemantics &Sem) {
121 if (Floating::singleWord(Sem))
122 return Floating(llvm::APFloatBase::SemanticsToEnum(Sem));
123
124 unsigned NumWords =
125 APInt::getNumWords(llvm::APFloatBase::getSizeInBits(Sem));
126 uint64_t *Mem = (uint64_t *)this->allocate(NumWords * sizeof(uint64_t));
127 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
128 return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem));
129 }
130 const CXXRecordDecl **allocMemberPointerPath(unsigned Length) {
131 return reinterpret_cast<const CXXRecordDecl **>(
132 this->allocate(Length * sizeof(CXXRecordDecl *)));
133 }
135 const PointerPathEntry *OldPP) {
136 assert(Length != 0);
137 auto *PP = reinterpret_cast<PointerPathEntry *>(
138 this->allocate(Length * sizeof(PointerPathEntry)));
139 if (OldPP)
140 std::memcpy(PP, OldPP, sizeof(PointerPathEntry) * Length);
141 return PP;
142 }
143 /// Allocate a new pointer path of Length \c NewLength.
144 /// NewLength - 1 elements are copied form \c OldPP.
146 const PointerPathEntry *OldPP,
147 PointerPathEntry NewEntry) {
148 auto *PP = reinterpret_cast<PointerPathEntry *>(
149 this->allocate(NewLength * sizeof(PointerPathEntry)));
150 if (OldPP)
151 std::memcpy(PP, OldPP, sizeof(PointerPathEntry) * (NewLength - 1));
152 PP[NewLength - 1] = NewEntry;
153 return PP;
154 }
155
156 /// Note that a step has been executed. If there are no more steps remaining,
157 /// diagnoses and returns \c false.
158 bool noteStep(CodePtr OpPC) {
159 if (InfiniteSteps)
160 return true;
161
162 --StepsLeft;
163 if (LLVM_LIKELY(StepsLeft != 0))
164 return true;
165
166 return diagnoseStepLimitExceeded(OpPC);
167 }
168
169 bool initializingBlock(const Block *B) const {
171 if (V.block() == B)
172 return true;
173 return false;
174 }
175
176 bool lifetimeStartedInEvaluation(const Block *B) const {
178 return B->getEvalID() == EvalID;
179
181 assert(EvaluatingDecl);
183 return EvaluatingDecl->getType().isConstQualified();
184 }
185 return false;
186 }
187
188 /// Return if we're checking if a global variable has a constant destructor.
191 }
192 /// Return if we're checking if a global variable has a constant destructor
193 /// and the given pointer is pointing to the variable we're checking that for.
194 bool checkingConstantDestruction(const Pointer &Ptr) const {
195 return checkingConstantDestruction(Ptr.getRootVarDecl());
196 }
197 bool checkingConstantDestruction(const VarDecl *VD) const {
199 }
200
201 unsigned newStringID() { return StringID++; }
202
203 /// Allocate memory and create a new InterpFrame for the given function.
204 template <typename... Ts>
205 InterpFrame *allocFrame(const Function *F, Ts &&...Args) {
206 size_t FrameSize = InterpFrame::allocSize(F);
207 assert(FrameSize < std::numeric_limits<unsigned>::max());
208 InterpFrame *NewFrame = new (FrameAlloc.reserve(FrameSize))
209 InterpFrame(*this, F, std::forward<Ts>(Args)...);
210 assert(NewFrame);
211 return NewFrame;
212 }
213
214 /// Free resources associated with the current frame and set the caller to be
215 /// the new current frame.
217 assert(Current);
218 unsigned CurrentSize = InterpFrame::allocSize(Current->getFunction());
219 InterpFrame *Caller = Current->Caller;
220 Current->~InterpFrame();
221 FrameAlloc.pop(CurrentSize);
222 Current = Caller;
223 }
224
225private:
226 friend class EvaluationResult;
228 /// Dead block chain.
229 DeadBlock *DeadBlocks = nullptr;
230 /// Reference to the offset-source mapping.
231 SourceMapper *M;
232 /// Allocator for everything else, e.g. floating-point values.
233 mutable std::optional<llvm::BumpPtrAllocator> Allocator;
234 /// Allocator used for dynamic allocations performed via the program.
235 std::unique_ptr<DynamicAllocator> Alloc;
236 /// Diagnose that we've reached the constexpr step limit.
237 bool diagnoseStepLimitExceeded(CodePtr OpPC);
238
239 FrameAllocator &FrameAlloc;
240
241public:
243 /// Reference to the module containing all bytecode.
245 /// Temporary stack.
247 /// Interpreter Context.
249 /// Bottom function frame.
251 /// The current frame.
253 /// Source location of the evaluating expression
255 /// Declaration we're initializing/evaluting, if any.
256 const VarDecl *EvaluatingDecl = nullptr;
257 /// Steps left during evaluation.
258 unsigned StepsLeft = 1;
259 /// Whether infinite evaluation steps have been requested. If this is false,
260 /// we use the StepsLeft value above.
261 const bool InfiniteSteps = false;
262 /// ID identifying this evaluation.
263 const unsigned EvalID;
264
265 unsigned StringID = 0;
266
268
269 /// Things needed to do speculative execution.
271 bool PrevDiagsEmitted = false;
272#ifndef NDEBUG
273 unsigned SpeculationDepth = 0;
274#endif
275 unsigned DiagIgnoreDepth = 0;
276 std::optional<bool> ConstantContextOverride;
277
279 std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
281
282 /// List of blocks we're currently running either constructors or destructors
283 /// for.
285};
286
288public:
290 : Ctx(Ctx), OldCC(Ctx.ConstantContextOverride) {
291 // We only override this if the new value is true.
292 Enabled = Value;
293 if (Enabled)
294 Ctx.ConstantContextOverride = Value;
295 }
297 if (Enabled)
298 Ctx.ConstantContextOverride = OldCC;
299 }
300
301private:
302 bool Enabled;
303 InterpState &Ctx;
304 std::optional<bool> OldCC;
305};
306
307} // namespace interp
308} // namespace clang
309
310#endif
#define V(N, I)
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
This represents one expression.
Definition Expr.h:113
A (possibly-)qualified type.
Definition TypeBase.h:938
Encodes a location in the source.
Represents a variable declaration or definition.
Definition Decl.h:933
A memory block, either on the stack or in the heap.
Definition InterpBlock.h:43
const Descriptor * getDescriptor() const
Returns the block's descriptor.
Definition InterpBlock.h:73
unsigned getEvalID() const
The Evaluation ID this block was created in.
Definition InterpBlock.h:95
Pointer into the code segment.
Definition Source.h:31
Holds all information required to evaluate constexpr code in a module.
Definition Context.h:48
Descriptor for a dead block.
Manages dynamic memory allocations done during bytecode interpretation.
If a Floating is constructed from Memory, it DOES NOT OWN THAT MEMORY.
Definition Floating.h:35
bool singleWord() const
Definition Floating.h:107
Allocator for function frames.
Base class for stack frames, shared between VM and walker.
Definition Frame.h:28
Bytecode function.
Definition Function.h:98
Frame storing local variables.
Definition InterpFrame.h:27
static size_t allocSize(const Function *F)
Returns the number of bytes needed to allocate an InterpFrame for the given function.
Definition InterpFrame.h:48
Stack frame storing temporaries and parameters.
Definition InterpStack.h:25
InterpStateCCOverride(InterpState &Ctx, bool Value)
Interpreter context.
Definition InterpState.h:46
SmallVectorImpl< PartialDiagnosticAt > * PrevDiags
Things needed to do speculative execution.
bool lifetimeStartedInEvaluation(const Block *B) const
unsigned getCallStackDepth() override
Definition InterpState.h:69
InterpFrame BottomFrame
Bottom function frame.
Context & getContext() const
Definition InterpState.h:81
bool initializingBlock(const Block *B) const
DynamicAllocator & getAllocator()
Definition InterpState.h:85
void resetCurrentFrame()
Free resources associated with the current frame and set the caller to be the new current frame.
InterpState(const State &Parent, Program &P, InterpStack &Stk, FrameAllocator &FrameAlloc, Context &Ctx, SourceMapper *M=nullptr)
Context & Ctx
Interpreter Context.
void * allocate(size_t Size, unsigned Align=8) const
Floating allocFloat(const llvm::fltSemantics &Sem)
const unsigned EvalID
ID identifying this evaluation.
bool noteStep(CodePtr OpPC)
Note that a step has been executed.
SourceInfo getSource(CodePtr PC) const
Delegates source mapping to the mapper.
Definition InterpState.h:79
InterpState(const InterpState &)=delete
bool stepsLeft() const override
Definition InterpState.h:72
llvm::SmallVector< std::pair< const Expr *, const LifetimeExtendedTemporaryDecl * > > SeenGlobalTemporaries
InterpFrame * allocFrame(const Function *F, Ts &&...Args)
Allocate memory and create a new InterpFrame for the given function.
InterpStack & Stk
Temporary stack.
bool maybeDiagnoseDanglingAllocations()
Diagnose any dynamic allocations that haven't been freed yet.
SourceLocation EvalLocation
Source location of the evaluating expression.
bool checkingConstantDestruction() const
Return if we're checking if a global variable has a constant destructor.
unsigned StepsLeft
Steps left during evaluation.
const VarDecl * EvaluatingDecl
Declaration we're initializing/evaluting, if any.
bool checkingConstantDestruction(const Pointer &Ptr) const
Return if we're checking if a global variable has a constant destructor and the given pointer is poin...
InterpFrame * Current
The current frame.
PointerPathEntry * allocPointerPath(unsigned Length, const PointerPathEntry *OldPP)
const CXXRecordDecl ** allocMemberPointerPath(unsigned Length)
const Frame * getCurrentFrame() override
std::optional< bool > ConstantContextOverride
const bool InfiniteSteps
Whether infinite evaluation steps have been requested.
InterpState & operator=(const InterpState &)=delete
friend class InterpStateCCOverride
T * allocate(size_t Num=1) const
void deallocate(Block *B)
Deallocates a pointer.
PointerPathEntry * extendPointerPath(unsigned NewLength, const PointerPathEntry *OldPP, PointerPathEntry NewEntry)
Allocate a new pointer path of Length NewLength.
llvm::SmallVector< PtrView > InitializingPtrs
List of blocks we're currently running either constructors or destructors for.
T allocAP(unsigned BitWidth)
void setEvalLocation(SourceLocation SL)
Definition InterpState.h:83
StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const
bool checkingConstantDestruction(const VarDecl *VD) const
Program & P
Reference to the module containing all bytecode.
A pointer to a memory block, live or dead.
Definition Pointer.h:541
The program contains and links the bytecode for all functions.
Definition Program.h:37
Describes the statement/declaration an opcode was generated from.
Definition Source.h:77
Interface for classes which map locations to sources.
Definition Source.h:138
Expr::EvalStatus & getEvalStatus() const
Definition State.h:89
State(ASTContext &ASTCtx, Expr::EvalStatus &EvalStatus)
Definition State.h:81
Top level wrappers for InstallAPI frontend operations.
const FunctionProtoType * T
__builtin_elementwise_add_sat __builtin_elementwise_sub_sat uint32_t __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 uint8_t
EvalStatus is a struct with detailed info about an evaluation in progress.
Definition Expr.h:622
SmallVectorImpl< PartialDiagnosticAt > * Diag
Diag - If this is non-null, it will be filled in with a stack of notes indicating why evaluation fail...
Definition Expr.h:650
const VarDecl * asVarDecl() const
Definition Descriptor.h:209