//===--- ByteCodeExprGen.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 "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" #include "clang/Basic/TargetInfo.h" namespace clang { class QualType; namespace interp { template class LocalScope; template class RecordScope; template class VariableScope; template class DeclScope; template class OptionScope; template class ArrayIndexScope; /// Compilation context for expressions. template class ByteCodeExprGen : public ConstStmtVisitor, bool>, public Emitter { protected: // Aliases for types defined in the emitter. using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; /// Current compilation context. Context &Ctx; /// Program to link to. Program &P; public: /// Initializes the compiler and the backend emitter. template ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args) : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {} // Expression visitors - result returned on interp stack. bool VisitCastExpr(const CastExpr *E); bool VisitIntegerLiteral(const IntegerLiteral *E); bool VisitParenExpr(const ParenExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); bool VisitPointerArithBinOp(const BinaryOperator *E); bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); bool VisitCallExpr(const CallExpr *E); bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *E); bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); bool VisitCXXThisExpr(const CXXThisExpr *E); bool VisitUnaryOperator(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 VisitConstantExpr(const ConstantExpr *E); bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); bool VisitMemberExpr(const MemberExpr *E); bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E); bool VisitOpaqueValueExpr(const OpaqueValueExpr *E); bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E); bool VisitStringLiteral(const StringLiteral *E); bool VisitCharacterLiteral(const CharacterLiteral *E); bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); protected: bool visitExpr(const Expr *E) override; bool visitDecl(const VarDecl *VD) override; protected: /// Emits scope cleanup instructions. void 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); /// Classifies a type. std::optional classify(const Expr *E) const { return E->isGLValue() ? PT_Ptr : classify(E->getType()); } std::optional classify(QualType Ty) const { return Ctx.classify(Ty); } /// Classifies a known primitive type PrimType classifyPrim(QualType Ty) const { if (auto T = classify(Ty)) { return *T; } llvm_unreachable("not a primitive type"); } /// Evaluates an expression for side effects and discards the result. bool discard(const Expr *E); /// Evaluates an expression and places result on stack. bool visit(const Expr *E); /// Compiles an initializer. bool visitInitializer(const Expr *E); /// Compiles an array initializer. bool visitArrayInitializer(const Expr *Initializer); /// Compiles a record initializer. bool visitRecordInitializer(const Expr *Initializer); /// Creates and initializes a variable from the given decl. bool visitVarDecl(const VarDecl *VD); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); /// Visits an initializer for a local. bool visitLocalInitializer(const Expr *Init, unsigned I) { if (!this->emitGetPtrLocal(I, Init)) return false; if (!visitInitializer(Init)) return false; return this->emitPopPtr(Init); } /// Visits an initializer for a global. bool visitGlobalInitializer(const Expr *Init, unsigned I) { if (!this->emitGetPtrGlobal(I, Init)) return false; if (!visitInitializer(Init)) return false; return this->emitPopPtr(Init); } /// Visits a delegated initializer. bool visitThisInitializer(const Expr *I) { if (!this->emitThis(I)) return false; if (!visitInitializer(I)) return false; return this->emitPopPtr(I); } /// Creates a local primitive value. unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable, bool IsExtended = false); /// Allocates a space storing a local given its type. std::optional allocateLocal(DeclTy &&Decl, bool IsExtended = false); private: friend class VariableScope; friend class LocalScope; friend class RecordScope; friend class DeclScope; friend class OptionScope; friend class ArrayIndexScope; /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, const Expr *E); enum class DerefKind { /// Value is read and pushed to stack. Read, /// Direct method generates a value which is written. Returns pointer. Write, /// Direct method receives the value, pushes mutated value. Returns pointer. ReadWrite, }; /// Method to directly load a value. If the value can be fetched directly, /// the direct handler is called. Otherwise, a pointer is left on the stack /// and the indirect handler is expected to operate on that. bool dereference(const Expr *LV, DerefKind AK, llvm::function_ref Direct, llvm::function_ref Indirect); bool dereferenceParam(const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK, llvm::function_ref Direct, llvm::function_ref Indirect); bool dereferenceVar(const Expr *LV, PrimType T, const VarDecl *PD, DerefKind AK, llvm::function_ref Direct, llvm::function_ref Indirect); /// Emits an APSInt constant. bool emitConst(const APSInt &Value, const Expr *E); bool emitConst(const APInt &Value, const Expr *E) { return emitConst(static_cast(Value), E); } /// Emits an integer constant. template bool emitConst(T Value, const Expr *E); /// Returns the CXXRecordDecl for the type of the given expression, /// or nullptr if no such decl exists. const CXXRecordDecl *getRecordDecl(const Expr *E) const { QualType T = E->getType(); if (const auto *RD = T->getPointeeCXXRecordDecl()) return RD; return T->getAsCXXRecordDecl(); } /// Returns whether we should create a global variable for the /// given VarDecl. bool shouldBeGloballyIndexed(const VarDecl *VD) const { return VD->hasGlobalStorage() || VD->isConstexpr(); } protected: /// Variable to storage mapping. llvm::DenseMap Locals; /// OpaqueValueExpr to location mapping. llvm::DenseMap OpaqueExprs; /// Current scope. VariableScope *VarScope = nullptr; /// Current argument index. Needed to emit ArrayInitIndexExpr. std::optional ArrayIndex; /// Flag indicating if return value is to be discarded. bool DiscardResult = false; }; extern template class ByteCodeExprGen; extern template class ByteCodeExprGen; /// Scope chain managing the variable lifetimes. template class VariableScope { public: VariableScope(ByteCodeExprGen *Ctx) : Ctx(Ctx), Parent(Ctx->VarScope) { Ctx->VarScope = this; } virtual ~VariableScope() { Ctx->VarScope = this->Parent; } void add(const Scope::Local &Local, bool IsExtended) { if (IsExtended) this->addExtended(Local); else this->addLocal(Local); } virtual void addLocal(const Scope::Local &Local) { if (this->Parent) this->Parent->addLocal(Local); } virtual void addExtended(const Scope::Local &Local) { if (this->Parent) this->Parent->addExtended(Local); } virtual void emitDestruction() {} VariableScope *getParent() { return Parent; } protected: /// ByteCodeExprGen instance. ByteCodeExprGen *Ctx; /// Link to the parent scope. VariableScope *Parent; }; /// Scope for local variables. /// /// When the scope is destroyed, instructions are emitted to tear down /// all variables declared in this scope. template class LocalScope : public VariableScope { public: LocalScope(ByteCodeExprGen *Ctx) : VariableScope(Ctx) {} ~LocalScope() override { this->emitDestruction(); } void addLocal(const Scope::Local &Local) override { if (!Idx) { Idx = this->Ctx->Descriptors.size(); this->Ctx->Descriptors.emplace_back(); } this->Ctx->Descriptors[*Idx].emplace_back(Local); } void emitDestruction() override { if (!Idx) return; this->Ctx->emitDestroy(*Idx, SourceInfo{}); } protected: /// Index of the scope in the chain. std::optional Idx; }; /// Scope for storage declared in a compound statement. template class BlockScope final : public LocalScope { public: BlockScope(ByteCodeExprGen *Ctx) : LocalScope(Ctx) {} void addExtended(const Scope::Local &Local) override { llvm_unreachable("Cannot create temporaries in full scopes"); } }; /// Expression scope which tracks potentially lifetime extended /// temporaries which are hoisted to the parent scope on exit. template class ExprScope final : public LocalScope { public: ExprScope(ByteCodeExprGen *Ctx) : LocalScope(Ctx) {} void addExtended(const Scope::Local &Local) override { assert(this->Parent); this->Parent->addLocal(Local); } }; template class ArrayIndexScope final { public: ArrayIndexScope(ByteCodeExprGen *Ctx, uint64_t Index) : Ctx(Ctx) { OldArrayIndex = Ctx->ArrayIndex; Ctx->ArrayIndex = Index; } ~ArrayIndexScope() { Ctx->ArrayIndex = OldArrayIndex; } private: ByteCodeExprGen *Ctx; std::optional OldArrayIndex; }; } // namespace interp } // namespace clang #endif