Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/AST/ByteCode/EvalEmitter.cpp
213799 views
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
15
using namespace clang;
16
using namespace clang::interp;
17
18
EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
19
InterpStack &Stk)
20
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
21
22
EvalEmitter::~EvalEmitter() {
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.
33
void EvalEmitter::cleanup() { S.cleanup(); }
34
35
EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
36
bool ConvertResultToRValue,
37
bool DestroyToplevelScope) {
38
S.setEvalLocation(E->getExprLoc());
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
52
EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
53
bool CheckFullyInitialized) {
54
this->CheckFullyInitialized = CheckFullyInitialized;
55
S.EvaluatingDecl = VD;
56
EvalResult.setSource(VD);
57
58
if (const Expr *Init = VD->getAnyInitializer()) {
59
QualType T = VD->getType();
60
this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
61
!T->isObjCObjectPointerType();
62
} else
63
this->ConvertResultToRValue = false;
64
65
EvalResult.setSource(VD);
66
67
if (!this->visitDeclAndReturn(VD, S.inConstantContext()))
68
EvalResult.setInvalid();
69
70
S.EvaluatingDecl = nullptr;
71
updateGlobalTemporaries();
72
return std::move(this->EvalResult);
73
}
74
75
EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,
76
PtrCallback PtrCB) {
77
78
S.setEvalLocation(E->getExprLoc());
79
this->ConvertResultToRValue = false;
80
this->CheckFullyInitialized = false;
81
this->PtrCB = PtrCB;
82
EvalResult.setSource(E);
83
84
if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) {
85
// EvalResult may already have a result set, but something failed
86
// after that (e.g. evaluating destructors).
87
EvalResult.setInvalid();
88
}
89
90
return std::move(this->EvalResult);
91
}
92
93
void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
94
95
EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
96
97
Scope::Local EvalEmitter::createLocal(Descriptor *D) {
98
// Allocate memory for a local.
99
auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
100
auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
101
B->invokeCtor();
102
103
// Initialize local variable inline descriptor.
104
InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
105
Desc.Desc = D;
106
Desc.Offset = sizeof(InlineDescriptor);
107
Desc.IsActive = true;
108
Desc.IsBase = false;
109
Desc.IsFieldMutable = false;
110
Desc.IsConst = false;
111
Desc.IsInitialized = false;
112
113
// Register the local.
114
unsigned Off = Locals.size();
115
Locals.push_back(std::move(Memory));
116
return {Off, D};
117
}
118
119
bool EvalEmitter::jumpTrue(const LabelTy &Label) {
120
if (isActive()) {
121
if (S.Stk.pop<bool>())
122
ActiveLabel = Label;
123
}
124
return true;
125
}
126
127
bool EvalEmitter::jumpFalse(const LabelTy &Label) {
128
if (isActive()) {
129
if (!S.Stk.pop<bool>())
130
ActiveLabel = Label;
131
}
132
return true;
133
}
134
135
bool EvalEmitter::jump(const LabelTy &Label) {
136
if (isActive())
137
CurrentLabel = ActiveLabel = Label;
138
return true;
139
}
140
141
bool EvalEmitter::fallthrough(const LabelTy &Label) {
142
if (isActive())
143
ActiveLabel = Label;
144
CurrentLabel = Label;
145
return true;
146
}
147
148
bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {
149
size_t StackSizeBefore = S.Stk.size();
150
const Expr *Arg = E->getArg(0);
151
if (!this->visit(Arg)) {
152
S.Stk.clearTo(StackSizeBefore);
153
154
if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext()))
155
return this->emitBool(false, E);
156
return Invalid(S, OpPC);
157
}
158
159
PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr);
160
if (T == PT_Ptr) {
161
const auto &Ptr = S.Stk.pop<Pointer>();
162
return this->emitBool(CheckBCPResult(S, Ptr), E);
163
}
164
165
// Otherwise, this is fine!
166
if (!this->emitPop(T, E))
167
return false;
168
return this->emitBool(true, E);
169
}
170
171
template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
172
if (!isActive())
173
return true;
174
175
using T = typename PrimConv<OpType>::T;
176
EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
177
return true;
178
}
179
180
template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
181
if (!isActive())
182
return true;
183
184
const Pointer &Ptr = S.Stk.pop<Pointer>();
185
186
if (Ptr.isFunctionPointer()) {
187
EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));
188
return true;
189
}
190
191
// If we're returning a raw pointer, call our callback.
192
if (this->PtrCB)
193
return (*this->PtrCB)(Ptr);
194
195
if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
196
return false;
197
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
198
return false;
199
200
// Implicitly convert lvalue to rvalue, if requested.
201
if (ConvertResultToRValue) {
202
if (!Ptr.isZero() && !Ptr.isDereferencable())
203
return false;
204
205
if (S.getLangOpts().CPlusPlus11 && Ptr.isBlockPointer() &&
206
!CheckFinalLoad(S, OpPC, Ptr)) {
207
return false;
208
}
209
210
// Never allow reading from a non-const pointer, unless the memory
211
// has been created in this evaluation.
212
if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&
213
Ptr.block()->getEvalID() != Ctx.getEvalID())
214
return false;
215
216
if (std::optional<APValue> V =
217
Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
218
EvalResult.setValue(*V);
219
} else {
220
return false;
221
}
222
} else {
223
if (!Ptr.isLive() && !Ptr.isTemporary())
224
return false;
225
226
EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));
227
}
228
229
return true;
230
}
231
232
bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
233
EvalResult.setValid();
234
return true;
235
}
236
237
bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
238
const auto &Ptr = S.Stk.pop<Pointer>();
239
240
if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
241
return false;
242
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
243
return false;
244
245
if (std::optional<APValue> APV =
246
Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
247
EvalResult.setValue(*APV);
248
return true;
249
}
250
251
EvalResult.setInvalid();
252
return false;
253
}
254
255
bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
256
if (!isActive())
257
return true;
258
259
Block *B = getLocal(I);
260
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
261
return true;
262
}
263
264
template <PrimType OpType>
265
bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
266
if (!isActive())
267
return true;
268
269
using T = typename PrimConv<OpType>::T;
270
271
Block *B = getLocal(I);
272
S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
273
return true;
274
}
275
276
template <PrimType OpType>
277
bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
278
if (!isActive())
279
return true;
280
281
using T = typename PrimConv<OpType>::T;
282
283
Block *B = getLocal(I);
284
*reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
285
InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
286
Desc.IsInitialized = true;
287
288
return true;
289
}
290
291
bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
292
if (!isActive())
293
return true;
294
295
for (auto &Local : Descriptors[I]) {
296
Block *B = getLocal(Local.Offset);
297
S.deallocate(B);
298
}
299
300
return true;
301
}
302
303
/// Global temporaries (LifetimeExtendedTemporary) carry their value
304
/// around as an APValue, which codegen accesses.
305
/// We set their value once when creating them, but we don't update it
306
/// afterwards when code changes it later.
307
/// This is what we do here.
308
void EvalEmitter::updateGlobalTemporaries() {
309
for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
310
if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
311
const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
312
APValue *Cached = Temp->getOrCreateValue(true);
313
314
if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
315
TYPE_SWITCH(
316
*T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
317
} else {
318
if (std::optional<APValue> APV =
319
Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
320
*Cached = *APV;
321
}
322
}
323
}
324
S.SeenGlobalTemporaries.clear();
325
}
326
327
//===----------------------------------------------------------------------===//
328
// Opcode evaluators
329
//===----------------------------------------------------------------------===//
330
331
#define GET_EVAL_IMPL
332
#include "Opcodes.inc"
333
#undef GET_EVAL_IMPL
334
335