From d2096a6445b803a74bd8cb56653f4ca1791d1d9f Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Fri, 6 Sep 2024 09:52:36 +0000 Subject: [PATCH 1/5] [clang-repl] Simplify the value printing logic to enable out-of-process. This patch improves the design of the IncrementalParser and Interpreter classes. Now the incremental parser is only responsible for building the partial translation unit declaration and the AST, while the Interpreter fills in the lower level llvm::Module and other JIT-related infrastructure. Finally the Interpreter class now orchestrates the AST and the LLVM IR with the IncrementalParser and IncrementalExecutor classes. The design improvement allows us to rework some of the logic that extracts an interpreter value into the clang::Value object. The new implementation simplifies use-cases which are used for out-of-process execution by allowing interpreter to be inherited or customized with an clang::ASTConsumer. This change will enable completing the pretty printing work which is in llvm/llvm-project#84769 --- .../clang/Frontend/MultiplexConsumer.h | 3 +- clang/include/clang/Interpreter/Interpreter.h | 53 +- clang/lib/Frontend/MultiplexConsumer.cpp | 7 + clang/lib/Interpreter/CMakeLists.txt | 1 + clang/lib/Interpreter/DeviceOffload.cpp | 10 +- clang/lib/Interpreter/DeviceOffload.h | 14 +- clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +- clang/lib/Interpreter/IncrementalParser.cpp | 253 +------ clang/lib/Interpreter/IncrementalParser.h | 45 +- clang/lib/Interpreter/Interpreter.cpp | 648 ++++++------------ .../Interpreter/InterpreterValuePrinter.cpp | 400 +++++++++++ .../Interpreter/CodeCompletionTest.cpp | 2 +- .../Interpreter/InterpreterExtensionsTest.cpp | 64 +- 13 files changed, 707 insertions(+), 795 deletions(-) create mode 100644 clang/lib/Interpreter/InterpreterValuePrinter.cpp diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h index 3a7670d7a51aa..b190750bb29fb 100644 --- a/clang/include/clang/Frontend/MultiplexConsumer.h +++ b/clang/include/clang/Frontend/MultiplexConsumer.h @@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer { public: // Takes ownership of the pointers in C. MultiplexConsumer(std::vector> C); + MultiplexConsumer(std::unique_ptr C); ~MultiplexConsumer() override; // ASTConsumer @@ -80,7 +81,7 @@ class MultiplexConsumer : public SemaConsumer { void InitializeSema(Sema &S) override; void ForgetSema() override; -private: +protected: std::vector> Consumers; // Owns these. std::unique_ptr MutationListener; std::unique_ptr DeserializationListener; diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 1234608bb5864..cbb1cfd4ab02a 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -14,11 +14,9 @@ #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H #define LLVM_CLANG_INTERPRETER_INTERPRETER_H -#include "clang/AST/Decl.h" #include "clang/AST/GlobalDecl.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Interpreter/Value.h" -#include "clang/Sema/Ownership.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITSymbol.h" @@ -38,6 +36,9 @@ class ThreadSafeContext; namespace clang { class CompilerInstance; +class CodeGenerator; +class CXXRecordDecl; +class Decl; class IncrementalExecutor; class IncrementalParser; @@ -77,26 +78,27 @@ class IncrementalCompilerBuilder { llvm::StringRef CudaSDKPath; }; -/// Generate glue code between the Interpreter's built-in runtime and user code. -class RuntimeInterfaceBuilder { -public: - virtual ~RuntimeInterfaceBuilder() = default; - - using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder, - Expr *, ArrayRef); - virtual TransformExprFunction *getPrintValueTransformer() = 0; -}; +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; - std::unique_ptr RuntimeIB; // An optional parser for CUDA offloading std::unique_ptr DeviceParser; + /// List containing every information about every 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 @@ -104,15 +106,15 @@ class Interpreter { // printing happens, it's in an invalid state. Value LastValue; - // Add a call to an Expr to report its result. We query the function from - // RuntimeInterfaceBuilder once and store it as a function pointer to avoid - // frequent virtual function calls. - RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr; + /// When CodeGen is created the first llvm::Module gets cached in many places + /// and we must keep it alive. + std::unique_ptr CachedInCodeGenModule; protected: // Derived classes can use an extended interface of the Interpreter. Interpreter(std::unique_ptr CI, llvm::Error &Err, - std::unique_ptr JITBuilder = nullptr); + std::unique_ptr JITBuilder = nullptr, + std::unique_ptr Consumer = nullptr); // Create the internal IncrementalExecutor, or re-create it after calling // ResetExecutor(). @@ -122,15 +124,8 @@ class Interpreter { // JIT engine. In particular, it doesn't run cleanup or destructors. void ResetExecutor(); - // Lazily construct the RuntimeInterfaceBuilder. The provided instance will be - // used for the entire lifetime of the interpreter. The default implementation - // targets the in-process __clang_Interpreter runtime. Override this to use a - // custom runtime. - virtual std::unique_ptr FindRuntimeInterface(); - public: virtual ~Interpreter(); - static llvm::Expected> create(std::unique_ptr CI); static llvm::Expected> @@ -145,7 +140,6 @@ class Interpreter { llvm::Expected Parse(llvm::StringRef Code); llvm::Error Execute(PartialTranslationUnit &T); llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); - llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); @@ -167,8 +161,6 @@ class Interpreter { llvm::Expected getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; - enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag }; - const llvm::SmallVectorImpl &getValuePrintingInfo() const { return ValuePrintingInfo; } @@ -178,7 +170,14 @@ class Interpreter { private: size_t getEffectivePTUSize() const; void markUserCodeStart(); + llvm::Expected AttachValuePrinting(Expr *E); + llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); + + CodeGenerator *getCodeGen() const; + std::unique_ptr GenModule(); + // A cache for the compiled destructors used to for de-allocation of managed + // clang::Values. llvm::DenseMap Dtors; llvm::SmallVector ValuePrintingInfo; diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp index 2158d176d1892..3fd3c9bd69037 100644 --- a/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/clang/lib/Frontend/MultiplexConsumer.cpp @@ -298,6 +298,13 @@ MultiplexConsumer::MultiplexConsumer( } } +MultiplexConsumer::MultiplexConsumer(std::unique_ptr C) + : MultiplexConsumer([](std::unique_ptr Consumer) { + std::vector> Consumers; + Consumers.push_back(std::move(Consumer)); + return Consumers; + }(std::move(C))) {} + MultiplexConsumer::~MultiplexConsumer() {} void MultiplexConsumer::Initialize(ASTContext &Context) { diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt index 6a069659ebb8d..2cc7c59b61d31 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -22,6 +22,7 @@ add_clang_library(clangInterpreter IncrementalExecutor.cpp IncrementalParser.cpp Interpreter.cpp + InterpreterValuePrinter.cpp InterpreterUtils.cpp Value.cpp ${WASM_SRC} diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp index 07c9e3005e5fd..4bf9ed2809689 100644 --- a/clang/lib/Interpreter/DeviceOffload.cpp +++ b/clang/lib/Interpreter/DeviceOffload.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/TargetOptions.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" @@ -24,11 +25,10 @@ namespace clang { IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( - Interpreter &Interp, std::unique_ptr Instance, - IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + std::unique_ptr Instance, IncrementalParser &HostParser, llvm::IntrusiveRefCntPtr FS, - llvm::Error &Err) - : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err), + llvm::Error &Err, const std::list &PTUs) + : IncrementalParser(std::move(Instance), Err), PTUs(PTUs), HostParser(HostParser), VFS(FS) { if (Err) return; @@ -41,7 +41,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( } } -llvm::Expected +llvm::Expected IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) { auto PTU = IncrementalParser::Parse(Input); if (!PTU) diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h index ce4f218c94c79..b84870474841a 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -18,19 +18,19 @@ #include "llvm/Support/VirtualFileSystem.h" namespace clang { - +struct PartialTranslationUnit; class IncrementalCUDADeviceParser : public IncrementalParser { + const std::list &PTUs; + public: IncrementalCUDADeviceParser( - Interpreter &Interp, std::unique_ptr Instance, - IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + std::unique_ptr Instance, IncrementalParser &HostParser, llvm::IntrusiveRefCntPtr VFS, - llvm::Error &Err); + llvm::Error &Err, const std::list &PTUs); - llvm::Expected - Parse(llvm::StringRef Input) override; + llvm::Expected Parse(llvm::StringRef Input) override; - // Generate PTX for the last PTU + // Generate PTX for the last PTU. llvm::Expected GeneratePTX(); // Generate fatbinary contents in memory diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 1824a5b4570a9..4d2adecaafce7 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, return SymOrErr->getAddress(); } -} // end namespace clang +} // namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index b7c809c45098c..615f61e9aec1b 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -13,233 +13,33 @@ #include "IncrementalParser.h" #include "clang/AST/DeclContextInternals.h" -#include "clang/CodeGen/BackendUtil.h" -#include "clang/CodeGen/CodeGenAction.h" -#include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" -#include "clang/FrontendTool/Utils.h" -#include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" -#include "llvm/Option/ArgList.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Error.h" -#include "llvm/Support/Timer.h" #include namespace clang { -class IncrementalASTConsumer final : public ASTConsumer { - Interpreter &Interp; - std::unique_ptr Consumer; - -public: - IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr C) - : Interp(InterpRef), Consumer(std::move(C)) {} - - bool HandleTopLevelDecl(DeclGroupRef DGR) override final { - if (DGR.isNull()) - return true; - if (!Consumer) - return true; - - for (Decl *D : DGR) - if (auto *TSD = llvm::dyn_cast(D); - TSD && TSD->isSemiMissing()) - TSD->setStmt(Interp.SynthesizeExpr(cast(TSD->getStmt()))); - - return Consumer->HandleTopLevelDecl(DGR); - } - void HandleTranslationUnit(ASTContext &Ctx) override final { - Consumer->HandleTranslationUnit(Ctx); - } - void HandleInlineFunctionDefinition(FunctionDecl *D) override final { - Consumer->HandleInlineFunctionDefinition(D); - } - void HandleInterestingDecl(DeclGroupRef D) override final { - Consumer->HandleInterestingDecl(D); - } - void HandleTagDeclDefinition(TagDecl *D) override final { - Consumer->HandleTagDeclDefinition(D); - } - void HandleTagDeclRequiredDefinition(const TagDecl *D) override final { - Consumer->HandleTagDeclRequiredDefinition(D); - } - void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final { - Consumer->HandleCXXImplicitFunctionInstantiation(D); - } - void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final { - Consumer->HandleTopLevelDeclInObjCContainer(D); - } - void HandleImplicitImportDecl(ImportDecl *D) override final { - Consumer->HandleImplicitImportDecl(D); - } - void CompleteTentativeDefinition(VarDecl *D) override final { - Consumer->CompleteTentativeDefinition(D); - } - void CompleteExternalDeclaration(DeclaratorDecl *D) override final { - Consumer->CompleteExternalDeclaration(D); - } - void AssignInheritanceModel(CXXRecordDecl *RD) override final { - Consumer->AssignInheritanceModel(RD); - } - void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final { - Consumer->HandleCXXStaticMemberVarInstantiation(D); - } - void HandleVTable(CXXRecordDecl *RD) override final { - Consumer->HandleVTable(RD); - } - ASTMutationListener *GetASTMutationListener() override final { - return Consumer->GetASTMutationListener(); - } - ASTDeserializationListener *GetASTDeserializationListener() override final { - return Consumer->GetASTDeserializationListener(); - } - void PrintStats() override final { Consumer->PrintStats(); } - bool shouldSkipFunctionBody(Decl *D) override final { - return Consumer->shouldSkipFunctionBody(D); - } - static bool classof(const clang::ASTConsumer *) { return true; } -}; - -/// A custom action enabling the incremental processing functionality. -/// -/// The usual \p FrontendAction expects one call to ExecuteAction and once it -/// sees a call to \p EndSourceFile it deletes some of the important objects -/// such as \p Preprocessor and \p Sema assuming no further input will come. -/// -/// \p IncrementalAction ensures it keep its underlying action's objects alive -/// as long as the \p IncrementalParser needs them. -/// -class IncrementalAction : public WrapperFrontendAction { -private: - bool IsTerminating = false; - -public: - IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, - llvm::Error &Err) - : WrapperFrontendAction([&]() { - llvm::ErrorAsOutParameter EAO(&Err); - std::unique_ptr Act; - switch (CI.getFrontendOpts().ProgramAction) { - default: - Err = llvm::createStringError( - std::errc::state_not_recoverable, - "Driver initialization failed. " - "Incremental mode for action %d is not supported", - CI.getFrontendOpts().ProgramAction); - return Act; - case frontend::ASTDump: - [[fallthrough]]; - case frontend::ASTPrint: - [[fallthrough]]; - case frontend::ParseSyntaxOnly: - Act = CreateFrontendAction(CI); - break; - case frontend::PluginAction: - [[fallthrough]]; - case frontend::EmitAssembly: - [[fallthrough]]; - case frontend::EmitBC: - [[fallthrough]]; - case frontend::EmitObj: - [[fallthrough]]; - case frontend::PrintPreprocessedInput: - [[fallthrough]]; - case frontend::EmitLLVMOnly: - Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); - break; - } - return Act; - }()) {} - FrontendAction *getWrapped() const { return WrappedAction.get(); } - TranslationUnitKind getTranslationUnitKind() override { - return TU_Incremental; - } - - void ExecuteAction() override { - CompilerInstance &CI = getCompilerInstance(); - assert(CI.hasPreprocessor() && "No PP!"); - - // Use a code completion consumer? - CodeCompleteConsumer *CompletionConsumer = nullptr; - if (CI.hasCodeCompletionConsumer()) - CompletionConsumer = &CI.getCodeCompletionConsumer(); - - Preprocessor &PP = CI.getPreprocessor(); - PP.EnterMainSourceFile(); - - if (!CI.hasSema()) - CI.createSema(getTranslationUnitKind(), CompletionConsumer); - } - - // Do not terminate after processing the input. This allows us to keep various - // clang objects alive and to incrementally grow the current TU. - void EndSourceFile() override { - // The WrappedAction can be nullptr if we issued an error in the ctor. - if (IsTerminating && getWrapped()) - WrapperFrontendAction::EndSourceFile(); - } - - void FinalizeAction() { - assert(!IsTerminating && "Already finalized!"); - IsTerminating = true; - EndSourceFile(); - } -}; - -CodeGenerator *IncrementalParser::getCodeGen() const { - FrontendAction *WrappedAct = Act->getWrapped(); - if (!WrappedAct->hasIRSupport()) - return nullptr; - return static_cast(WrappedAct)->getCodeGenerator(); -} - IncrementalParser::IncrementalParser() {} -IncrementalParser::IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, +IncrementalParser::IncrementalParser(std::unique_ptr Instance, llvm::Error &Err) : CI(std::move(Instance)) { llvm::ErrorAsOutParameter EAO(&Err); - Act = std::make_unique(*CI, LLVMCtx, Err); - if (Err) - return; - CI->ExecuteAction(*Act); - if (getCodeGen()) - CachedInCodeGenModule = GenModule(); - - std::unique_ptr IncrConsumer = - std::make_unique(Interp, CI->takeASTConsumer()); - CI->setASTConsumer(std::move(IncrConsumer)); Consumer = &CI->getASTConsumer(); P.reset( new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); P->Initialize(); - - // An initial PTU is needed as CUDA includes some headers automatically - auto PTU = ParseOrWrapTopLevelDecl(); - if (auto E = PTU.takeError()) { - consumeError(std::move(E)); // FIXME - return; // PTU.takeError(); - } - - if (getCodeGen()) { - PTU->TheModule = GenModule(); - assert(PTU->TheModule && "Failed to create initial PTU"); - } } -IncrementalParser::~IncrementalParser() { - P.reset(); - Act->FinalizeAction(); -} +IncrementalParser::~IncrementalParser() { P.reset(); } -llvm::Expected +llvm::Expected IncrementalParser::ParseOrWrapTopLevelDecl() { // Recover resources if we crash before exiting this method. Sema &S = CI->getSema(); @@ -247,12 +47,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true); Sema::LocalEagerInstantiationScope LocalInstantiations(S); - PTUs.emplace_back(PartialTranslationUnit()); - PartialTranslationUnit &LastPTU = PTUs.back(); // Add a new PTU. ASTContext &C = S.getASTContext(); C.addTranslationUnitDecl(); - LastPTU.TUPart = C.getTranslationUnitDecl(); // Skip previous eof due to last incremental input. if (P->getCurToken().is(tok::annot_repl_input_end)) { @@ -278,9 +75,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { DiagnosticsEngine &Diags = getCI()->getDiagnostics(); if (Diags.hasErrorOccurred()) { - PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(), - nullptr}; - CleanUpPTU(MostRecentPTU); + CleanUpPTU(C.getTranslationUnitDecl()); Diags.Reset(/*soft=*/true); Diags.getClient()->clear(); @@ -299,10 +94,10 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { Consumer->HandleTranslationUnit(C); - return LastPTU; + return C.getTranslationUnitDecl(); } -llvm::Expected +llvm::Expected IncrementalParser::Parse(llvm::StringRef input) { Preprocessor &PP = CI->getPreprocessor(); assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); @@ -356,37 +151,10 @@ IncrementalParser::Parse(llvm::StringRef input) { "Lexer must be EOF when starting incremental parse!"); } - if (std::unique_ptr M = GenModule()) - PTU->TheModule = std::move(M); - return PTU; } -std::unique_ptr IncrementalParser::GenModule() { - static unsigned ID = 0; - if (CodeGenerator *CG = getCodeGen()) { - // Clang's CodeGen is designed to work with a single llvm::Module. In many - // cases for convenience various CodeGen parts have a reference to the - // llvm::Module (TheModule or Module) which does not change when a new - // module is pushed. However, the execution engine wants to take ownership - // of the module which does not map well to CodeGen's design. To work this - // around we created an empty module to make CodeGen happy. We should make - // sure it always stays empty. - assert((!CachedInCodeGenModule || - (CachedInCodeGenModule->empty() && - CachedInCodeGenModule->global_empty() && - CachedInCodeGenModule->alias_empty() && - CachedInCodeGenModule->ifunc_empty())) && - "CodeGen wrote to a readonly module"); - std::unique_ptr M(CG->ReleaseModule()); - CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); - return M; - } - return nullptr; -} - -void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { - TranslationUnitDecl *MostRecentTU = PTU.TUPart; +void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) { for (auto &&[Key, List] : *Map) { DeclContextLookupResult R = List.getLookupResult(); @@ -419,9 +187,4 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { } } -llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { - CodeGenerator *CG = getCodeGen(); - assert(CG); - return CG->GetMangledName(GD); -} } // end namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index f63bce50acd3b..4ce361143b581 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -13,35 +13,24 @@ #ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H #define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H -#include "clang/AST/GlobalDecl.h" -#include "clang/Interpreter/PartialTranslationUnit.h" - -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include #include -namespace llvm { -class LLVMContext; -class Module; -} // namespace llvm namespace clang { class ASTConsumer; class CodeGenerator; class CompilerInstance; -class IncrementalAction; -class Interpreter; class Parser; +class TranslationUnitDecl; + /// Provides support for incremental compilation. Keeps track of the state /// changes between the subsequent incremental input. /// class IncrementalParser { protected: - /// Long-lived, incremental parsing action. - std::unique_ptr Act; - /// Compiler instance performing the incremental compilation. std::unique_ptr CI; @@ -54,42 +43,24 @@ class IncrementalParser { /// Counts the number of direct user input lines that have been parsed. unsigned InputCount = 0; - /// List containing every information about every incrementally parsed piece - /// of code. - std::list PTUs; - - /// When CodeGen is created the first llvm::Module gets cached in many places - /// and we must keep it alive. - std::unique_ptr CachedInCodeGenModule; - IncrementalParser(); public: - IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, llvm::Error &Err); + IncrementalParser(std::unique_ptr Instance, + llvm::Error &Err); virtual ~IncrementalParser(); CompilerInstance *getCI() { return CI.get(); } - CodeGenerator *getCodeGen() const; /// Parses incremental input by creating an in-memory file. ///\returns a \c PartialTranslationUnit which holds information about the - /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input. - virtual llvm::Expected Parse(llvm::StringRef Input); - - /// Uses the CodeGenModule mangled name cache and avoids recomputing. - ///\returns the mangled name of a \c GD. - llvm::StringRef GetMangledName(GlobalDecl GD) const; - - void CleanUpPTU(PartialTranslationUnit &PTU); - - std::list &getPTUs() { return PTUs; } + /// \c TranslationUnitDecl. + virtual llvm::Expected Parse(llvm::StringRef Input); - std::unique_ptr GenModule(); + void CleanUpPTU(TranslationUnitDecl *MostRecentTU); private: - llvm::Expected ParseOrWrapTopLevelDecl(); + llvm::Expected ParseOrWrapTopLevelDecl(); }; } // end namespace clang diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 7209a33272ef2..e1135c2cf88fe 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -19,6 +19,7 @@ #include "Wasm.h" #endif // __EMSCRIPTEN__ +#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Mangle.h" #include "clang/AST/TypeVisitor.h" @@ -33,7 +34,10 @@ #include "clang/Driver/Options.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/FrontendTool/Utils.h" #include "clang/Interpreter/Interpreter.h" #include "clang/Interpreter/Value.h" #include "clang/Lex/PreprocessorOptions.h" @@ -47,10 +51,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" -#include - using namespace clang; - // FIXME: Figure out how to unify with namespace init_convenience from // tools/clang-import-test/clang-import-test.cpp namespace { @@ -138,6 +139,8 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { } // anonymous namespace +namespace clang { + llvm::Expected> IncrementalCompilerBuilder::create(std::string TT, std::vector &ClangArgv) { @@ -241,20 +244,177 @@ IncrementalCompilerBuilder::CreateCudaHost() { return IncrementalCompilerBuilder::createCuda(false); } +class InProcessPrintingASTConsumer final : public MultiplexConsumer { + Interpreter &Interp; + +public: + InProcessPrintingASTConsumer(std::unique_ptr C, Interpreter &I) + : MultiplexConsumer(std::move(C)), Interp(I) {} + bool HandleTopLevelDecl(DeclGroupRef DGR) override final { + if (DGR.isNull()) + return true; + // if (!Consumer) + // return true; + + for (Decl *D : DGR) + if (auto *TLSD = llvm::dyn_cast(D)) + if (TLSD && TLSD->isSemiMissing()) { + auto ExprOrErr = + Interp.AttachValuePrinting(cast(TLSD->getStmt())); + if (llvm::Error E = ExprOrErr.takeError()) { + llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), + "Value printing failed: "); + return false; // abort parsing + } + TLSD->setStmt(*ExprOrErr); + } + + return MultiplexConsumer::HandleTopLevelDecl(DGR); + } +}; + +/// A custom action enabling the incremental processing functionality. +/// +/// The usual \p FrontendAction expects one call to ExecuteAction and once it +/// sees a call to \p EndSourceFile it deletes some of the important objects +/// such as \p Preprocessor and \p Sema assuming no further input will come. +/// +/// \p IncrementalAction ensures it keep its underlying action's objects alive +/// as long as the \p IncrementalParser needs them. +/// +class IncrementalAction : public WrapperFrontendAction { +private: + bool IsTerminating = false; + Interpreter &Interp; + std::unique_ptr Consumer; + +public: + IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, + llvm::Error &Err, Interpreter &I, + std::unique_ptr Consumer = nullptr) + : WrapperFrontendAction([&]() { + llvm::ErrorAsOutParameter EAO(&Err); + std::unique_ptr Act; + switch (CI.getFrontendOpts().ProgramAction) { + default: + Err = llvm::createStringError( + std::errc::state_not_recoverable, + "Driver initialization failed. " + "Incremental mode for action %d is not supported", + CI.getFrontendOpts().ProgramAction); + return Act; + case frontend::ASTDump: + [[fallthrough]]; + case frontend::ASTPrint: + [[fallthrough]]; + case frontend::ParseSyntaxOnly: + Act = CreateFrontendAction(CI); + break; + case frontend::PluginAction: + [[fallthrough]]; + case frontend::EmitAssembly: + [[fallthrough]]; + case frontend::EmitBC: + [[fallthrough]]; + case frontend::EmitObj: + [[fallthrough]]; + case frontend::PrintPreprocessedInput: + [[fallthrough]]; + case frontend::EmitLLVMOnly: + Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); + break; + } + return Act; + }()), + Interp(I), Consumer(std::move(Consumer)) {} + FrontendAction *getWrapped() const { return WrappedAction.get(); } + TranslationUnitKind getTranslationUnitKind() override { + return TU_Incremental; + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + std::unique_ptr C = + WrapperFrontendAction::CreateASTConsumer(CI, InFile); + + if (Consumer) { + std::vector> Cs; + Cs.push_back(std::move(Consumer)); + Cs.push_back(std::move(C)); + return std::make_unique(std::move(Cs)); + } + + return std::make_unique(std::move(C), Interp); + } + + void ExecuteAction() override { + CompilerInstance &CI = getCompilerInstance(); + assert(CI.hasPreprocessor() && "No PP!"); + + // Use a code completion consumer? + CodeCompleteConsumer *CompletionConsumer = nullptr; + if (CI.hasCodeCompletionConsumer()) + CompletionConsumer = &CI.getCodeCompletionConsumer(); + + Preprocessor &PP = CI.getPreprocessor(); + PP.EnterMainSourceFile(); + + if (!CI.hasSema()) + CI.createSema(getTranslationUnitKind(), CompletionConsumer); + } + + // Do not terminate after processing the input. This allows us to keep various + // clang objects alive and to incrementally grow the current TU. + void EndSourceFile() override { + // The WrappedAction can be nullptr if we issued an error in the ctor. + if (IsTerminating && getWrapped()) + WrapperFrontendAction::EndSourceFile(); + } + + void FinalizeAction() { + assert(!IsTerminating && "Already finalized!"); + IsTerminating = true; + EndSourceFile(); + } +}; + Interpreter::Interpreter(std::unique_ptr CI, llvm::Error &ErrOut, - std::unique_ptr JITBuilder) + std::unique_ptr JITBuilder, + std::unique_ptr Consumer) : JITBuilder(std::move(JITBuilder)) { llvm::ErrorAsOutParameter EAO(&ErrOut); auto LLVMCtx = std::make_unique(); TSCtx = std::make_unique(std::move(LLVMCtx)); - IncrParser = std::make_unique( - *this, std::move(CI), *TSCtx->getContext(), ErrOut); + + Act = std::make_unique(*CI, *TSCtx->getContext(), ErrOut, + *this, std::move(Consumer)); + if (ErrOut) + return; + CI->ExecuteAction(*Act); + + if (getCodeGen()) + CachedInCodeGenModule = GenModule(); + + IncrParser = std::make_unique(std::move(CI), ErrOut); + + // An initial PTU is needed as CUDA includes some headers automatically. + auto PTU = Parse(""); + if (auto E = PTU.takeError()) { + ErrOut = joinErrors(std::move(ErrOut), std::move(E)); + return; + } + + if (getCodeGen()) { + PTU->TheModule = GenModule(); + assert(PTU->TheModule && "Failed to create initial PTU"); + } + if (ErrOut) return; // Not all frontends support code-generation, e.g. ast-dump actions don't - if (IncrParser->getCodeGen()) { + if (getCodeGen()) { if (llvm::Error Err = CreateExecutor()) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; @@ -262,7 +422,7 @@ Interpreter::Interpreter(std::unique_ptr CI, // Process the PTUs that came from initialization. For example -include will // give us a header that's processed at initialization of the preprocessor. - for (PartialTranslationUnit &PTU : IncrParser->getPTUs()) + for (PartialTranslationUnit &PTU : PTUs) if (llvm::Error Err = Execute(PTU)) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; @@ -271,6 +431,8 @@ Interpreter::Interpreter(std::unique_ptr CI, } Interpreter::~Interpreter() { + Act->FinalizeAction(); + IncrParser.reset(); if (IncrExecutor) { if (llvm::Error Err = IncrExecutor->cleanUp()) llvm::report_fatal_error( @@ -342,8 +504,8 @@ Interpreter::createWithCUDA(std::unique_ptr CI, llvm::Error Err = llvm::Error::success(); auto DeviceParser = std::make_unique( - **Interp, std::move(DCI), *(*Interp)->IncrParser.get(), - *(*Interp)->TSCtx->getContext(), IMVFS, Err); + std::move(DCI), *(*Interp)->IncrParser.get(), IMVFS, Err, + (*Interp)->PTUs); if (Err) return std::move(Err); @@ -379,22 +541,21 @@ const ASTContext &Interpreter::getASTContext() const { void Interpreter::markUserCodeStart() { assert(!InitPTUSize && "We only do this once"); - InitPTUSize = IncrParser->getPTUs().size(); + InitPTUSize = PTUs.size(); } size_t Interpreter::getEffectivePTUSize() const { - std::list &PTUs = IncrParser->getPTUs(); assert(PTUs.size() >= InitPTUSize && "empty PTU list?"); return PTUs.size() - InitPTUSize; } llvm::Expected Interpreter::Parse(llvm::StringRef Code) { - // If we have a device parser, parse it first. - // The generated code will be included in the host compilation + // If we have a device parser, parse it first. The generated code will be + // included in the host compilation if (DeviceParser) { - auto DevicePTU = DeviceParser->Parse(Code); - if (auto E = DevicePTU.takeError()) + llvm::Expected DeviceTU = DeviceParser->Parse(Code); + if (auto E = DeviceTU.takeError()) return std::move(E); } @@ -402,7 +563,19 @@ Interpreter::Parse(llvm::StringRef Code) { // printing could cause it. getCompilerInstance()->getDiagnostics().setSeverity( clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation()); - return IncrParser->Parse(Code); + + llvm::Expected TuOrErr = IncrParser->Parse(Code); + if (!TuOrErr) + return TuOrErr.takeError(); + + PTUs.emplace_back(PartialTranslationUnit()); + PartialTranslationUnit &LastPTU = PTUs.back(); + LastPTU.TUPart = *TuOrErr; + + if (std::unique_ptr M = GenModule()) + LastPTU.TheModule = std::move(M); + + return LastPTU; } static llvm::Expected @@ -420,7 +593,7 @@ llvm::Error Interpreter::CreateExecutor() { return llvm::make_error("Operation failed. " "Execution engine exists", std::error_code()); - if (!IncrParser->getCodeGen()) + if (!getCodeGen()) return llvm::make_error("Operation failed. " "No code generator available", std::error_code()); @@ -492,7 +665,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const { return llvm::make_error("Operation failed. " "No execution engine", std::error_code()); - llvm::StringRef MangledName = IncrParser->GetMangledName(GD); + llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD); return getSymbolAddress(MangledName); } @@ -518,7 +691,6 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { llvm::Error Interpreter::Undo(unsigned N) { - std::list &PTUs = IncrParser->getPTUs(); if (N > getEffectivePTUSize()) return llvm::make_error("Operation failed. " "Too many undos", @@ -529,7 +701,7 @@ llvm::Error Interpreter::Undo(unsigned N) { return Err; } - IncrParser->CleanUpPTU(PTUs.back()); + IncrParser->CleanUpPTU(PTUs.back().TUPart); PTUs.pop_back(); } return llvm::Error::success(); @@ -551,416 +723,32 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { return llvm::Error::success(); } -llvm::Expected -Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { - assert(CXXRD && "Cannot compile a destructor for a nullptr"); - if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end()) - return Dtor->getSecond(); - - if (CXXRD->hasIrrelevantDestructor()) - return llvm::orc::ExecutorAddr{}; - - CXXDestructorDecl *DtorRD = - getCompilerInstance()->getSema().LookupDestructor(CXXRD); - - llvm::StringRef Name = - IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base)); - auto AddrOrErr = getSymbolAddress(Name); - if (!AddrOrErr) - return AddrOrErr.takeError(); - - Dtors[CXXRD] = *AddrOrErr; - return AddrOrErr; -} - -static constexpr llvm::StringRef MagicRuntimeInterface[] = { - "__clang_Interpreter_SetValueNoAlloc", - "__clang_Interpreter_SetValueWithAlloc", - "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; - -static std::unique_ptr -createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx, - Sema &S); - -std::unique_ptr Interpreter::FindRuntimeInterface() { - if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; })) - return nullptr; - - Sema &S = getCompilerInstance()->getSema(); - ASTContext &Ctx = S.getASTContext(); - - auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) { - LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), - Sema::LookupOrdinaryName, - RedeclarationKind::ForVisibleRedeclaration); - S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); - if (R.empty()) - return false; - - CXXScopeSpec CSS; - Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); - return true; - }; - - if (!LookupInterface(ValuePrintingInfo[NoAlloc], - MagicRuntimeInterface[NoAlloc])) - return nullptr; - if (Ctx.getLangOpts().CPlusPlus) { - if (!LookupInterface(ValuePrintingInfo[WithAlloc], - MagicRuntimeInterface[WithAlloc])) - return nullptr; - if (!LookupInterface(ValuePrintingInfo[CopyArray], - MagicRuntimeInterface[CopyArray])) - return nullptr; - if (!LookupInterface(ValuePrintingInfo[NewTag], - MagicRuntimeInterface[NewTag])) - return nullptr; +std::unique_ptr Interpreter::GenModule() { + static unsigned ID = 0; + if (CodeGenerator *CG = getCodeGen()) { + // Clang's CodeGen is designed to work with a single llvm::Module. In many + // cases for convenience various CodeGen parts have a reference to the + // llvm::Module (TheModule or Module) which does not change when a new + // module is pushed. However, the execution engine wants to take ownership + // of the module which does not map well to CodeGen's design. To work this + // around we created an empty module to make CodeGen happy. We should make + // sure it always stays empty. + assert((!CachedInCodeGenModule || (CachedInCodeGenModule->empty() && + CachedInCodeGenModule->global_empty() && + CachedInCodeGenModule->alias_empty() && + CachedInCodeGenModule->ifunc_empty())) && + "CodeGen wrote to a readonly module"); + std::unique_ptr M(CG->ReleaseModule()); + CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); + return M; } - - return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S); + return nullptr; } -namespace { - -class InterfaceKindVisitor - : public TypeVisitor { - friend class InProcessRuntimeInterfaceBuilder; - - ASTContext &Ctx; - Sema &S; - Expr *E; - llvm::SmallVector Args; - -public: - InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E) - : Ctx(Ctx), S(S), E(E) {} - - Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) { - return Interpreter::InterfaceKind::WithAlloc; - } - - Interpreter::InterfaceKind - VisitMemberPointerType(const MemberPointerType *Ty) { - return Interpreter::InterfaceKind::WithAlloc; - } - - Interpreter::InterfaceKind - VisitConstantArrayType(const ConstantArrayType *Ty) { - return Interpreter::InterfaceKind::CopyArray; - } - - Interpreter::InterfaceKind - VisitFunctionProtoType(const FunctionProtoType *Ty) { - HandlePtrType(Ty); - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) { - HandlePtrType(Ty); - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) { - ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); - assert(!AddrOfE.isInvalid() && "Can not create unary expression"); - Args.push_back(AddrOfE.get()); - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { - if (Ty->isNullPtrType()) - Args.push_back(E); - else if (Ty->isFloatingType()) - Args.push_back(E); - else if (Ty->isIntegralOrEnumerationType()) - HandleIntegralOrEnumType(Ty); - else if (Ty->isVoidType()) { - // Do we need to still run `E`? - } - - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) { - HandleIntegralOrEnumType(Ty); - return Interpreter::InterfaceKind::NoAlloc; - } - -private: - // Force cast these types to the uint that fits the register size. That way we - // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`. - void HandleIntegralOrEnumType(const Type *Ty) { - uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy); - QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits); - TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy); - ExprResult CastedExpr = - S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); - assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); - Args.push_back(CastedExpr.get()); - } - - void HandlePtrType(const Type *Ty) { - TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); - ExprResult CastedExpr = - S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); - assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); - Args.push_back(CastedExpr.get()); - } -}; - -class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder { - Interpreter &Interp; - ASTContext &Ctx; - Sema &S; - -public: - InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S) - : Interp(Interp), Ctx(C), S(S) {} - - TransformExprFunction *getPrintValueTransformer() override { - return &transformForValuePrinting; - } - -private: - static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder, - Expr *E, - ArrayRef FixedArgs) { - auto *B = static_cast(Builder); - - // Get rid of ExprWithCleanups. - if (auto *EWC = llvm::dyn_cast_if_present(E)) - E = EWC->getSubExpr(); - - InterfaceKindVisitor Visitor(B->Ctx, B->S, E); - - // The Interpreter* parameter and the out parameter `OutVal`. - for (Expr *E : FixedArgs) - Visitor.Args.push_back(E); - - QualType Ty = E->getType(); - QualType DesugaredTy = Ty.getDesugaredType(B->Ctx); - - // For lvalue struct, we treat it as a reference. - if (DesugaredTy->isRecordType() && E->isLValue()) { - DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy); - Ty = B->Ctx.getLValueReferenceType(Ty); - } - - Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy, - (uintptr_t)Ty.getAsOpaquePtr()); - // The QualType parameter `OpaqueType`, represented as `void*`. - Visitor.Args.push_back(TypeArg); - - // We push the last parameter based on the type of the Expr. Note we need - // special care for rvalue struct. - Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy); - switch (Kind) { - case Interpreter::InterfaceKind::WithAlloc: - case Interpreter::InterfaceKind::CopyArray: { - // __clang_Interpreter_SetValueWithAlloc. - ExprResult AllocCall = B->S.ActOnCallExpr( - /*Scope=*/nullptr, - B->Interp - .getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc], - E->getBeginLoc(), Visitor.Args, E->getEndLoc()); - assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); - - TypeSourceInfo *TSI = - B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); - - // Force CodeGen to emit destructor. - if (auto *RD = Ty->getAsCXXRecordDecl()) { - auto *Dtor = B->S.LookupDestructor(RD); - Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx)); - B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( - DeclGroupRef(Dtor)); - } - - // __clang_Interpreter_SetValueCopyArr. - if (Kind == Interpreter::InterfaceKind::CopyArray) { - const auto *ConstantArrTy = - cast(DesugaredTy.getTypePtr()); - size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy); - Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize); - Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr}; - return B->S.ActOnCallExpr( - /*Scope *=*/nullptr, - B->Interp - .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray], - SourceLocation(), Args, SourceLocation()); - } - Expr *Args[] = { - AllocCall.get(), - B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]}; - ExprResult CXXNewCall = B->S.BuildCXXNew( - E->getSourceRange(), - /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, - /*PlacementRParen=*/SourceLocation(), - /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, - E->getSourceRange(), E); - - assert(!CXXNewCall.isInvalid() && - "Can't create runtime placement new call!"); - - return B->S.ActOnFinishFullExpr(CXXNewCall.get(), - /*DiscardedValue=*/false); - } - // __clang_Interpreter_SetValueNoAlloc. - case Interpreter::InterfaceKind::NoAlloc: { - return B->S.ActOnCallExpr( - /*Scope=*/nullptr, - B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc], - E->getBeginLoc(), Visitor.Args, E->getEndLoc()); - } - default: - llvm_unreachable("Unhandled Interpreter::InterfaceKind"); - } - } -}; -} // namespace - -static std::unique_ptr -createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx, - Sema &S) { - return std::make_unique(Interp, Ctx, S); -} - -// This synthesizes a call expression to a speciall -// function that is responsible for generating the Value. -// In general, we transform: -// clang-repl> x -// To: -// // 1. If x is a built-in type like int, float. -// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); -// // 2. If x is a struct, and a lvalue. -// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, -// &x); -// // 3. If x is a struct, but a rvalue. -// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, -// xQualType)) (x); - -Expr *Interpreter::SynthesizeExpr(Expr *E) { - Sema &S = getCompilerInstance()->getSema(); - ASTContext &Ctx = S.getASTContext(); - - if (!RuntimeIB) { - RuntimeIB = FindRuntimeInterface(); - AddPrintValueCall = RuntimeIB->getPrintValueTransformer(); - } - - assert(AddPrintValueCall && - "We don't have a runtime interface for pretty print!"); - - // Create parameter `ThisInterp`. - auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this); - - // Create parameter `OutVal`. - auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue); - - // Build `__clang_Interpreter_SetValue*` call. - ExprResult Result = - AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue}); - - // It could fail, like printing an array type in C. (not supported) - if (Result.isInvalid()) - return E; - return Result.get(); -} - -// Temporary rvalue struct that need special care. -REPL_EXTERNAL_VISIBILITY void * -__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal, - void *OpaqueType) { - Value &VRef = *(Value *)OutVal; - VRef = Value(static_cast(This), OpaqueType); - return VRef.getPtr(); -} - -extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc( - void *This, void *OutVal, void *OpaqueType, ...) { - Value &VRef = *(Value *)OutVal; - Interpreter *I = static_cast(This); - VRef = Value(I, OpaqueType); - if (VRef.isVoid()) - return; - - va_list args; - va_start(args, /*last named param*/ OpaqueType); - - QualType QT = VRef.getType(); - if (VRef.getKind() == Value::K_PtrOrObj) { - VRef.setPtr(va_arg(args, void *)); - } else { - if (const auto *ET = QT->getAs()) - QT = ET->getDecl()->getIntegerType(); - switch (QT->castAs()->getKind()) { - default: - llvm_unreachable("unknown type kind!"); - break; - // Types shorter than int are resolved as int, else va_arg has UB. - case BuiltinType::Bool: - VRef.setBool(va_arg(args, int)); - break; - case BuiltinType::Char_S: - VRef.setChar_S(va_arg(args, int)); - break; - case BuiltinType::SChar: - VRef.setSChar(va_arg(args, int)); - break; - case BuiltinType::Char_U: - VRef.setChar_U(va_arg(args, unsigned)); - break; - case BuiltinType::UChar: - VRef.setUChar(va_arg(args, unsigned)); - break; - case BuiltinType::Short: - VRef.setShort(va_arg(args, int)); - break; - case BuiltinType::UShort: - VRef.setUShort(va_arg(args, unsigned)); - break; - case BuiltinType::Int: - VRef.setInt(va_arg(args, int)); - break; - case BuiltinType::UInt: - VRef.setUInt(va_arg(args, unsigned)); - break; - case BuiltinType::Long: - VRef.setLong(va_arg(args, long)); - break; - case BuiltinType::ULong: - VRef.setULong(va_arg(args, unsigned long)); - break; - case BuiltinType::LongLong: - VRef.setLongLong(va_arg(args, long long)); - break; - case BuiltinType::ULongLong: - VRef.setULongLong(va_arg(args, unsigned long long)); - break; - // Types shorter than double are resolved as double, else va_arg has UB. - case BuiltinType::Float: - VRef.setFloat(va_arg(args, double)); - break; - case BuiltinType::Double: - VRef.setDouble(va_arg(args, double)); - break; - case BuiltinType::LongDouble: - VRef.setLongDouble(va_arg(args, long double)); - break; - // See REPL_BUILTIN_TYPES. - } - } - va_end(args); -} - -// A trampoline to work around the fact that operator placement new cannot -// really be forward declared due to libc++ and libstdc++ declaration mismatch. -// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same -// definition in the interpreter runtime. We should move it in a runtime header -// which gets included by the interpreter and here. -struct __clang_Interpreter_NewTag {}; -REPL_EXTERNAL_VISIBILITY void * -operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept { - // Just forward to the standard operator placement new. - return operator new(__sz, __p); +CodeGenerator *Interpreter::getCodeGen() const { + FrontendAction *WrappedAct = Act->getWrapped(); + if (!WrappedAct->hasIRSupport()) + return nullptr; + return static_cast(WrappedAct)->getCodeGenerator(); } +} // namespace clang diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp new file mode 100644 index 0000000000000..6673c2f773785 --- /dev/null +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -0,0 +1,400 @@ +//===--- InterpreterValuePrinter.cpp - Value printing utils -----*- 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 implements routines for in-process value printing in clang-repl. +// +//===----------------------------------------------------------------------===// + +#include "IncrementalParser.h" +#include "InterpreterUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Type.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/Value.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" + +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +#include + +namespace clang { + +llvm::Expected +Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { + assert(CXXRD && "Cannot compile a destructor for a nullptr"); + if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end()) + return Dtor->getSecond(); + + if (CXXRD->hasIrrelevantDestructor()) + return llvm::orc::ExecutorAddr{}; + + CXXDestructorDecl *DtorRD = + getCompilerInstance()->getSema().LookupDestructor(CXXRD); + + llvm::StringRef Name = + getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base)); + auto AddrOrErr = getSymbolAddress(Name); + if (!AddrOrErr) + return AddrOrErr.takeError(); + + Dtors[CXXRD] = *AddrOrErr; + return AddrOrErr; +} + +enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag }; + +class InterfaceKindVisitor + : public TypeVisitor { + + Sema &S; + Expr *E; + llvm::SmallVectorImpl &Args; + +public: + InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl &Args) + : S(S), E(E), Args(Args) {} + + InterfaceKind computeInterfaceKind(QualType Ty) { + return Visit(Ty.getTypePtr()); + } + + InterfaceKind VisitRecordType(const RecordType *Ty) { + return InterfaceKind::WithAlloc; + } + + InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) { + return InterfaceKind::WithAlloc; + } + + InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) { + return InterfaceKind::CopyArray; + } + + InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) { + HandlePtrType(Ty); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitPointerType(const PointerType *Ty) { + HandlePtrType(Ty); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitReferenceType(const ReferenceType *Ty) { + ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); + assert(!AddrOfE.isInvalid() && "Can not create unary expression"); + Args.push_back(AddrOfE.get()); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { + if (Ty->isNullPtrType()) + Args.push_back(E); + else if (Ty->isFloatingType()) + Args.push_back(E); + else if (Ty->isIntegralOrEnumerationType()) + HandleIntegralOrEnumType(Ty); + else if (Ty->isVoidType()) { + // Do we need to still run `E`? + } + + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitEnumType(const EnumType *Ty) { + HandleIntegralOrEnumType(Ty); + return InterfaceKind::NoAlloc; + } + +private: + // Force cast these types to the uint that fits the register size. That way we + // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`. + void HandleIntegralOrEnumType(const Type *Ty) { + ASTContext &Ctx = S.getASTContext(); + uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy); + QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits); + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); + Args.push_back(CastedExpr.get()); + } + + void HandlePtrType(const Type *Ty) { + ASTContext &Ctx = S.getASTContext(); + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); + Args.push_back(CastedExpr.get()); + } +}; + +// This synthesizes a call expression to a speciall +// function that is responsible for generating the Value. +// In general, we transform: +// clang-repl> x +// To: +// // 1. If x is a built-in type like int, float. +// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); +// // 2. If x is a struct, and a lvalue. +// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, +// &x); +// // 3. If x is a struct, but a rvalue. +// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, +// xQualType)) (x); +llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + // Find the value printing builtins. + if (!ValuePrintingInfo[0]) { + assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; })); + + auto LookupInterface = [&](Expr *&Interface, + llvm::StringRef Name) -> llvm::Error { + LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), + Sema::LookupOrdinaryName, + RedeclarationKind::ForVisibleRedeclaration); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + if (R.empty()) + return llvm::make_error( + Name + " not found!", llvm::inconvertibleErrorCode()); + + CXXScopeSpec CSS; + Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); + return llvm::Error::success(); + }; + static constexpr llvm::StringRef Builtin[] = { + "__clang_Interpreter_SetValueNoAlloc", + "__clang_Interpreter_SetValueWithAlloc", + "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc])) + return std::move(Err); + + if (Ctx.getLangOpts().CPlusPlus) { + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc])) + return std::move(Err); + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray])) + return std::move(Err); + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag])) + return std::move(Err); + } + } + + llvm::SmallVector AdjustedArgs; + // Create parameter `ThisInterp`. + AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this)); + + // Create parameter `OutVal`. + AdjustedArgs.push_back( + CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue)); + + // Build `__clang_Interpreter_SetValue*` call. + + // Get rid of ExprWithCleanups. + if (auto *EWC = llvm::dyn_cast_if_present(E)) + E = EWC->getSubExpr(); + + QualType Ty = E->getType(); + QualType DesugaredTy = Ty.getDesugaredType(Ctx); + + // For lvalue struct, we treat it as a reference. + if (DesugaredTy->isRecordType() && E->isLValue()) { + DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy); + Ty = Ctx.getLValueReferenceType(Ty); + } + + Expr *TypeArg = + CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr()); + // The QualType parameter `OpaqueType`, represented as `void*`. + AdjustedArgs.push_back(TypeArg); + + // We push the last parameter based on the type of the Expr. Note we need + // special care for rvalue struct. + InterfaceKindVisitor V(S, E, AdjustedArgs); + Scope *Scope = nullptr; + ExprResult SetValueE; + InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy); + switch (Kind) { + case InterfaceKind::WithAlloc: + LLVM_FALLTHROUGH; + case InterfaceKind::CopyArray: { + // __clang_Interpreter_SetValueWithAlloc. + ExprResult AllocCall = + S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc], + E->getBeginLoc(), AdjustedArgs, E->getEndLoc()); + assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + + // Force CodeGen to emit destructor. + if (auto *RD = Ty->getAsCXXRecordDecl()) { + auto *Dtor = S.LookupDestructor(RD); + Dtor->addAttr(UsedAttr::CreateImplicit(Ctx)); + getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( + DeclGroupRef(Dtor)); + } + + // __clang_Interpreter_SetValueCopyArr. + if (Kind == InterfaceKind::CopyArray) { + const auto *ConstantArrTy = + cast(DesugaredTy.getTypePtr()); + size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy); + Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize); + Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr}; + SetValueE = + S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray], + SourceLocation(), Args, SourceLocation()); + } + Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]}; + ExprResult CXXNewCall = S.BuildCXXNew( + E->getSourceRange(), + /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, + /*PlacementRParen=*/SourceLocation(), + /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, + E->getSourceRange(), E); + + assert(!CXXNewCall.isInvalid() && + "Can't create runtime placement new call!"); + + SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(), + /*DiscardedValue=*/false); + break; + } + // __clang_Interpreter_SetValueNoAlloc. + case InterfaceKind::NoAlloc: { + SetValueE = + S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc], + E->getBeginLoc(), AdjustedArgs, E->getEndLoc()); + break; + } + default: + llvm_unreachable("Unhandled InterfaceKind"); + } + + // It could fail, like printing an array type in C. (not supported) + if (SetValueE.isInvalid()) + return E; + + return SetValueE.get(); +} + +} // namespace clang + +using namespace clang; + +// Temporary rvalue struct that need special care. +REPL_EXTERNAL_VISIBILITY void * +__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal, + void *OpaqueType) { + Value &VRef = *(Value *)OutVal; + VRef = Value(static_cast(This), OpaqueType); + return VRef.getPtr(); +} + +extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc( + void *This, void *OutVal, void *OpaqueType, ...) { + Value &VRef = *(Value *)OutVal; + Interpreter *I = static_cast(This); + VRef = Value(I, OpaqueType); + if (VRef.isVoid()) + return; + + va_list args; + va_start(args, /*last named param*/ OpaqueType); + + QualType QT = VRef.getType(); + if (VRef.getKind() == Value::K_PtrOrObj) { + VRef.setPtr(va_arg(args, void *)); + } else { + if (const auto *ET = QT->getAs()) + QT = ET->getDecl()->getIntegerType(); + switch (QT->castAs()->getKind()) { + default: + llvm_unreachable("unknown type kind!"); + break; + // Types shorter than int are resolved as int, else va_arg has UB. + case BuiltinType::Bool: + VRef.setBool(va_arg(args, int)); + break; + case BuiltinType::Char_S: + VRef.setChar_S(va_arg(args, int)); + break; + case BuiltinType::SChar: + VRef.setSChar(va_arg(args, int)); + break; + case BuiltinType::Char_U: + VRef.setChar_U(va_arg(args, unsigned)); + break; + case BuiltinType::UChar: + VRef.setUChar(va_arg(args, unsigned)); + break; + case BuiltinType::Short: + VRef.setShort(va_arg(args, int)); + break; + case BuiltinType::UShort: + VRef.setUShort(va_arg(args, unsigned)); + break; + case BuiltinType::Int: + VRef.setInt(va_arg(args, int)); + break; + case BuiltinType::UInt: + VRef.setUInt(va_arg(args, unsigned)); + break; + case BuiltinType::Long: + VRef.setLong(va_arg(args, long)); + break; + case BuiltinType::ULong: + VRef.setULong(va_arg(args, unsigned long)); + break; + case BuiltinType::LongLong: + VRef.setLongLong(va_arg(args, long long)); + break; + case BuiltinType::ULongLong: + VRef.setULongLong(va_arg(args, unsigned long long)); + break; + // Types shorter than double are resolved as double, else va_arg has UB. + case BuiltinType::Float: + VRef.setFloat(va_arg(args, double)); + break; + case BuiltinType::Double: + VRef.setDouble(va_arg(args, double)); + break; + case BuiltinType::LongDouble: + VRef.setLongDouble(va_arg(args, long double)); + break; + // See REPL_BUILTIN_TYPES. + } + } + va_end(args); +} + +// A trampoline to work around the fact that operator placement new cannot +// really be forward declared due to libc++ and libstdc++ declaration mismatch. +// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same +// definition in the interpreter runtime. We should move it in a runtime header +// which gets included by the interpreter and here. +struct __clang_Interpreter_NewTag {}; +REPL_EXTERNAL_VISIBILITY void * +operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept { + // Just forward to the standard operator placement new. + return operator new(__sz, __p); +} diff --git a/clang/unittests/Interpreter/CodeCompletionTest.cpp b/clang/unittests/Interpreter/CodeCompletionTest.cpp index 72fcce76a1029..23cfc469695d2 100644 --- a/clang/unittests/Interpreter/CodeCompletionTest.cpp +++ b/clang/unittests/Interpreter/CodeCompletionTest.cpp @@ -26,7 +26,7 @@ auto CB = clang::IncrementalCompilerBuilder(); class CodeCompletionTest : public InterpreterTestBase { public: - std::unique_ptr Interp; + std::unique_ptr Interp; void SetUp() override { if (!HostSupportsJIT()) diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp index 5f1f29cebab14..29af464dbcebb 100644 --- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp +++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp @@ -65,41 +65,13 @@ class InterpreterExtensionsTest : public InterpreterTestBase { } }; -class RecordRuntimeIBMetrics : public Interpreter { - struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder { - NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {} - - TransformExprFunction *getPrintValueTransformer() override { - TransformerQueries += 1; - return &noop; - } - - static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E, - ArrayRef FixedArgs) { - auto *B = static_cast(Builder); - B->TransformedExprs += 1; - return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false); - } - - Sema &S; - size_t TransformedExprs = 0; - size_t TransformerQueries = 0; - }; - -public: - // Inherit with using wouldn't make it public - RecordRuntimeIBMetrics(std::unique_ptr CI, llvm::Error &Err) - : Interpreter(std::move(CI), Err) {} - - std::unique_ptr FindRuntimeInterface() override { - assert(RuntimeIBPtr == nullptr && "We create the builder only once"); - Sema &S = getCompilerInstance()->getSema(); - auto RuntimeIB = std::make_unique(S); - RuntimeIBPtr = RuntimeIB.get(); - return RuntimeIB; - } - - NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr; +struct OutOfProcInterpreter : public Interpreter { + OutOfProcInterpreter( + std::unique_ptr CI, llvm::Error &ErrOut, + std::unique_ptr Consumer, + std::unique_ptr JITBuilder = nullptr) + : Interpreter(std::move(CI), ErrOut, std::move(JITBuilder), + std::move(Consumer)) {} }; TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) { @@ -108,13 +80,23 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) { clang::IncrementalCompilerBuilder CB; llvm::Error ErrOut = llvm::Error::success(); - RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut); + auto CI = cantFail(CB.CreateCpp()); + // Do not attach the default consumer which is specialized for in-process. + class NoopConsumer : public ASTConsumer {}; + std::unique_ptr C = std::make_unique(); + OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C), + /*JITBuilder=*/nullptr); cantFail(std::move(ErrOut)); - cantFail(Interp.Parse("int a = 1; a")); - cantFail(Interp.Parse("int b = 2; b")); - cantFail(Interp.Parse("int c = 3; c")); - EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs); - EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries); + cantFail(I.Parse("int a = 1; a")); + cantFail(I.Parse("int b = 2; b")); + cantFail(I.Parse("int c = 3; c")); + + // Make sure no clang::Value logic is attached by the Interpreter. + Value V1; + llvm::cantFail(I.ParseAndExecute("int x = 42;")); + llvm::cantFail(I.ParseAndExecute("x", &V1)); + EXPECT_FALSE(V1.isValid()); + EXPECT_FALSE(V1.hasValue()); } class CustomJBInterpreter : public Interpreter { From 31b012602d81790d80198e29bea8ae7bece28e32 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Mon, 9 Sep 2024 15:48:22 +0000 Subject: [PATCH 2/5] Tell the jit about -include files --- clang/include/clang/Interpreter/Interpreter.h | 1 + clang/lib/Interpreter/Interpreter.cpp | 46 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index cbb1cfd4ab02a..55e60d28a11cf 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -175,6 +175,7 @@ class Interpreter { CodeGenerator *getCodeGen() const; std::unique_ptr GenModule(); + PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU); // A cache for the compiled destructors used to for de-allocation of managed // clang::Values. diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index e1135c2cf88fe..93af355c9466b 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -393,33 +393,29 @@ Interpreter::Interpreter(std::unique_ptr CI, return; CI->ExecuteAction(*Act); - if (getCodeGen()) - CachedInCodeGenModule = GenModule(); + ASTContext &C = CI->getASTContext(); IncrParser = std::make_unique(std::move(CI), ErrOut); - // An initial PTU is needed as CUDA includes some headers automatically. - auto PTU = Parse(""); - if (auto E = PTU.takeError()) { - ErrOut = joinErrors(std::move(ErrOut), std::move(E)); - return; - } - - if (getCodeGen()) { - PTU->TheModule = GenModule(); - assert(PTU->TheModule && "Failed to create initial PTU"); - } - if (ErrOut) return; - // Not all frontends support code-generation, e.g. ast-dump actions don't if (getCodeGen()) { + CachedInCodeGenModule = GenModule(); if (llvm::Error Err = CreateExecutor()) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; } + } + + // The initial PTU is filled by `-include` or by CUDA includes automatically. + RegisterPTU(C.getTranslationUnitDecl()); + + // Prepare the IncrParser for input. + llvm::cantFail(Parse("")); + // Not all frontends support code-generation, e.g. ast-dump actions don't + if (getCodeGen()) { // Process the PTUs that came from initialization. For example -include will // give us a header that's processed at initialization of the preprocessor. for (PartialTranslationUnit &PTU : PTUs) @@ -549,6 +545,17 @@ size_t Interpreter::getEffectivePTUSize() const { return PTUs.size() - InitPTUSize; } +PartialTranslationUnit &Interpreter::RegisterPTU(TranslationUnitDecl *TU) { + PTUs.emplace_back(PartialTranslationUnit()); + PartialTranslationUnit &LastPTU = PTUs.back(); + LastPTU.TUPart = TU; + + if (std::unique_ptr M = GenModule()) + LastPTU.TheModule = std::move(M); + + return LastPTU; +} + llvm::Expected Interpreter::Parse(llvm::StringRef Code) { // If we have a device parser, parse it first. The generated code will be @@ -568,14 +575,7 @@ Interpreter::Parse(llvm::StringRef Code) { if (!TuOrErr) return TuOrErr.takeError(); - PTUs.emplace_back(PartialTranslationUnit()); - PartialTranslationUnit &LastPTU = PTUs.back(); - LastPTU.TUPart = *TuOrErr; - - if (std::unique_ptr M = GenModule()) - LastPTU.TheModule = std::move(M); - - return LastPTU; + return RegisterPTU(*TuOrErr); } static llvm::Expected From 85eafb453883deda0046d6ea108fbd783bdb4523 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Tue, 10 Sep 2024 11:05:25 +0000 Subject: [PATCH 3/5] Fix valgrind/windows --- clang/include/clang/Interpreter/Interpreter.h | 5 ++++- clang/lib/Interpreter/DeviceOffload.cpp | 15 +++++++------ clang/lib/Interpreter/DeviceOffload.h | 11 ++++++++-- clang/lib/Interpreter/IncrementalParser.cpp | 21 ++++++++----------- clang/lib/Interpreter/IncrementalParser.h | 12 +++++------ clang/lib/Interpreter/Interpreter.cpp | 15 +++++++------ 6 files changed, 43 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 55e60d28a11cf..800d83517fd7b 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -110,9 +110,12 @@ class Interpreter { /// and we must keep it alive. std::unique_ptr CachedInCodeGenModule; + /// Compiler instance performing the incremental compilation. + std::unique_ptr CI; + protected: // Derived classes can use an extended interface of the Interpreter. - Interpreter(std::unique_ptr CI, llvm::Error &Err, + Interpreter(std::unique_ptr Instance, llvm::Error &Err, std::unique_ptr JITBuilder = nullptr, std::unique_ptr Consumer = nullptr); diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp index 4bf9ed2809689..1999d63d1aa04 100644 --- a/clang/lib/Interpreter/DeviceOffload.cpp +++ b/clang/lib/Interpreter/DeviceOffload.cpp @@ -25,14 +25,17 @@ namespace clang { IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( - std::unique_ptr Instance, IncrementalParser &HostParser, + std::unique_ptr DeviceInstance, + CompilerInstance &HostInstance, llvm::IntrusiveRefCntPtr FS, llvm::Error &Err, const std::list &PTUs) - : IncrementalParser(std::move(Instance), Err), PTUs(PTUs), - HostParser(HostParser), VFS(FS) { + : IncrementalParser(*DeviceInstance, Err), PTUs(PTUs), VFS(FS), + CodeGenOpts(HostInstance.getCodeGenOpts()), + TargetOpts(HostInstance.getTargetOpts()) { if (Err) return; - StringRef Arch = CI->getTargetOpts().CPU; + DeviceCI = std::move(DeviceInstance); + StringRef Arch = TargetOpts.CPU; if (!Arch.starts_with("sm_") || Arch.substr(3).getAsInteger(10, SMVersion)) { Err = llvm::joinErrors(std::move(Err), llvm::make_error( "Invalid CUDA architecture", @@ -62,7 +65,7 @@ IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) { llvm::StringRef(FatbinContent.data(), FatbinContent.size()), "", false)); - HostParser.getCI()->getCodeGenOpts().CudaGpuBinaryFileName = FatbinFileName; + CodeGenOpts.CudaGpuBinaryFileName = FatbinFileName; FatbinContent.clear(); @@ -80,7 +83,7 @@ llvm::Expected IncrementalCUDADeviceParser::GeneratePTX() { std::error_code()); llvm::TargetOptions TO = llvm::TargetOptions(); llvm::TargetMachine *TargetMachine = Target->createTargetMachine( - PTU.TheModule->getTargetTriple(), getCI()->getTargetOpts().CPU, "", TO, + PTU.TheModule->getTargetTriple(), TargetOpts.CPU, "", TO, llvm::Reloc::Model::PIC_); PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h index b84870474841a..e658be7f0ee22 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -19,12 +19,17 @@ namespace clang { struct PartialTranslationUnit; +class CompilerInstance; +class CodeGenOptions; +class TargetOptions; + class IncrementalCUDADeviceParser : public IncrementalParser { const std::list &PTUs; public: IncrementalCUDADeviceParser( - std::unique_ptr Instance, IncrementalParser &HostParser, + std::unique_ptr DeviceInstance, + CompilerInstance &HostInstance, llvm::IntrusiveRefCntPtr VFS, llvm::Error &Err, const std::list &PTUs); @@ -39,11 +44,13 @@ class IncrementalCUDADeviceParser : public IncrementalParser { ~IncrementalCUDADeviceParser(); protected: - IncrementalParser &HostParser; + std::unique_ptr DeviceCI; int SMVersion; llvm::SmallString<1024> PTXCode; llvm::SmallVector FatbinContent; llvm::IntrusiveRefCntPtr VFS; + CodeGenOptions &CodeGenOpts; // intentionally a reference. + const TargetOptions &TargetOpts; }; } // namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index 615f61e9aec1b..e43cea1baf43a 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -24,16 +24,14 @@ namespace clang { -IncrementalParser::IncrementalParser() {} +// IncrementalParser::IncrementalParser() {} -IncrementalParser::IncrementalParser(std::unique_ptr Instance, +IncrementalParser::IncrementalParser(CompilerInstance &Instance, llvm::Error &Err) - : CI(std::move(Instance)) { + : S(Instance.getSema()) { llvm::ErrorAsOutParameter EAO(&Err); - - Consumer = &CI->getASTConsumer(); - P.reset( - new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); + Consumer = &S.getASTConsumer(); + P.reset(new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false)); P->Initialize(); } @@ -42,7 +40,6 @@ IncrementalParser::~IncrementalParser() { P.reset(); } llvm::Expected IncrementalParser::ParseOrWrapTopLevelDecl() { // Recover resources if we crash before exiting this method. - Sema &S = CI->getSema(); llvm::CrashRecoveryContextCleanupRegistrar CleanupSema(&S); Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true); Sema::LocalEagerInstantiationScope LocalInstantiations(S); @@ -73,7 +70,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { std::error_code()); } - DiagnosticsEngine &Diags = getCI()->getDiagnostics(); + DiagnosticsEngine &Diags = S.getDiagnostics(); if (Diags.hasErrorOccurred()) { CleanUpPTU(C.getTranslationUnitDecl()); @@ -99,7 +96,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { llvm::Expected IncrementalParser::Parse(llvm::StringRef input) { - Preprocessor &PP = CI->getPreprocessor(); + Preprocessor &PP = S.getPreprocessor(); assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); std::ostringstream SourceName; @@ -115,7 +112,7 @@ IncrementalParser::Parse(llvm::StringRef input) { memcpy(MBStart, input.data(), InputSize); MBStart[InputSize] = '\n'; - SourceManager &SM = CI->getSourceManager(); + SourceManager &SM = S.getSourceManager(); // FIXME: Create SourceLocation, which will allow clang to order the overload // candidates for example @@ -183,7 +180,7 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { // Check if we need to clean up the IdResolver chain. if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC && !D->getLangOpts().CPlusPlus) - getCI()->getSema().IdResolver.RemoveDecl(ND); + S.IdResolver.RemoveDecl(ND); } } diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index 4ce361143b581..4fdde749a2e75 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -24,6 +24,7 @@ class ASTConsumer; class CodeGenerator; class CompilerInstance; class Parser; +class Sema; class TranslationUnitDecl; /// Provides support for incremental compilation. Keeps track of the state @@ -31,8 +32,8 @@ class TranslationUnitDecl; /// class IncrementalParser { protected: - /// Compiler instance performing the incremental compilation. - std::unique_ptr CI; + /// The Sema performing the incremental compilation. + Sema &S; /// Parser. std::unique_ptr P; @@ -43,15 +44,12 @@ class IncrementalParser { /// Counts the number of direct user input lines that have been parsed. unsigned InputCount = 0; - IncrementalParser(); + // IncrementalParser(); public: - IncrementalParser(std::unique_ptr Instance, - llvm::Error &Err); + IncrementalParser(CompilerInstance &Instance, llvm::Error &Err); virtual ~IncrementalParser(); - CompilerInstance *getCI() { return CI.get(); } - /// Parses incremental input by creating an in-memory file. ///\returns a \c PartialTranslationUnit which holds information about the /// \c TranslationUnitDecl. diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 93af355c9466b..ad38b56e52c43 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -378,11 +378,12 @@ class IncrementalAction : public WrapperFrontendAction { } }; -Interpreter::Interpreter(std::unique_ptr CI, +Interpreter::Interpreter(std::unique_ptr Instance, llvm::Error &ErrOut, std::unique_ptr JITBuilder, std::unique_ptr Consumer) : JITBuilder(std::move(JITBuilder)) { + CI = std::move(Instance); llvm::ErrorAsOutParameter EAO(&ErrOut); auto LLVMCtx = std::make_unique(); TSCtx = std::make_unique(std::move(LLVMCtx)); @@ -395,7 +396,7 @@ Interpreter::Interpreter(std::unique_ptr CI, ASTContext &C = CI->getASTContext(); - IncrParser = std::make_unique(std::move(CI), ErrOut); + IncrParser = std::make_unique(*CI, ErrOut); if (ErrOut) return; @@ -427,8 +428,8 @@ Interpreter::Interpreter(std::unique_ptr CI, } Interpreter::~Interpreter() { - Act->FinalizeAction(); IncrParser.reset(); + Act->FinalizeAction(); if (IncrExecutor) { if (llvm::Error Err = IncrExecutor->cleanUp()) llvm::report_fatal_error( @@ -500,7 +501,7 @@ Interpreter::createWithCUDA(std::unique_ptr CI, llvm::Error Err = llvm::Error::success(); auto DeviceParser = std::make_unique( - std::move(DCI), *(*Interp)->IncrParser.get(), IMVFS, Err, + std::move(DCI), *(*Interp)->getCompilerInstance(), IMVFS, Err, (*Interp)->PTUs); if (Err) return std::move(Err); @@ -511,12 +512,10 @@ Interpreter::createWithCUDA(std::unique_ptr CI, } const CompilerInstance *Interpreter::getCompilerInstance() const { - return IncrParser->getCI(); + return CI.get(); } -CompilerInstance *Interpreter::getCompilerInstance() { - return IncrParser->getCI(); -} +CompilerInstance *Interpreter::getCompilerInstance() { return CI.get(); } llvm::Expected Interpreter::getExecutionEngine() { if (!IncrExecutor) { From 8d88049eae1307db8c14e4d6926ed6d9dcc000b3 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Tue, 10 Sep 2024 11:20:33 +0000 Subject: [PATCH 4/5] Address comments --- clang/include/clang/Interpreter/Interpreter.h | 2 +- clang/lib/Interpreter/Interpreter.cpp | 4 +--- clang/lib/Interpreter/InterpreterValuePrinter.cpp | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 800d83517fd7b..c44ce28376fa0 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -173,7 +173,7 @@ class Interpreter { private: size_t getEffectivePTUSize() const; void markUserCodeStart(); - llvm::Expected AttachValuePrinting(Expr *E); + llvm::Expected ExtractValueFromExpr(Expr *E); llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); CodeGenerator *getCodeGen() const; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index ad38b56e52c43..e4cec6775df7a 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -253,14 +253,12 @@ class InProcessPrintingASTConsumer final : public MultiplexConsumer { bool HandleTopLevelDecl(DeclGroupRef DGR) override final { if (DGR.isNull()) return true; - // if (!Consumer) - // return true; for (Decl *D : DGR) if (auto *TLSD = llvm::dyn_cast(D)) if (TLSD && TLSD->isSemiMissing()) { auto ExprOrErr = - Interp.AttachValuePrinting(cast(TLSD->getStmt())); + Interp.ExtractValueFromExpr(cast(TLSD->getStmt())); if (llvm::Error E = ExprOrErr.takeError()) { llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Value printing failed: "); diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index 6673c2f773785..3e3fbfd172caa 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -156,7 +156,7 @@ class InterfaceKindVisitor // // 3. If x is a struct, but a rvalue. // new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, // xQualType)) (x); -llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { +llvm::Expected Interpreter::ExtractValueFromExpr(Expr *E) { Sema &S = getCompilerInstance()->getSema(); ASTContext &Ctx = S.getASTContext(); From a2b51aaef84351ab8c8685220ed127b70ee26add Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Mon, 23 Sep 2024 07:53:59 +0000 Subject: [PATCH 5/5] Address more comments --- clang/include/clang/Interpreter/Interpreter.h | 3 +-- clang/lib/Interpreter/DeviceOffload.h | 2 +- clang/lib/Interpreter/Interpreter.cpp | 7 ------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index c44ce28376fa0..1230a3a7016fa 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -95,8 +95,7 @@ class Interpreter { // An optional parser for CUDA offloading std::unique_ptr DeviceParser; - /// List containing every information about every incrementally parsed piece - /// of code. + /// List containing information about each incrementally parsed piece of code. std::list PTUs; unsigned InitPTUSize = 0; diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h index e658be7f0ee22..b9a1acab004c3 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -49,7 +49,7 @@ class IncrementalCUDADeviceParser : public IncrementalParser { llvm::SmallString<1024> PTXCode; llvm::SmallVector FatbinContent; llvm::IntrusiveRefCntPtr VFS; - CodeGenOptions &CodeGenOpts; // intentionally a reference. + CodeGenOptions &CodeGenOpts; // Intentionally a reference. const TargetOptions &TargetOpts; }; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index e4cec6775df7a..bc96da811d44c 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -302,22 +302,15 @@ class IncrementalAction : public WrapperFrontendAction { CI.getFrontendOpts().ProgramAction); return Act; case frontend::ASTDump: - [[fallthrough]]; case frontend::ASTPrint: - [[fallthrough]]; case frontend::ParseSyntaxOnly: Act = CreateFrontendAction(CI); break; case frontend::PluginAction: - [[fallthrough]]; case frontend::EmitAssembly: - [[fallthrough]]; case frontend::EmitBC: - [[fallthrough]]; case frontend::EmitObj: - [[fallthrough]]; case frontend::PrintPreprocessedInput: - [[fallthrough]]; case frontend::EmitLLVMOnly: Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); break;