//===--- Program.h - Bytecode for the constexpr VM --------------*- 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 a program which organises and links multiple bytecode functions. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_AST_INTERP_PROGRAM_H #define LLVM_CLANG_AST_INTERP_PROGRAM_H #include #include #include "Function.h" #include "Pointer.h" #include "PrimType.h" #include "Record.h" #include "Source.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" namespace clang { class RecordDecl; class Expr; class FunctionDecl; class StringLiteral; class VarDecl; namespace interp { class Context; /// The program contains and links the bytecode for all functions. class Program final { public: Program(Context &Ctx) : Ctx(Ctx) {} ~Program() { // Manually destroy all the blocks. They are almost all harmless, // but primitive arrays might have an InitMap* heap allocated and // that needs to be freed. for (Global *G : Globals) if (Block *B = G->block(); B->isInitialized()) B->invokeDtor(); // Records might actually allocate memory themselves, but they // are allocated using a BumpPtrAllocator. Call their desctructors // here manually so they are properly freeing their resources. for (auto RecordPair : Records) { if (Record *R = RecordPair.second) R->~Record(); } } /// Marshals a native pointer to an ID for embedding in bytecode. unsigned getOrCreateNativePointer(const void *Ptr); /// Returns the value of a marshalled native pointer. const void *getNativePointer(unsigned Idx); /// Emits a string literal among global data. unsigned createGlobalString(const StringLiteral *S); /// Returns a pointer to a global. Pointer getPtrGlobal(unsigned Idx) const; /// Returns the value of a global. Block *getGlobal(unsigned Idx) { assert(Idx < Globals.size()); return Globals[Idx]->block(); } /// Finds a global's index. std::optional getGlobal(const ValueDecl *VD); std::optional getGlobal(const Expr *E); /// Returns or creates a global an creates an index to it. std::optional getOrCreateGlobal(const ValueDecl *VD, const Expr *Init = nullptr); /// Returns or creates a dummy value for unknown declarations. std::optional getOrCreateDummy(const ValueDecl *VD); /// Creates a global and returns its index. std::optional createGlobal(const ValueDecl *VD, const Expr *Init); /// Creates a global from a lifetime-extended temporary. std::optional createGlobal(const Expr *E); /// Creates a new function from a code range. template Function *createFunction(const FunctionDecl *Def, Ts &&... Args) { Def = Def->getCanonicalDecl(); auto *Func = new Function(*this, Def, std::forward(Args)...); Funcs.insert({Def, std::unique_ptr(Func)}); return Func; } /// Creates an anonymous function. template Function *createFunction(Ts &&... Args) { auto *Func = new Function(*this, std::forward(Args)...); AnonFuncs.emplace_back(Func); return Func; } /// Returns a function. Function *getFunction(const FunctionDecl *F); /// Returns a record or creates one if it does not exist. Record *getOrCreateRecord(const RecordDecl *RD); /// Creates a descriptor for a primitive type. Descriptor *createDescriptor(const DeclTy &D, PrimType Type, Descriptor::MetadataSize MDSize = std::nullopt, bool IsConst = false, bool IsTemporary = false, bool IsMutable = false) { return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable); } /// Creates a descriptor for a composite type. Descriptor *createDescriptor(const DeclTy &D, const Type *Ty, Descriptor::MetadataSize MDSize = std::nullopt, bool IsConst = false, bool IsTemporary = false, bool IsMutable = false, const Expr *Init = nullptr); /// Context to manage declaration lifetimes. class DeclScope { public: DeclScope(Program &P, const ValueDecl *VD) : P(P) { P.startDeclaration(VD); } ~DeclScope() { P.endDeclaration(); } private: Program &P; }; /// Returns the current declaration ID. std::optional getCurrentDecl() const { if (CurrentDeclaration == NoDeclaration) return std::optional{}; return LastDeclaration; } private: friend class DeclScope; std::optional createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, bool IsExtern, const Expr *Init = nullptr); /// Reference to the VM context. Context &Ctx; /// Mapping from decls to cached bytecode functions. llvm::DenseMap> Funcs; /// List of anonymous functions. std::vector> AnonFuncs; /// Function relocation locations. llvm::DenseMap> Relocs; /// Native pointers referenced by bytecode. std::vector NativePointers; /// Cached native pointer indices. llvm::DenseMap NativePointerIndices; /// Custom allocator for global storage. using PoolAllocTy = llvm::BumpPtrAllocatorImpl; /// Descriptor + storage for a global object. /// /// Global objects never go out of scope, thus they do not track pointers. class Global { public: /// Create a global descriptor for string literals. template Global(Tys... Args) : B(std::forward(Args)...) {} /// Allocates the global in the pool, reserving storate for data. void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) { return Alloc.Allocate(Meta + Data, alignof(void *)); } /// Return a pointer to the data. std::byte *data() { return B.data(); } /// Return a pointer to the block. Block *block() { return &B; } const Block *block() const { return &B; } private: /// Required metadata - does not actually track pointers. Block B; }; /// Allocator for globals. PoolAllocTy Allocator; /// Global objects. std::vector Globals; /// Cached global indices. llvm::DenseMap GlobalIndices; /// Mapping from decls to record metadata. llvm::DenseMap Records; /// Dummy parameter to generate pointers from. llvm::DenseMap DummyVariables; /// Creates a new descriptor. template Descriptor *allocateDescriptor(Ts &&... Args) { return new (Allocator) Descriptor(std::forward(Args)...); } /// No declaration ID. static constexpr unsigned NoDeclaration = (unsigned)-1; /// Last declaration ID. unsigned LastDeclaration = 0; /// Current declaration ID. unsigned CurrentDeclaration = NoDeclaration; /// Starts evaluating a declaration. void startDeclaration(const ValueDecl *Decl) { LastDeclaration += 1; CurrentDeclaration = LastDeclaration; } /// Ends a global declaration. void endDeclaration() { CurrentDeclaration = NoDeclaration; } public: /// Dumps the disassembled bytecode to \c llvm::errs(). void dump() const; void dump(llvm::raw_ostream &OS) const; }; } // namespace interp } // namespace clang #endif