//===--- 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" #include "clang/Basic/LLVM.h" using namespace clang; using namespace clang::interp; namespace clang { namespace interp { /// Scope managing label targets. template class LabelScope { public: virtual ~LabelScope() { } protected: LabelScope(ByteCodeStmtGen *Ctx) : Ctx(Ctx) {} /// ByteCodeStmtGen instance. ByteCodeStmtGen *Ctx; }; /// Sets the context for break/continue statements. template class LoopScope final : public LabelScope { public: using LabelTy = typename ByteCodeStmtGen::LabelTy; using OptLabelTy = typename ByteCodeStmtGen::OptLabelTy; LoopScope(ByteCodeStmtGen *Ctx, LabelTy BreakLabel, LabelTy ContinueLabel) : LabelScope(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 SwitchScope final : public LabelScope { public: using LabelTy = typename ByteCodeStmtGen::LabelTy; using OptLabelTy = typename ByteCodeStmtGen::OptLabelTy; using CaseMap = typename ByteCodeStmtGen::CaseMap; SwitchScope(ByteCodeStmtGen *Ctx, CaseMap &&CaseLabels, LabelTy BreakLabel, OptLabelTy DefaultLabel) : LabelScope(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 bool ByteCodeStmtGen::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(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 bool ByteCodeStmtGen::visitStmt(const Stmt *S) { switch (S->getStmtClass()) { case Stmt::CompoundStmtClass: return visitCompoundStmt(cast(S)); case Stmt::DeclStmtClass: return visitDeclStmt(cast(S)); case Stmt::ReturnStmtClass: return visitReturnStmt(cast(S)); case Stmt::IfStmtClass: return visitIfStmt(cast(S)); case Stmt::NullStmtClass: return true; default: { if (auto *Exp = dyn_cast(S)) return this->discard(Exp); return this->bail(S); } } } template bool ByteCodeStmtGen::visitCompoundStmt( const CompoundStmt *CompoundStmt) { BlockScope Scope(this); for (auto *InnerStmt : CompoundStmt->body()) if (!visitStmt(InnerStmt)) return false; return true; } template bool ByteCodeStmtGen::visitDeclStmt(const DeclStmt *DS) { for (auto *D : DS->decls()) { // Variable declarator. if (auto *VD = dyn_cast(D)) { if (!visitVarDecl(VD)) return false; continue; } // Decomposition declarator. if (auto *DD = dyn_cast(D)) { return this->bail(DD); } } return true; } template bool ByteCodeStmtGen::visitReturnStmt(const ReturnStmt *RS) { if (const Expr *RE = RS->getRetValue()) { ExprScope 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 bool ByteCodeStmtGen::visitIfStmt(const IfStmt *IS) { BlockScope IfScope(this); if (IS->isNonNegatedConsteval()) return visitStmt(IS->getThen()); if (IS->isNegatedConsteval()) return IS->getElse() ? visitStmt(IS->getElse()) : true; 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 bool ByteCodeStmtGen::visitVarDecl(const VarDecl *VD) { auto DT = VD->getType(); if (!VD->hasLocalStorage()) { // No code generation required. return true; } // Integers, pointers, primitives. if (Optional T = this->classify(DT)) { auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); // Compile the initialiser in its own scope. { ExprScope 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; } // namespace interp } // namespace clang