This is illegal in a constexpr context. We can already figure that out, but we'd still run into an assertion later on when trying to visit the missing initializer or run the invalid function. Differential Revision: https://reviews.llvm.org/D132832
275 lines
7.4 KiB
C++
275 lines
7.4 KiB
C++
//===--- 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)) {
|
|
const Expr *Init = VD->getInit();
|
|
|
|
if (!Init)
|
|
return false;
|
|
|
|
auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
|
|
// Compile the initializer in its own scope.
|
|
{
|
|
ExprScope<Emitter> Scope(this);
|
|
if (!this->visit(Init))
|
|
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
|