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 | #include "clang/AST/APValue.h" |
24 | #include "clang/AST/ASTDiagnostic.h" |
25 | #include "clang/AST/Expr.h" |
26 | #include "clang/AST/OptionalDiagnostic.h" |
27 | |
28 | namespace clang { |
29 | namespace interp { |
30 | class Context; |
31 | class Function; |
32 | class InterpStack; |
33 | class InterpFrame; |
34 | class SourceMapper; |
35 | |
36 | struct StdAllocatorCaller { |
37 | const Expr *Call = nullptr; |
38 | QualType AllocType; |
39 | explicit operator bool() { return Call; } |
40 | }; |
41 | |
42 | /// Interpreter context. |
43 | class InterpState final : public State, public SourceMapper { |
44 | public: |
45 | InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, |
46 | SourceMapper *M = nullptr); |
47 | InterpState(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 | Frame *getSplitFrame() { return Parent.getCurrentFrame(); } |
61 | Frame *getCurrentFrame() override; |
62 | unsigned getCallStackDepth() override { |
63 | return Current ? (Current->getDepth() + 1) : 1; |
64 | } |
65 | const Frame *getBottomFrame() const override { |
66 | return Parent.getBottomFrame(); |
67 | } |
68 | |
69 | // Access objects from the walker context. |
70 | Expr::EvalStatus &getEvalStatus() const override { |
71 | return Parent.getEvalStatus(); |
72 | } |
73 | ASTContext &getASTContext() const override { return Parent.getASTContext(); } |
74 | |
75 | // Forward status checks and updates to the walker. |
76 | bool checkingForUndefinedBehavior() const override { |
77 | return Parent.checkingForUndefinedBehavior(); |
78 | } |
79 | bool keepEvaluatingAfterFailure() const override { |
80 | return Parent.keepEvaluatingAfterFailure(); |
81 | } |
82 | bool keepEvaluatingAfterSideEffect() const override { |
83 | return Parent.keepEvaluatingAfterSideEffect(); |
84 | } |
85 | bool checkingPotentialConstantExpression() const override { |
86 | return Parent.checkingPotentialConstantExpression(); |
87 | } |
88 | bool noteUndefinedBehavior() override { |
89 | return Parent.noteUndefinedBehavior(); |
90 | } |
91 | bool inConstantContext() const; |
92 | bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); } |
93 | void setActiveDiagnostic(bool Flag) override { |
94 | Parent.setActiveDiagnostic(Flag); |
95 | } |
96 | void setFoldFailureDiagnostic(bool Flag) override { |
97 | Parent.setFoldFailureDiagnostic(Flag); |
98 | } |
99 | bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); } |
100 | bool noteSideEffect() override { return Parent.noteSideEffect(); } |
101 | |
102 | /// Reports overflow and return true if evaluation should continue. |
103 | bool reportOverflow(const Expr *E, const llvm::APSInt &Value); |
104 | |
105 | /// Deallocates a pointer. |
106 | void deallocate(Block *B); |
107 | |
108 | /// Delegates source mapping to the mapper. |
109 | SourceInfo getSource(const Function *F, CodePtr PC) const override { |
110 | if (M) |
111 | return M->getSource(F, PC); |
112 | |
113 | assert(F && "Function cannot be null" ); |
114 | return F->getSource(PC); |
115 | } |
116 | |
117 | Context &getContext() const { return Ctx; } |
118 | |
119 | void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; } |
120 | |
121 | DynamicAllocator &getAllocator() { return Alloc; } |
122 | |
123 | /// Diagnose any dynamic allocations that haven't been freed yet. |
124 | /// Will return \c false if there were any allocations to diagnose, |
125 | /// \c true otherwise. |
126 | bool maybeDiagnoseDanglingAllocations(); |
127 | |
128 | StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const; |
129 | |
130 | void *allocate(size_t Size, unsigned Align = 8) const { |
131 | return Allocator.Allocate(Size, Alignment: Align); |
132 | } |
133 | template <typename T> T *allocate(size_t Num = 1) const { |
134 | return static_cast<T *>(allocate(Size: Num * sizeof(T), Align: alignof(T))); |
135 | } |
136 | |
137 | template <typename T> T allocAP(unsigned BitWidth) { |
138 | unsigned NumWords = APInt::getNumWords(BitWidth); |
139 | if (NumWords == 1) |
140 | return T(BitWidth); |
141 | uint64_t *Mem = (uint64_t *)this->allocate(Size: NumWords * sizeof(uint64_t)); |
142 | // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug |
143 | return T(Mem, BitWidth); |
144 | } |
145 | |
146 | Floating allocFloat(const llvm::fltSemantics &Sem) { |
147 | if (Floating::singleWord(Sem)) |
148 | return Floating(llvm::APFloatBase::SemanticsToEnum(Sem)); |
149 | |
150 | unsigned NumWords = |
151 | APInt::getNumWords(BitWidth: llvm::APFloatBase::getSizeInBits(Sem)); |
152 | uint64_t *Mem = (uint64_t *)this->allocate(Size: NumWords * sizeof(uint64_t)); |
153 | // std::memset(Mem, 0, NumWords * sizeof(uint64_t)); // Debug |
154 | return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem)); |
155 | } |
156 | |
157 | private: |
158 | friend class EvaluationResult; |
159 | friend class InterpStateCCOverride; |
160 | /// AST Walker state. |
161 | State &Parent; |
162 | /// Dead block chain. |
163 | DeadBlock *DeadBlocks = nullptr; |
164 | /// Reference to the offset-source mapping. |
165 | SourceMapper *M; |
166 | /// Allocator used for dynamic allocations performed via the program. |
167 | DynamicAllocator Alloc; |
168 | |
169 | public: |
170 | /// Reference to the module containing all bytecode. |
171 | Program &P; |
172 | /// Temporary stack. |
173 | InterpStack &Stk; |
174 | /// Interpreter Context. |
175 | Context &Ctx; |
176 | /// Bottom function frame. |
177 | InterpFrame BottomFrame; |
178 | /// The current frame. |
179 | InterpFrame *Current = nullptr; |
180 | /// Source location of the evaluating expression |
181 | SourceLocation EvalLocation; |
182 | /// Declaration we're initializing/evaluting, if any. |
183 | const VarDecl *EvaluatingDecl = nullptr; |
184 | /// Things needed to do speculative execution. |
185 | SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr; |
186 | unsigned SpeculationDepth = 0; |
187 | std::optional<bool> ConstantContextOverride; |
188 | |
189 | llvm::SmallVector< |
190 | std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>> |
191 | SeenGlobalTemporaries; |
192 | |
193 | mutable llvm::BumpPtrAllocator Allocator; |
194 | }; |
195 | |
196 | class InterpStateCCOverride final { |
197 | public: |
198 | InterpStateCCOverride(InterpState &Ctx, bool Value) |
199 | : Ctx(Ctx), OldCC(Ctx.ConstantContextOverride) { |
200 | // We only override this if the new value is true. |
201 | Enabled = Value; |
202 | if (Enabled) |
203 | Ctx.ConstantContextOverride = Value; |
204 | } |
205 | ~InterpStateCCOverride() { |
206 | if (Enabled) |
207 | Ctx.ConstantContextOverride = OldCC; |
208 | } |
209 | |
210 | private: |
211 | bool Enabled; |
212 | InterpState &Ctx; |
213 | std::optional<bool> OldCC; |
214 | }; |
215 | |
216 | } // namespace interp |
217 | } // namespace clang |
218 | |
219 | #endif |
220 | |