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