clang 22.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
15using namespace clang;
16using namespace clang::interp;
17
19 InterpStack &Stk)
20 : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
21
23 for (auto &V : Locals) {
24 Block *B = reinterpret_cast<Block *>(V.get());
25 if (B->isInitialized())
26 B->invokeDtor();
27 }
28}
29
30/// Clean up all our resources. This needs to done in failed evaluations before
31/// we call InterpStack::clear(), because there might be a Pointer on the stack
32/// pointing into a Block in the EvalEmitter.
34
36 bool ConvertResultToRValue,
37 bool DestroyToplevelScope) {
39 this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);
40 this->CheckFullyInitialized = isa<ConstantExpr>(E);
41 EvalResult.setSource(E);
42
43 if (!this->visitExpr(E, DestroyToplevelScope)) {
44 // EvalResult may already have a result set, but something failed
45 // after that (e.g. evaluating destructors).
46 EvalResult.setInvalid();
47 }
48
49 return std::move(this->EvalResult);
50}
51
54 this->CheckFullyInitialized = CheckFullyInitialized;
55 S.EvaluatingDecl = VD;
57 EvalResult.setSource(VD);
58
59 if (const Expr *Init = VD->getAnyInitializer()) {
60 QualType T = VD->getType();
61 this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
63 } else
64 this->ConvertResultToRValue = false;
65
66 EvalResult.setSource(VD);
67
68 if (!this->visitDeclAndReturn(VD, S.inConstantContext()))
69 EvalResult.setInvalid();
70
71 S.EvaluatingDecl = nullptr;
72 updateGlobalTemporaries();
73 return std::move(this->EvalResult);
74}
75
77 PtrCallback PtrCB) {
78
80 this->ConvertResultToRValue = false;
81 this->CheckFullyInitialized = false;
82 this->PtrCB = PtrCB;
83 EvalResult.setSource(E);
84
85 if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) {
86 // EvalResult may already have a result set, but something failed
87 // after that (e.g. evaluating destructors).
88 EvalResult.setInvalid();
89 }
90
91 return std::move(this->EvalResult);
92}
93
95 // Add parameters to the parameter map. The values in the ParamOffset don't
96 // matter in this case as reading from them can't ever work.
97 for (const ParmVarDecl *PD : FD->parameters()) {
98 this->Params.insert({PD, {0, false}});
99 }
100
101 return this->visitExpr(E, /*DestroyToplevelScope=*/false);
102}
103
104void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
105
107
109 // Allocate memory for a local.
110 auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
111 auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
112 B->invokeCtor();
113
114 // Initialize local variable inline descriptor.
115 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
116 Desc.Desc = D;
117 Desc.Offset = sizeof(InlineDescriptor);
118 Desc.IsActive = true;
119 Desc.IsBase = false;
120 Desc.IsFieldMutable = false;
121 Desc.IsConst = false;
122 Desc.IsInitialized = false;
123
124 // Register the local.
125 unsigned Off = Locals.size();
126 Locals.push_back(std::move(Memory));
127 return {Off, D};
128}
129
131 if (isActive()) {
132 if (S.Stk.pop<bool>())
133 ActiveLabel = Label;
134 }
135 return true;
136}
137
139 if (isActive()) {
140 if (!S.Stk.pop<bool>())
141 ActiveLabel = Label;
142 }
143 return true;
144}
145
147 if (isActive())
148 CurrentLabel = ActiveLabel = Label;
149 return true;
150}
151
153 if (isActive())
154 ActiveLabel = Label;
155 CurrentLabel = Label;
156 return true;
157}
158
159bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {
160 size_t StackSizeBefore = S.Stk.size();
161 const Expr *Arg = E->getArg(0);
162 if (!this->visit(Arg)) {
163 S.Stk.clearTo(StackSizeBefore);
164
166 return this->emitBool(false, E);
167 return Invalid(S, OpPC);
168 }
169
170 PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr);
171 if (T == PT_Ptr) {
172 const auto &Ptr = S.Stk.pop<Pointer>();
173 return this->emitBool(CheckBCPResult(S, Ptr), E);
174 }
175
176 // Otherwise, this is fine!
177 if (!this->emitPop(T, E))
178 return false;
179 return this->emitBool(true, E);
180}
181
182template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
183 if (!isActive())
184 return true;
185
186 using T = typename PrimConv<OpType>::T;
187 EvalResult.takeValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
188 return true;
189}
190
191template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
192 if (!isActive())
193 return true;
194
195 const Pointer &Ptr = S.Stk.pop<Pointer>();
196
197 if (Ptr.isFunctionPointer()) {
198 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
199 return true;
200 }
201
202 // If we're returning a raw pointer, call our callback.
203 if (this->PtrCB)
204 return (*this->PtrCB)(Ptr);
205
206 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
207 return false;
208 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
209 return false;
210
211 // Implicitly convert lvalue to rvalue, if requested.
212 if (ConvertResultToRValue) {
213 if (!Ptr.isZero() && !Ptr.isDereferencable())
214 return false;
215
216 if (!Ptr.isZero() && !CheckFinalLoad(S, OpPC, Ptr))
217 return false;
218
219 // Never allow reading from a non-const pointer, unless the memory
220 // has been created in this evaluation.
221 if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&
222 Ptr.block()->getEvalID() != Ctx.getEvalID())
223 return false;
224
225 if (std::optional<APValue> V =
226 Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
227 EvalResult.takeValue(std::move(*V));
228 } else {
229 return false;
230 }
231 } else {
232 // If this is pointing to a local variable, just return
233 // the result, even if the pointer is dead.
234 // This will later be diagnosed by CheckLValueConstantExpression.
235 if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) {
236 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
237 return true;
238 }
239
240 if (!Ptr.isLive() && !Ptr.isTemporary())
241 return false;
242
243 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
244 }
245
246 return true;
247}
248
249bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
250 EvalResult.setValid();
251 return true;
252}
253
254bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
255 const auto &Ptr = S.Stk.pop<Pointer>();
256
257 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
258 return false;
259 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
260 return false;
261
262 if (std::optional<APValue> APV =
263 Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
264 EvalResult.takeValue(std::move(*APV));
265 return true;
266 }
267
268 EvalResult.setInvalid();
269 return false;
270}
271
272bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
273 if (!isActive())
274 return true;
275
276 Block *B = getLocal(I);
277 S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
278 return true;
279}
280
281template <PrimType OpType>
282bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
283 if (!isActive())
284 return true;
285
286 using T = typename PrimConv<OpType>::T;
287
288 Block *B = getLocal(I);
289
290 if (!CheckLocalLoad(S, OpPC, B))
291 return false;
292
293 S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
294 return true;
295}
296
297template <PrimType OpType>
298bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
299 if (!isActive())
300 return true;
301
302 using T = typename PrimConv<OpType>::T;
303
304 Block *B = getLocal(I);
305 *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
306 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
307 Desc.IsInitialized = true;
308
309 return true;
310}
311
312bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
313 if (!isActive())
314 return true;
315
316 for (auto &Local : Descriptors[I]) {
317 Block *B = getLocal(Local.Offset);
318 S.deallocate(B);
319 }
320
321 return true;
322}
323
324/// Global temporaries (LifetimeExtendedTemporary) carry their value
325/// around as an APValue, which codegen accesses.
326/// We set their value once when creating them, but we don't update it
327/// afterwards when code changes it later.
328/// This is what we do here.
329void EvalEmitter::updateGlobalTemporaries() {
330 for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
331 if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
332 const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
333 APValue *Cached = Temp->getOrCreateValue(true);
334
335 if (OptPrimType T = Ctx.classify(E->getType())) {
337 *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
338 } else {
339 if (std::optional<APValue> APV =
340 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
341 *Cached = *APV;
342 }
343 }
344 }
345 S.SeenGlobalTemporaries.clear();
346}
347
348//===----------------------------------------------------------------------===//
349// Opcode evaluators
350//===----------------------------------------------------------------------===//
351
352#define GET_EVAL_IMPL
353#include "Opcodes.inc"
354#undef GET_EVAL_IMPL
#define V(N, I)
Definition: ASTContext.h:3597
NodeId Parent
Definition: ASTDiff.cpp:191
StringRef P
const Decl * D
Expr * E
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
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:207
std::string Label
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:2879
SourceLocation getLocation() const
Definition: DeclBase.h:439
This represents one expression.
Definition: Expr.h:112
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:3624
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition: Expr.cpp:273
QualType getType() const
Definition: Expr.h:144
Represents a function declaration or definition.
Definition: Decl.h:1999
ArrayRef< ParmVarDecl * > parameters() const
Definition: Decl.h:2771
Represents a parameter to a function.
Definition: Decl.h:1789
A (possibly-)qualified type.
Definition: TypeBase.h:937
bool isPointerType() const
Definition: TypeBase.h:8580
bool isObjCObjectPointerType() const
Definition: TypeBase.h:8749
QualType getType() const
Definition: Decl.h:722
Represents a variable declaration or definition.
Definition: Decl.h:925
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to.
Definition: Decl.h:1357
A memory block, either on the stack or in the heap.
Definition: InterpBlock.h:44
void invokeDtor()
Invokes the Destructor.
Definition: InterpBlock.h:135
std::byte * data()
Returns a pointer to the stored data.
Definition: InterpBlock.h:98
void invokeCtor()
Invokes the constructor.
Definition: InterpBlock.h:123
bool isStatic() const
Checks if the block has static storage duration.
Definition: InterpBlock.h:79
std::byte * rawData()
Returns a pointer to the raw data, including metadata.
Definition: InterpBlock.h:111
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:41
ASTContext & getASTContext() const
Returns the AST context.
Definition: Context.h:74
OptPrimType classify(QualType T) const
Classifies a type.
Definition: Context.cpp:310
unsigned getEvalID() const
Definition: Context.h:140
bool jump(const LabelTy &Label)
EvaluationResult interpretDecl(const VarDecl *VD, bool CheckFullyInitialized)
Definition: EvalEmitter.cpp:52
EvaluationResult interpretExpr(const Expr *E, bool ConvertResultToRValue=false, bool DestroyToplevelScope=false)
Definition: EvalEmitter.cpp:35
bool jumpFalse(const LabelTy &Label)
virtual bool visit(const Expr *E)=0
bool speculate(const CallExpr *E, const LabelTy &EndLabel)
Speculative execution.
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.
Definition: EvalEmitter.cpp:94
llvm::function_ref< bool(const Pointer &)> PtrCallback
Definition: EvalEmitter.h:35
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:77
virtual bool visitExpr(const Expr *E, bool DestroyToplevelScope)=0
Methods implemented by the compiler.
bool fallthrough(const LabelTy &Label)
void cleanup()
Clean up all resources.
Definition: EvalEmitter.cpp:33
LabelTy getLabel()
Create a label.
EvaluationResult interpretAsPointer(const Expr *E, PtrCallback PtrCB)
Interpret the given Expr to a Pointer.
Definition: EvalEmitter.cpp:76
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk)
Definition: EvalEmitter.cpp:18
virtual bool visitDeclAndReturn(const VarDecl *VD, bool ConstantContext)=0
llvm::DenseMap< const ParmVarDecl *, ParamOffset > Params
Parameter indices.
Definition: EvalEmitter.h:91
virtual bool emitBool(bool V, const Expr *E)=0
llvm::SmallVector< SmallVector< Local, 8 >, 2 > Descriptors
Local descriptors.
Definition: EvalEmitter.h:97
bool jumpTrue(const LabelTy &Label)
Emits jumps.
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
void clearTo(size_t NewSize)
Definition: InterpStack.cpp:41
T pop()
Returns the value from the top of the stack and removes it.
Definition: InterpStack.h:39
void push(Tys &&...Args)
Constructs a value in place on the top of the stack.
Definition: InterpStack.h:33
size_t size() const
Returns the size of the stack in bytes.
Definition: InterpStack.h:77
ASTContext & getASTContext() const override
Definition: InterpState.h:70
llvm::SmallVector< std::pair< const Expr *, const LifetimeExtendedTemporaryDecl * > > SeenGlobalTemporaries
Definition: InterpState.h:194
InterpStack & Stk
Temporary stack.
Definition: InterpState.h:176
const VarDecl * EvaluatingDecl
Declaration we're initializing/evaluting, if any.
Definition: InterpState.h:186
void deallocate(Block *B)
Deallocates a pointer.
Definition: InterpState.cpp:74
void setEvalLocation(SourceLocation SL)
Definition: InterpState.h:116
bool inConstantContext() const
Definition: InterpState.cpp:31
PrimType value_or(PrimType PT) const
Definition: PrimType.h:68
A pointer to a memory block, live or dead.
Definition: Pointer.h:90
bool isConst() const
Checks if an object or a subfield is mutable.
Definition: Pointer.h:553
T & deref() const
Dereferences the pointer, if it's live.
Definition: Pointer.h:659
bool isLive() const
Checks if the pointer is live.
Definition: Pointer.h:264
bool isZero() const
Checks if the pointer is null.
Definition: Pointer.h:253
APValue toAPValue(const ASTContext &ASTCtx) const
Converts the pointer to an APValue.
Definition: Pointer.cpp:167
bool isDereferencable() const
Whether this block can be read from at all.
Definition: Pointer.h:693
bool isBlockPointer() const
Definition: Pointer.h:464
std::optional< APValue > toRValue(const Context &Ctx, QualType ResultType) const
Converts the pointer to an APValue that is an rvalue.
Definition: Pointer.cpp:711
bool isTemporary() const
Checks if the storage is temporary.
Definition: Pointer.h:497
const Block * block() const
Definition: Pointer.h:598
bool isFunctionPointer() const
Definition: Pointer.h:466
The program contains and links the bytecode for all functions.
Definition: Program.h:36
Block * getGlobal(unsigned Idx)
Returns the value of a global.
Definition: Program.h:71
Pointer getPtrGlobal(unsigned Idx) const
Returns a pointer to a global.
Definition: Program.cpp:109
Describes the statement/declaration an opcode was generated from.
Definition: Source.h:73
Interface for the VM to interact with the AST walker's context.
Definition: State.h:58
bool CheckBCPResult(InterpState &S, const Pointer &Ptr)
Definition: Interp.cpp:308
bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr)
This is not used by any of the opcodes directly.
Definition: Interp.cpp:841
PrimType
Enumeration of the primitive types of the VM.
Definition: PrimType.h:34
bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B)
Definition: Interp.cpp:771
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
Describes a memory block created by an allocation site.
Definition: Descriptor.h:122
Inline descriptor embedded in structures and arrays.
Definition: Descriptor.h:67
unsigned IsActive
Flag indicating if the field is the active member of a union.
Definition: Descriptor.h:89
unsigned IsBase
Flag indicating if the field is an embedded base class.
Definition: Descriptor.h:83
unsigned Offset
Offset inside the structure/array.
Definition: Descriptor.h:69
unsigned IsInitialized
For primitive fields, it indicates if the field was initialized.
Definition: Descriptor.h:80
unsigned IsConst
Flag indicating if the storage is constant or not.
Definition: Descriptor.h:74
unsigned IsFieldMutable
Flag indicating if the field is mutable (if in a record).
Definition: Descriptor.h:95
Mapping from primitive types to their representation.
Definition: PrimType.h:134
Information about a local's storage.
Definition: Function.h:39
unsigned Offset
Offset of the local in frame.
Definition: Function.h:41