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
32struct StdAllocatorCaller {
33
34 const Expr *Call = nullptr;
35 QualType AllocType;
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 {
42 None,
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
56 InterpState(Expr::EvalStatus &Status, Program &P, InterpStack &Stk,
57 FrameAllocator &FA, Context &Ctx, SourceMapper *M);
58
59 ~InterpState();
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
84 void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; }
85
86 DynamicAllocator &getAllocator() {
87 if (!Alloc) {
88 if (!Allocator)
89 Allocator.emplace();
90 Alloc = std::make_unique<DynamicAllocator>(args&: *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.
99 bool maybeDiagnoseDanglingAllocations();
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, Alignment: Align);
107 }
108 template <typename T> T *allocate(size_t Num = 1) const {
109 return static_cast<T *>(allocate(Size: Num * sizeof(T), Align: 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(Size: 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(BitWidth: llvm::APFloatBase::getSizeInBits(Sem));
127 uint64_t *Mem = (uint64_t *)this->allocate(Size: 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(Size: Length * sizeof(CXXRecordDecl *)));
134 }
135 PointerPathEntry *allocPointerPath(unsigned Length,
136 const PointerPathEntry *OldPP) {
137 assert(Length != 0);
138 auto *PP = reinterpret_cast<PointerPathEntry *>(
139 this->allocate(Size: Length * sizeof(PointerPathEntry)));
140 if (OldPP)
141 std::memcpy(dest: PP, src: OldPP, n: 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.
146 PointerPathEntry *extendPointerPath(unsigned NewLength,
147 const PointerPathEntry *OldPP,
148 PointerPathEntry NewEntry) {
149 auto *PP = reinterpret_cast<PointerPathEntry *>(
150 this->allocate(Size: NewLength * sizeof(PointerPathEntry)));
151 if (OldPP)
152 std::memcpy(dest: PP, src: OldPP, n: 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 {
171 for (PtrView V : InitializingPtrs)
172 if (V.block() == B)
173 return true;
174 return false;
175 }
176
177 bool lifetimeStartedInEvaluation(const Block *B) const {
178 if (EvalKind == EvaluationKind::None)
179 return B->getEvalID() == EvalID;
180
181 if (EvalKind == EvaluationKind::Dtor) {
182 assert(EvaluatingDecl);
183 if (B->getDescriptor()->asVarDecl() == 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.
190 bool checkingConstantDestruction() const {
191 return EvalKind == EvaluationKind::Dtor;
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(VD: Ptr.getRootVarDecl());
197 }
198 bool checkingConstantDestruction(const VarDecl *VD) const {
199 return EvalKind == EvaluationKind::Dtor && VD == EvaluatingDecl;
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(Size: 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.
217 void resetCurrentFrame() {
218 assert(Current);
219 unsigned CurrentSize = InterpFrame::allocSize(F: Current->getFunction());
220 InterpFrame *Caller = Current->Caller;
221 Current->~InterpFrame();
222 FrameAlloc.pop(FrameSize: CurrentSize);
223 Current = Caller;
224 }
225
226private:
227 friend class EvaluationResult;
228 friend class InterpStateCCOverride;
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:
243 CodePtr PC;
244 /// Reference to the module containing all bytecode.
245 Program &P;
246 /// Temporary stack.
247 InterpStack &Stk;
248 /// Interpreter Context.
249 Context &Ctx;
250 /// Bottom function frame.
251 InterpFrame BottomFrame;
252 /// The current frame.
253 InterpFrame *Current = nullptr;
254 /// Source location of the evaluating expression
255 SourceLocation EvalLocation;
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
268 EvaluationKind EvalKind = EvaluationKind::None;
269
270 /// Things needed to do speculative execution.
271 SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
272 bool PrevDiagsEmitted = false;
273#ifndef NDEBUG
274 unsigned SpeculationDepth = 0;
275#endif
276 unsigned DiagIgnoreDepth = 0;
277 std::optional<bool> ConstantContextOverride;
278
279 llvm::SmallVector<
280 std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
281 SeenGlobalTemporaries;
282
283 /// List of blocks we're currently running either constructors or destructors
284 /// for.
285 llvm::SmallVector<PtrView> InitializingPtrs;
286};
287
288class InterpStateCCOverride final {
289public:
290 InterpStateCCOverride(InterpState &Ctx, bool Value)
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 }
297 ~InterpStateCCOverride() {
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
312