xref: /llvm-project/clang/lib/AST/ByteCode/EvalEmitter.cpp (revision a07aba5d44204a7ca0d891a3da05af9960081e4c)
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 "Opcode.h"
14 #include "clang/AST/DeclCXX.h"
15 
16 using namespace clang;
17 using namespace clang::interp;
18 
19 EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
20                          InterpStack &Stk)
21     : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
22   // Create a dummy frame for the interpreter which does not have locals.
23   S.Current =
24       new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr(), 0);
25 }
26 
27 EvalEmitter::~EvalEmitter() {
28   for (auto &[K, V] : Locals) {
29     Block *B = reinterpret_cast<Block *>(V.get());
30     if (B->isInitialized())
31       B->invokeDtor();
32   }
33 }
34 
35 /// Clean up all our resources. This needs to done in failed evaluations before
36 /// we call InterpStack::clear(), because there might be a Pointer on the stack
37 /// pointing into a Block in the EvalEmitter.
38 void EvalEmitter::cleanup() { S.cleanup(); }
39 
40 EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
41                                             bool ConvertResultToRValue) {
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)) {
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 static bool checkReturnState(InterpState &S) {
135   return S.maybeDiagnoseDanglingAllocations();
136 }
137 
138 template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
139   if (!isActive())
140     return true;
141 
142   if (!checkReturnState(S))
143     return false;
144 
145   using T = typename PrimConv<OpType>::T;
146   EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
147   return true;
148 }
149 
150 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
151   if (!isActive())
152     return true;
153 
154   const Pointer &Ptr = S.Stk.pop<Pointer>();
155 
156   if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
157     return false;
158   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
159     return false;
160 
161   if (!checkReturnState(S))
162     return false;
163 
164   // Implicitly convert lvalue to rvalue, if requested.
165   if (ConvertResultToRValue) {
166     if (!Ptr.isZero() && !Ptr.isDereferencable())
167       return false;
168     // Never allow reading from a non-const pointer, unless the memory
169     // has been created in this evaluation.
170     if (!Ptr.isZero() && Ptr.isBlockPointer() &&
171         Ptr.block()->getEvalID() != Ctx.getEvalID() &&
172         (!CheckLoad(S, OpPC, Ptr, AK_Read) || !Ptr.isConst()))
173       return false;
174 
175     if (std::optional<APValue> V =
176             Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
177       EvalResult.setValue(*V);
178     } else {
179       return false;
180     }
181   } else {
182     EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));
183   }
184 
185   return true;
186 }
187 template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
188   if (!isActive())
189     return true;
190 
191   if (!checkReturnState(S))
192     return false;
193   // Function pointers cannot be converted to rvalues.
194   EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
195   return true;
196 }
197 
198 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
199   if (!checkReturnState(S))
200     return false;
201   EvalResult.setValid();
202   return true;
203 }
204 
205 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
206   const auto &Ptr = S.Stk.pop<Pointer>();
207 
208   if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
209     return false;
210   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
211     return false;
212 
213   if (!checkReturnState(S))
214     return false;
215 
216   if (std::optional<APValue> APV =
217           Ptr.toRValue(S.getCtx(), EvalResult.getSourceType())) {
218     EvalResult.setValue(*APV);
219     return true;
220   }
221 
222   EvalResult.setInvalid();
223   return false;
224 }
225 
226 bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
227   if (!isActive())
228     return true;
229 
230   Block *B = getLocal(I);
231   S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
232   return true;
233 }
234 
235 template <PrimType OpType>
236 bool EvalEmitter::emitGetLocal(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   S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
244   return true;
245 }
246 
247 template <PrimType OpType>
248 bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
249   if (!isActive())
250     return true;
251 
252   using T = typename PrimConv<OpType>::T;
253 
254   Block *B = getLocal(I);
255   *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
256   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
257   Desc.IsInitialized = true;
258 
259   return true;
260 }
261 
262 bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
263   if (!isActive())
264     return true;
265 
266   for (auto &Local : Descriptors[I]) {
267     Block *B = getLocal(Local.Offset);
268     S.deallocate(B);
269   }
270 
271   return true;
272 }
273 
274 /// Global temporaries (LifetimeExtendedTemporary) carry their value
275 /// around as an APValue, which codegen accesses.
276 /// We set their value once when creating them, but we don't update it
277 /// afterwards when code changes it later.
278 /// This is what we do here.
279 void EvalEmitter::updateGlobalTemporaries() {
280   for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
281     if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
282       const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
283       APValue *Cached = Temp->getOrCreateValue(true);
284 
285       if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
286         TYPE_SWITCH(
287             *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
288       } else {
289         if (std::optional<APValue> APV =
290                 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
291           *Cached = *APV;
292       }
293     }
294   }
295   S.SeenGlobalTemporaries.clear();
296 }
297 
298 //===----------------------------------------------------------------------===//
299 // Opcode evaluators
300 //===----------------------------------------------------------------------===//
301 
302 #define GET_EVAL_IMPL
303 #include "Opcodes.inc"
304 #undef GET_EVAL_IMPL
305