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,7 +48,8 @@ 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; 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 @@ -178,27 +178,10 @@ 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->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; + MostRecentPTU.TUPart = C.getTranslationUnitDecl(); + MostRecentPTU.TheModule = nullptr; + Restore(MostRecentPTU); // FIXME: Do not reset the pragma handlers. Diags.Reset(); @@ -293,6 +276,24 @@ return PTU; } +void IncrementalParser::Restore(PartialTranslationUnit &PTU) { + TranslationUnitDecl *MostRecentTU = PTU.TUPart; + TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); + if (StoredDeclsMap *Map = FirstTU->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 @@ -218,8 +218,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()) @@ -228,6 +227,10 @@ return llvm::Error::success(); } +void Interpreter::Restore(PartialTranslationUnit &PTU) { + IncrParser->Restore(PTU); +} + llvm::Expected Interpreter::getSymbolAddress(GlobalDecl GD) const { if (!IncrExecutor) @@ -257,3 +260,18 @@ return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName); } + +llvm::Error Interpreter::Undo(unsigned N) { + auto &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 (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 @@ -5,16 +5,14 @@ // 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 = 42; +%undo int x1 = 24; +auto r1 = printf("x1 = %d\n", x1); +// CHECK: x1 = 24 -struct S { float f = 1.0; S *m = nullptr;} s; +int foo() { return 1; } +%undo int foo() { return 2; } +auto r2 = 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 @@ -17,4 +17,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: "); }