diff options
Diffstat (limited to 'lib/AST/Interp/ByteCodeStmtGen.cpp')
-rw-r--r-- | lib/AST/Interp/ByteCodeStmtGen.cpp | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/lib/AST/Interp/ByteCodeStmtGen.cpp b/lib/AST/Interp/ByteCodeStmtGen.cpp new file mode 100644 index 000000000000..c71301598bde --- /dev/null +++ b/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 |