xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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) {
80   CurrentLabel = Label;
81 }
82 
83 EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
84 
85 Scope::Local EvalEmitter::createLocal(Descriptor *D) {
86   // Allocate memory for a local.
87   auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
88   auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
89   B->invokeCtor();
90 
91   // Initialize local variable inline descriptor.
92   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
93   Desc.Desc = D;
94   Desc.Offset = sizeof(InlineDescriptor);
95   Desc.IsActive = true;
96   Desc.IsBase = false;
97   Desc.IsFieldMutable = false;
98   Desc.IsConst = false;
99   Desc.IsInitialized = false;
100 
101   // Register the local.
102   unsigned Off = Locals.size();
103   Locals.insert({Off, std::move(Memory)});
104   return {Off, D};
105 }
106 
107 bool EvalEmitter::jumpTrue(const LabelTy &Label) {
108   if (isActive()) {
109     if (S.Stk.pop<bool>())
110       ActiveLabel = Label;
111   }
112   return true;
113 }
114 
115 bool EvalEmitter::jumpFalse(const LabelTy &Label) {
116   if (isActive()) {
117     if (!S.Stk.pop<bool>())
118       ActiveLabel = Label;
119   }
120   return true;
121 }
122 
123 bool EvalEmitter::jump(const LabelTy &Label) {
124   if (isActive())
125     CurrentLabel = ActiveLabel = Label;
126   return true;
127 }
128 
129 bool EvalEmitter::fallthrough(const LabelTy &Label) {
130   if (isActive())
131     ActiveLabel = Label;
132   CurrentLabel = Label;
133   return true;
134 }
135 
136 static bool checkReturnState(InterpState &S) {
137   return S.maybeDiagnoseDanglingAllocations();
138 }
139 
140 template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
141   if (!isActive())
142     return true;
143 
144   if (!checkReturnState(S))
145     return false;
146 
147   using T = typename PrimConv<OpType>::T;
148   EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
149   return true;
150 }
151 
152 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
153   if (!isActive())
154     return true;
155 
156   const Pointer &Ptr = S.Stk.pop<Pointer>();
157 
158   if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
159     return false;
160   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
161     return false;
162 
163   if (!checkReturnState(S))
164     return false;
165 
166   // Implicitly convert lvalue to rvalue, if requested.
167   if (ConvertResultToRValue) {
168     if (!Ptr.isZero() && !Ptr.isDereferencable())
169       return false;
170     // Never allow reading from a non-const pointer, unless the memory
171     // has been created in this evaluation.
172     if (!Ptr.isZero() && Ptr.isBlockPointer() &&
173         Ptr.block()->getEvalID() != Ctx.getEvalID() &&
174         (!CheckLoad(S, OpPC, Ptr, AK_Read) || !Ptr.isConst()))
175       return false;
176 
177     if (std::optional<APValue> V =
178             Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
179       EvalResult.setValue(*V);
180     } else {
181       return false;
182     }
183   } else {
184     EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));
185   }
186 
187   return true;
188 }
189 template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
190   if (!isActive())
191     return true;
192 
193   if (!checkReturnState(S))
194     return false;
195   // Function pointers cannot be converted to rvalues.
196   EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
197   return true;
198 }
199 
200 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
201   if (!checkReturnState(S))
202     return false;
203   EvalResult.setValid();
204   return true;
205 }
206 
207 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
208   const auto &Ptr = S.Stk.pop<Pointer>();
209 
210   if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
211     return false;
212   if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
213     return false;
214 
215   if (!checkReturnState(S))
216     return false;
217 
218   if (std::optional<APValue> APV =
219           Ptr.toRValue(S.getCtx(), EvalResult.getSourceType())) {
220     EvalResult.setValue(*APV);
221     return true;
222   }
223 
224   EvalResult.setInvalid();
225   return false;
226 }
227 
228 bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
229   if (!isActive())
230     return true;
231 
232   Block *B = getLocal(I);
233   S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
234   return true;
235 }
236 
237 template <PrimType OpType>
238 bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
239   if (!isActive())
240     return true;
241 
242   using T = typename PrimConv<OpType>::T;
243 
244   Block *B = getLocal(I);
245   S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
246   return true;
247 }
248 
249 template <PrimType OpType>
250 bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
251   if (!isActive())
252     return true;
253 
254   using T = typename PrimConv<OpType>::T;
255 
256   Block *B = getLocal(I);
257   *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
258   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
259   Desc.IsInitialized = true;
260 
261   return true;
262 }
263 
264 bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
265   if (!isActive())
266     return true;
267 
268   for (auto &Local : Descriptors[I]) {
269     Block *B = getLocal(Local.Offset);
270     S.deallocate(B);
271   }
272 
273   return true;
274 }
275 
276 /// Global temporaries (LifetimeExtendedTemporary) carry their value
277 /// around as an APValue, which codegen accesses.
278 /// We set their value once when creating them, but we don't update it
279 /// afterwards when code changes it later.
280 /// This is what we do here.
281 void EvalEmitter::updateGlobalTemporaries() {
282   for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {
283     if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {
284       const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
285       APValue *Cached = Temp->getOrCreateValue(true);
286 
287       if (std::optional<PrimType> T = Ctx.classify(E->getType())) {
288         TYPE_SWITCH(
289             *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
290       } else {
291         if (std::optional<APValue> APV =
292                 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
293           *Cached = *APV;
294       }
295     }
296   }
297   S.SeenGlobalTemporaries.clear();
298 }
299 
300 //===----------------------------------------------------------------------===//
301 // Opcode evaluators
302 //===----------------------------------------------------------------------===//
303 
304 #define GET_EVAL_IMPL
305 #include "Opcodes.inc"
306 #undef GET_EVAL_IMPL
307