diff options
Diffstat (limited to 'clang/lib/AST/Interp/ByteCodeStmtGen.cpp')
| -rw-r--r-- | clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 265 | 
1 files changed, 265 insertions, 0 deletions
| diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp new file mode 100644 index 000000000000..c71301598bde --- /dev/null +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -0,0 +1,265 @@ +//===--- ByteCodeStmtGen.cpp - Code generator for expressions ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ByteCodeStmtGen.h" +#include "ByteCodeEmitter.h" +#include "ByteCodeGenError.h" +#include "Context.h" +#include "Function.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" + +using namespace clang; +using namespace clang::interp; + +template <typename T> using Expected = llvm::Expected<T>; +template <typename T> using Optional = llvm::Optional<T>; + +namespace clang { +namespace interp { + +/// Scope managing label targets. +template <class Emitter> class LabelScope { +public: +  virtual ~LabelScope() {  } + +protected: +  LabelScope(ByteCodeStmtGen<Emitter> *Ctx) : Ctx(Ctx) {} +  /// ByteCodeStmtGen instance. +  ByteCodeStmtGen<Emitter> *Ctx; +}; + +/// Sets the context for break/continue statements. +template <class Emitter> class LoopScope final : public LabelScope<Emitter> { +public: +  using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy; +  using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy; + +  LoopScope(ByteCodeStmtGen<Emitter> *Ctx, LabelTy BreakLabel, +            LabelTy ContinueLabel) +      : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel), +        OldContinueLabel(Ctx->ContinueLabel) { +    this->Ctx->BreakLabel = BreakLabel; +    this->Ctx->ContinueLabel = ContinueLabel; +  } + +  ~LoopScope() { +    this->Ctx->BreakLabel = OldBreakLabel; +    this->Ctx->ContinueLabel = OldContinueLabel; +  } + +private: +  OptLabelTy OldBreakLabel; +  OptLabelTy OldContinueLabel; +}; + +// Sets the context for a switch scope, mapping labels. +template <class Emitter> class SwitchScope final : public LabelScope<Emitter> { +public: +  using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy; +  using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy; +  using CaseMap = typename ByteCodeStmtGen<Emitter>::CaseMap; + +  SwitchScope(ByteCodeStmtGen<Emitter> *Ctx, CaseMap &&CaseLabels, +              LabelTy BreakLabel, OptLabelTy DefaultLabel) +      : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel), +        OldDefaultLabel(this->Ctx->DefaultLabel), +        OldCaseLabels(std::move(this->Ctx->CaseLabels)) { +    this->Ctx->BreakLabel = BreakLabel; +    this->Ctx->DefaultLabel = DefaultLabel; +    this->Ctx->CaseLabels = std::move(CaseLabels); +  } + +  ~SwitchScope() { +    this->Ctx->BreakLabel = OldBreakLabel; +    this->Ctx->DefaultLabel = OldDefaultLabel; +    this->Ctx->CaseLabels = std::move(OldCaseLabels); +  } + +private: +  OptLabelTy OldBreakLabel; +  OptLabelTy OldDefaultLabel; +  CaseMap OldCaseLabels; +}; + +} // namespace interp +} // namespace clang + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { +  // Classify the return type. +  ReturnType = this->classify(F->getReturnType()); + +  // Set up fields and context if a constructor. +  if (auto *MD = dyn_cast<CXXMethodDecl>(F)) +    return this->bail(MD); + +  if (auto *Body = F->getBody()) +    if (!visitStmt(Body)) +      return false; + +  // Emit a guard return to protect against a code path missing one. +  if (F->getReturnType()->isVoidType()) +    return this->emitRetVoid(SourceInfo{}); +  else +    return this->emitNoRet(SourceInfo{}); +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) { +  switch (S->getStmtClass()) { +  case Stmt::CompoundStmtClass: +    return visitCompoundStmt(cast<CompoundStmt>(S)); +  case Stmt::DeclStmtClass: +    return visitDeclStmt(cast<DeclStmt>(S)); +  case Stmt::ReturnStmtClass: +    return visitReturnStmt(cast<ReturnStmt>(S)); +  case Stmt::IfStmtClass: +    return visitIfStmt(cast<IfStmt>(S)); +  case Stmt::NullStmtClass: +    return true; +  default: { +    if (auto *Exp = dyn_cast<Expr>(S)) +      return this->discard(Exp); +    return this->bail(S); +  } +  } +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitCompoundStmt( +    const CompoundStmt *CompoundStmt) { +  BlockScope<Emitter> Scope(this); +  for (auto *InnerStmt : CompoundStmt->body()) +    if (!visitStmt(InnerStmt)) +      return false; +  return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) { +  for (auto *D : DS->decls()) { +    // Variable declarator. +    if (auto *VD = dyn_cast<VarDecl>(D)) { +      if (!visitVarDecl(VD)) +        return false; +      continue; +    } + +    // Decomposition declarator. +    if (auto *DD = dyn_cast<DecompositionDecl>(D)) { +      return this->bail(DD); +    } +  } + +  return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) { +  if (const Expr *RE = RS->getRetValue()) { +    ExprScope<Emitter> RetScope(this); +    if (ReturnType) { +      // Primitive types are simply returned. +      if (!this->visit(RE)) +        return false; +      this->emitCleanup(); +      return this->emitRet(*ReturnType, RS); +    } else { +      // RVO - construct the value in the return location. +      auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); }; +      if (!this->visitInitializer(RE, ReturnLocation)) +        return false; +      this->emitCleanup(); +      return this->emitRetVoid(RS); +    } +  } else { +    this->emitCleanup(); +    if (!this->emitRetVoid(RS)) +      return false; +    return true; +  } +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) { +  BlockScope<Emitter> IfScope(this); +  if (auto *CondInit = IS->getInit()) +    if (!visitStmt(IS->getInit())) +      return false; + +  if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt()) +    if (!visitDeclStmt(CondDecl)) +      return false; + +  if (!this->visitBool(IS->getCond())) +    return false; + +  if (const Stmt *Else = IS->getElse()) { +    LabelTy LabelElse = this->getLabel(); +    LabelTy LabelEnd = this->getLabel(); +    if (!this->jumpFalse(LabelElse)) +      return false; +    if (!visitStmt(IS->getThen())) +      return false; +    if (!this->jump(LabelEnd)) +      return false; +    this->emitLabel(LabelElse); +    if (!visitStmt(Else)) +      return false; +    this->emitLabel(LabelEnd); +  } else { +    LabelTy LabelEnd = this->getLabel(); +    if (!this->jumpFalse(LabelEnd)) +      return false; +    if (!visitStmt(IS->getThen())) +      return false; +    this->emitLabel(LabelEnd); +  } + +  return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) { +  auto DT = VD->getType(); + +  if (!VD->hasLocalStorage()) { +    // No code generation required. +    return true; +  } + +  // Integers, pointers, primitives. +  if (Optional<PrimType> T = this->classify(DT)) { +    auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); +    // Compile the initialiser in its own scope. +    { +      ExprScope<Emitter> Scope(this); +      if (!this->visit(VD->getInit())) +        return false; +    } +    // Set the value. +    return this->emitSetLocal(*T, Off, VD); +  } else { +    // Composite types - allocate storage and initialize it. +    if (auto Off = this->allocateLocal(VD)) { +      return this->visitLocalInitializer(VD->getInit(), *Off); +    } else { +      return this->bail(VD); +    } +  } +} + +namespace clang { +namespace interp { + +template class ByteCodeStmtGen<ByteCodeEmitter>; + +} // namespace interp +} // namespace clang | 
