Path: blob/main/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp
35292 views
//===--- EvalEmitter.cpp - Instruction emitter for the VM -------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "EvalEmitter.h"9#include "Context.h"10#include "IntegralAP.h"11#include "Interp.h"12#include "Opcode.h"13#include "clang/AST/DeclCXX.h"1415using namespace clang;16using namespace clang::interp;1718EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,19InterpStack &Stk)20: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {21// Create a dummy frame for the interpreter which does not have locals.22S.Current =23new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr(), 0);24}2526EvalEmitter::~EvalEmitter() {27for (auto &[K, V] : Locals) {28Block *B = reinterpret_cast<Block *>(V.get());29if (B->isInitialized())30B->invokeDtor();31}32}3334/// Clean up all our resources. This needs to done in failed evaluations before35/// we call InterpStack::clear(), because there might be a Pointer on the stack36/// pointing into a Block in the EvalEmitter.37void EvalEmitter::cleanup() { S.cleanup(); }3839EvaluationResult EvalEmitter::interpretExpr(const Expr *E,40bool ConvertResultToRValue) {41S.setEvalLocation(E->getExprLoc());42this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);43this->CheckFullyInitialized = isa<ConstantExpr>(E);44EvalResult.setSource(E);4546if (!this->visitExpr(E)) {47// EvalResult may already have a result set, but something failed48// after that (e.g. evaluating destructors).49EvalResult.setInvalid();50}5152return std::move(this->EvalResult);53}5455EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,56bool CheckFullyInitialized) {57this->CheckFullyInitialized = CheckFullyInitialized;58S.EvaluatingDecl = VD;59EvalResult.setSource(VD);6061if (const Expr *Init = VD->getAnyInitializer()) {62QualType T = VD->getType();63this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&64!T->isObjCObjectPointerType();65} else66this->ConvertResultToRValue = false;6768EvalResult.setSource(VD);6970if (!this->visitDeclAndReturn(VD, S.inConstantContext()))71EvalResult.setInvalid();7273S.EvaluatingDecl = nullptr;74updateGlobalTemporaries();75return std::move(this->EvalResult);76}7778void EvalEmitter::emitLabel(LabelTy Label) {79CurrentLabel = Label;80}8182EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }8384Scope::Local EvalEmitter::createLocal(Descriptor *D) {85// Allocate memory for a local.86auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());87auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);88B->invokeCtor();8990// Initialize local variable inline descriptor.91InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());92Desc.Desc = D;93Desc.Offset = sizeof(InlineDescriptor);94Desc.IsActive = true;95Desc.IsBase = false;96Desc.IsFieldMutable = false;97Desc.IsConst = false;98Desc.IsInitialized = false;99100// Register the local.101unsigned Off = Locals.size();102Locals.insert({Off, std::move(Memory)});103return {Off, D};104}105106bool EvalEmitter::jumpTrue(const LabelTy &Label) {107if (isActive()) {108if (S.Stk.pop<bool>())109ActiveLabel = Label;110}111return true;112}113114bool EvalEmitter::jumpFalse(const LabelTy &Label) {115if (isActive()) {116if (!S.Stk.pop<bool>())117ActiveLabel = Label;118}119return true;120}121122bool EvalEmitter::jump(const LabelTy &Label) {123if (isActive())124CurrentLabel = ActiveLabel = Label;125return true;126}127128bool EvalEmitter::fallthrough(const LabelTy &Label) {129if (isActive())130ActiveLabel = Label;131CurrentLabel = Label;132return true;133}134135static bool checkReturnState(InterpState &S) {136return S.maybeDiagnoseDanglingAllocations();137}138139template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {140if (!isActive())141return true;142143if (!checkReturnState(S))144return false;145146using T = typename PrimConv<OpType>::T;147EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));148return true;149}150151template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {152if (!isActive())153return true;154155const Pointer &Ptr = S.Stk.pop<Pointer>();156157if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))158return false;159if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))160return false;161162if (!checkReturnState(S))163return false;164165// Implicitly convert lvalue to rvalue, if requested.166if (ConvertResultToRValue) {167if (!Ptr.isZero() && !Ptr.isDereferencable())168return false;169// Never allow reading from a non-const pointer, unless the memory170// has been created in this evaluation.171if (!Ptr.isZero() && Ptr.isBlockPointer() &&172Ptr.block()->getEvalID() != Ctx.getEvalID() &&173(!CheckLoad(S, OpPC, Ptr, AK_Read) || !Ptr.isConst()))174return false;175176if (std::optional<APValue> V =177Ptr.toRValue(Ctx, EvalResult.getSourceType())) {178EvalResult.setValue(*V);179} else {180return false;181}182} else {183EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));184}185186return true;187}188template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {189if (!isActive())190return true;191192if (!checkReturnState(S))193return false;194// Function pointers cannot be converted to rvalues.195EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());196return true;197}198199bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {200if (!checkReturnState(S))201return false;202EvalResult.setValid();203return true;204}205206bool EvalEmitter::emitRetValue(const SourceInfo &Info) {207const auto &Ptr = S.Stk.pop<Pointer>();208209if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))210return false;211if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))212return false;213214if (!checkReturnState(S))215return false;216217if (std::optional<APValue> APV =218Ptr.toRValue(S.getCtx(), EvalResult.getSourceType())) {219EvalResult.setValue(*APV);220return true;221}222223EvalResult.setInvalid();224return false;225}226227bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {228if (!isActive())229return true;230231Block *B = getLocal(I);232S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));233return true;234}235236template <PrimType OpType>237bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {238if (!isActive())239return true;240241using T = typename PrimConv<OpType>::T;242243Block *B = getLocal(I);244S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));245return true;246}247248template <PrimType OpType>249bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {250if (!isActive())251return true;252253using T = typename PrimConv<OpType>::T;254255Block *B = getLocal(I);256*reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();257InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());258Desc.IsInitialized = true;259260return true;261}262263bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {264if (!isActive())265return true;266267for (auto &Local : Descriptors[I]) {268Block *B = getLocal(Local.Offset);269S.deallocate(B);270}271272return true;273}274275/// Global temporaries (LifetimeExtendedTemporary) carry their value276/// around as an APValue, which codegen accesses.277/// We set their value once when creating them, but we don't update it278/// afterwards when code changes it later.279/// This is what we do here.280void EvalEmitter::updateGlobalTemporaries() {281for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {282if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {283const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);284APValue *Cached = Temp->getOrCreateValue(true);285286if (std::optional<PrimType> T = Ctx.classify(E->getType())) {287TYPE_SWITCH(288*T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });289} else {290if (std::optional<APValue> APV =291Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))292*Cached = *APV;293}294}295}296S.SeenGlobalTemporaries.clear();297}298299//===----------------------------------------------------------------------===//300// Opcode evaluators301//===----------------------------------------------------------------------===//302303#define GET_EVAL_IMPL304#include "Opcodes.inc"305#undef GET_EVAL_IMPL306307308