diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index f2fdb90f5ba487..fd22af9766135f 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -69,6 +69,9 @@ class Interpreter { return llvm::Error::success(); } + /// Undo N previous incremental inputs. + 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.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index c5ed9b0fb45716..2445ba906a4c25 100644 --- 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" @@ -58,8 +59,24 @@ llvm::Error IncrementalExecutor::cleanUp() { return Jit->deinitialize(Jit->getMainJITDylib()); } -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/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index 75c181d76302a5..fd93b1d3903575 100644 --- 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 @@ class ThreadSafeContext; } // 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,7 +48,8 @@ class IncrementalExecutor { 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::Error cleanUp(); llvm::Expected diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index 42cda512a8ae3f..db854c4161b4fc 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -181,27 +181,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { 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}; + CleanUpPTU(MostRecentPTU); Diags.Reset(/*soft=*/true); Diags.getClient()->clear(); @@ -296,6 +278,24 @@ IncrementalParser::Parse(llvm::StringRef input) { return PTU; } +void IncrementalParser::CleanUpPTU(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/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index d1f454f2123949..8e45d6b5931bc2 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -72,6 +72,10 @@ class IncrementalParser { ///\returns the mangled name of a \c GD. llvm::StringRef GetMangledName(GlobalDecl GD) const; + void CleanUpPTU(PartialTranslationUnit &PTU); + + std::list &getPTUs() { return PTUs; } + private: llvm::Expected ParseOrWrapTopLevelDecl(); }; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index c3bbfcfec8cbea..0ffb40c217cd9a 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -229,7 +229,7 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { 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()) @@ -267,3 +267,22 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { 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; + } + + IncrParser->CleanUpPTU(PTUs.back()); + PTUs.pop_back(); + } + return llvm::Error::success(); +} diff --git a/clang/test/Interpreter/code-undo.cpp b/clang/test/Interpreter/code-undo.cpp new file mode 100644 index 00000000000000..d825460f3b4a42 --- /dev/null +++ b/clang/test/Interpreter/code-undo.cpp @@ -0,0 +1,23 @@ +// 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 +// UNSUPPORTED: system-aix +// CHECK-DRIVER: i = 10 +// RUN: cat %s | clang-repl | FileCheck %s +extern "C" int printf(const char *, ...); +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 + +int foo() { return 1; } +%undo +int foo() { return 2; } +auto r3 = printf("foo() = %d\n", foo()); +// CHECK-NEXT: foo() = 2 + +%quit diff --git a/clang/test/Interpreter/execute.cpp b/clang/test/Interpreter/execute.cpp index 4f01f8349be2fd..914a9285117e05 100644 --- a/clang/test/Interpreter/execute.cpp +++ b/clang/test/Interpreter/execute.cpp @@ -22,4 +22,4 @@ int r3 = foo(); struct D { float f = 1.0; D *m = nullptr; D(){} ~D() { printf("D[f=%f, m=0x%llx]\n", f, reinterpret_cast(m)); }} d; // CHECK: D[f=1.000000, m=0x0] -quit +%quit diff --git a/clang/test/Interpreter/plugins.cpp b/clang/test/Interpreter/plugins.cpp index 032f704624ad74..9faa7e96c33297 100644 --- 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 index f77faaf6748b48..8893a12f9216a5 100644 --- a/clang/test/Interpreter/sanity.c +++ b/clang/test/Interpreter/sanity.c @@ -15,4 +15,4 @@ void TestFunc() { ++TestVar; } // 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 index 271ec2695789e4..d3253738c6da19 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -111,8 +111,14 @@ int main(int argc, const char **argv) { 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 index 280c6d7fdae2b6..720e30fafca7e0 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -128,6 +128,51 @@ TEST(InterpreterTest, DeclsAndStatements) { 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); + EXPECT_EQ("Parsing failed.", llvm::toString(Err10.takeError())); + 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());