brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.0 KiB · a2e01ef Raw
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