[clang][bytecode] Stack-allocate bottom function frame (#125253)
[llvm-project.git] / clang / lib / AST / ByteCode / EvalEmitter.cpp
blobe857e2395605754e0d4fd2abba7148067154de81
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 //===----------------------------------------------------------------------===//
9 #include "EvalEmitter.h"
10 #include "Context.h"
11 #include "IntegralAP.h"
12 #include "Interp.h"
13 #include "clang/AST/DeclCXX.h"
15 using namespace clang;
16 using namespace clang::interp;
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 BottomFrame(S) {
22 S.Current = &BottomFrame;
25 EvalEmitter::~EvalEmitter() {
26 for (auto &[K, V] : Locals) {
27 Block *B = reinterpret_cast<Block *>(V.get());
28 if (B->isInitialized())
29 B->invokeDtor();
33 /// Clean up all our resources. This needs to done in failed evaluations before
34 /// we call InterpStack::clear(), because there might be a Pointer on the stack
35 /// pointing into a Block in the EvalEmitter.
36 void EvalEmitter::cleanup() { S.cleanup(); }
38 EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
39 bool ConvertResultToRValue,
40 bool DestroyToplevelScope) {
41 S.setEvalLocation(E->getExprLoc());
42 this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);
43 this->CheckFullyInitialized = isa<ConstantExpr>(E);
44 EvalResult.setSource(E);
46 if (!this->visitExpr(E, DestroyToplevelScope)) {
47 // EvalResult may already have a result set, but something failed
48 // after that (e.g. evaluating destructors).
49 EvalResult.setInvalid();
52 return std::move(this->EvalResult);
55 EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
56 bool CheckFullyInitialized) {
57 this->CheckFullyInitialized = CheckFullyInitialized;
58 S.EvaluatingDecl = VD;
59 EvalResult.setSource(VD);
61 if (const Expr *Init = VD->getAnyInitializer()) {
62 QualType T = VD->getType();
63 this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
64 !T->isObjCObjectPointerType();
65 } else
66 this->ConvertResultToRValue = false;
68 EvalResult.setSource(VD);
70 if (!this->visitDeclAndReturn(VD, S.inConstantContext()))
71 EvalResult.setInvalid();
73 S.EvaluatingDecl = nullptr;
74 updateGlobalTemporaries();
75 return std::move(this->EvalResult);
78 void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
80 EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
82 Scope::Local EvalEmitter::createLocal(Descriptor *D) {
83 // Allocate memory for a local.
84 auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
85 auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
86 B->invokeCtor();
88 // Initialize local variable inline descriptor.
89 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
90 Desc.Desc = D;
91 Desc.Offset = sizeof(InlineDescriptor);
92 Desc.IsActive = true;
93 Desc.IsBase = false;
94 Desc.IsFieldMutable = false;
95 Desc.IsConst = false;
96 Desc.IsInitialized = false;
98 // Register the local.
99 unsigned Off = Locals.size();
100 Locals.insert({Off, std::move(Memory)});
101 return {Off, D};
104 bool EvalEmitter::jumpTrue(const LabelTy &Label) {
105 if (isActive()) {
106 if (S.Stk.pop<bool>())
107 ActiveLabel = Label;
109 return true;
112 bool EvalEmitter::jumpFalse(const LabelTy &Label) {
113 if (isActive()) {
114 if (!S.Stk.pop<bool>())
115 ActiveLabel = Label;
117 return true;
120 bool EvalEmitter::jump(const LabelTy &Label) {
121 if (isActive())
122 CurrentLabel = ActiveLabel = Label;
123 return true;
126 bool EvalEmitter::fallthrough(const LabelTy &Label) {
127 if (isActive())
128 ActiveLabel = Label;
129 CurrentLabel = Label;
130 return true;
133 template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
134 if (!isActive())
135 return true;
137 using T = typename PrimConv<OpType>::T;
138 EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
139 return true;
142 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
143 if (!isActive())
144 return true;
146 const Pointer &Ptr = S.Stk.pop<Pointer>();
148 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
149 return false;
150 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
151 return false;
153 // Implicitly convert lvalue to rvalue, if requested.
154 if (ConvertResultToRValue) {
155 if (!Ptr.isZero() && !Ptr.isDereferencable())
156 return false;
158 if (S.getLangOpts().CPlusPlus11 && Ptr.isBlockPointer() &&
159 !CheckFinalLoad(S, OpPC, Ptr)) {
160 return false;
163 // Never allow reading from a non-const pointer, unless the memory
164 // has been created in this evaluation.
165 if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&
166 Ptr.block()->getEvalID() != Ctx.getEvalID())
167 return false;
169 if (std::optional<APValue> V =
170 Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
171 EvalResult.setValue(*V);
172 } else {
173 return false;
175 } else {
176 if (!Ptr.isLive() && !Ptr.isTemporary())
177 return false;
179 EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));
182 return true;
184 template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
185 if (!isActive())
186 return true;
188 // Function pointers cannot be converted to rvalues.
189 EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
190 return true;
193 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
194 EvalResult.setValid();
195 return true;
198 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
199 const auto &Ptr = S.Stk.pop<Pointer>();
201 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
202 return false;
203 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
204 return false;
206 if (std::optional<APValue> APV =
207 Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
208 EvalResult.setValue(*APV);
209 return true;
212 EvalResult.setInvalid();
213 return false;
216 bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
217 if (!isActive())
218 return true;
220 Block *B = getLocal(I);
221 S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
222 return true;
225 template <PrimType OpType>
226 bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
227 if (!isActive())
228 return true;
230 using T = typename PrimConv<OpType>::T;
232 Block *B = getLocal(I);
233 S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
234 return true;
237 template <PrimType OpType>
238 bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
239 if (!isActive())
240 return true;
242 using T = typename PrimConv<OpType>::T;
244 Block *B = getLocal(I);
245 *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
246 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
247 Desc.IsInitialized = true;
249 return true;
252 bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
253 if (!isActive())
254 return true;
256 for (auto &Local : Descriptors[I]) {
257 Block *B = getLocal(Local.Offset);
258 S.deallocate(B);
261 return true;
264 /// Global temporaries (LifetimeExtendedTemporary) carry their value
265 /// around as an APValue, which codegen accesses.
266 /// We set their value once when creating them, but we don't update it
267 /// afterwards when code changes it later.
268 /// This is what we do here.
269 void EvalEmitter::updateGlobalTemporaries() {
270 for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
271 if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
272 const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
273 APValue *Cached = Temp->getOrCreateValue(true);
275 if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
276 TYPE_SWITCH(
277 *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
278 } else {
279 if (std::optional<APValue> APV =
280 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
281 *Cached = *APV;
285 S.SeenGlobalTemporaries.clear();
288 //===----------------------------------------------------------------------===//
289 // Opcode evaluators
290 //===----------------------------------------------------------------------===//
292 #define GET_EVAL_IMPL
293 #include "Opcodes.inc"
294 #undef GET_EVAL_IMPL