382 lines · cpp
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-exception6//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 15using namespace clang;16using namespace clang::interp;17 18EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,19 InterpStack &Stk)20 : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}21 22EvalEmitter::~EvalEmitter() {23 for (auto &V : Locals) {24 Block *B = reinterpret_cast<Block *>(V.get());25 if (B->isInitialized())26 B->invokeDtor();27 }28}29 30/// Clean up all our resources. This needs to done in failed evaluations before31/// we call InterpStack::clear(), because there might be a Pointer on the stack32/// pointing into a Block in the EvalEmitter.33void EvalEmitter::cleanup() { S.cleanup(); }34 35EvaluationResult EvalEmitter::interpretExpr(const Expr *E,36 bool ConvertResultToRValue,37 bool DestroyToplevelScope) {38 S.setEvalLocation(E->getExprLoc());39 this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);40 this->CheckFullyInitialized = isa<ConstantExpr>(E);41 EvalResult.setSource(E);42 43 if (!this->visitExpr(E, DestroyToplevelScope)) {44 // EvalResult may already have a result set, but something failed45 // after that (e.g. evaluating destructors).46 EvalResult.setInvalid();47 }48 49 return std::move(this->EvalResult);50}51 52EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD, const Expr *Init,53 bool CheckFullyInitialized) {54 assert(VD);55 assert(Init);56 this->CheckFullyInitialized = CheckFullyInitialized;57 S.EvaluatingDecl = VD;58 S.setEvalLocation(VD->getLocation());59 EvalResult.setSource(VD);60 61 QualType T = VD->getType();62 this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&63 !T->isObjCObjectPointerType();64 EvalResult.setSource(VD);65 66 if (!this->visitDeclAndReturn(VD, Init, S.inConstantContext()))67 EvalResult.setInvalid();68 69 S.EvaluatingDecl = nullptr;70 updateGlobalTemporaries();71 return std::move(this->EvalResult);72}73 74EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,75 PtrCallback PtrCB) {76 77 S.setEvalLocation(E->getExprLoc());78 this->ConvertResultToRValue = false;79 this->CheckFullyInitialized = false;80 this->PtrCB = PtrCB;81 EvalResult.setSource(E);82 83 if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) {84 // EvalResult may already have a result set, but something failed85 // after that (e.g. evaluating destructors).86 EvalResult.setInvalid();87 }88 89 return std::move(this->EvalResult);90}91 92bool EvalEmitter::interpretCall(const FunctionDecl *FD, const Expr *E) {93 // Add parameters to the parameter map. The values in the ParamOffset don't94 // matter in this case as reading from them can't ever work.95 for (const ParmVarDecl *PD : FD->parameters()) {96 this->Params.insert({PD, {0, false}});97 }98 99 return this->visitExpr(E, /*DestroyToplevelScope=*/false);100}101 102void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }103 104EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }105 106Scope::Local EvalEmitter::createLocal(Descriptor *D) {107 // Allocate memory for a local.108 auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());109 auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);110 B->invokeCtor();111 112 // Initialize local variable inline descriptor.113 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());114 Desc.Desc = D;115 Desc.Offset = sizeof(InlineDescriptor);116 Desc.IsActive = false;117 Desc.IsBase = false;118 Desc.IsFieldMutable = false;119 Desc.IsConst = false;120 Desc.IsInitialized = false;121 122 // Register the local.123 unsigned Off = Locals.size();124 Locals.push_back(std::move(Memory));125 return {Off, D};126}127 128bool EvalEmitter::jumpTrue(const LabelTy &Label) {129 if (isActive()) {130 if (S.Stk.pop<bool>())131 ActiveLabel = Label;132 }133 return true;134}135 136bool EvalEmitter::jumpFalse(const LabelTy &Label) {137 if (isActive()) {138 if (!S.Stk.pop<bool>())139 ActiveLabel = Label;140 }141 return true;142}143 144bool EvalEmitter::jump(const LabelTy &Label) {145 if (isActive())146 CurrentLabel = ActiveLabel = Label;147 return true;148}149 150bool EvalEmitter::fallthrough(const LabelTy &Label) {151 if (isActive())152 ActiveLabel = Label;153 CurrentLabel = Label;154 return true;155}156 157bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {158 size_t StackSizeBefore = S.Stk.size();159 const Expr *Arg = E->getArg(0);160 if (!this->visit(Arg)) {161 S.Stk.clearTo(StackSizeBefore);162 163 if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext()))164 return this->emitBool(false, E);165 return Invalid(S, OpPC);166 }167 168 PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr);169 if (T == PT_Ptr) {170 const auto &Ptr = S.Stk.pop<Pointer>();171 return this->emitBool(CheckBCPResult(S, Ptr), E);172 }173 174 // Otherwise, this is fine!175 if (!this->emitPop(T, E))176 return false;177 return this->emitBool(true, E);178}179 180template <PrimType OpType> bool EvalEmitter::emitRet(SourceInfo Info) {181 if (!isActive())182 return true;183 184 using T = typename PrimConv<OpType>::T;185 EvalResult.takeValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));186 return true;187}188 189template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {190 if (!isActive())191 return true;192 193 const Pointer &Ptr = S.Stk.pop<Pointer>();194 195 if (Ptr.isFunctionPointer()) {196 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));197 return true;198 }199 200 // If we're returning a raw pointer, call our callback.201 if (this->PtrCB)202 return (*this->PtrCB)(Ptr);203 204 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))205 return false;206 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))207 return false;208 209 // Implicitly convert lvalue to rvalue, if requested.210 if (ConvertResultToRValue) {211 if (!Ptr.isZero() && !Ptr.isDereferencable())212 return false;213 214 if (Ptr.pointsToStringLiteral() && Ptr.isArrayRoot())215 return false;216 217 if (!Ptr.isZero() && !CheckFinalLoad(S, OpPC, Ptr))218 return false;219 220 // Never allow reading from a non-const pointer, unless the memory221 // has been created in this evaluation.222 if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&223 Ptr.block()->getEvalID() != Ctx.getEvalID())224 return false;225 226 if (std::optional<APValue> V =227 Ptr.toRValue(Ctx, EvalResult.getSourceType())) {228 EvalResult.takeValue(std::move(*V));229 } else {230 return false;231 }232 } else {233 // If this is pointing to a local variable, just return234 // the result, even if the pointer is dead.235 // This will later be diagnosed by CheckLValueConstantExpression.236 if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) {237 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));238 return true;239 }240 241 if (!Ptr.isLive() && !Ptr.isTemporary())242 return false;243 244 EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));245 }246 247 return true;248}249 250bool EvalEmitter::emitRetVoid(SourceInfo Info) {251 EvalResult.setValid();252 return true;253}254 255bool EvalEmitter::emitRetValue(SourceInfo Info) {256 const auto &Ptr = S.Stk.pop<Pointer>();257 258 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))259 return false;260 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))261 return false;262 263 if (std::optional<APValue> APV =264 Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {265 EvalResult.takeValue(std::move(*APV));266 return true;267 }268 269 EvalResult.setInvalid();270 return false;271}272 273bool EvalEmitter::emitGetPtrLocal(uint32_t I, SourceInfo Info) {274 if (!isActive())275 return true;276 277 Block *B = getLocal(I);278 S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));279 return true;280}281 282template <PrimType OpType>283bool EvalEmitter::emitGetLocal(uint32_t I, SourceInfo Info) {284 if (!isActive())285 return true;286 287 using T = typename PrimConv<OpType>::T;288 289 Block *B = getLocal(I);290 291 if (!CheckLocalLoad(S, OpPC, B))292 return false;293 294 S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));295 return true;296}297 298template <PrimType OpType>299bool EvalEmitter::emitSetLocal(uint32_t I, SourceInfo Info) {300 if (!isActive())301 return true;302 303 using T = typename PrimConv<OpType>::T;304 305 Block *B = getLocal(I);306 *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();307 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());308 Desc.IsInitialized = true;309 310 return true;311}312 313bool EvalEmitter::emitDestroy(uint32_t I, SourceInfo Info) {314 if (!isActive())315 return true;316 317 for (auto &Local : Descriptors[I]) {318 Block *B = getLocal(Local.Offset);319 S.deallocate(B);320 }321 322 return true;323}324 325bool EvalEmitter::emitGetLocalEnabled(uint32_t I, SourceInfo Info) {326 if (!isActive())327 return true;328 329 Block *B = getLocal(I);330 const InlineDescriptor &Desc =331 *reinterpret_cast<InlineDescriptor *>(B->rawData());332 333 S.Stk.push<bool>(Desc.IsActive);334 return true;335}336 337bool EvalEmitter::emitEnableLocal(uint32_t I, SourceInfo Info) {338 if (!isActive())339 return true;340 341 // FIXME: This is a little dirty, but to avoid adding a flag to342 // InlineDescriptor that's only ever useful on the toplevel of local343 // variables, we reuse the IsActive flag for the enabled state. We should344 // probably use a different struct than InlineDescriptor for the block-level345 // inline descriptor of local varaibles.346 Block *B = getLocal(I);347 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());348 Desc.IsActive = true;349 return true;350}351 352/// Global temporaries (LifetimeExtendedTemporary) carry their value353/// around as an APValue, which codegen accesses.354/// We set their value once when creating them, but we don't update it355/// afterwards when code changes it later.356/// This is what we do here.357void EvalEmitter::updateGlobalTemporaries() {358 for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {359 UnsignedOrNone GlobalIndex = P.getGlobal(E);360 assert(GlobalIndex);361 const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);362 APValue *Cached = Temp->getOrCreateValue(true);363 if (OptPrimType T = Ctx.classify(E->getType())) {364 TYPE_SWITCH(*T,365 { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });366 } else {367 if (std::optional<APValue> APV =368 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))369 *Cached = *APV;370 }371 }372 S.SeenGlobalTemporaries.clear();373}374 375//===----------------------------------------------------------------------===//376// Opcode evaluators377//===----------------------------------------------------------------------===//378 379#define GET_EVAL_IMPL380#include "Opcodes.inc"381#undef GET_EVAL_IMPL382