diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -69,6 +69,12 @@ return llvm::Error::success(); } + void Restore(PartialTranslationUnit &PTU); + + /// Undo previous parse results for N times. It'll stop and report an error + /// if an error occurs. + llvm::Error Undo(unsigned N = 1); + /// \returns the \c JITTargetAddress of a \c GlobalDecl. This interface uses /// the CodeGenModule's internal mangling cache to avoid recomputing the /// mangled name. diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H #define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" @@ -29,11 +30,17 @@ } // namespace llvm namespace clang { + +struct PartialTranslationUnit; + class IncrementalExecutor { using CtorDtorIterator = llvm::orc::CtorDtorIterator; std::unique_ptr Jit; llvm::orc::ThreadSafeContext &TSCtx; + llvm::DenseMap + ResourceTrackers; + public: enum SymbolNameKind { IRName, LinkerName }; @@ -41,11 +48,14 @@ const llvm::Triple &Triple); ~IncrementalExecutor(); - llvm::Error addModule(std::unique_ptr M); + llvm::Error addModule(PartialTranslationUnit &PTU); + llvm::Error removeModule(PartialTranslationUnit &PTU); llvm::Error runCtors() const; llvm::Expected getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const; llvm::orc::LLJIT *getExecutionEngine() const { return Jit.get(); } + + std::size_t getAvailableModuleSize() const { return ResourceTrackers.size(); } }; } // end namespace clang diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -12,6 +12,7 @@ #include "IncrementalExecutor.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" @@ -52,8 +53,24 @@ IncrementalExecutor::~IncrementalExecutor() {} -llvm::Error IncrementalExecutor::addModule(std::unique_ptr M) { - return Jit->addIRModule(llvm::orc::ThreadSafeModule(std::move(M), TSCtx)); +llvm::Error IncrementalExecutor::addModule(PartialTranslationUnit &PTU) { + llvm::orc::ResourceTrackerSP RT = + Jit->getMainJITDylib().createResourceTracker(); + ResourceTrackers[&PTU] = RT; + + return Jit->addIRModule(RT, {std::move(PTU.TheModule), TSCtx}); +} + +llvm::Error IncrementalExecutor::removeModule(PartialTranslationUnit &PTU) { + + llvm::orc::ResourceTrackerSP RT = std::move(ResourceTrackers[&PTU]); + if (!RT) + return llvm::Error::success(); + + ResourceTrackers.erase(&PTU); + if (llvm::Error Err = RT->remove()) + return Err; + return llvm::Error::success(); } llvm::Error IncrementalExecutor::runCtors() const { diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -72,6 +72,10 @@ ///\returns the mangled name of a \c GD. llvm::StringRef GetMangledName(GlobalDecl GD) const; + void Restore(PartialTranslationUnit &PTU); + + std::list &getPTUs() { return PTUs; } + private: llvm::Expected ParseOrWrapTopLevelDecl(); }; diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -181,27 +181,9 @@ DiagnosticsEngine &Diags = getCI()->getDiagnostics(); if (Diags.hasErrorOccurred()) { - TranslationUnitDecl *MostRecentTU = C.getTranslationUnitDecl(); - TranslationUnitDecl *PreviousTU = MostRecentTU->getPreviousDecl(); - assert(PreviousTU && "Must have a TU from the ASTContext initialization!"); - TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); - assert(FirstTU); - FirstTU->RedeclLink.setLatest(PreviousTU); - C.TUDecl = PreviousTU; - S.TUScope->setEntity(PreviousTU); - - // Clean up the lookup table - if (StoredDeclsMap *Map = PreviousTU->getPrimaryContext()->getLookupPtr()) { - for (auto I = Map->begin(); I != Map->end(); ++I) { - StoredDeclsList &List = I->second; - DeclContextLookupResult R = List.getLookupResult(); - for (NamedDecl *D : R) - if (D->getTranslationUnitDecl() == MostRecentTU) - List.remove(D); - if (List.isNull()) - Map->erase(I); - } - } + PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(), + nullptr}; + Restore(MostRecentPTU); // FIXME: Do not reset the pragma handlers. Diags.Reset(); @@ -296,6 +278,24 @@ return PTU; } +void IncrementalParser::Restore(PartialTranslationUnit &PTU) { + TranslationUnitDecl *MostRecentTU = PTU.TUPart; + TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); + if (StoredDeclsMap *Map = FirstTU->getPrimaryContext()->getLookupPtr()) { + for (auto I = Map->begin(); I != Map->end(); ++I) { + StoredDeclsList &List = I->second; + DeclContextLookupResult R = List.getLookupResult(); + for (NamedDecl *D : R) { + if (D->getTranslationUnitDecl() == MostRecentTU) { + List.remove(D); + } + } + if (List.isNull()) + Map->erase(I); + } + } +} + llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { CodeGenerator *CG = getCodeGen(Act.get()); assert(CG); diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -221,8 +221,7 @@ if (Err) return Err; } - // FIXME: Add a callback to retain the llvm::Module once the JIT is done. - if (auto Err = IncrExecutor->addModule(std::move(T.TheModule))) + if (auto Err = IncrExecutor->addModule(T)) return Err; if (auto Err = IncrExecutor->runCtors()) @@ -231,6 +230,10 @@ return llvm::Error::success(); } +void Interpreter::Restore(PartialTranslationUnit &PTU) { + IncrParser->Restore(PTU); +} + llvm::Expected Interpreter::getSymbolAddress(GlobalDecl GD) const { if (!IncrExecutor) @@ -260,3 +263,22 @@ return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName); } + +llvm::Error Interpreter::Undo(unsigned N) { + + std::list &PTUs = IncrParser->getPTUs(); + if (N > PTUs.size()) + return llvm::make_error("Operation failed. " + "Too many undos", + std::error_code()); + for (unsigned I = 0; I < N; I++) { + if (IncrExecutor) { + if (llvm::Error Err = IncrExecutor->removeModule(PTUs.back())) + return Err; + } + + Restore(PTUs.back()); + PTUs.pop_back(); + } + return llvm::Error::success(); +} diff --git a/clang/test/Interpreter/execute.cpp b/clang/test/Interpreter/code-undo.cpp copy from clang/test/Interpreter/execute.cpp copy to clang/test/Interpreter/code-undo.cpp --- a/clang/test/Interpreter/execute.cpp +++ b/clang/test/Interpreter/code-undo.cpp @@ -1,4 +1,3 @@ -// RUN: clang-repl "int x = 10;" "int y=7; err;" "int y = 10;" // RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \ // RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s // REQUIRES: host-supports-jit @@ -6,16 +5,19 @@ // CHECK-DRIVER: i = 10 // RUN: cat %s | clang-repl | FileCheck %s extern "C" int printf(const char *, ...); -int i = 42; -auto r1 = printf("i = %d\n", i); -// CHECK: i = 42 +int x1 = 0; +int x2 = 42; +%undo +int x2 = 24; +auto r1 = printf("x1 = %d\n", x1); +// CHECK: x1 = 0 +auto r2 = printf("x2 = %d\n", x2); +// CHECK-NEXT: x2 = 24 -struct S { float f = 1.0; S *m = nullptr;} s; +int foo() { return 1; } +%undo +int foo() { return 2; } +auto r3 = printf("foo() = %d\n", foo()); +// CHECK-NEXT: foo() = 2 -auto r2 = printf("S[f=%f, m=0x%llx]\n", s.f, reinterpret_cast(s.m)); -// CHECK-NEXT: S[f=1.000000, m=0x0] - -inline int foo() { return 42; } -int r3 = foo(); - -quit +%quit diff --git a/clang/test/Interpreter/execute.cpp b/clang/test/Interpreter/execute.cpp --- a/clang/test/Interpreter/execute.cpp +++ b/clang/test/Interpreter/execute.cpp @@ -18,4 +18,4 @@ inline int foo() { return 42; } int r3 = foo(); -quit +%quit diff --git a/clang/test/Interpreter/plugins.cpp b/clang/test/Interpreter/plugins.cpp --- a/clang/test/Interpreter/plugins.cpp +++ b/clang/test/Interpreter/plugins.cpp @@ -6,8 +6,7 @@ int i = 10; extern "C" int printf(const char*,...); auto r1 = printf("i = %d\n", i); -quit - +%quit // CHECK: top-level-decl: "i" // CHECK-NEXT: top-level-decl: "r1" diff --git a/clang/test/Interpreter/sanity.c b/clang/test/Interpreter/sanity.c --- a/clang/test/Interpreter/sanity.c +++ b/clang/test/Interpreter/sanity.c @@ -15,4 +15,4 @@ // CHECK-NEXT: UnaryOperator{{.*}} 'int' lvalue prefix '++' // CHECK-NEXT: DeclRefExpr{{.*}} 'int' lvalue Var [[var_ptr]] 'TestVar' 'int' -quit +%quit diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -92,8 +92,14 @@ llvm::LineEditor LE("clang-repl"); // FIXME: Add LE.setListCompleter while (llvm::Optional Line = LE.readLine()) { - if (*Line == "quit") + if (*Line == R"(%quit)") break; + if (*Line == R"(%undo)") { + if (auto Err = Interp->Undo()) + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); + continue; + } + if (auto Err = Interp->ParseAndExecute(*Line)) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -128,6 +128,50 @@ EXPECT_EQ("Parsing failed.", llvm::toString(std::move(Err))); } +TEST(InterpreterTest, UndoCommand) { + Args ExtraArgs = {"-Xclang", "-diagnostic-log-file", "-Xclang", "-"}; + + // Create the diagnostic engine with unowned consumer. + std::string DiagnosticOutput; + llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); + auto DiagPrinter = std::make_unique( + DiagnosticsOS, new DiagnosticOptions()); + + auto Interp = createInterpreter(ExtraArgs, DiagPrinter.get()); + + // Fail to undo. + auto Err1 = Interp->Undo(); + EXPECT_EQ("Operation failed. Too many undos", + llvm::toString(std::move(Err1))); + auto Err2 = Interp->Parse("int foo = 42;"); + EXPECT_TRUE(!!Err2); + auto Err3 = Interp->Undo(2); + EXPECT_EQ("Operation failed. Too many undos", + llvm::toString(std::move(Err3))); + + // Succeed to undo. + auto Err4 = Interp->Parse("int x = 42;"); + EXPECT_TRUE(!!Err4); + auto Err5 = Interp->Undo(); + EXPECT_FALSE(!!Err5); + auto Err6 = Interp->Parse("int x = 24;"); + EXPECT_TRUE(!!Err6); + auto Err7 = Interp->Parse("#define X 42"); + EXPECT_TRUE(!!Err7); + auto Err8 = Interp->Undo(); + EXPECT_FALSE(!!Err8); + auto Err9 = Interp->Parse("#define X 24"); + EXPECT_TRUE(!!Err9); + + // Undo input contains errors. + auto Err10 = Interp->Parse("int y = ;"); + EXPECT_FALSE(!!Err10); + auto Err11 = Interp->Parse("int y = 42;"); + EXPECT_TRUE(!!Err11); + auto Err12 = Interp->Undo(); + EXPECT_FALSE(!!Err12); +} + static std::string MangleName(NamedDecl *ND) { ASTContext &C = ND->getASTContext(); std::unique_ptr MangleC(C.createMangleContext());