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;
30struct EvalSettings;
31
33
34 const Expr *Call = nullptr;
36 explicit operator bool() { return Call; }
37};
38
39// FIXME: Create one for the "checking potential constant expression"
40// evaluation.
41enum class EvaluationKind : uint8_t {
43 Dtor, /// We're checking for constant destruction of a global variable.
44};
45
46/// Interpreter context.
47class InterpState final : public State {
48public:
49 InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk,
50 FrameAllocator &FrameAlloc, Context &Ctx,
51 SourceMapper *M = nullptr);
52
53 InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk,
54 FrameAllocator &FA, Context &Ctx, const Function *Func);
55
58
60
61 void cleanup();
62
63 InterpState(const InterpState &) = delete;
64 InterpState &operator=(const InterpState &) = delete;
65
66 bool diagnosing() const { return getEvalStatus().Diag != nullptr; }
67
68 // Stack frame accessors.
69 const Frame *getCurrentFrame() override;
70 unsigned getCallStackDepth() override {
71 return Current ? (Current->getDepth() + 1) : 1;
72 }
73 bool stepsLeft() const override { return true; }
74 bool inConstantContext() const;
75
76 /// Deallocates a pointer.
77 void deallocate(Block *B);
78
79 /// Delegates source mapping to the mapper.
80 SourceInfo getSource(CodePtr PC) const { return M->getSource(PC); }
81
82 Context &getContext() const { return Ctx; }
83
85
87 if (!Alloc) {
88 if (!Allocator)
89 Allocator.emplace();
90 Alloc = std::make_unique<DynamicAllocator>(*Allocator);
91 }
92
93 return *Alloc;
94 }
95
96 /// Diagnose any dynamic allocations that haven't been freed yet.
97 /// Will return \c false if there were any allocations to diagnose,
98 /// \c true otherwise.
100
101 StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const;
102
103 void *allocate(size_t Size, unsigned Align = 8) const {
104 if (!Allocator)
105 Allocator.emplace();
106 return Allocator->Allocate(Size, Align);
107 }
108 template <typename T> T *allocate(size_t Num = 1) const {
109 return static_cast<T *>(allocate(Num * sizeof(T), alignof(T)));
110 }
111
112 template <typename T> T allocAP(unsigned BitWidth) {
113 unsigned NumWords = APInt::getNumWords(BitWidth);
114 if (NumWords == 1)
115 return T(BitWidth);
116 uint64_t *Mem = (uint64_t *)this->allocate(NumWords * sizeof(uint64_t));
117 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
118 return T(Mem, BitWidth);
119 }
120
121 Floating allocFloat(const llvm::fltSemantics &Sem) {
122 if (Floating::singleWord(Sem))
123 return Floating(llvm::APFloatBase::SemanticsToEnum(Sem));
124
125 unsigned NumWords =
126 APInt::getNumWords(llvm::APFloatBase::getSizeInBits(Sem));
127 uint64_t *Mem = (uint64_t *)this->allocate(NumWords * sizeof(uint64_t));
128 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
129 return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem));
130 }
131 const CXXRecordDecl **allocMemberPointerPath(unsigned Length) {
132 return reinterpret_cast<const CXXRecordDecl **>(
133 this->allocate(Length * sizeof(CXXRecordDecl *)));
134 }
136 const PointerPathEntry *OldPP) {
137 assert(Length != 0);
138 auto *PP = reinterpret_cast<PointerPathEntry *>(
139 this->allocate(Length * sizeof(PointerPathEntry)));
140 if (OldPP)
141 std::memcpy(PP, OldPP, sizeof(PointerPathEntry) * Length);
142 return PP;
143 }
144 /// Allocate a new pointer path of Length \c NewLength.
145 /// NewLength - 1 elements are copied form \c OldPP.
147 const PointerPathEntry *OldPP,
148 PointerPathEntry NewEntry) {
149 auto *PP = reinterpret_cast<PointerPathEntry *>(
150 this->allocate(NewLength * sizeof(PointerPathEntry)));
151 if (OldPP)
152 std::memcpy(PP, OldPP, sizeof(PointerPathEntry) * (NewLength - 1));
153 PP[NewLength - 1] = NewEntry;
154 return PP;
155 }
156
157 /// Note that a step has been executed. If there are no more steps remaining,
158 /// diagnoses and returns \c false.
159 bool noteStep(CodePtr OpPC) {
160 if (InfiniteSteps)
161 return true;
162
163 --StepsLeft;
164 if (LLVM_LIKELY(StepsLeft != 0))
165 return true;
166
167 return diagnoseStepLimitExceeded(OpPC);
168 }
169
170 bool initializingBlock(const Block *B) const {
172 if (V.block() == B)
173 return true;
174 return false;
175 }
176
177 bool lifetimeStartedInEvaluation(const Block *B) const {
179 return B->getEvalID() == EvalID;
180
182 assert(EvaluatingDecl);
184 return EvaluatingDecl->getType().isConstQualified();
185 }
186 return false;
187 }
188
189 /// Return if we're checking if a global variable has a constant destructor.
192 }
193 /// Return if we're checking if a global variable has a constant destructor
194 /// and the given pointer is pointing to the variable we're checking that for.
195 bool checkingConstantDestruction(const Pointer &Ptr) const {
196 return checkingConstantDestruction(Ptr.getRootVarDecl());
197 }
198 bool checkingConstantDestruction(const VarDecl *VD) const {
200 }
201
202 unsigned newStringID() { return StringID++; }
203
204 /// Allocate memory and create a new InterpFrame for the given function.
205 template <typename... Ts>
206 InterpFrame *allocFrame(const Function *F, Ts &&...Args) {
207 size_t FrameSize = InterpFrame::allocSize(F);
208 assert(FrameSize < std::numeric_limits<unsigned>::max());
209 InterpFrame *NewFrame = new (FrameAlloc.reserve(FrameSize))
210 InterpFrame(*this, F, std::forward<Ts>(Args)...);
211 assert(NewFrame);
212 return NewFrame;
213 }
214
215 /// Free resources associated with the current frame and set the caller to be
216 /// the new current frame.
218 assert(Current);
219 unsigned CurrentSize = InterpFrame::allocSize(Current->getFunction());
220 InterpFrame *Caller = Current->Caller;
221 Current->~InterpFrame();
222 FrameAlloc.pop(CurrentSize);
223 Current = Caller;
224 }
225
226private:
227 friend class EvaluationResult;
229 /// Dead block chain.
230 DeadBlock *DeadBlocks = nullptr;
231 /// Reference to the offset-source mapping.
232 SourceMapper *M;
233 /// Allocator for everything else, e.g. floating-point values.
234 mutable std::optional<llvm::BumpPtrAllocator> Allocator;
235 /// Allocator used for dynamic allocations performed via the program.
236 std::unique_ptr<DynamicAllocator> Alloc;
237 /// Diagnose that we've reached the constexpr step limit.
238 bool diagnoseStepLimitExceeded(CodePtr OpPC);
239
240 FrameAllocator &FrameAlloc;
241
242public:
244 /// Reference to the module containing all bytecode.
246 /// Temporary stack.
248 /// Interpreter Context.
250 /// Bottom function frame.
252 /// The current frame.
254 /// Source location of the evaluating expression
256 /// Declaration we're initializing/evaluting, if any.
257 const VarDecl *EvaluatingDecl = nullptr;
258 /// Steps left during evaluation.
259 unsigned StepsLeft = 1;
260 /// Whether infinite evaluation steps have been requested. If this is false,
261 /// we use the StepsLeft value above.
262 const bool InfiniteSteps = false;
263 /// ID identifying this evaluation.
264 const unsigned EvalID;
265
266 unsigned StringID = 0;
267
269
270 /// Things needed to do speculative execution.
272 bool PrevDiagsEmitted = false;
273#ifndef NDEBUG
274 unsigned SpeculationDepth = 0;
275#endif
276 unsigned DiagIgnoreDepth = 0;
277 std::optional<bool> ConstantContextOverride;
278
280 std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
282
283 /// List of blocks we're currently running either constructors or destructors
284 /// for.
286};
287
289public:
291 : Ctx(Ctx), OldCC(Ctx.ConstantContextOverride) {
292 // We only override this if the new value is true.
293 Enabled = Value;
294 if (Enabled)
295 Ctx.ConstantContextOverride = Value;
296 }
298 if (Enabled)
299 Ctx.ConstantContextOverride = OldCC;
300 }
301
302private:
303 bool Enabled;
304 InterpState &Ctx;
305 std::optional<bool> OldCC;
306};
307
308} // namespace interp
309} // namespace clang
310
311#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.
Pointer into the code segment.
Definition Source.h:31
Holds all information required to evaluate constexpr code in a module.
Definition Context.h:49
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:26
InterpStateCCOverride(InterpState &Ctx, bool Value)
Interpreter context.
Definition InterpState.h:47
SmallVectorImpl< PartialDiagnosticAt > * PrevDiags
Things needed to do speculative execution.
bool lifetimeStartedInEvaluation(const Block *B) const
unsigned getCallStackDepth() override
Definition InterpState.h:70
InterpFrame BottomFrame
Bottom function frame.
Context & getContext() const
Definition InterpState.h:82
bool initializingBlock(const Block *B) const
DynamicAllocator & getAllocator()
Definition InterpState.h:86
void resetCurrentFrame()
Free resources associated with the current frame and set the caller to be the new current frame.
Context & Ctx
Interpreter Context.
InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk, FrameAllocator &FrameAlloc, Context &Ctx, SourceMapper *M=nullptr)
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:80
InterpState(const InterpState &)=delete
bool stepsLeft() const override
Definition InterpState.h:73
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:84
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:546
The program contains and links the bytecode for all functions.
Definition Program.h:38
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
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:210