//===--- Interpreter.h - Incremental Compilation and Execution---*- 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 // //===----------------------------------------------------------------------===// // // This file defines the component which performs incremental code // compilation and execution. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H #define LLVM_CLANG_INTERPRETER_INTERPRETER_H #include "clang/AST/GlobalDecl.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Interpreter/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/Support/Error.h" #include #include #include namespace llvm { namespace orc { class LLJIT; class LLJITBuilder; class ThreadSafeContext; } // namespace orc } // namespace llvm namespace clang { namespace driver { class Compilation; } // namespace driver class CompilerInstance; class CXXRecordDecl; class Decl; class IncrementalExecutor; class IncrementalParser; class IncrementalCUDADeviceParser; /// Create a pre-configured \c CompilerInstance for incremental processing. class IncrementalCompilerBuilder { using DriverCompilationFn = llvm::Error(const driver::Compilation &); public: IncrementalCompilerBuilder() {} void SetCompilerArgs(const std::vector &Args) { UserArgs = Args; } void SetTargetTriple(std::string TT) { TargetTriple = TT; } // General C++ llvm::Expected> CreateCpp(); // Offload options void SetOffloadArch(llvm::StringRef Arch) { OffloadArch = Arch; }; // CUDA specific void SetCudaSDK(llvm::StringRef path) { CudaSDKPath = path; }; // Hand over the compilation. void SetDriverCompilationCallback(std::function C) { CompilationCB = C; } llvm::Expected> CreateCudaHost(); llvm::Expected> CreateCudaDevice(); private: llvm::Expected> create(std::string TT, std::vector &ClangArgv); llvm::Expected> createCuda(bool device); std::vector UserArgs; std::optional TargetTriple; llvm::StringRef OffloadArch; llvm::StringRef CudaSDKPath; std::optional> CompilationCB; }; // FIXME: Consider deriving from the LLJITBuilder into a common interpreter // creation configuraion class. class IncrementalExecutorBuilder { public: /// Indicates whether out-of-process JIT execution is enabled. bool IsOutOfProcess = false; /// Path to the out-of-process JIT executor. std::string OOPExecutor = ""; std::string OOPExecutorConnect = ""; /// Indicates whether to use shared memory for communication. bool UseSharedMemory = false; /// Representing the slab allocation size for memory management in kb. unsigned SlabAllocateSize = 0; /// Path to the ORC runtime library. std::string OrcRuntimePath = ""; /// PID of the out-of-process JIT executor. uint32_t ExecutorPID = 0; /// Custom lambda to be executed inside child process/executor std::function CustomizeFork = nullptr; /// An optional code model to provide to the JITTargetMachineBuilder std::optional CM = std::nullopt; std::function UpdateOrcRuntimePathCB = [this](const driver::Compilation &C) { return UpdateOrcRuntimePath(C); }; ~IncrementalExecutorBuilder(); llvm::Expected> create(llvm::orc::ThreadSafeContext &TSC, llvm::orc::LLJITBuilder &JITBuilder); private: llvm::Error UpdateOrcRuntimePath(const driver::Compilation &C); }; class IncrementalAction; class InProcessPrintingASTConsumer; /// Provides top-level interfaces for incremental compilation and execution. class Interpreter { friend class Value; friend InProcessPrintingASTConsumer; std::unique_ptr TSCtx; /// Long-lived, incremental parsing action. std::unique_ptr Act; std::unique_ptr IncrParser; std::unique_ptr IncrExecutor; // An optional parser for CUDA offloading std::unique_ptr DeviceParser; // An optional action for CUDA offloading std::unique_ptr DeviceAct; /// List containing information about each incrementally parsed piece of code. std::list PTUs; unsigned InitPTUSize = 0; // This member holds the last result of the value printing. It's a class // member because we might want to access it after more inputs. If no value // printing happens, it's in an invalid state. Value LastValue; /// Compiler instance performing the incremental compilation. std::unique_ptr CI; /// An optional compiler instance for CUDA offloading std::unique_ptr DeviceCI; protected: // Derived classes can use an extended interface of the Interpreter. Interpreter(std::unique_ptr Instance, llvm::Error &Err, std::unique_ptr JITBuilder = nullptr, std::unique_ptr Consumer = nullptr, std::unique_ptr IEB = nullptr); // Create the internal IncrementalExecutor, or re-create it after calling // ResetExecutor(). llvm::Error CreateExecutor(); // Delete the internal IncrementalExecutor. This causes a hard shutdown of the // JIT engine. In particular, it doesn't run cleanup or destructors. void ResetExecutor(); public: virtual ~Interpreter(); static llvm::Expected> create(std::unique_ptr CI, std::unique_ptr IEB = nullptr); static llvm::Expected> createWithCUDA(std::unique_ptr CI, std::unique_ptr DCI); static llvm::Expected> createLLJITBuilder(std::unique_ptr EPC, llvm::StringRef OrcRuntimePath); const ASTContext &getASTContext() const; ASTContext &getASTContext(); const CompilerInstance *getCompilerInstance() const; CompilerInstance *getCompilerInstance(); llvm::Expected getExecutionEngine(); llvm::Expected Parse(llvm::StringRef Code); llvm::Error Execute(PartialTranslationUnit &T); llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); /// Link a dynamic library llvm::Error LoadDynamicLibrary(const char *name); /// \returns the \c ExecutorAddr of a \c GlobalDecl. This interface uses /// the CodeGenModule's internal mangling cache to avoid recomputing the /// mangled name. llvm::Expected getSymbolAddress(GlobalDecl GD) const; /// \returns the \c ExecutorAddr of a given name as written in the IR. llvm::Expected getSymbolAddress(llvm::StringRef IRName) const; /// \returns the \c ExecutorAddr of a given name as written in the object /// file. llvm::Expected getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; uint32_t getOutOfProcessExecutorPID() const; private: size_t getEffectivePTUSize() const; void markUserCodeStart(); // A cache for the compiled destructors used to for de-allocation of managed // clang::Values. mutable llvm::DenseMap Dtors; std::array ValuePrintingInfo = {0}; std::unique_ptr JITBuilder; std::unique_ptr IncrExecutorBuilder; /// @} /// @name Value and pretty printing support /// @{ std::string ValueDataToString(const Value &V) const; std::string ValueTypeToString(const Value &V) const; llvm::Expected convertExprToValue(Expr *E); // When we deallocate clang::Value we need to run the destructor of the type. // This function forces emission of the needed dtor. llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD) const; }; } // namespace clang #endif // LLVM_CLANG_INTERPRETER_INTERPRETER_H