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 { return FrameDepth + 1; }
71 bool stepsLeft() const override { return true; }
72 bool inConstantContext() const;
73
74 /// Deallocates a pointer.
75 void deallocate(Block *B);
76
77 /// Delegates source mapping to the mapper.
78 SourceInfo getSource(CodePtr PC) const { return M->getSource(PC); }
79
80 Context &getContext() const { return Ctx; }
81
82 void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; }
83
84 DynamicAllocator &getAllocator() {
85 if (!Alloc) {
86 if (!Allocator)
87 Allocator.emplace();
88 Alloc = std::make_unique<DynamicAllocator>(args&: *Allocator);
89 }
90
91 return *Alloc;
92 }
93
94 /// Diagnose any dynamic allocations that haven't been freed yet.
95 /// Will return \c false if there were any allocations to diagnose,
96 /// \c true otherwise.
97 bool maybeDiagnoseDanglingAllocations();
98
99 StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const;
100
101 void *allocate(size_t Size, unsigned Align = 8) const {
102 if (!Allocator)
103 Allocator.emplace();
104 return Allocator->Allocate(Size, Alignment: Align);
105 }
106 template <typename T> T *allocate(size_t Num = 1) const {
107 return static_cast<T *>(allocate(Size: Num * sizeof(T), Align: alignof(T)));
108 }
109
110 template <typename T> T allocAP(unsigned BitWidth) {
111 unsigned NumWords = APInt::getNumWords(BitWidth);
112 if (NumWords == 1)
113 return T(BitWidth);
114 uint64_t *Mem = (uint64_t *)this->allocate(Size: NumWords * sizeof(uint64_t));
115 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
116 return T(Mem, BitWidth);
117 }
118
119 Floating allocFloat(const llvm::fltSemantics &Sem) {
120 if (Floating::singleWord(Sem))
121 return Floating(llvm::APFloatBase::SemanticsToEnum(Sem));
122
123 unsigned NumWords =
124 APInt::getNumWords(BitWidth: llvm::APFloatBase::getSizeInBits(Sem));
125 uint64_t *Mem = (uint64_t *)this->allocate(Size: NumWords * sizeof(uint64_t));
126 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
127 return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem));
128 }
129 const CXXRecordDecl **allocMemberPointerPath(unsigned Length) {
130 return reinterpret_cast<const CXXRecordDecl **>(
131 this->allocate(Size: Length * sizeof(CXXRecordDecl *)));
132 }
133 PointerPathEntry *allocPointerPath(unsigned Length,
134 const PointerPathEntry *OldPP) {
135 assert(Length != 0);
136 auto *PP = reinterpret_cast<PointerPathEntry *>(
137 this->allocate(Size: Length * sizeof(PointerPathEntry)));
138 if (OldPP)
139 std::memcpy(dest: PP, src: OldPP, n: sizeof(PointerPathEntry) * Length);
140 return PP;
141 }
142 /// Allocate a new pointer path of Length \c NewLength.
143 /// NewLength - 1 elements are copied form \c OldPP.
144 PointerPathEntry *extendPointerPath(unsigned NewLength,
145 const PointerPathEntry *OldPP,
146 PointerPathEntry NewEntry) {
147 auto *PP = reinterpret_cast<PointerPathEntry *>(
148 this->allocate(Size: NewLength * sizeof(PointerPathEntry)));
149 if (OldPP)
150 std::memcpy(dest: PP, src: OldPP, n: sizeof(PointerPathEntry) * (NewLength - 1));
151 PP[NewLength - 1] = NewEntry;
152 return PP;
153 }
154
155 /// Note that a step has been executed. If there are no more steps remaining,
156 /// diagnoses and returns \c false.
157 bool noteStep(CodePtr OpPC) {
158 if (InfiniteSteps)
159 return true;
160
161 --StepsLeft;
162 if (LLVM_LIKELY(StepsLeft != 0))
163 return true;
164
165 return diagnoseStepLimitExceeded(OpPC);
166 }
167
168 bool initializingBlock(const Block *B) const {
169 for (PtrView V : InitializingPtrs)
170 if (V.block() == B)
171 return true;
172 return false;
173 }
174
175 bool lifetimeStartedInEvaluation(const Block *B) const {
176 if (EvalKind == EvaluationKind::None)
177 return B->getEvalID() == EvalID;
178
179 if (EvalKind == EvaluationKind::Dtor) {
180 assert(EvaluatingDecl);
181 if (B->getDescriptor()->asVarDecl() == EvaluatingDecl)
182 return EvaluatingDecl->getType().isConstQualified();
183 }
184 return false;
185 }
186
187 /// Return if we're checking if a global variable has a constant destructor.
188 bool checkingConstantDestruction() const {
189 return EvalKind == EvaluationKind::Dtor;
190 }
191 /// Return if we're checking if a global variable has a constant destructor
192 /// and the given pointer is pointing to the variable we're checking that for.
193 bool checkingConstantDestruction(const Pointer &Ptr) const {
194 return checkingConstantDestruction(VD: Ptr.getRootVarDecl());
195 }
196 bool checkingConstantDestruction(const VarDecl *VD) const {
197 return EvalKind == EvaluationKind::Dtor && VD == EvaluatingDecl;
198 }
199
200 unsigned newStringID() { return StringID++; }
201
202 /// Allocate memory and create a new InterpFrame for the given function.
203 template <typename... Ts>
204 InterpFrame *allocFrame(const Function *F, Ts &&...Args) {
205 size_t FrameSize = InterpFrame::allocSize(F);
206 assert(FrameSize < std::numeric_limits<unsigned>::max());
207 InterpFrame *NewFrame = new (FrameAlloc.reserve(Size: FrameSize))
208 InterpFrame(*this, F, std::forward<Ts>(Args)...);
209 assert(NewFrame);
210 return NewFrame;
211 }
212
213 /// Set the given frame as the current one.
214 void enterFrame(InterpFrame *F) {
215 Current = F;
216 ++FrameDepth;
217 }
218
219 /// Free resources associated with the current frame and set the caller to be
220 /// the new current frame.
221 void exitFrame() {
222 assert(Current);
223 assert(FrameDepth > 0);
224 unsigned CurrentSize = InterpFrame::allocSize(F: Current->getFunction());
225 InterpFrame *Caller = Current->Caller;
226 Current->~InterpFrame();
227 FrameAlloc.pop(FrameSize: CurrentSize);
228 Current = Caller;
229 --FrameDepth;
230 }
231
232private:
233 friend class EvaluationResult;
234 friend class InterpStateCCOverride;
235 /// Dead block chain.
236 DeadBlock *DeadBlocks = nullptr;
237 /// Reference to the offset-source mapping.
238 SourceMapper *M;
239 /// Allocator for everything else, e.g. floating-point values.
240 mutable std::optional<llvm::BumpPtrAllocator> Allocator;
241 /// Allocator used for dynamic allocations performed via the program.
242 std::unique_ptr<DynamicAllocator> Alloc;
243 /// Diagnose that we've reached the constexpr step limit.
244 bool diagnoseStepLimitExceeded(CodePtr OpPC);
245
246 FrameAllocator &FrameAlloc;
247
248public:
249 CodePtr PC;
250 /// Reference to the module containing all bytecode.
251 Program &P;
252 /// Temporary stack.
253 InterpStack &Stk;
254 /// Interpreter Context.
255 Context &Ctx;
256 /// Bottom function frame.
257 InterpFrame BottomFrame;
258 /// The current frame.
259 InterpFrame *Current = nullptr;
260 /// Source location of the evaluating expression
261 SourceLocation EvalLocation;
262 /// Declaration we're initializing/evaluting, if any.
263 const VarDecl *EvaluatingDecl = nullptr;
264 /// Steps left during evaluation.
265 unsigned StepsLeft = 1;
266 /// Whether infinite evaluation steps have been requested. If this is false,
267 /// we use the StepsLeft value above.
268 const bool InfiniteSteps = false;
269 /// ID identifying this evaluation.
270 const unsigned EvalID;
271 unsigned FrameDepth = 0;
272
273 unsigned StringID = 0;
274
275 EvaluationKind EvalKind = EvaluationKind::None;
276
277 /// Things needed to do speculative execution.
278 SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
279 bool PrevDiagsEmitted = false;
280#ifndef NDEBUG
281 unsigned SpeculationDepth = 0;
282#endif
283 unsigned DiagIgnoreDepth = 0;
284 std::optional<bool> ConstantContextOverride;
285
286 llvm::SmallVector<
287 std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
288 SeenGlobalTemporaries;
289
290 /// List of blocks we're currently running either constructors or destructors
291 /// for.
292 llvm::SmallVector<PtrView> InitializingPtrs;
293};
294
295class InterpStateCCOverride final {
296public:
297 InterpStateCCOverride(InterpState &Ctx, bool Value)
298 : Ctx(Ctx), OldCC(Ctx.ConstantContextOverride) {
299 // We only override this if the new value is true.
300 Enabled = Value;
301 if (Enabled)
302 Ctx.ConstantContextOverride = Value;
303 }
304 ~InterpStateCCOverride() {
305 if (Enabled)
306 Ctx.ConstantContextOverride = OldCC;
307 }
308
309private:
310 bool Enabled;
311 InterpState &Ctx;
312 std::optional<bool> OldCC;
313};
314
315} // namespace interp
316} // namespace clang
317
318#endif
319