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 "Function.h"
20#include "InterpFrame.h"
21#include "InterpStack.h"
22#include "State.h"
23
24namespace clang {
25namespace interp {
26class Context;
27class SourceMapper;
28
29struct StdAllocatorCaller {
30 const Expr *Call = nullptr;
31 QualType AllocType;
32 explicit operator bool() { return Call; }
33};
34
35// FIXME: Create one for the "checking potential constant expression"
36// evaluation.
37enum class EvaluationKind : uint8_t {
38 None,
39 Dtor, /// We're checking for constant destruction of a global variable.
40};
41
42/// Interpreter context.
43class InterpState final : public State {
44public:
45 InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
46 SourceMapper *M = nullptr);
47 InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
48 const Function *Func);
49
50 ~InterpState();
51
52 void cleanup();
53
54 InterpState(const InterpState &) = delete;
55 InterpState &operator=(const InterpState &) = delete;
56
57 bool diagnosing() const { return getEvalStatus().Diag != nullptr; }
58
59 // Stack frame accessors.
60 const Frame *getCurrentFrame() override;
61 unsigned getCallStackDepth() override {
62 return Current ? (Current->getDepth() + 1) : 1;
63 }
64 bool stepsLeft() const override { return true; }
65 bool inConstantContext() const;
66
67 /// Deallocates a pointer.
68 void deallocate(Block *B);
69
70 /// Delegates source mapping to the mapper.
71 SourceInfo getSource(CodePtr PC) const { return M->getSource(PC); }
72
73 Context &getContext() const { return Ctx; }
74
75 void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; }
76
77 DynamicAllocator &getAllocator() {
78 if (!Alloc) {
79 Alloc = std::make_unique<DynamicAllocator>();
80 }
81
82 return *Alloc;
83 }
84
85 /// Diagnose any dynamic allocations that haven't been freed yet.
86 /// Will return \c false if there were any allocations to diagnose,
87 /// \c true otherwise.
88 bool maybeDiagnoseDanglingAllocations();
89
90 StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const;
91
92 void *allocate(size_t Size, unsigned Align = 8) const {
93 if (!Allocator)
94 Allocator.emplace();
95 return Allocator->Allocate(Size, Alignment: Align);
96 }
97 template <typename T> T *allocate(size_t Num = 1) const {
98 return static_cast<T *>(allocate(Size: Num * sizeof(T), Align: alignof(T)));
99 }
100
101 template <typename T> T allocAP(unsigned BitWidth) {
102 unsigned NumWords = APInt::getNumWords(BitWidth);
103 if (NumWords == 1)
104 return T(BitWidth);
105 uint64_t *Mem = (uint64_t *)this->allocate(Size: NumWords * sizeof(uint64_t));
106 // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug
107 return T(Mem, BitWidth);
108 }
109
110 Floating allocFloat(const llvm::fltSemantics &Sem) {
111 if (Floating::singleWord(Sem))
112 return Floating(llvm::APFloatBase::SemanticsToEnum(Sem));
113
114 unsigned NumWords =
115 APInt::getNumWords(BitWidth: llvm::APFloatBase::getSizeInBits(Sem));
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 Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem));
119 }
120 const CXXRecordDecl **allocMemberPointerPath(unsigned Length) {
121 return reinterpret_cast<const CXXRecordDecl **>(
122 this->allocate(Size: Length * sizeof(CXXRecordDecl *)));
123 }
124
125 /// Note that a step has been executed. If there are no more steps remaining,
126 /// diagnoses and returns \c false.
127 bool noteStep(CodePtr OpPC) {
128 if (InfiniteSteps)
129 return true;
130
131 --StepsLeft;
132 if (LLVM_LIKELY(StepsLeft != 0))
133 return true;
134
135 return diagnoseStepLimitExceeded(OpPC);
136 }
137
138 bool initializingBlock(const Block *B) const {
139 for (PtrView V : InitializingPtrs)
140 if (V.block() == B)
141 return true;
142 return false;
143 }
144
145 bool lifetimeStartedInEvaluation(const Block *B) const {
146 if (EvalKind == EvaluationKind::None)
147 return B->getEvalID() == EvalID;
148
149 if (EvalKind == EvaluationKind::Dtor) {
150 assert(EvaluatingDecl);
151 if (B->getDescriptor()->asVarDecl() == EvaluatingDecl)
152 return EvaluatingDecl->getType().isConstQualified();
153 }
154 return false;
155 }
156
157 /// Return if we're checking if a global variable has a constant destructor.
158 bool checkingConstantDestruction() const {
159 return EvalKind == EvaluationKind::Dtor;
160 }
161 /// Return if we're checking if a global variable has a constant destructor
162 /// and the given pointer is pointing to the variable we're checking that for.
163 bool checkingConstantDestruction(const Pointer &Ptr) const {
164 return checkingConstantDestruction(VD: Ptr.getRootVarDecl());
165 }
166 bool checkingConstantDestruction(const VarDecl *VD) const {
167 return EvalKind == EvaluationKind::Dtor && VD == EvaluatingDecl;
168 }
169
170 unsigned newStringID() { return StringID++; }
171
172private:
173 friend class EvaluationResult;
174 friend class InterpStateCCOverride;
175 /// Dead block chain.
176 DeadBlock *DeadBlocks = nullptr;
177 /// Reference to the offset-source mapping.
178 SourceMapper *M;
179 /// Allocator used for dynamic allocations performed via the program.
180 std::unique_ptr<DynamicAllocator> Alloc;
181 /// Allocator for everything else, e.g. floating-point values.
182 mutable std::optional<llvm::BumpPtrAllocator> Allocator;
183 /// Diagnose that we've reached the constexpr step limit.
184 bool diagnoseStepLimitExceeded(CodePtr OpPC);
185
186public:
187 CodePtr PC;
188 /// Reference to the module containing all bytecode.
189 Program &P;
190 /// Temporary stack.
191 InterpStack &Stk;
192 /// Interpreter Context.
193 Context &Ctx;
194 /// Bottom function frame.
195 InterpFrame BottomFrame;
196 /// The current frame.
197 InterpFrame *Current = nullptr;
198 /// Source location of the evaluating expression
199 SourceLocation EvalLocation;
200 /// Declaration we're initializing/evaluting, if any.
201 const VarDecl *EvaluatingDecl = nullptr;
202 /// Steps left during evaluation.
203 unsigned StepsLeft = 1;
204 /// Whether infinite evaluation steps have been requested. If this is false,
205 /// we use the StepsLeft value above.
206 const bool InfiniteSteps = false;
207 /// ID identifying this evaluation.
208 const unsigned EvalID;
209
210 unsigned StringID = 0;
211
212 EvaluationKind EvalKind = EvaluationKind::None;
213
214 /// Things needed to do speculative execution.
215 SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
216 bool PrevDiagsEmitted = false;
217#ifndef NDEBUG
218 unsigned SpeculationDepth = 0;
219#endif
220 unsigned DiagIgnoreDepth = 0;
221 std::optional<bool> ConstantContextOverride;
222
223 llvm::SmallVector<
224 std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>>
225 SeenGlobalTemporaries;
226
227 /// List of blocks we're currently running either constructors or destructors
228 /// for.
229 llvm::SmallVector<PtrView> InitializingPtrs;
230};
231
232class InterpStateCCOverride final {
233public:
234 InterpStateCCOverride(InterpState &Ctx, bool Value)
235 : Ctx(Ctx), OldCC(Ctx.ConstantContextOverride) {
236 // We only override this if the new value is true.
237 Enabled = Value;
238 if (Enabled)
239 Ctx.ConstantContextOverride = Value;
240 }
241 ~InterpStateCCOverride() {
242 if (Enabled)
243 Ctx.ConstantContextOverride = OldCC;
244 }
245
246private:
247 bool Enabled;
248 InterpState &Ctx;
249 std::optional<bool> OldCC;
250};
251
252} // namespace interp
253} // namespace clang
254
255#endif
256