//===--- 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 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 (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 <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