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..1230a3a7016fa 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,26 @@ 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 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 @@ -104,15 +105,18 @@ 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; + + /// 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, - std::unique_ptr JITBuilder = nullptr); + Interpreter(std::unique_ptr Instance, llvm::Error &Err, + std::unique_ptr JITBuilder = nullptr, + std::unique_ptr Consumer = nullptr); // Create the internal IncrementalExecutor, or re-create it after calling // ResetExecutor(). @@ -122,15 +126,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 +142,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 +163,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 +172,15 @@ class Interpreter { private: size_t getEffectivePTUSize() const; void markUserCodeStart(); + llvm::Expected ExtractValueFromExpr(Expr *E); + llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); + + 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. 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..1999d63d1aa04 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,15 +25,17 @@ namespace clang { IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( - Interpreter &Interp, std::unique_ptr Instance, - IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + std::unique_ptr DeviceInstance, + CompilerInstance &HostInstance, llvm::IntrusiveRefCntPtr FS, - llvm::Error &Err) - : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err), - HostParser(HostParser), VFS(FS) { + llvm::Error &Err, const std::list &PTUs) + : 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", @@ -41,7 +44,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( } } -llvm::Expected +llvm::Expected IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) { auto PTU = IncrementalParser::Parse(Input); if (!PTU) @@ -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 ce4f218c94c79..b9a1acab004c3 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -18,19 +18,24 @@ #include "llvm/Support/VirtualFileSystem.h" namespace clang { +struct PartialTranslationUnit; +class CompilerInstance; +class CodeGenOptions; +class TargetOptions; class IncrementalCUDADeviceParser : public IncrementalParser { + const std::list &PTUs; + public: IncrementalCUDADeviceParser( - Interpreter &Interp, std::unique_ptr Instance, - IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + std::unique_ptr DeviceInstance, + CompilerInstance &HostInstance, 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 @@ -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/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..e43cea1baf43a 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -13,246 +13,40 @@ #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; +// IncrementalParser::IncrementalParser() {} -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(CompilerInstance &Instance, llvm::Error &Err) - : CI(std::move(Instance)) { + : S(Instance.getSema()) { 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)); + Consumer = &S.getASTConsumer(); + P.reset(new Parser(S.getPreprocessor(), S, /*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(); llvm::CrashRecoveryContextCleanupRegistrar CleanupSema(&S); 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)) { @@ -276,11 +70,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { std::error_code()); } - DiagnosticsEngine &Diags = getCI()->getDiagnostics(); + DiagnosticsEngine &Diags = S.getDiagnostics(); if (Diags.hasErrorOccurred()) { - PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(), - nullptr}; - CleanUpPTU(MostRecentPTU); + CleanUpPTU(C.getTranslationUnitDecl()); Diags.Reset(/*soft=*/true); Diags.getClient()->clear(); @@ -299,12 +91,12 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { Consumer->HandleTranslationUnit(C); - return LastPTU; + return C.getTranslationUnitDecl(); } -llvm::Expected +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; @@ -320,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 @@ -356,37 +148,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(); @@ -415,13 +180,8 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { // 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); } } -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..4fdde749a2e75 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -13,37 +13,27 @@ #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 Sema; +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; + /// The Sema performing the incremental compilation. + Sema &S; /// Parser. std::unique_ptr P; @@ -54,42 +44,21 @@ 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(); + // IncrementalParser(); public: - IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, llvm::Error &Err); + IncrementalParser(CompilerInstance &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..bc96da811d44c 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,28 +244,173 @@ IncrementalCompilerBuilder::CreateCudaHost() { return IncrementalCompilerBuilder::createCuda(false); } -Interpreter::Interpreter(std::unique_ptr CI, +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; + + for (Decl *D : DGR) + if (auto *TLSD = llvm::dyn_cast(D)) + if (TLSD && TLSD->isSemiMissing()) { + auto ExprOrErr = + Interp.ExtractValueFromExpr(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: + case frontend::ASTPrint: + case frontend::ParseSyntaxOnly: + Act = CreateFrontendAction(CI); + break; + case frontend::PluginAction: + case frontend::EmitAssembly: + case frontend::EmitBC: + case frontend::EmitObj: + case frontend::PrintPreprocessedInput: + 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 Instance, llvm::Error &ErrOut, - std::unique_ptr JITBuilder) + 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)); - 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); - // Not all frontends support code-generation, e.g. ast-dump actions don't - if (IncrParser->getCodeGen()) { + ASTContext &C = CI->getASTContext(); + + IncrParser = std::make_unique(*CI, ErrOut); + + if (ErrOut) + return; + + 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 : IncrParser->getPTUs()) + for (PartialTranslationUnit &PTU : PTUs) if (llvm::Error Err = Execute(PTU)) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; @@ -271,6 +419,8 @@ Interpreter::Interpreter(std::unique_ptr CI, } Interpreter::~Interpreter() { + IncrParser.reset(); + Act->FinalizeAction(); if (IncrExecutor) { if (llvm::Error Err = IncrExecutor->cleanUp()) llvm::report_fatal_error( @@ -342,8 +492,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)->getCompilerInstance(), IMVFS, Err, + (*Interp)->PTUs); if (Err) return std::move(Err); @@ -353,12 +503,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) { @@ -379,22 +527,32 @@ 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; } +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 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 +560,12 @@ 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(); + + return RegisterPTU(*TuOrErr); } static llvm::Expected @@ -420,7 +583,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 +655,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 +681,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 +691,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 +713,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; - } - - return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S); -} - -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(); +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; } - - 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(); + return nullptr; } -// 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..3e3fbfd172caa --- /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::ExtractValueFromExpr(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 {