xref: /llvm-project/clang/lib/AST/ByteCode/EvalEmitter.cpp (revision a0bd40e5a3df94229ec06243f2958289071ca75c)
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   // Create a dummy frame for the interpreter which does not have locals.
22   S.Current =
23       new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr(), 0);
24 }
25 
26 EvalEmitter::~EvalEmitter() {
27   for (auto &[K, V] : Locals) {
28     Block *B = reinterpret_cast<Block *>(V.get());
29     if (B->isInitialized())
30       B->invokeDtor();
31   }
32 }
33 
34 /// Clean up all our resources. This needs to done in failed evaluations before
35 /// we call InterpStack::clear(), because there might be a Pointer on the stack
36 /// pointing into a Block in the EvalEmitter.
37 void EvalEmitter::cleanup() { S.cleanup(); }
38 
39 EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
40                                             bool ConvertResultToRValue,
41                                             bool DestroyToplevelScope) {
42   S.setEvalLocation(E->getExprLoc());
43   this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);
44   this->CheckFullyInitialized = isa<ConstantExpr>(E);
45   EvalResult.setSource(E);
46 
47   if (!this->visitExpr(E, DestroyToplevelScope)) {
48     // EvalResult may already have a result set, but something failed
49     // after that (e.g. evaluating destructors).
50     EvalResult.setInvalid();
51   }
52 
53   return std::move(this->EvalResult);
54 }
55 
56 EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,
57                                             bool CheckFullyInitialized) {
58   this->CheckFullyInitialized = CheckFullyInitialized;
59   S.EvaluatingDecl = VD;
60   EvalResult.setSource(VD);
61 
62   if (const Expr *Init = VD->getAnyInitializer()) {
63     QualType T = VD->getType();
64     this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&
65                                   !T->isObjCObjectPointerType();
66   } else
67     this->ConvertResultToRValue = false;
68 
69   EvalResult.setSource(VD);
70 
71   if (!this->visitDeclAndReturn(VD, S.inConstantContext()))
72     EvalResult.setInvalid();
73 
74   S.EvaluatingDecl = nullptr;
75   updateGlobalTemporaries();
76   return std::move(this->EvalResult);
77 }
78 
79 void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
80 
81 EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
82 
83 Scope::Local EvalEmitter::createLocal(Descriptor *D) {
84   // Allocate memory for a local.
85   auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
86   auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
87   B->invokeCtor();
88 
89   // Initialize local variable inline descriptor.
90   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
91   Desc.Desc = D;
92   Desc.Offset = sizeof(InlineDescriptor);
93   Desc.IsActive = true;
94   Desc.IsBase = false;
95   Desc.IsFieldMutable = false;
96   Desc.IsConst = false;
97   Desc.IsInitialized = false;
98 
99   // Register the local.
100   unsigned Off = Locals.size();
101   Locals.insert({Off, std::move(Memory)});
102   return {Off, D};
103 }
104 
105 bool EvalEmitter::jumpTrue(const LabelTy &Label) {
106   if (isActive()) {
107     if (S.Stk.pop<bool>())
108       ActiveLabel = Label;
109   }
110   return true;
111 }
112 
113 bool EvalEmitter::jumpFalse(const LabelTy &Label) {
114   if (isActive()) {
115     if (!S.Stk.pop<bool>())
116       ActiveLabel = Label;
117   }
118   return true;
119 }
120 
121 bool EvalEmitter::jump(const LabelTy &Label) {
122   if (isActive())
123     CurrentLabel = ActiveLabel = Label;
124   return true;
125 }
126 
127 bool EvalEmitter::fallthrough(const LabelTy &Label) {
128   if (isActive())
129     ActiveLabel = Label;
130   CurrentLabel = Label;
131   return true;
132 }
133 
134 template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
135   if (!isActive())
136     return true;
137 
138   using T = typename PrimConv<OpType>::T;
139   EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
140   return true;
141 }
142 
143 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
144   if (!isActive())
145     return true;
146 
147   const Pointer &Ptr = S.Stk.pop<Pointer>();
148 
149   if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
150     return false;
151   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
152     return false;
153 
154   // Implicitly convert lvalue to rvalue, if requested.
155   if (ConvertResultToRValue) {
156     if (!Ptr.isZero() && !Ptr.isDereferencable())
157       return false;
158 
159     if (S.getLangOpts().CPlusPlus11 && Ptr.isBlockPointer() &&
160         !CheckFinalLoad(S, OpPC, Ptr)) {
161       return false;
162     }
163 
164     // Never allow reading from a non-const pointer, unless the memory
165     // has been created in this evaluation.
166     if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&
167         Ptr.block()->getEvalID() != Ctx.getEvalID())
168       return false;
169 
170     if (std::optional<APValue> V =
171             Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
172       EvalResult.setValue(*V);
173     } else {
174       return false;
175     }
176   } else {
177     EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));
178   }
179 
180   return true;
181 }
182 template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
183   if (!isActive())
184     return true;
185 
186   // Function pointers cannot be converted to rvalues.
187   EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
188   return true;
189 }
190 
191 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
192   EvalResult.setValid();
193   return true;
194 }
195 
196 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
197   const auto &Ptr = S.Stk.pop<Pointer>();
198 
199   if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
200     return false;
201   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
202     return false;
203 
204   if (std::optional<APValue> APV =
205           Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
206     EvalResult.setValue(*APV);
207     return true;
208   }
209 
210   EvalResult.setInvalid();
211   return false;
212 }
213 
214 bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
215   if (!isActive())
216     return true;
217 
218   Block *B = getLocal(I);
219   S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
220   return true;
221 }
222 
223 template <PrimType OpType>
224 bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
225   if (!isActive())
226     return true;
227 
228   using T = typename PrimConv<OpType>::T;
229 
230   Block *B = getLocal(I);
231   S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
232   return true;
233 }
234 
235 template <PrimType OpType>
236 bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
237   if (!isActive())
238     return true;
239 
240   using T = typename PrimConv<OpType>::T;
241 
242   Block *B = getLocal(I);
243   *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
244   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
245   Desc.IsInitialized = true;
246 
247   return true;
248 }
249 
250 bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
251   if (!isActive())
252     return true;
253 
254   for (auto &Local : Descriptors[I]) {
255     Block *B = getLocal(Local.Offset);
256     S.deallocate(B);
257   }
258 
259   return true;
260 }
261 
262 /// Global temporaries (LifetimeExtendedTemporary) carry their value
263 /// around as an APValue, which codegen accesses.
264 /// We set their value once when creating them, but we don't update it
265 /// afterwards when code changes it later.
266 /// This is what we do here.
267 void EvalEmitter::updateGlobalTemporaries() {
268   for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
269     if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
270       const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
271       APValue *Cached = Temp->getOrCreateValue(true);
272 
273       if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
274         TYPE_SWITCH(
275             *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
276       } else {
277         if (std::optional<APValue> APV =
278                 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
279           *Cached = *APV;
280       }
281     }
282   }
283   S.SeenGlobalTemporaries.clear();
284 }
285 
286 //===----------------------------------------------------------------------===//
287 // Opcode evaluators
288 //===----------------------------------------------------------------------===//
289 
290 #define GET_EVAL_IMPL
291 #include "Opcodes.inc"
292 #undef GET_EVAL_IMPL
293