clang 23.0.0git
EvalEmitter.cpp
Go to the documentation of this file.
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
21 InterpStack &Stk)
22 : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
23
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
38 bool ConvertResultToRValue,
39 bool DestroyToplevelScope) {
40 S.setEvalLocation(E->getExprLoc());
41 this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);
42 this->CheckFullyInitialized = isa<ConstantExpr>(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
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 EvalResult.setSource(VD);
67
68 if (!this->visitDeclAndReturn(VD, Init, S.inConstantContext()))
69 EvalResult.setInvalid();
70
71 S.EvaluatingDecl = nullptr;
72 updateGlobalTemporaries();
73 return std::move(this->EvalResult);
74}
75
77 const APValue &Value) {
78 assert(VD);
79 S.setEvalLocation(VD->getLocation());
80 EvalResult.setSource(VD);
81
82 if (!this->visitDtorCall(VD, Value))
83 EvalResult.setInvalid();
84
85 return std::move(this->EvalResult);
86}
87
89 PtrCallback PtrCB) {
90 S.setEvalLocation(E->getExprLoc());
91 this->ConvertResultToRValue = false;
92 this->CheckFullyInitialized = false;
93 this->PtrCB = PtrCB;
94 EvalResult.setSource(E);
95
96 if (!this->visitExpr(E, true)) {
97 // EvalResult may already have a result set, but something failed
98 // after that (e.g. evaluating destructors).
99 EvalResult.setInvalid();
100 }
101
102 return std::move(this->EvalResult);
103}
104
106 PtrCallback PtrCB) {
107 S.setEvalLocation(E->getExprLoc());
108 this->ConvertResultToRValue = false;
109 this->CheckFullyInitialized = false;
110 this->PtrCB = PtrCB;
111 EvalResult.setSource(E);
112
113 if (!this->visitLValueExpr(E, true))
114 EvalResult.setInvalid();
115
116 return std::move(this->EvalResult);
117}
118
120 // Add parameters to the parameter map. The values in the ParamOffset don't
121 // matter in this case as reading from them can't ever work.
122 for (const ParmVarDecl *PD : FD->parameters()) {
123 this->Params.insert({PD, {0, false}});
124 }
125
126 return this->visitExpr(E, /*DestroyToplevelScope=*/false);
127}
128
129void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
130
132
134 // Allocate memory for a local.
135 auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
136 auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*IsStatic=*/false);
137 B->invokeCtor();
138
139 // Initialize local variable inline descriptor.
140 auto &Desc = B->getBlockDesc<InlineDescriptor>();
141 Desc.Desc = D;
142 Desc.Offset = sizeof(InlineDescriptor);
143 Desc.IsActive = false;
144 Desc.IsBase = false;
145 Desc.IsFieldMutable = false;
146 Desc.IsConst = false;
147 Desc.IsInitialized = false;
148
149 // Register the local.
150 unsigned Off = Locals.size();
151 Locals.push_back(std::move(Memory));
152 return {Off, D};
153}
154
156 if (isActive()) {
157 CurrentSource = SI;
158 if (S.Stk.pop<bool>())
159 ActiveLabel = Label;
160 }
161 return true;
162}
163
165 if (isActive()) {
166 CurrentSource = SI;
167 if (!S.Stk.pop<bool>())
168 ActiveLabel = Label;
169 }
170 return true;
171}
172
173bool EvalEmitter::jump(const LabelTy &Label, SourceInfo SI) {
174 if (isActive()) {
175 CurrentSource = SI;
176 CurrentLabel = ActiveLabel = Label;
177 }
178 return true;
179}
180
182 if (isActive())
183 ActiveLabel = Label;
184 CurrentLabel = Label;
185 return true;
186}
187
188bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {
189 if (!isActive())
190 return true;
191
192 PushIgnoreDiags(S, OpPC);
193 auto _ = llvm::scope_exit([&]() { PopIgnoreDiags(S, OpPC); });
194
195 size_t StackSizeBefore = S.Stk.size();
196 const Expr *Arg = E->getArg(0);
197 if (!this->visit(Arg)) {
198 S.Stk.clearTo(StackSizeBefore);
199
200 if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext()))
201 return this->emitBool(false, E);
202 return Invalid(S, OpPC);
203 }
204
205 PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr);
206 if (T == PT_Ptr) {
207 const auto &Ptr = S.Stk.pop<Pointer>();
208 return this->emitBool(CheckBCPResult(S, Ptr), E);
209 }
210
211 // Otherwise, this is fine!
212 if (!this->emitPop(T, E))
213 return false;
214 return this->emitBool(true, E);
215}
216
217template <PrimType OpType> bool EvalEmitter::emitRet(SourceInfo Info) {
218 if (!isActive())
219 return true;
220
221 using T = typename PrimConv<OpType>::T;
222 EvalResult.takeValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
223 return true;
224}
225
226template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
227 if (!isActive())
228 return true;
229
230 const Pointer &Ptr = S.Stk.pop<Pointer>();
231 // If we're returning a raw pointer, call our callback.
232 if (this->PtrCB)
233 return (*this->PtrCB)(Ptr);
234
235 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
236 return false;
237 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
238 return false;
239
240 // Function pointers are always returned as lvalues.
241 if (Ptr.isFunctionPointer()) {
242 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
243 return true;
244 }
245
246 // Implicitly convert lvalue to rvalue, if requested.
247 if (ConvertResultToRValue) {
248 if (Ptr.isPastEnd())
249 return false;
250
251 if (Ptr.pointsToStringLiteral() && Ptr.isArrayRoot())
252 return false;
253
254 if (!Ptr.isZero() && !CheckFinalLoad(S, OpPC, Ptr))
255 return false;
256
257 // Never allow reading from a non-const pointer, unless the memory
258 // has been created in this evaluation.
259 if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&
260 Ptr.block()->getEvalID() != Ctx.getEvalID())
261 return false;
262
263 if (std::optional<APValue> V =
264 Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
265 EvalResult.takeValue(std::move(*V));
266 } else {
267 return false;
268 }
269 } else {
270 // If this is pointing to a local variable, just return
271 // the result, even if the pointer is dead.
272 // This will later be diagnosed by CheckLValueConstantExpression.
273 if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) {
274 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
275 return true;
276 }
277
278 if (!Ptr.isLive() && !Ptr.isTemporary())
279 return false;
280
281 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
282 }
283
284 return true;
285}
286
287bool EvalEmitter::emitRetVoid(SourceInfo Info) {
288 EvalResult.setValid();
289 return true;
290}
291
292bool EvalEmitter::emitRetValue(SourceInfo Info) {
293 const auto &Ptr = S.Stk.pop<Pointer>();
294
295 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
296 return false;
297 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
298 return false;
299
300 if (std::optional<APValue> APV =
301 Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
302 EvalResult.takeValue(std::move(*APV));
303 return true;
304 }
305
306 EvalResult.setInvalid();
307 return false;
308}
309
310bool EvalEmitter::emitGetPtrLocal(uint32_t I, SourceInfo Info) {
311 if (!isActive())
312 return true;
313
314 Block *B = getLocal(I);
315 S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
316 return true;
317}
318
319bool EvalEmitter::emitGetRefLocal(uint32_t I, SourceInfo Info) {
320 if (!isActive())
321 return true;
322
323 Block *B = getLocal(I);
324 return handleReference(S, OpPC, B);
325}
326
327template <PrimType OpType>
328bool EvalEmitter::emitGetLocal(uint32_t I, SourceInfo Info) {
329 if (!isActive())
330 return true;
331
332 using T = typename PrimConv<OpType>::T;
333
334 Block *B = getLocal(I);
335
336 if (!CheckLocalLoad(S, OpPC, B))
337 return false;
338
339 S.Stk.push<T>(B->deref<T>());
340 return true;
341}
342
343template <PrimType OpType>
344bool EvalEmitter::emitSetLocal(uint32_t I, SourceInfo Info) {
345 if (!isActive())
346 return true;
347
348 using T = typename PrimConv<OpType>::T;
349
350 Block *B = getLocal(I);
351 B->deref<T>() = S.Stk.pop<T>();
352 auto &Desc = B->getBlockDesc<InlineDescriptor>();
353 Desc.IsInitialized = true;
354 Desc.LifeState = Lifetime::Started;
355
356 return true;
357}
358
359bool EvalEmitter::emitDestroy(uint32_t I, SourceInfo Info) {
360 if (!isActive())
361 return true;
362
363 for (auto &Local : Descriptors[I]) {
364 Block *B = getLocal(Local.Offset);
365 S.deallocate(B);
366 }
367
368 return true;
369}
370
371bool EvalEmitter::emitGetLocalEnabled(uint32_t I, SourceInfo Info) {
372 if (!isActive())
373 return true;
374
375 Block *B = getLocal(I);
376 const auto &Desc = B->getBlockDesc<InlineDescriptor>();
377
378 S.Stk.push<bool>(Desc.IsActive);
379 return true;
380}
381
382bool EvalEmitter::emitEnableLocal(uint32_t I, SourceInfo Info) {
383 if (!isActive())
384 return true;
385
386 // FIXME: This is a little dirty, but to avoid adding a flag to
387 // InlineDescriptor that's only ever useful on the toplevel of local
388 // variables, we reuse the IsActive flag for the enabled state. We should
389 // probably use a different struct than InlineDescriptor for the block-level
390 // inline descriptor of local varaibles.
391 Block *B = getLocal(I);
392 auto &Desc = B->getBlockDesc<InlineDescriptor>();
393 Desc.IsActive = true;
394 return true;
395}
396
397/// Global temporaries (LifetimeExtendedTemporary) carry their value
398/// around as an APValue, which codegen accesses.
399/// We set their value once when creating them, but we don't update it
400/// afterwards when code changes it later.
401/// This is what we do here.
402void EvalEmitter::updateGlobalTemporaries() {
403 for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
404 UnsignedOrNone GlobalIndex = P.getGlobal(E);
405 assert(GlobalIndex);
406 const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
407 APValue *Cached = Temp->getOrCreateValue(true);
408
409 QualType TempType = E->getType();
410 if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E))
411 TempType = MTE->getSubExpr()->skipRValueSubobjectAdjustments()->getType();
412
413 if (OptPrimType T = Ctx.classify(TempType)) {
414 TYPE_SWITCH(*T,
415 { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
416 } else {
417 if (std::optional<APValue> APV = Ptr.toRValue(Ctx, TempType))
418 *Cached = *APV;
419 }
420 }
421 S.SeenGlobalTemporaries.clear();
422}
423
424//===----------------------------------------------------------------------===//
425// Opcode evaluators
426//===----------------------------------------------------------------------===//
427
428#define GET_EVAL_IMPL
429#include "Opcodes.inc"
430#undef GET_EVAL_IMPL
#define V(N, I)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value)
Check that this evaluated value is fully-initialized and can be loaded by an lvalue-to-rvalue convers...
#define TYPE_SWITCH(Expr, B)
Definition PrimType.h:223
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition APValue.h:122
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2946
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3150
SourceLocation getLocation() const
Definition DeclBase.h:447
This represents one expression.
Definition Expr.h:112
bool isGLValue() const
Definition Expr.h:287
bool HasSideEffects(const ASTContext &Ctx, bool IncludePossibleEffects=true) const
HasSideEffects - This routine returns true for all those expressions which have any effect other than...
Definition Expr.cpp:3695
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:282
QualType getType() const
Definition Expr.h:144
Represents a function declaration or definition.
Definition Decl.h:2018
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2792
Represents a parameter to a function.
Definition Decl.h:1808
A (possibly-)qualified type.
Definition TypeBase.h:937
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:924
A memory block, either on the stack or in the heap.
Definition InterpBlock.h:44
void invokeDtor()
Invokes the Destructor.
const T & deref() const
bool isStatic() const
Checks if the block has static storage duration.
Definition InterpBlock.h:79
bool isInitialized() const
Returns whether the data of this block has been initialized via invoking the Ctor func.
Definition InterpBlock.h:92
unsigned getEvalID() const
The Evaluation ID this block was created in.
Definition InterpBlock.h:94
Holds all information required to evaluate constexpr code in a module.
Definition Context.h:47
ASTContext & getASTContext() const
Returns the AST context.
Definition Context.h:101
unsigned getEvalID() const
Definition Context.h:169
llvm::DenseMap< const ParmVarDecl *, FuncParam > Params
Parameter indices.
Definition EvalEmitter.h:98
EvaluationResult interpretDecl(const VarDecl *VD, const Expr *Init, bool CheckFullyInitialized)
EvaluationResult interpretDestructor(const VarDecl *VD, const APValue &Value)
EvaluationResult interpretExpr(const Expr *E, bool ConvertResultToRValue=false, bool DestroyToplevelScope=false)
bool jump(const LabelTy &Label, SourceInfo SI)
virtual bool visit(const Expr *E)=0
bool speculate(const CallExpr *E, const LabelTy &EndLabel)
Speculative execution.
virtual bool visitDtorCall(const VarDecl *VD, const APValue &Value)=0
bool jumpFalse(const LabelTy &Label, SourceInfo SI)
Local createLocal(Descriptor *D)
Callback for registering a local.
bool interpretCall(const FunctionDecl *FD, const Expr *E)
Interpret the given expression as if it was in the body of the given function, i.e.
llvm::function_ref< bool(const Pointer &)> PtrCallback
Definition EvalEmitter.h:36
void emitLabel(LabelTy Label)
Define a label.
bool isActive() const
Since expressions can only jump forward, predicated execution is used to deal with if-else statements...
Definition EvalEmitter.h:84
EvaluationResult interpretAsLValuePointer(const Expr *E, PtrCallback PtrCB)
virtual bool visitExpr(const Expr *E, bool DestroyToplevelScope)=0
Methods implemented by the compiler.
bool fallthrough(const LabelTy &Label)
virtual bool visitDeclAndReturn(const VarDecl *VD, const Expr *Init, bool ConstantContext)=0
void cleanup()
Clean up all resources.
LabelTy getLabel()
Create a label.
EvaluationResult interpretAsPointer(const Expr *E, PtrCallback PtrCB)
Interpret the given Expr to a Pointer.
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk)
virtual bool emitBool(bool V, const Expr *E)=0
bool jumpTrue(const LabelTy &Label, SourceInfo SI)
Emits jumps.
llvm::SmallVector< SmallVector< Local, 8 >, 2 > Descriptors
Local descriptors.
virtual bool visitLValueExpr(const Expr *E, bool DestroyToplevelScope)=0
Defines the result of an evaluation.
bool checkReturnValue(InterpState &S, const Context &Ctx, const Pointer &Ptr, const SourceInfo &Info)
Check that none of the blocks the given pointer (transitively) points to are dynamically allocated.
bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const
Check that all subobjects of the given pointer have been initialized.
Stack frame storing temporaries and parameters.
Definition InterpStack.h:25
T pop()
Returns the value from the top of the stack and removes it.
Definition InterpStack.h:39
InterpStack & Stk
Temporary stack.
A pointer to a memory block, live or dead.
Definition Pointer.h:97
bool isConst() const
Checks if an object or a subfield is mutable.
Definition Pointer.h:572
T & deref() const
Dereferences the pointer, if it's live.
Definition Pointer.h:687
QualType getType() const
Returns the type of the innermost field.
Definition Pointer.h:346
bool pointsToStringLiteral() const
Definition Pointer.cpp:744
bool isArrayRoot() const
Whether this array refers to an array, but not to the first element.
Definition Pointer.h:405
bool isLive() const
Checks if the pointer is live.
Definition Pointer.h:278
bool isZero() const
Checks if the pointer is null.
Definition Pointer.h:264
APValue toAPValue(const ASTContext &ASTCtx) const
Converts the pointer to an APValue.
Definition Pointer.cpp:173
bool isPastEnd() const
Checks if the pointer points past the end of the object.
Definition Pointer.h:658
bool isBlockPointer() const
Definition Pointer.h:479
std::optional< APValue > toRValue(const Context &Ctx, QualType ResultType) const
Converts the pointer to an APValue that is an rvalue.
Definition Pointer.cpp:806
bool isTemporary() const
Checks if the storage is temporary.
Definition Pointer.h:512
const Block * block() const
Definition Pointer.h:617
bool isFunctionPointer() const
Definition Pointer.h:481
The program contains and links the bytecode for all functions.
Definition Program.h:36
Describes the statement/declaration an opcode was generated from.
Definition Source.h:74
Interface for the VM to interact with the AST walker's context.
Definition State.h:81
bool handleReference(InterpState &S, CodePtr OpPC, Block *B)
Definition Interp.cpp:2425
bool CheckBCPResult(InterpState &S, const Pointer &Ptr)
Definition Interp.cpp:285
bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr)
This is not used by any of the opcodes directly.
Definition Interp.cpp:877
bool PushIgnoreDiags(InterpState &S, CodePtr OpPC)
Definition Interp.h:3586
bool PopIgnoreDiags(InterpState &S, CodePtr OpPC)
Definition Interp.h:3597
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:34
bool Init(InterpState &S, CodePtr OpPC)
Definition Interp.h:2323
bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B)
Definition Interp.cpp:779
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
OptionalUnsigned< unsigned > UnsignedOrNone
Describes a memory block created by an allocation site.
Definition Descriptor.h:123
const bool IsConst
Flag indicating if the block is mutable.
Definition Descriptor.h:162
unsigned getAllocSize() const
Returns the allocated size, including metadata.
Definition Descriptor.h:248
Inline descriptor embedded in structures and arrays.
Definition Descriptor.h:68
Mapping from primitive types to their representation.
Definition PrimType.h:150
Information about a local's storage.
Definition Function.h:39