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 @@ -49,6 +49,8 @@ std::unique_ptr TSCtx; std::unique_ptr IncrParser; std::unique_ptr IncrExecutor; + Expr *RuntimeInterface = nullptr; + bool PrettyPrintMode = false; Interpreter(std::unique_ptr CI, llvm::Error &Err); @@ -85,6 +87,26 @@ /// file. llvm::Expected getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; + + llvm::Expected CaptureExpr(llvm::StringRef Line); + + bool FindRuntimeInterface(); + + Expr *SynthesizeCall(clang::Expr *E); + + llvm::Expected GenPTU(clang::Expr *E); + + bool isPrettyPrintMode() const { return PrettyPrintMode; } + void enterPrettyPrintMode() { PrettyPrintMode = true; } + void exitPrettyPrintMode() { PrettyPrintMode = false; } + + struct PrettyPrintRAII { + Interpreter &Interp; + PrettyPrintRAII(Interpreter &I) : Interp(I) { + Interp.enterPrettyPrintMode(); + } + ~PrettyPrintRAII() { Interp.exitPrettyPrintMode(); } + }; }; } // namespace clang diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -12,6 +12,7 @@ IncrementalExecutor.cpp IncrementalParser.cpp Interpreter.cpp + PrettyPrint.cpp DEPENDS intrinsics_gen 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 @@ -16,7 +16,7 @@ #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/AST/GlobalDecl.h" - +#include "clang/Parse/Parser.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -32,7 +32,7 @@ class CompilerInstance; class IncrementalAction; class Parser; - +class Interpreter; /// Provides support for incremental compilation. Keeps track of the state /// changes between the subsequent incremental input. /// @@ -49,15 +49,15 @@ /// Consumer to process the produced top level decls. Owned by Act. ASTConsumer *Consumer = nullptr; - /// 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; + Interpreter &Interp; + public: - IncrementalParser(std::unique_ptr Instance, + IncrementalParser(Interpreter &Interp, + std::unique_ptr Instance, llvm::LLVMContext &LLVMCtx, llvm::Error &Err); ~IncrementalParser(); @@ -68,6 +68,8 @@ /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input. llvm::Expected Parse(llvm::StringRef Input); + llvm::Expected Parse(Parser::DeclGroupPtrTy D); + /// Uses the CodeGenModule mangled name cache and avoids recomputing. ///\returns the mangled name of a \c GD. llvm::StringRef GetMangledName(GlobalDecl GD) const; @@ -76,8 +78,8 @@ std::list &getPTUs() { return PTUs; } -private: - llvm::Expected ParseOrWrapTopLevelDecl(); + llvm::Expected + ParseOrWrapTopLevelDecl(Parser::DeclGroupPtrTy SynthesizedDecl = nullptr); }; } // end namespace clang 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 @@ -19,9 +19,9 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Interpreter/Interpreter.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" @@ -122,10 +122,11 @@ } }; -IncrementalParser::IncrementalParser(std::unique_ptr Instance, +IncrementalParser::IncrementalParser(Interpreter &Interp, + std::unique_ptr Instance, llvm::LLVMContext &LLVMCtx, llvm::Error &Err) - : CI(std::move(Instance)) { + : CI(std::move(Instance)), Interp(Interp) { llvm::ErrorAsOutParameter EAO(&Err); Act = std::make_unique(*CI, LLVMCtx, Err); if (Err) @@ -143,7 +144,8 @@ } llvm::Expected -IncrementalParser::ParseOrWrapTopLevelDecl() { +IncrementalParser::ParseOrWrapTopLevelDecl( + Parser::DeclGroupPtrTy SynthesizedDecl) { // Recover resources if we crash before exiting this method. Sema &S = CI->getSema(); llvm::CrashRecoveryContextCleanupRegistrar CleanupSema(&S); @@ -169,14 +171,24 @@ S.ActOnTranslationUnitScope(P->getCurScope()); } - Parser::DeclGroupPtrTy ADecl; - Sema::ModuleImportState ImportState; - for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; - AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { - if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) - return llvm::make_error("Parsing failed. " - "The consumer rejected a decl", - std::error_code()); + if (SynthesizedDecl) { + if (!Consumer->HandleTopLevelDecl(SynthesizedDecl.get())) { + return llvm::make_error( + "Parsing failed. " + "The consumer rejected the synthesized decl", + std::error_code()); + } + } else { + Parser::DeclGroupPtrTy ADecl; + Sema::ModuleImportState ImportState; + for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; + AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) + return llvm::make_error( + "Parsing failed. " + "The consumer rejected a decl", + std::error_code()); + } } DiagnosticsEngine &Diags = getCI()->getDiagnostics(); @@ -213,24 +225,41 @@ return static_cast(WrappedAct)->getCodeGenerator(); } -llvm::Expected -IncrementalParser::Parse(llvm::StringRef input) { - Preprocessor &PP = CI->getPreprocessor(); - assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); - +static std::unique_ptr MakeInputBuf(StringRef Input, + bool NeedExtraSemi) { + /// Counts the number of direct user input lines that have been parsed. + static unsigned InputCount = 0; std::ostringstream SourceName; SourceName << "input_line_" << InputCount++; - // Create an uninitialized memory buffer, copy code in and append "\n" - size_t InputSize = input.size(); // don't include trailing 0 + size_t InputSize = Input.size(); // don't include trailing 0 + size_t AllocSize = Input.size() + 1; + + // Space for the extra missing semi. + if (NeedExtraSemi) { + AllocSize += 1; + } + // MemBuffer size should *not* include terminating zero std::unique_ptr MB( - llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1, + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(AllocSize, SourceName.str())); char *MBStart = const_cast(MB->getBufferStart()); - memcpy(MBStart, input.data(), InputSize); - MBStart[InputSize] = '\n'; + memcpy(MBStart, Input.data(), InputSize); + if (NeedExtraSemi) { + MBStart[AllocSize - 2] = ';'; + } + MBStart[AllocSize - 1] = '\n'; + return MB; +} + +llvm::Expected +IncrementalParser::Parse(llvm::StringRef input) { + Preprocessor &PP = CI->getPreprocessor(); + assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); + std::unique_ptr MB = + MakeInputBuf(input, Interp.isPrettyPrintMode()); SourceManager &SM = CI->getSourceManager(); // FIXME: Create SourceLocation, which will allow clang to order the overload @@ -278,6 +307,24 @@ return PTU; } +llvm::Expected +IncrementalParser::Parse(Parser::DeclGroupPtrTy D) { + llvm::Expected PTUOrErr = + ParseOrWrapTopLevelDecl(D); + if (!PTUOrErr) { + return PTUOrErr.takeError(); + } + if (CodeGenerator *CG = getCodeGen(Act.get())) { + std::unique_ptr M(CG->ReleaseModule()); + CG->StartModule("incr_module_" + std::to_string(PTUs.size()), + M->getContext()); + + PTUOrErr.get().TheModule = std::move(M); + } + + return PTUOrErr; +} + void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { TranslationUnitDecl *MostRecentTU = PTU.TUPart; TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); 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 @@ -15,8 +15,8 @@ #include "IncrementalExecutor.h" #include "IncrementalParser.h" - #include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" @@ -28,9 +28,13 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" #include "llvm/IR/Module.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" using namespace clang; @@ -176,7 +180,7 @@ llvm::ErrorAsOutParameter EAO(&Err); auto LLVMCtx = std::make_unique(); TSCtx = std::make_unique(std::move(LLVMCtx)); - IncrParser = std::make_unique(std::move(CI), + IncrParser = std::make_unique(*this, std::move(CI), *TSCtx->getContext(), Err); } @@ -283,3 +287,82 @@ } return llvm::Error::success(); } + +llvm::Expected Interpreter::CaptureExpr(llvm::StringRef Line) { + auto PTU = Parse(Line); + if (!PTU) + return PTU.takeError(); + for (auto *D : PTU->TUPart->decls()) { + if (const auto *S = llvm::dyn_cast(D)) { + if (const auto *E = llvm::dyn_cast(S->getStmt())) { + return const_cast(E); + } + } + } + return llvm::make_error("Can not capture any Expr*!", + std::error_code()); +} + +llvm::Expected Interpreter::GenPTU(clang::Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + SmallVector DeclsInGroup; + DeclsInGroup.push_back(S.ActOnTopLevelStmtDecl(E)); + Sema::DeclGroupPtrTy DeclGroupPtr = S.BuildDeclaratorGroup(DeclsInGroup); + return IncrParser->Parse(DeclGroupPtr); +} + +static constexpr llvm::StringRef MagicRuntimeInterface = + "__InterpreterPrettyPrint"; + +bool Interpreter::FindRuntimeInterface() { + if (RuntimeInterface) + return true; + + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + LookupResult R(S, &Ctx.Idents.get(MagicRuntimeInterface), SourceLocation(), + Sema::LookupOrdinaryName, Sema::ForVisibleRedeclaration); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + if (R.empty()) { + return false; + } + CXXScopeSpec CSS; + RuntimeInterface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); + return true; +} + +static IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uintptr_t Ptr) { + const llvm::APInt Addr(8 * sizeof(void *), Ptr); + return IntegerLiteral::Create(C, Addr, C.getUIntPtrType(), SourceLocation()); +} + +static Expr *CStyleCastPtrExpr(Sema *S, QualType Ty, Expr *E) { + ASTContext &Ctx = S->getASTContext(); + if (!Ty->isPointerType()) + Ty = Ctx.getPointerType(Ty); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + Expr *Result = + S->BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E).get(); + assert(Result && "Cannot create CStyleCastPtrExpr"); + return Result; +} + +static Expr *CStyleCastPtrExpr(Sema *S, QualType Ty, uintptr_t Ptr) { + ASTContext &Ctx = S->getASTContext(); + return CStyleCastPtrExpr(S, Ty, IntegerLiteralExpr(Ctx, Ptr)); +} + +Expr *Interpreter::SynthesizeCall(clang::Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + if (!FindRuntimeInterface()) { + llvm_unreachable("We can't find the runtime iterface for pretty print!"); + } + Expr *TypeArg = CStyleCastPtrExpr(&S, S.getASTContext().VoidPtrTy, + (uintptr_t)E->getType().getAsOpaquePtr()); + llvm::SmallVector Args = {TypeArg, E}; + ExprResult Result = S.ActOnCallExpr(/*Scope=*/nullptr, RuntimeInterface, + E->getBeginLoc(), Args, E->getEndLoc()); + assert(!Result.isInvalid() && "Failed to generate the CallExpr!"); + return Result.get(); +} diff --git a/clang/lib/Interpreter/PrettyPrint.cpp b/clang/lib/Interpreter/PrettyPrint.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Interpreter/PrettyPrint.cpp @@ -0,0 +1,37 @@ +#include "clang/AST/Type.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +static void PrintType(void *OpaqueType) { + QualType *QT = reinterpret_cast(OpaqueType); + assert(QT && "type can't be null during value pretty printing"); + llvm::errs() << "[" << QT->getAsString() << "]"; +} + +template static void PrintImpl(void *OpaqueType, T Val) { + PrintType(OpaqueType); + llvm::errs() << ": "; + llvm::errs() << Val; + llvm::errs() << "\n"; +} + +LLVM_EXTERNAL_VISIBILITY void __InterpreterPrettyPrint(void *OpaqueType, + char Val) { + PrintImpl(OpaqueType, Val); +} + +LLVM_EXTERNAL_VISIBILITY void __InterpreterPrettyPrint(void *OpaqueType, + int Val) { + PrintImpl(OpaqueType, Val); +} + +LLVM_EXTERNAL_VISIBILITY void __InterpreterPrettyPrint(void *OpaqueType, + float Val) { + PrintImpl(OpaqueType, Val); +} + +LLVM_EXTERNAL_VISIBILITY void __InterpreterPrettyPrint(void *OpaqueType, + double Val) { + PrintImpl(OpaqueType, Val); +} diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt --- a/clang/tools/clang-repl/CMakeLists.txt +++ b/clang/tools/clang-repl/CMakeLists.txt @@ -12,6 +12,7 @@ ) clang_target_link_libraries(clang-repl PRIVATE + clangAST clangBasic clangFrontend clangInterpreter 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 @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/Decl.h" #include "clang/Basic/Diagnostic.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -18,6 +19,8 @@ #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/LineEditor/LineEditor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" // llvm_shutdown #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" // llvm::Initialize* @@ -65,6 +68,18 @@ return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS; } +static void DeclareMagicFunctions(clang::Interpreter &Interp) { + llvm::ArrayRef MagicFunctions = { + "void __InterpreterPrettyPrint(void*, char);", + "void __InterpreterPrettyPrint(void*, int);", + "void __InterpreterPrettyPrint(void*, float);", + "void __InterpreterPrettyPrint(void*, double);", + }; + for (llvm::StringRef Function : MagicFunctions) { + llvm::cantFail(Interp.ParseAndExecute(Function)); + } +} + llvm::ExitOnError ExitOnErr; int main(int argc, const char **argv) { ExitOnErr.setBanner("clang-repl: "); @@ -109,21 +124,57 @@ bool HasError = false; + DeclareMagicFunctions(*Interp); if (OptInputs.empty()) { llvm::LineEditor LE("clang-repl"); // FIXME: Add LE.setListCompleter while (llvm::Optional Line = LE.readLine()) { - if (*Line == R"(%quit)") + llvm::StringRef Code = *Line; + if (Code.empty()) { + continue; + } + if (Code == R"(%quit)") break; - if (*Line == R"(%undo)") { + if (Code == R"(%undo)") { if (auto Err = Interp->Undo()) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); HasError = true; } continue; } + // TODO: Find a more reliable way to determine if this is something we + // need to prettty print. + if (!Code.starts_with("//") && !Code.starts_with("#") && + !Code.ends_with("}") && !Code.ends_with(";")) { + clang::Interpreter::PrettyPrintRAII X(*Interp); + // Capture the expression we want to print. + auto ExprOrErr = Interp->CaptureExpr(Code); + if (!ExprOrErr) { + llvm::logAllUnhandledErrors(ExprOrErr.takeError(), llvm::errs(), + "error: "); + HasError = true; + } else { + // Synthesize a CallExpr to `__InterpreterPrettyPrint`. + clang::Expr *E = Interp->SynthesizeCall(ExprOrErr.get()); + // Generate a PartialTranslationUnit from the CallExpr. + llvm::Expected SynthesizedPTUOrErr = + Interp->GenPTU(E); + if (!SynthesizedPTUOrErr) { + llvm::logAllUnhandledErrors(SynthesizedPTUOrErr.takeError(), + llvm::errs(), "error: "); + HasError = true; + } + // Let JIT handle all the rest. + if (auto Err = Interp->Execute(SynthesizedPTUOrErr.get())) { + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), + "error: "); + HasError = true; + } + } + continue; + } - if (auto Err = Interp->ParseAndExecute(*Line)) { + if (auto Err = Interp->ParseAndExecute(Code)) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); HasError = true; }