blob: 56a4d098640c334b4d925bbb1f9c69521ba29538 [file]
//===--- Compiler.h - 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
//
//===----------------------------------------------------------------------===//
//
// Defines the constexpr bytecode compiler.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H
#define LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H
#include "ByteCodeEmitter.h"
#include "DeclOrExpr.h"
#include "EvalEmitter.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/StmtVisitor.h"
namespace clang {
class QualType;
namespace interp {
template <class Emitter> class LocalScope;
template <class Emitter> class DestructorScope;
template <class Emitter> class VariableScope;
template <class Emitter> class DeclScope;
template <class Emitter> class InitLinkScope;
template <class Emitter> class InitStackScope;
template <class Emitter> class OptionScope;
template <class Emitter> class ArrayIndexScope;
template <class Emitter> class SourceLocScope;
template <class Emitter> class LoopScope;
template <class Emitter> class LabelScope;
template <class Emitter> class SwitchScope;
template <class Emitter> class StmtExprScope;
template <class Emitter> class LocOverrideScope;
template <class Emitter> class Compiler;
struct InitLink {
public:
enum {
K_This = 0,
K_Field = 1,
K_Base = 2,
K_Temp = 3,
K_Decl = 4,
K_Elem = 6,
K_RVO = 7,
K_InitList = 8,
K_DIE = 9,
};
static InitLink This() { return InitLink{K_This}; }
static InitLink InitList() { return InitLink{K_InitList}; }
static InitLink RVO() { return InitLink{K_RVO}; }
static InitLink DIE() { return InitLink{K_DIE}; }
static InitLink Field(unsigned Offset) {
InitLink IL{K_Field};
IL.Offset = Offset;
return IL;
}
static InitLink Base(unsigned Offset) {
InitLink IL{K_Base};
IL.Offset = Offset;
return IL;
}
static InitLink Temp(unsigned Offset) {
InitLink IL{K_Temp};
IL.Offset = Offset;
return IL;
}
static InitLink Decl(const ValueDecl *D) {
InitLink IL{K_Decl};
IL.D = D;
return IL;
}
static InitLink Elem(unsigned Index) {
InitLink IL{K_Elem};
IL.Offset = Index;
return IL;
}
InitLink(uint8_t Kind) : Kind(Kind) {}
template <class Emitter>
bool emit(Compiler<Emitter> *Ctx, const Expr *E) const;
uint32_t Kind;
union {
unsigned Offset;
const ValueDecl *D;
};
};
/// State encapsulating if a the variable creation has been successful,
/// unsuccessful, or no variable has been created at all.
struct VarCreationState {
std::optional<bool> S = std::nullopt;
VarCreationState() = default;
VarCreationState(bool b) : S(b) {}
static VarCreationState NotCreated() { return VarCreationState(); }
operator bool() const { return S && *S; }
bool notCreated() const { return !S; }
};
enum class ScopeKind { Block, FullExpression, Call };
/// Compilation context for expressions.
template <class Emitter>
class Compiler final : public ConstStmtVisitor<Compiler<Emitter>, bool>,
public Emitter {
protected:
// Aliases for types defined in the emitter.
using LabelTy = typename Emitter::LabelTy;
using AddrTy = typename Emitter::AddrTy;
using OptLabelTy = UnsignedOrNone;
using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;
struct LabelInfo {
const Stmt *Name;
const VariableScope<Emitter> *BreakOrContinueScope;
OptLabelTy BreakLabel;
OptLabelTy ContinueLabel;
OptLabelTy DefaultLabel;
LabelInfo(const Stmt *Name, OptLabelTy BreakLabel, OptLabelTy ContinueLabel,
OptLabelTy DefaultLabel,
const VariableScope<Emitter> *BreakOrContinueScope)
: Name(Name), BreakOrContinueScope(BreakOrContinueScope),
BreakLabel(BreakLabel), ContinueLabel(ContinueLabel),
DefaultLabel(DefaultLabel) {}
};
/// Current compilation context.
Context &Ctx;
/// Program to link to.
Program &P;
public:
/// Initializes the compiler and the backend emitter.
template <typename... Tys>
Compiler(Context &Ctx, Program &P, Tys &&...Args)
: Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}
// Expressions.
bool VisitCastExpr(const CastExpr *E);
bool VisitBuiltinBitCastExpr(const BuiltinBitCastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
bool VisitFloatingLiteral(const FloatingLiteral *E);
bool VisitImaginaryLiteral(const ImaginaryLiteral *E);
bool VisitFixedPointLiteral(const FixedPointLiteral *E);
bool VisitParenExpr(const ParenExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
bool VisitLogicalBinOp(const BinaryOperator *E);
bool VisitPointerArithBinOp(const BinaryOperator *E);
bool VisitComplexBinOp(const BinaryOperator *E);
bool VisitVectorBinOp(const BinaryOperator *E);
bool VisitFixedPointBinOp(const BinaryOperator *E);
bool VisitFixedPointUnaryOperator(const UnaryOperator *E);
bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E);
bool VisitCallExpr(const CallExpr *E);
bool VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinID);
bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E);
bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E);
bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E);
bool VisitGNUNullExpr(const GNUNullExpr *E);
bool VisitCXXThisExpr(const CXXThisExpr *E);
bool VisitUnaryOperator(const UnaryOperator *E);
bool VisitVectorUnaryOperator(const UnaryOperator *E);
bool VisitComplexUnaryOperator(const UnaryOperator *E);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E);
bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E);
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
bool VisitInitListExpr(const InitListExpr *E);
bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E);
bool VisitConstantExpr(const ConstantExpr *E);
bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E);
bool VisitMemberExpr(const MemberExpr *E);
bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E);
bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E);
bool VisitOpaqueValueExpr(const OpaqueValueExpr *E);
bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E);
bool VisitStringLiteral(const StringLiteral *E);
bool VisitObjCStringLiteral(const ObjCStringLiteral *E);
bool VisitObjCEncodeExpr(const ObjCEncodeExpr *E);
bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E);
bool VisitCharacterLiteral(const CharacterLiteral *E);
bool VisitCompoundAssignOperator(const CompoundAssignOperator *E);
bool VisitFloatCompoundAssignOperator(const CompoundAssignOperator *E);
bool VisitPointerCompoundAssignOperator(const CompoundAssignOperator *E);
bool VisitExprWithCleanups(const ExprWithCleanups *E);
bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E);
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
bool VisitTypeTraitExpr(const TypeTraitExpr *E);
bool VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E);
bool VisitLambdaExpr(const LambdaExpr *E);
bool VisitPredefinedExpr(const PredefinedExpr *E);
bool VisitCXXThrowExpr(const CXXThrowExpr *E);
bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E);
bool VisitCXXDynamicCastExpr(const CXXDynamicCastExpr *E);
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
bool VisitCXXConstructExpr(const CXXConstructExpr *E);
bool VisitSourceLocExpr(const SourceLocExpr *E);
bool VisitOffsetOfExpr(const OffsetOfExpr *E);
bool VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E);
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
bool VisitGenericSelectionExpr(const GenericSelectionExpr *E);
bool VisitChooseExpr(const ChooseExpr *E);
bool VisitEmbedExpr(const EmbedExpr *E);
bool VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *E);
bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E);
bool VisitExpressionTraitExpr(const ExpressionTraitExpr *E);
bool VisitCXXUuidofExpr(const CXXUuidofExpr *E);
bool VisitRequiresExpr(const RequiresExpr *E);
bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E);
bool VisitPseudoObjectExpr(const PseudoObjectExpr *E);
bool VisitPackIndexingExpr(const PackIndexingExpr *E);
bool VisitRecoveryExpr(const RecoveryExpr *E);
bool VisitAddrLabelExpr(const AddrLabelExpr *E);
bool VisitConvertVectorExpr(const ConvertVectorExpr *E);
bool VisitShuffleVectorExpr(const ShuffleVectorExpr *E);
bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E);
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E);
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
bool VisitStmtExpr(const StmtExpr *E);
bool VisitCXXNewExpr(const CXXNewExpr *E);
bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);
bool VisitBlockExpr(const BlockExpr *E);
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E);
bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
bool visitDoStmt(const DoStmt *S);
bool visitForStmt(const ForStmt *S);
bool visitCXXForRangeStmt(const CXXForRangeStmt *S);
bool visitBreakStmt(const BreakStmt *S);
bool visitContinueStmt(const ContinueStmt *S);
bool visitSwitchStmt(const SwitchStmt *S);
bool visitCaseStmt(const CaseStmt *S);
bool visitDefaultStmt(const DefaultStmt *S);
bool visitAttributedStmt(const AttributedStmt *S);
bool visitCXXTryStmt(const CXXTryStmt *S);
bool
visitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *S);
bool registerRedecl(const VarDecl *VD, const APValue &V);
protected:
bool visitStmt(const Stmt *S);
bool visitExpr(const Expr *E, bool DestroyToplevelScope) override;
bool visitLValueExpr(const Expr *E, bool DestroyToplevelScope) override;
bool visitFunc(const FunctionDecl *F) override;
bool visitDeclAndReturn(const VarDecl *VD, const Expr *Init,
bool ConstantContext) override;
bool visitDtorCall(const VarDecl *VD, const APValue &Value) override;
bool visitWithSubstitutions(const FunctionDecl *Callee,
ArrayRef<const Expr *> Args, const Expr *This,
const Expr *Condition) override;
protected:
/// Emits scope cleanup instructions.
bool emitCleanup();
/// Returns a record type from a record or pointer type.
const RecordType *getRecordTy(QualType Ty);
/// Returns a record from a record or pointer type.
Record *getRecord(QualType Ty);
Record *getRecord(const RecordDecl *RD);
/// Returns a function for the given FunctionDecl.
/// If the function does not exist yet, it is compiled.
const Function *getFunction(const FunctionDecl *FD);
OptPrimType classify(const Expr *E) const { return Ctx.classify(E); }
OptPrimType classify(QualType Ty) const { return Ctx.classify(Ty); }
bool canClassify(const Expr *E) const { return Ctx.canClassify(E); }
bool canClassify(QualType T) const { return Ctx.canClassify(T); }
/// Classifies a known primitive type.
PrimType classifyPrim(QualType Ty) const {
if (auto T = classify(Ty)) {
return *T;
}
llvm_unreachable("not a primitive type");
}
/// Classifies a known primitive expression.
PrimType classifyPrim(const Expr *E) const {
if (auto T = classify(E))
return *T;
llvm_unreachable("not a primitive type");
}
/// Evaluates an expression and places the result on the stack. If the
/// expression is of composite type, a local variable will be created
/// and a pointer to said variable will be placed on the stack.
bool visit(const Expr *E) override;
/// Compiles an initializer. This is like visit() but it will never
/// create a variable and instead rely on a variable already having
/// been created. visitInitializer() then relies on a pointer to this
/// variable being on top of the stack.
bool visitInitializer(const Expr *E);
/// Similar, but will also pop the pointer.
bool visitInitializerPop(const Expr *E);
bool visitAsLValue(const Expr *E);
/// Evaluates an expression for side effects and discards the result.
bool discard(const Expr *E);
/// Just pass evaluation on to \p E. This leaves all the parsing flags
/// intact.
bool delegate(const Expr *E);
/// Creates and initializes a variable from the given decl.
VarCreationState visitVarDecl(const VarDecl *VD, const Expr *Init,
bool Toplevel = false);
VarCreationState visitDecl(const VarDecl *VD);
/// Visit an APValue.
bool visitAPValue(const APValue &Val, PrimType ValType, SourceInfo Info);
bool visitAPValueInitializer(const APValue &Val, SourceInfo Info, QualType T,
bool IsCompleteClass = true);
/// Visit the given decl as if we have a reference to it.
bool visitDeclRef(const ValueDecl *D, const Expr *E);
/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
bool visitInitList(ArrayRef<const Expr *> Inits, const Expr *ArrayFiller,
const Expr *E);
bool visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
OptPrimType InitT);
bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl,
bool Activate, bool IsOperatorCall);
/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclOrExpr Decl, PrimType Ty, bool IsConst,
bool IsVolatile = false,
ScopeKind SC = ScopeKind::Block);
/// Allocates a space storing a local given its type.
UnsignedOrNone allocateLocal(DeclOrExpr Decl, QualType Ty = QualType(),
ScopeKind = ScopeKind::Block);
UnsignedOrNone allocateTemporary(const Expr *E);
private:
friend class VariableScope<Emitter>;
friend class LocalScope<Emitter>;
friend class DestructorScope<Emitter>;
friend class DeclScope<Emitter>;
friend class InitLinkScope<Emitter>;
friend class InitStackScope<Emitter>;
friend class OptionScope<Emitter>;
friend class ArrayIndexScope<Emitter>;
friend class SourceLocScope<Emitter>;
friend struct InitLink;
friend class LoopScope<Emitter>;
friend class LabelScope<Emitter>;
friend class SwitchScope<Emitter>;
friend class StmtExprScope<Emitter>;
friend class LocOverrideScope<Emitter>;
/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
bool visitZeroRecordInitializer(const Record *R, const Expr *E,
bool IsCompleteClass = true);
bool visitZeroArrayInitializer(QualType T, const Expr *E);
bool visitAssignment(const Expr *LHS, const Expr *RHS, const Expr *E);
/// Emits an APSInt constant.
bool emitConst(const llvm::APSInt &Value, PrimType Ty, SourceInfo Info);
bool emitConst(const llvm::APInt &Value, PrimType Ty, SourceInfo Info);
bool emitConst(const llvm::APSInt &Value, const Expr *E);
bool emitConst(const llvm::APInt &Value, const Expr *E) {
return emitConst(Value, classifyPrim(E), E);
}
/// Emits an integer constant.
template <typename T> bool emitConst(T Value, PrimType Ty, SourceInfo Info);
template <typename T> bool emitConst(T Value, const Expr *E);
bool emitBool(bool V, const Expr *E) override {
return this->emitConst(V, E);
}
llvm::RoundingMode getRoundingMode(const Expr *E) const {
FPOptions FPO = E->getFPFeaturesInEffect(Ctx.getLangOpts());
if (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)
return llvm::RoundingMode::NearestTiesToEven;
return FPO.getRoundingMode();
}
uint32_t getFPOptions(const Expr *E) const {
return E->getFPFeaturesInEffect(Ctx.getLangOpts()).getAsOpaqueInt();
}
bool emitPrimCast(PrimType FromT, PrimType ToT, QualType ToQT, const Expr *E);
bool emitIntegralCast(PrimType FromT, PrimType ToT, QualType ToQT,
const Expr *E);
PrimType classifyComplexElementType(QualType T) const {
assert(T->isAnyComplexType());
QualType ElemType = T->getAs<ComplexType>()->getElementType();
return *this->classify(ElemType);
}
PrimType classifyVectorElementType(QualType T) const {
assert(T->isVectorType());
return *this->classify(T->getAs<VectorType>()->getElementType());
}
PrimType classifyMatrixElementType(QualType T) const {
assert(T->isMatrixType());
return *this->classify(T->getAs<MatrixType>()->getElementType());
}
bool emitComplexReal(const Expr *SubExpr);
bool emitComplexBoolCast(const Expr *E);
bool emitComplexComparison(const Expr *LHS, const Expr *RHS,
const BinaryOperator *E);
bool emitRecordDestructionPop(const Record *R, SourceInfo Loc);
bool emitDestructionPop(const Descriptor *Desc, SourceInfo Loc);
bool emitDummyPtr(DeclOrExpr D, const Expr *E, bool CU = false);
bool emitFloat(const APFloat &F, SourceInfo Info);
unsigned collectBaseOffset(const QualType BaseType,
const QualType DerivedType);
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);
bool emitBuiltinBitCast(const CastExpr *E);
bool emitHLSLAggregateSplat(PrimType SrcT, unsigned SrcOffset,
QualType DestType, const Expr *E);
bool emitVectorConversion(const Expr *Src, const Expr *E);
/// A scalar element extracted during HLSL aggregate flattening.
struct HLSLFlatElement {
unsigned LocalOffset;
PrimType Type;
};
unsigned countHLSLFlatElements(QualType Ty);
bool emitHLSLFlattenAggregate(QualType SrcType, unsigned SrcPtrOffset,
SmallVectorImpl<HLSLFlatElement> &Elements,
unsigned MaxElements, const Expr *E);
bool emitHLSLConstructAggregate(QualType DestType,
ArrayRef<HLSLFlatElement> Elements,
unsigned &ElemIdx, const Expr *E);
bool emitHLSLConstructAggregate(QualType DestType,
ArrayRef<HLSLFlatElement> Elements,
const Expr *E) {
unsigned ElemIdx = 0;
return emitHLSLConstructAggregate(DestType, Elements, ElemIdx, E);
}
bool compileConstructor(const CXXConstructorDecl *Ctor);
bool compileDestructor(const CXXDestructorDecl *Dtor);
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
bool checkLiteralType(const Expr *E);
bool maybeEmitDeferredVarInit(const VarDecl *VD);
bool refersToUnion(const Expr *E);
protected:
/// Variable to storage mapping.
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
/// OpaqueValueExpr to location mapping.
llvm::DenseMap<const OpaqueValueExpr *, unsigned> OpaqueExprs;
/// Current scope.
VariableScope<Emitter> *VarScope = nullptr;
/// Current argument index. Needed to emit ArrayInitIndexExpr.
std::optional<uint64_t> ArrayIndex;
/// DefaultInit- or DefaultArgExpr, needed for SourceLocExpr.
const Expr *SourceLocDefaultExpr = nullptr;
/// Flag indicating if return value is to be discarded.
bool DiscardResult = false;
bool SwitchInStmtExpr = false;
bool InStmtExpr = false;
bool ToLValue = false;
bool VariablesAreConstexprUnknown = false;
/// Flag inidicating if we're initializing an already created
/// variable. This is set in visitInitializer().
bool Initializing = false;
const VarDecl *InitializingDecl = nullptr;
llvm::SmallVector<InitLink> InitStack;
bool InitStackActive = false;
/// Type of the expression returned by the function.
OptPrimType ReturnType;
/// Switch case mapping.
CaseMap CaseLabels;
/// Stack of label information for loops and switch statements.
llvm::SmallVector<LabelInfo> LabelInfoStack;
const FunctionDecl *CompilingFunction = nullptr;
};
extern template class Compiler<ByteCodeEmitter>;
extern template class Compiler<EvalEmitter>;
} // namespace interp
} // namespace clang
#endif