//===--- 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" #include "llvm/ADT/Optional.h" namespace clang { class QualType; namespace interp { class Function; class State; template class LocalScope; template class RecordScope; template class VariableScope; template class DeclScope; template class OptionScope; /// Compilation context for expressions. template class ByteCodeExprGen : public ConstStmtVisitor, bool>, public Emitter { protected: // Emitters for opcodes of various arities. using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &); using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &); using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType, const SourceInfo &); // Aliases for types defined in the emitter. using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; // Reference to a function generating the pointer of an initialized object.s using InitFnRef = std::function; /// 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 stack. bool VisitCastExpr(const CastExpr *E); bool VisitIntegerLiteral(const IntegerLiteral *E); bool VisitParenExpr(const ParenExpr *E); bool VisitBinaryOperator(const BinaryOperator *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 the size int bits of an integer. unsigned getIntWidth(QualType Ty) { auto &ASTContext = Ctx.getASTContext(); return ASTContext.getIntWidth(Ty); } /// Returns the value of CHAR_BIT. unsigned getCharBit() const { auto &ASTContext = Ctx.getASTContext(); return ASTContext.getTargetInfo().getCharWidth(); } /// Classifies a type. llvm::Optional classify(const Expr *E) const { return E->isGLValue() ? PT_Ptr : classify(E->getType()); } llvm::Optional classify(QualType Ty) const { return Ctx.classify(Ty); } /// Checks if a pointer needs adjustment. bool needsAdjust(QualType Ty) const { return true; } /// 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 for a local. bool visitInitializer(const Expr *E, InitFnRef GenPtr); /// 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) { return visitInitializer(Init, [this, I, Init] { return this->emitGetPtrLocal(I, Init); }); } /// Visits an initializer for a global. bool visitGlobalInitializer(const Expr *Init, unsigned I) { return visitInitializer(Init, [this, I, Init] { return this->emitGetPtrGlobal(I, Init); }); } /// Visits a delegated initializer. bool visitThisInitializer(const Expr *I) { return visitInitializer(I, [this, I] { return this->emitThis(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. llvm::Optional allocateLocal(DeclTy &&Decl, bool IsExtended = false); private: friend class VariableScope; friend class LocalScope; friend class RecordScope; friend class DeclScope; friend class OptionScope; /// 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 APInt constant. bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value, const Expr *E); /// Emits an integer constant. template bool emitConst(const Expr *E, T Value) { QualType Ty = E->getType(); unsigned NumBits = getIntWidth(Ty); APInt WrappedValue(NumBits, Value, std::is_signed::value); return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E); } /// Returns a pointer to a variable declaration. bool getPtrVarDecl(const VarDecl *VD, const Expr *E); /// Returns the index of a global. llvm::Optional getGlobalIdx(const VarDecl *VD); /// Emits the initialized pointer. bool emitInitFn() { assert(InitFn && "missing initializer"); return (*InitFn)(); } protected: /// Variable to storage mapping. llvm::DenseMap Locals; /// OpaqueValueExpr to location mapping. llvm::DenseMap OpaqueExprs; /// Current scope. VariableScope *VarScope = nullptr; /// Current argument index. llvm::Optional ArrayIndex; /// Flag indicating if return value is to be discarded. bool DiscardResult = false; /// Expression being initialized. llvm::Optional InitFn = {}; }; extern template class ByteCodeExprGen; extern template class ByteCodeExprGen; /// Scope chain managing the variable lifetimes. template class VariableScope { public: 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: VariableScope(ByteCodeExprGen *Ctx) : Ctx(Ctx), Parent(Ctx->VarScope) { Ctx->VarScope = this; } /// 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.hasValue()) { Idx = this->Ctx->Descriptors.size(); this->Ctx->Descriptors.emplace_back(); } this->Ctx->Descriptors[*Idx].emplace_back(Local); } void emitDestruction() override { if (!Idx.hasValue()) return; this->Ctx->emitDestroy(*Idx, SourceInfo{}); } protected: /// Index of the scope in the chain. 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 { this->Parent->addLocal(Local); } }; } // namespace interp } // namespace clang #endif