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 "clang/AST/DeclCXX.h"
14#include "clang/AST/ExprCXX.h"
15#include "llvm/ADT/ScopeExit.h"
16
17using namespace clang;
18using namespace clang::interp;
19
20EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
21 InterpStack &Stk)
22 : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
23
24EvalEmitter::~EvalEmitter() {
25 for (auto &V : Locals) {
26 Block *B = reinterpret_cast<Block *>(V.get());
27 if (B->isInitialized())
28 B->invokeDtor();
29 }
30}
31
32/// Clean up all our resources. This needs to done in failed evaluations before
33/// we call InterpStack::clear(), because there might be a Pointer on the stack
34/// pointing into a Block in the EvalEmitter.
35void EvalEmitter::cleanup() { S.cleanup(); }
36
37EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
38 bool ConvertResultToRValue,
39 bool DestroyToplevelScope) {
40 S.setEvalLocation(E->getExprLoc());
41 this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(Val: E);
42 this->CheckFullyInitialized = isa<ConstantExpr>(Val: E) && !E->isGLValue();
43 EvalResult.setSource(E);
44
45 if (!this->visitExpr(E, DestroyToplevelScope)) {
46 // EvalResult may already have a result set, but something failed
47 // after that (e.g. evaluating destructors).
48 EvalResult.setInvalid();
49 }
50
51 return std::move(this->EvalResult);
52}
53
54EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD, const Expr *Init,
55 bool CheckFullyInitialized) {
56 assert(VD);
57 assert(Init);
58 this->CheckFullyInitialized = CheckFullyInitialized;
59 S.EvaluatingDecl = VD;
60 S.setEvalLocation(VD->getLocation());
61 EvalResult.setSource(VD);
62
63 QualType T = VD->getType();
64 this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
65 !T->isObjCObjectPointerType();
66
67 if (!this->visitDeclAndReturn(VD, Init, ConstantContext: S.inConstantContext()))
68 EvalResult.setInvalid();
69
70 S.EvaluatingDecl = nullptr;
71 updateGlobalTemporaries();
72 return std::move(this->EvalResult);
73}
74
75EvaluationResult EvalEmitter::interpretDestructor(const VarDecl *VD,
76 const APValue &Value) {
77 assert(VD);
78 S.setEvalLocation(VD->getLocation());
79 S.EvaluatingDecl = VD;
80 S.EvalKind = EvaluationKind::Dtor;
81 EvalResult.setSource(VD);
82
83 if (!this->visitDtorCall(VD, Value))
84 EvalResult.setInvalid();
85
86 S.EvaluatingDecl = nullptr;
87 return std::move(this->EvalResult);
88}
89
90EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,
91 PtrCallback PtrCB) {
92 S.setEvalLocation(E->getExprLoc());
93 this->ConvertResultToRValue = false;
94 this->CheckFullyInitialized = false;
95 this->PtrCB = PtrCB;
96 EvalResult.setSource(E);
97
98 if (!this->visitExpr(E, DestroyToplevelScope: true)) {
99 // EvalResult may already have a result set, but something failed
100 // after that (e.g. evaluating destructors).
101 EvalResult.setInvalid();
102 }
103
104 return std::move(this->EvalResult);
105}
106
107EvaluationResult EvalEmitter::interpretAsLValuePointer(const Expr *E,
108 PtrCallback PtrCB) {
109 S.setEvalLocation(E->getExprLoc());
110 this->ConvertResultToRValue = false;
111 this->CheckFullyInitialized = false;
112 this->PtrCB = PtrCB;
113 EvalResult.setSource(E);
114
115 if (!this->visitLValueExpr(E, DestroyToplevelScope: true))
116 EvalResult.setInvalid();
117
118 return std::move(this->EvalResult);
119}
120
121bool EvalEmitter::interpretCall(const FunctionDecl *FD, const Expr *E) {
122 // Add parameters to the parameter map. The values in the ParamOffset don't
123 // matter in this case as reading from them can't ever work.
124 for (const ParmVarDecl *PD : FD->parameters()) {
125 this->Params.insert(KV: {PD, {.Index: 0, .IsPtr: false}});
126 }
127
128 return this->visitExpr(E, /*DestroyToplevelScope=*/false);
129}
130
131std::optional<bool> EvalEmitter::interpretWithSubstitutions(
132 const FunctionDecl *Callee, ArrayRef<const Expr *> Args, const Expr *This,
133 const Expr *Condition) {
134
135 if (!this->visitWithSubstitutions(Callee, Args, This, Condition))
136 return std::nullopt;
137
138 if (EvalResult.empty() || EvalResult.isInvalid())
139 return false;
140
141 assert(!EvalResult.empty());
142 APValue Result = EvalResult.stealAPValue();
143
144 assert(Result.isInt());
145 return Result.getInt().getBoolValue();
146}
147
148void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
149
150EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
151
152Scope::Local EvalEmitter::createLocal(const Descriptor *D) {
153 // Allocate memory for a local.
154 auto Memory = std::make_unique<char[]>(num: sizeof(Block) + D->getAllocSize() +
155 Block::InlineDescMD);
156 auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, Block::InlineDescMD,
157 /*IsStatic=*/false);
158 B->invokeCtorNoMemset();
159
160 // Initialize local variable inline descriptor.
161 auto &Desc = B->getBlockDesc<InlineDescriptor>();
162 Desc.Desc = D;
163 Desc.Offset = sizeof(InlineDescriptor);
164 Desc.IsActive = false;
165 Desc.IsBase = false;
166 Desc.IsFieldMutable = false;
167 Desc.IsConst = false;
168 Desc.IsInitialized = false;
169
170 // Register the local.
171 unsigned Off = Locals.size();
172 Locals.push_back(Elt: std::move(Memory));
173 return {.Desc: D, .Offset: Off};
174}
175
176bool EvalEmitter::jumpTrue(const LabelTy &Label, SourceInfo SI) {
177 if (isActive()) {
178 CurrentSource = SI;
179 if (S.Stk.pop<bool>())
180 ActiveLabel = Label;
181 }
182 return true;
183}
184
185bool EvalEmitter::jumpFalse(const LabelTy &Label, SourceInfo SI) {
186 if (isActive()) {
187 CurrentSource = SI;
188 if (!S.Stk.pop<bool>())
189 ActiveLabel = Label;
190 }
191 return true;
192}
193
194bool EvalEmitter::jump(const LabelTy &Label, SourceInfo SI) {
195 if (isActive()) {
196 CurrentSource = SI;
197 CurrentLabel = ActiveLabel = Label;
198 }
199 return true;
200}
201
202bool EvalEmitter::fallthrough(const LabelTy &Label) {
203 if (isActive())
204 ActiveLabel = Label;
205 CurrentLabel = Label;
206 return true;
207}
208
209bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {
210 if (!isActive())
211 return true;
212
213 PushIgnoreDiags(S);
214 auto _ = llvm::scope_exit([&]() { PopIgnoreDiags(S); });
215
216 size_t StackSizeBefore = S.Stk.size();
217 const Expr *Arg = E->getArg(Arg: 0);
218 if (!this->visit(E: Arg)) {
219 S.Stk.clearTo(NewSize: StackSizeBefore);
220
221 if (S.inConstantContext() || Arg->HasSideEffects(Ctx: S.getASTContext()))
222 return this->emitBool(V: false, E);
223 return Invalid(S, OpPC: CodePtr());
224 }
225
226 PrimType T = Ctx.classify(T: Arg->getType()).value_or(PT: PT_Ptr);
227 if (T == PT_Ptr) {
228 const auto &Ptr = S.Stk.pop<Pointer>();
229 return this->emitBool(V: CheckBCPResult(S, Ptr), E);
230 }
231
232 // Otherwise, this is fine!
233 if (!this->emitPop(T, I: E))
234 return false;
235 return this->emitBool(V: true, E);
236}
237
238template <PrimType OpType> bool EvalEmitter::emitRet(SourceInfo Info) {
239 if (!isActive())
240 return true;
241
242 using T = typename PrimConv<OpType>::T;
243 EvalResult.takeValue(V: S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
244 return true;
245}
246
247template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
248 if (!isActive())
249 return true;
250
251 const Pointer &Ptr = S.Stk.pop<Pointer>();
252 // If we're returning a raw pointer, call our callback.
253 if (this->PtrCB)
254 return (*this->PtrCB)(S, CodePtr(), Ptr);
255
256 if (!EvalResult.checkDynamicAllocations(S, Ptr, Info))
257 return false;
258 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
259 return false;
260
261 // Function pointers are always returned as lvalues.
262 if (Ptr.isFunctionPointer()) {
263 EvalResult.takeValue(V: Ptr.toAPValue(ASTCtx: Ctx.getASTContext()));
264 return true;
265 }
266
267 // Implicitly convert lvalue to rvalue, if requested.
268 if (ConvertResultToRValue) {
269 if (Ptr.isPastEnd())
270 return false;
271
272 if (!Ptr.isZero() && !CheckFinalLoad(S, OpPC: CodePtr(), Ptr))
273 return false;
274
275 // Never allow reading from a non-const pointer, unless the memory
276 // has been created in this evaluation.
277 if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&
278 Ptr.block()->getEvalID() != Ctx.getEvalID())
279 return false;
280
281 if (std::optional<APValue> V =
282 Ptr.toRValue(Ctx, ResultType: EvalResult.getSourceType())) {
283 EvalResult.takeValue(V: std::move(*V));
284 } else {
285 return false;
286 }
287 } else {
288 // If this is pointing to a local variable, just return
289 // the result, even if the pointer is dead.
290 // This will later be diagnosed by CheckLValueConstantExpression.
291 if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) {
292 EvalResult.takeValue(V: Ptr.toAPValue(ASTCtx: Ctx.getASTContext()));
293 return true;
294 }
295
296 if (!Ptr.isLive() && !Ptr.isTemporary())
297 return false;
298
299 // If the variable of this pointer is being evaluated when returning
300 // its value, mark it as constexpr-unknown.
301 APValue V = Ptr.toAPValue(ASTCtx: Ctx.getASTContext());
302 if (const Descriptor *DeclDesc = Ptr.getDeclDesc();
303 DeclDesc && S.EvaluatingDecl &&
304 DeclDesc->asVarDecl() == S.EvaluatingDecl &&
305 S.getLangOpts().CPlusPlus23 &&
306 S.EvaluatingDecl->getType()->isReferenceType()) {
307 V.setConstexprUnknown(true);
308 }
309 EvalResult.takeValue(V: std::move(V));
310 }
311
312 return true;
313}
314
315bool EvalEmitter::emitRetVoid(SourceInfo Info) {
316 EvalResult.setValid();
317 return true;
318}
319
320bool EvalEmitter::emitRetValue(SourceInfo Info) {
321 const auto &Ptr = S.Stk.pop<Pointer>();
322
323 if (!EvalResult.checkDynamicAllocations(S, Ptr, Info))
324 return false;
325 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
326 return false;
327
328 if (std::optional<APValue> APV =
329 Ptr.toRValue(Ctx, ResultType: EvalResult.getSourceType())) {
330 EvalResult.takeValue(V: std::move(*APV));
331 return true;
332 }
333
334 EvalResult.setInvalid();
335 return false;
336}
337
338bool EvalEmitter::emitGetPtrLocal(uint32_t I, SourceInfo Info) {
339 if (!isActive())
340 return true;
341
342 Block *B = getLocal(Index: I);
343 S.Stk.push<Pointer>(Args&: B, Args: sizeof(InlineDescriptor));
344 return true;
345}
346
347bool EvalEmitter::emitGetRefLocal(uint32_t I, SourceInfo Info) {
348 if (!isActive())
349 return true;
350
351 Block *B = getLocal(Index: I);
352 return handleReference(S, OpPC: CodePtr(), B);
353}
354
355template <PrimType OpType>
356bool EvalEmitter::emitGetLocal(uint32_t I, SourceInfo Info) {
357 if (!isActive())
358 return true;
359
360 using T = typename PrimConv<OpType>::T;
361
362 Block *B = getLocal(Index: I);
363
364 if (!CheckLocalLoad(S, OpPC: CodePtr(), B))
365 return false;
366
367 S.Stk.push<T>(B->deref<T>());
368 return true;
369}
370
371template <PrimType OpType>
372bool EvalEmitter::emitSetLocal(uint32_t I, SourceInfo Info) {
373 if (!isActive())
374 return true;
375
376 using T = typename PrimConv<OpType>::T;
377
378 Block *B = getLocal(Index: I);
379 B->deref<T>() = S.Stk.pop<T>();
380 auto &Desc = B->getBlockDesc<InlineDescriptor>();
381 Desc.IsInitialized = true;
382 Desc.LifeState = Lifetime::Started;
383
384 return true;
385}
386
387bool EvalEmitter::emitDestroy(uint32_t I, SourceInfo Info) {
388 if (!isActive())
389 return true;
390
391 for (auto &Local : Descriptors[I]) {
392 Block *B = getLocal(Index: Local.Offset);
393 S.deallocate(B);
394 }
395
396 return true;
397}
398
399bool EvalEmitter::emitGetLocalEnabled(uint32_t I, SourceInfo Info) {
400 if (!isActive())
401 return true;
402
403 Block *B = getLocal(Index: I);
404 const auto &Desc = B->getBlockDesc<InlineDescriptor>();
405
406 S.Stk.push<bool>(Args: Desc.IsActive);
407 return true;
408}
409
410bool EvalEmitter::emitEnableLocal(uint32_t I, SourceInfo Info) {
411 if (!isActive())
412 return true;
413
414 // FIXME: This is a little dirty, but to avoid adding a flag to
415 // InlineDescriptor that's only ever useful on the toplevel of local
416 // variables, we reuse the IsActive flag for the enabled state. We should
417 // probably use a different struct than InlineDescriptor for the block-level
418 // inline descriptor of local varaibles.
419 Block *B = getLocal(Index: I);
420 auto &Desc = B->getBlockDesc<InlineDescriptor>();
421 Desc.IsActive = true;
422 return true;
423}
424
425/// Global temporaries (LifetimeExtendedTemporary) carry their value
426/// around as an APValue, which codegen accesses.
427/// We set their value once when creating them, but we don't update it
428/// afterwards when code changes it later.
429/// This is what we do here.
430void EvalEmitter::updateGlobalTemporaries() {
431 for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
432 UnsignedOrNone GlobalIndex = P.getGlobal(E);
433 assert(GlobalIndex);
434 const Pointer &Ptr = P.getPtrGlobal(Idx: *GlobalIndex);
435 APValue *Cached = Temp->getOrCreateValue(MayCreate: true);
436
437 QualType TempType = E->getType();
438 if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: E))
439 TempType = MTE->getSubExpr()->skipRValueSubobjectAdjustments()->getType();
440
441 if (OptPrimType T = Ctx.classify(T: TempType)) {
442 TYPE_SWITCH(*T,
443 { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
444 } else {
445 if (std::optional<APValue> APV = Ptr.toRValue(Ctx, ResultType: TempType))
446 *Cached = *APV;
447 }
448 }
449 S.SeenGlobalTemporaries.clear();
450}
451
452//===----------------------------------------------------------------------===//
453// Opcode evaluators
454//===----------------------------------------------------------------------===//
455
456#define GET_EVAL_IMPL
457#include "Opcodes.inc"
458#undef GET_EVAL_IMPL
459