1 | //===--- EvalEmitter.cpp - Instruction emitter for the 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 | #include "EvalEmitter.h" |
10 | #include "Context.h" |
11 | #include "IntegralAP.h" |
12 | #include "Interp.h" |
13 | #include "Opcode.h" |
14 | #include "clang/AST/DeclCXX.h" |
15 | |
16 | using namespace clang; |
17 | using namespace clang::interp; |
18 | |
19 | EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, |
20 | InterpStack &Stk) |
21 | : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) { |
22 | // Create a dummy frame for the interpreter which does not have locals. |
23 | S.Current = |
24 | new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr(), 0); |
25 | } |
26 | |
27 | EvalEmitter::~EvalEmitter() { |
28 | for (auto &[K, V] : Locals) { |
29 | Block *B = reinterpret_cast<Block *>(V.get()); |
30 | if (B->isInitialized()) |
31 | B->invokeDtor(); |
32 | } |
33 | } |
34 | |
35 | /// Clean up all our resources. This needs to done in failed evaluations before |
36 | /// we call InterpStack::clear(), because there might be a Pointer on the stack |
37 | /// pointing into a Block in the EvalEmitter. |
38 | void EvalEmitter::cleanup() { S.cleanup(); } |
39 | |
40 | EvaluationResult EvalEmitter::interpretExpr(const Expr *E, |
41 | bool ConvertResultToRValue) { |
42 | S.setEvalLocation(E->getExprLoc()); |
43 | this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(Val: E); |
44 | this->CheckFullyInitialized = isa<ConstantExpr>(Val: E); |
45 | EvalResult.setSource(E); |
46 | |
47 | if (!this->visitExpr(E)) { |
48 | // EvalResult may already have a result set, but something failed |
49 | // after that (e.g. evaluating destructors). |
50 | EvalResult.setInvalid(); |
51 | } |
52 | |
53 | return std::move(this->EvalResult); |
54 | } |
55 | |
56 | EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD, |
57 | bool CheckFullyInitialized) { |
58 | this->CheckFullyInitialized = CheckFullyInitialized; |
59 | S.EvaluatingDecl = VD; |
60 | EvalResult.setSource(VD); |
61 | |
62 | if (const Expr *Init = VD->getAnyInitializer()) { |
63 | QualType T = VD->getType(); |
64 | this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() && |
65 | !T->isObjCObjectPointerType(); |
66 | } else |
67 | this->ConvertResultToRValue = false; |
68 | |
69 | EvalResult.setSource(VD); |
70 | |
71 | if (!this->visitDeclAndReturn(VD, ConstantContext: S.inConstantContext())) |
72 | EvalResult.setInvalid(); |
73 | |
74 | S.EvaluatingDecl = nullptr; |
75 | updateGlobalTemporaries(); |
76 | return std::move(this->EvalResult); |
77 | } |
78 | |
79 | void EvalEmitter::emitLabel(LabelTy Label) { |
80 | CurrentLabel = Label; |
81 | } |
82 | |
83 | EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; } |
84 | |
85 | Scope::Local EvalEmitter::createLocal(Descriptor *D) { |
86 | // Allocate memory for a local. |
87 | auto Memory = std::make_unique<char[]>(num: sizeof(Block) + D->getAllocSize()); |
88 | auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false); |
89 | B->invokeCtor(); |
90 | |
91 | // Initialize local variable inline descriptor. |
92 | InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); |
93 | Desc.Desc = D; |
94 | Desc.Offset = sizeof(InlineDescriptor); |
95 | Desc.IsActive = true; |
96 | Desc.IsBase = false; |
97 | Desc.IsFieldMutable = false; |
98 | Desc.IsConst = false; |
99 | Desc.IsInitialized = false; |
100 | |
101 | // Register the local. |
102 | unsigned Off = Locals.size(); |
103 | Locals.insert(KV: {Off, std::move(Memory)}); |
104 | return {.Offset: Off, .Desc: D}; |
105 | } |
106 | |
107 | bool EvalEmitter::jumpTrue(const LabelTy &Label) { |
108 | if (isActive()) { |
109 | if (S.Stk.pop<bool>()) |
110 | ActiveLabel = Label; |
111 | } |
112 | return true; |
113 | } |
114 | |
115 | bool EvalEmitter::jumpFalse(const LabelTy &Label) { |
116 | if (isActive()) { |
117 | if (!S.Stk.pop<bool>()) |
118 | ActiveLabel = Label; |
119 | } |
120 | return true; |
121 | } |
122 | |
123 | bool EvalEmitter::jump(const LabelTy &Label) { |
124 | if (isActive()) |
125 | CurrentLabel = ActiveLabel = Label; |
126 | return true; |
127 | } |
128 | |
129 | bool EvalEmitter::fallthrough(const LabelTy &Label) { |
130 | if (isActive()) |
131 | ActiveLabel = Label; |
132 | CurrentLabel = Label; |
133 | return true; |
134 | } |
135 | |
136 | static bool checkReturnState(InterpState &S) { |
137 | return S.maybeDiagnoseDanglingAllocations(); |
138 | } |
139 | |
140 | template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { |
141 | if (!isActive()) |
142 | return true; |
143 | |
144 | if (!checkReturnState(S)) |
145 | return false; |
146 | |
147 | using T = typename PrimConv<OpType>::T; |
148 | EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext())); |
149 | return true; |
150 | } |
151 | |
152 | template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { |
153 | if (!isActive()) |
154 | return true; |
155 | |
156 | const Pointer &Ptr = S.Stk.pop<Pointer>(); |
157 | |
158 | if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info)) |
159 | return false; |
160 | if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr)) |
161 | return false; |
162 | |
163 | if (!checkReturnState(S)) |
164 | return false; |
165 | |
166 | // Implicitly convert lvalue to rvalue, if requested. |
167 | if (ConvertResultToRValue) { |
168 | if (!Ptr.isZero() && !Ptr.isDereferencable()) |
169 | return false; |
170 | // Never allow reading from a non-const pointer, unless the memory |
171 | // has been created in this evaluation. |
172 | if (!Ptr.isZero() && Ptr.isBlockPointer() && |
173 | Ptr.block()->getEvalID() != Ctx.getEvalID() && |
174 | (!CheckLoad(S, OpPC, Ptr, AK: AK_Read) || !Ptr.isConst())) |
175 | return false; |
176 | |
177 | if (std::optional<APValue> V = |
178 | Ptr.toRValue(Ctx, ResultType: EvalResult.getSourceType())) { |
179 | EvalResult.setValue(*V); |
180 | } else { |
181 | return false; |
182 | } |
183 | } else { |
184 | EvalResult.setValue(Ptr.toAPValue(ASTCtx: Ctx.getASTContext())); |
185 | } |
186 | |
187 | return true; |
188 | } |
189 | template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) { |
190 | if (!isActive()) |
191 | return true; |
192 | |
193 | if (!checkReturnState(S)) |
194 | return false; |
195 | // Function pointers cannot be converted to rvalues. |
196 | EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>()); |
197 | return true; |
198 | } |
199 | |
200 | bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { |
201 | if (!checkReturnState(S)) |
202 | return false; |
203 | EvalResult.setValid(); |
204 | return true; |
205 | } |
206 | |
207 | bool EvalEmitter::emitRetValue(const SourceInfo &Info) { |
208 | const auto &Ptr = S.Stk.pop<Pointer>(); |
209 | |
210 | if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info)) |
211 | return false; |
212 | if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr)) |
213 | return false; |
214 | |
215 | if (!checkReturnState(S)) |
216 | return false; |
217 | |
218 | if (std::optional<APValue> APV = |
219 | Ptr.toRValue(Ctx: S.getCtx(), ResultType: EvalResult.getSourceType())) { |
220 | EvalResult.setValue(*APV); |
221 | return true; |
222 | } |
223 | |
224 | EvalResult.setInvalid(); |
225 | return false; |
226 | } |
227 | |
228 | bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { |
229 | if (!isActive()) |
230 | return true; |
231 | |
232 | Block *B = getLocal(Index: I); |
233 | S.Stk.push<Pointer>(Args&: B, Args: sizeof(InlineDescriptor)); |
234 | return true; |
235 | } |
236 | |
237 | template <PrimType OpType> |
238 | bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { |
239 | if (!isActive()) |
240 | return true; |
241 | |
242 | using T = typename PrimConv<OpType>::T; |
243 | |
244 | Block *B = getLocal(Index: I); |
245 | S.Stk.push<T>(*reinterpret_cast<T *>(B->data())); |
246 | return true; |
247 | } |
248 | |
249 | template <PrimType OpType> |
250 | bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { |
251 | if (!isActive()) |
252 | return true; |
253 | |
254 | using T = typename PrimConv<OpType>::T; |
255 | |
256 | Block *B = getLocal(Index: I); |
257 | *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>(); |
258 | InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); |
259 | Desc.IsInitialized = true; |
260 | |
261 | return true; |
262 | } |
263 | |
264 | bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { |
265 | if (!isActive()) |
266 | return true; |
267 | |
268 | for (auto &Local : Descriptors[I]) { |
269 | Block *B = getLocal(Index: Local.Offset); |
270 | S.deallocate(B); |
271 | } |
272 | |
273 | return true; |
274 | } |
275 | |
276 | /// Global temporaries (LifetimeExtendedTemporary) carry their value |
277 | /// around as an APValue, which codegen accesses. |
278 | /// We set their value once when creating them, but we don't update it |
279 | /// afterwards when code changes it later. |
280 | /// This is what we do here. |
281 | void EvalEmitter::updateGlobalTemporaries() { |
282 | for (const auto &[E, Temp] : S.SeenGlobalTemporaries) { |
283 | if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) { |
284 | const Pointer &Ptr = P.getPtrGlobal(Idx: *GlobalIndex); |
285 | APValue *Cached = Temp->getOrCreateValue(MayCreate: true); |
286 | |
287 | if (std::optional<PrimType> T = Ctx.classify(T: E->getType())) { |
288 | TYPE_SWITCH( |
289 | *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); }); |
290 | } else { |
291 | if (std::optional<APValue> APV = |
292 | Ptr.toRValue(Ctx, ResultType: Temp->getTemporaryExpr()->getType())) |
293 | *Cached = *APV; |
294 | } |
295 | } |
296 | } |
297 | S.SeenGlobalTemporaries.clear(); |
298 | } |
299 | |
300 | //===----------------------------------------------------------------------===// |
301 | // Opcode evaluators |
302 | //===----------------------------------------------------------------------===// |
303 | |
304 | #define GET_EVAL_IMPL |
305 | #include "Opcodes.inc" |
306 | #undef GET_EVAL_IMPL |
307 | |