1 //===--- EvalEmitter.cpp - Instruction emitter for the VM -------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "EvalEmitter.h"
11 #include "IntegralAP.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
,
20 : Ctx(Ctx
), P(P
), S(Parent
, P
, Stk
, Ctx
, this), EvalResult(&Ctx
),
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())
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();
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);
88 // Initialize local variable inline descriptor.
89 InlineDescriptor
&Desc
= *reinterpret_cast<InlineDescriptor
*>(B
->rawData());
91 Desc
.Offset
= sizeof(InlineDescriptor
);
94 Desc
.IsFieldMutable
= false;
96 Desc
.IsInitialized
= false;
98 // Register the local.
99 unsigned Off
= Locals
.size();
100 Locals
.insert({Off
, std::move(Memory
)});
104 bool EvalEmitter::jumpTrue(const LabelTy
&Label
) {
106 if (S
.Stk
.pop
<bool>())
112 bool EvalEmitter::jumpFalse(const LabelTy
&Label
) {
114 if (!S
.Stk
.pop
<bool>())
120 bool EvalEmitter::jump(const LabelTy
&Label
) {
122 CurrentLabel
= ActiveLabel
= Label
;
126 bool EvalEmitter::fallthrough(const LabelTy
&Label
) {
129 CurrentLabel
= Label
;
133 template <PrimType OpType
> bool EvalEmitter::emitRet(const SourceInfo
&Info
) {
137 using T
= typename PrimConv
<OpType
>::T
;
138 EvalResult
.setValue(S
.Stk
.pop
<T
>().toAPValue(Ctx
.getASTContext()));
142 template <> bool EvalEmitter::emitRet
<PT_Ptr
>(const SourceInfo
&Info
) {
146 const Pointer
&Ptr
= S
.Stk
.pop
<Pointer
>();
148 if (!EvalResult
.checkReturnValue(S
, Ctx
, Ptr
, Info
))
150 if (CheckFullyInitialized
&& !EvalResult
.checkFullyInitialized(S
, Ptr
))
153 // Implicitly convert lvalue to rvalue, if requested.
154 if (ConvertResultToRValue
) {
155 if (!Ptr
.isZero() && !Ptr
.isDereferencable())
158 if (S
.getLangOpts().CPlusPlus11
&& Ptr
.isBlockPointer() &&
159 !CheckFinalLoad(S
, OpPC
, Ptr
)) {
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())
169 if (std::optional
<APValue
> V
=
170 Ptr
.toRValue(Ctx
, EvalResult
.getSourceType())) {
171 EvalResult
.setValue(*V
);
176 if (!Ptr
.isLive() && !Ptr
.isTemporary())
179 EvalResult
.setValue(Ptr
.toAPValue(Ctx
.getASTContext()));
184 template <> bool EvalEmitter::emitRet
<PT_FnPtr
>(const SourceInfo
&Info
) {
188 // Function pointers cannot be converted to rvalues.
189 EvalResult
.setFunctionPointer(S
.Stk
.pop
<FunctionPointer
>());
193 bool EvalEmitter::emitRetVoid(const SourceInfo
&Info
) {
194 EvalResult
.setValid();
198 bool EvalEmitter::emitRetValue(const SourceInfo
&Info
) {
199 const auto &Ptr
= S
.Stk
.pop
<Pointer
>();
201 if (!EvalResult
.checkReturnValue(S
, Ctx
, Ptr
, Info
))
203 if (CheckFullyInitialized
&& !EvalResult
.checkFullyInitialized(S
, Ptr
))
206 if (std::optional
<APValue
> APV
=
207 Ptr
.toRValue(S
.getASTContext(), EvalResult
.getSourceType())) {
208 EvalResult
.setValue(*APV
);
212 EvalResult
.setInvalid();
216 bool EvalEmitter::emitGetPtrLocal(uint32_t I
, const SourceInfo
&Info
) {
220 Block
*B
= getLocal(I
);
221 S
.Stk
.push
<Pointer
>(B
, sizeof(InlineDescriptor
));
225 template <PrimType OpType
>
226 bool EvalEmitter::emitGetLocal(uint32_t I
, const SourceInfo
&Info
) {
230 using T
= typename PrimConv
<OpType
>::T
;
232 Block
*B
= getLocal(I
);
233 S
.Stk
.push
<T
>(*reinterpret_cast<T
*>(B
->data()));
237 template <PrimType OpType
>
238 bool EvalEmitter::emitSetLocal(uint32_t I
, const SourceInfo
&Info
) {
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;
252 bool EvalEmitter::emitDestroy(uint32_t I
, const SourceInfo
&Info
) {
256 for (auto &Local
: Descriptors
[I
]) {
257 Block
*B
= getLocal(Local
.Offset
);
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())) {
277 *T
, { *Cached
= Ptr
.deref
<T
>().toAPValue(Ctx
.getASTContext()); });
279 if (std::optional
<APValue
> APV
=
280 Ptr
.toRValue(Ctx
, Temp
->getTemporaryExpr()->getType()))
285 S
.SeenGlobalTemporaries
.clear();
288 //===----------------------------------------------------------------------===//
290 //===----------------------------------------------------------------------===//
292 #define GET_EVAL_IMPL
293 #include "Opcodes.inc"