Path: blob/main/contrib/llvm-project/clang/lib/AST/ByteCode/EvalEmitter.cpp
213799 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 "clang/AST/DeclCXX.h"1314using namespace clang;15using namespace clang::interp;1617EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,18InterpStack &Stk)19: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}2021EvalEmitter::~EvalEmitter() {22for (auto &V : Locals) {23Block *B = reinterpret_cast<Block *>(V.get());24if (B->isInitialized())25B->invokeDtor();26}27}2829/// Clean up all our resources. This needs to done in failed evaluations before30/// we call InterpStack::clear(), because there might be a Pointer on the stack31/// pointing into a Block in the EvalEmitter.32void EvalEmitter::cleanup() { S.cleanup(); }3334EvaluationResult EvalEmitter::interpretExpr(const Expr *E,35bool ConvertResultToRValue,36bool DestroyToplevelScope) {37S.setEvalLocation(E->getExprLoc());38this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E);39this->CheckFullyInitialized = isa<ConstantExpr>(E);40EvalResult.setSource(E);4142if (!this->visitExpr(E, DestroyToplevelScope)) {43// EvalResult may already have a result set, but something failed44// after that (e.g. evaluating destructors).45EvalResult.setInvalid();46}4748return std::move(this->EvalResult);49}5051EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD,52bool CheckFullyInitialized) {53this->CheckFullyInitialized = CheckFullyInitialized;54S.EvaluatingDecl = VD;55EvalResult.setSource(VD);5657if (const Expr *Init = VD->getAnyInitializer()) {58QualType T = VD->getType();59this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() &&60!T->isObjCObjectPointerType();61} else62this->ConvertResultToRValue = false;6364EvalResult.setSource(VD);6566if (!this->visitDeclAndReturn(VD, S.inConstantContext()))67EvalResult.setInvalid();6869S.EvaluatingDecl = nullptr;70updateGlobalTemporaries();71return std::move(this->EvalResult);72}7374EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,75PtrCallback PtrCB) {7677S.setEvalLocation(E->getExprLoc());78this->ConvertResultToRValue = false;79this->CheckFullyInitialized = false;80this->PtrCB = PtrCB;81EvalResult.setSource(E);8283if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) {84// EvalResult may already have a result set, but something failed85// after that (e.g. evaluating destructors).86EvalResult.setInvalid();87}8889return std::move(this->EvalResult);90}9192void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }9394EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }9596Scope::Local EvalEmitter::createLocal(Descriptor *D) {97// Allocate memory for a local.98auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());99auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);100B->invokeCtor();101102// Initialize local variable inline descriptor.103InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());104Desc.Desc = D;105Desc.Offset = sizeof(InlineDescriptor);106Desc.IsActive = true;107Desc.IsBase = false;108Desc.IsFieldMutable = false;109Desc.IsConst = false;110Desc.IsInitialized = false;111112// Register the local.113unsigned Off = Locals.size();114Locals.push_back(std::move(Memory));115return {Off, D};116}117118bool EvalEmitter::jumpTrue(const LabelTy &Label) {119if (isActive()) {120if (S.Stk.pop<bool>())121ActiveLabel = Label;122}123return true;124}125126bool EvalEmitter::jumpFalse(const LabelTy &Label) {127if (isActive()) {128if (!S.Stk.pop<bool>())129ActiveLabel = Label;130}131return true;132}133134bool EvalEmitter::jump(const LabelTy &Label) {135if (isActive())136CurrentLabel = ActiveLabel = Label;137return true;138}139140bool EvalEmitter::fallthrough(const LabelTy &Label) {141if (isActive())142ActiveLabel = Label;143CurrentLabel = Label;144return true;145}146147bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {148size_t StackSizeBefore = S.Stk.size();149const Expr *Arg = E->getArg(0);150if (!this->visit(Arg)) {151S.Stk.clearTo(StackSizeBefore);152153if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext()))154return this->emitBool(false, E);155return Invalid(S, OpPC);156}157158PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr);159if (T == PT_Ptr) {160const auto &Ptr = S.Stk.pop<Pointer>();161return this->emitBool(CheckBCPResult(S, Ptr), E);162}163164// Otherwise, this is fine!165if (!this->emitPop(T, E))166return false;167return this->emitBool(true, E);168}169170template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {171if (!isActive())172return true;173174using T = typename PrimConv<OpType>::T;175EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));176return true;177}178179template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {180if (!isActive())181return true;182183const Pointer &Ptr = S.Stk.pop<Pointer>();184185if (Ptr.isFunctionPointer()) {186EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));187return true;188}189190// If we're returning a raw pointer, call our callback.191if (this->PtrCB)192return (*this->PtrCB)(Ptr);193194if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))195return false;196if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))197return false;198199// Implicitly convert lvalue to rvalue, if requested.200if (ConvertResultToRValue) {201if (!Ptr.isZero() && !Ptr.isDereferencable())202return false;203204if (S.getLangOpts().CPlusPlus11 && Ptr.isBlockPointer() &&205!CheckFinalLoad(S, OpPC, Ptr)) {206return false;207}208209// Never allow reading from a non-const pointer, unless the memory210// has been created in this evaluation.211if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() &&212Ptr.block()->getEvalID() != Ctx.getEvalID())213return false;214215if (std::optional<APValue> V =216Ptr.toRValue(Ctx, EvalResult.getSourceType())) {217EvalResult.setValue(*V);218} else {219return false;220}221} else {222if (!Ptr.isLive() && !Ptr.isTemporary())223return false;224225EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext()));226}227228return true;229}230231bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {232EvalResult.setValid();233return true;234}235236bool EvalEmitter::emitRetValue(const SourceInfo &Info) {237const auto &Ptr = S.Stk.pop<Pointer>();238239if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))240return false;241if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))242return false;243244if (std::optional<APValue> APV =245Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {246EvalResult.setValue(*APV);247return true;248}249250EvalResult.setInvalid();251return false;252}253254bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {255if (!isActive())256return true;257258Block *B = getLocal(I);259S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));260return true;261}262263template <PrimType OpType>264bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {265if (!isActive())266return true;267268using T = typename PrimConv<OpType>::T;269270Block *B = getLocal(I);271S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));272return true;273}274275template <PrimType OpType>276bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {277if (!isActive())278return true;279280using T = typename PrimConv<OpType>::T;281282Block *B = getLocal(I);283*reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();284InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());285Desc.IsInitialized = true;286287return true;288}289290bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {291if (!isActive())292return true;293294for (auto &Local : Descriptors[I]) {295Block *B = getLocal(Local.Offset);296S.deallocate(B);297}298299return true;300}301302/// Global temporaries (LifetimeExtendedTemporary) carry their value303/// around as an APValue, which codegen accesses.304/// We set their value once when creating them, but we don't update it305/// afterwards when code changes it later.306/// This is what we do here.307void EvalEmitter::updateGlobalTemporaries() {308for (const auto &[E, Temp] : S.SeenGlobalTemporaries) {309if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) {310const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);311APValue *Cached = Temp->getOrCreateValue(true);312313if (std::optional<PrimType> T = Ctx.classify(E->getType())) {314TYPE_SWITCH(315*T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });316} else {317if (std::optional<APValue> APV =318Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))319*Cached = *APV;320}321}322}323S.SeenGlobalTemporaries.clear();324}325326//===----------------------------------------------------------------------===//327// Opcode evaluators328//===----------------------------------------------------------------------===//329330#define GET_EVAL_IMPL331#include "Opcodes.inc"332#undef GET_EVAL_IMPL333334335