xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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 "ByteCodeGenError.h"
11 #include "Context.h"
12 #include "IntegralAP.h"
13 #include "Interp.h"
14 #include "Opcode.h"
15 #include "clang/AST/DeclCXX.h"
16 
17 using namespace clang;
18 using namespace clang::interp;
19 
20 EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
21                          InterpStack &Stk, APValue &Result)
22     : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
23   // Create a dummy frame for the interpreter which does not have locals.
24   S.Current =
25       new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
26 }
27 
28 EvalEmitter::~EvalEmitter() {
29   for (auto &[K, V] : Locals) {
30     Block *B = reinterpret_cast<Block *>(V.get());
31     if (B->isInitialized())
32       B->invokeDtor();
33   }
34 }
35 
36 EvaluationResult EvalEmitter::interpretExpr(const Expr *E) {
37   EvalResult.setSource(E);
38 
39   if (!this->visitExpr(E))
40     EvalResult.setInvalid();
41 
42   return std::move(this->EvalResult);
43 }
44 
45 EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD) {
46   EvalResult.setSource(VD);
47 
48   if (!this->visitDecl(VD))
49     EvalResult.setInvalid();
50 
51   return std::move(this->EvalResult);
52 }
53 
54 void EvalEmitter::emitLabel(LabelTy Label) {
55   CurrentLabel = Label;
56 }
57 
58 EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
59 
60 Scope::Local EvalEmitter::createLocal(Descriptor *D) {
61   // Allocate memory for a local.
62   auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
63   auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
64   B->invokeCtor();
65 
66   // Initialize local variable inline descriptor.
67   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
68   Desc.Desc = D;
69   Desc.Offset = sizeof(InlineDescriptor);
70   Desc.IsActive = true;
71   Desc.IsBase = false;
72   Desc.IsFieldMutable = false;
73   Desc.IsConst = false;
74   Desc.IsInitialized = false;
75 
76   // Register the local.
77   unsigned Off = Locals.size();
78   Locals.insert({Off, std::move(Memory)});
79   return {Off, D};
80 }
81 
82 bool EvalEmitter::jumpTrue(const LabelTy &Label) {
83   if (isActive()) {
84     if (S.Stk.pop<bool>())
85       ActiveLabel = Label;
86   }
87   return true;
88 }
89 
90 bool EvalEmitter::jumpFalse(const LabelTy &Label) {
91   if (isActive()) {
92     if (!S.Stk.pop<bool>())
93       ActiveLabel = Label;
94   }
95   return true;
96 }
97 
98 bool EvalEmitter::jump(const LabelTy &Label) {
99   if (isActive())
100     CurrentLabel = ActiveLabel = Label;
101   return true;
102 }
103 
104 bool EvalEmitter::fallthrough(const LabelTy &Label) {
105   if (isActive())
106     ActiveLabel = Label;
107   CurrentLabel = Label;
108   return true;
109 }
110 
111 template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
112   if (!isActive())
113     return true;
114   using T = typename PrimConv<OpType>::T;
115   EvalResult.setValue(S.Stk.pop<T>().toAPValue());
116   return true;
117 }
118 
119 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
120   if (!isActive())
121     return true;
122   EvalResult.setPointer(S.Stk.pop<Pointer>());
123   return true;
124 }
125 template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
126   if (!isActive())
127     return true;
128   EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
129   return true;
130 }
131 
132 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
133   EvalResult.setValid();
134   return true;
135 }
136 
137 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
138   const auto &Ptr = S.Stk.pop<Pointer>();
139   if (std::optional<APValue> APV = Ptr.toRValue(S.getCtx())) {
140     EvalResult.setValue(*APV);
141     return true;
142   }
143 
144   EvalResult.setInvalid();
145   return false;
146 }
147 
148 bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
149   if (!isActive())
150     return true;
151 
152   Block *B = getLocal(I);
153   S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
154   return true;
155 }
156 
157 template <PrimType OpType>
158 bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
159   if (!isActive())
160     return true;
161 
162   using T = typename PrimConv<OpType>::T;
163 
164   Block *B = getLocal(I);
165   S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
166   return true;
167 }
168 
169 template <PrimType OpType>
170 bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
171   if (!isActive())
172     return true;
173 
174   using T = typename PrimConv<OpType>::T;
175 
176   Block *B = getLocal(I);
177   *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
178   InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
179   Desc.IsInitialized = true;
180 
181   return true;
182 }
183 
184 bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
185   if (!isActive())
186     return true;
187 
188   for (auto &Local : Descriptors[I]) {
189     Block *B = getLocal(Local.Offset);
190     S.deallocate(B);
191   }
192 
193   return true;
194 }
195 
196 //===----------------------------------------------------------------------===//
197 // Opcode evaluators
198 //===----------------------------------------------------------------------===//
199 
200 #define GET_EVAL_IMPL
201 #include "Opcodes.inc"
202 #undef GET_EVAL_IMPL
203