[Clang Interpreter] Initial patch for the constexpr interpreter

Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.

Reviewers: Bigcheese, jfb, rsmith

Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D64146

llvm-svn: 371834
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
new file mode 100644
index 0000000..c713015
--- /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