Index: clang/docs/ClangFormattedStatus.rst =================================================================== --- clang/docs/ClangFormattedStatus.rst +++ clang/docs/ClangFormattedStatus.rst @@ -59,11 +59,6 @@ - `1` - `0` - :good:`100%` - * - clang/examples/clang-interpreter - - `1` - - `0` - - `1` - - :none:`0%` * - clang/examples/PrintFunctionNames - `1` - `0` Index: clang/examples/CMakeLists.txt =================================================================== --- clang/examples/CMakeLists.txt +++ clang/examples/CMakeLists.txt @@ -3,7 +3,6 @@ set(EXCLUDE_FROM_ALL ON) endif() -add_subdirectory(clang-interpreter) add_subdirectory(PrintFunctionNames) add_subdirectory(AnnotateFunctions) add_subdirectory(Attribute) Index: clang/examples/clang-interpreter/CMakeLists.txt =================================================================== --- clang/examples/clang-interpreter/CMakeLists.txt +++ /dev/null @@ -1,93 +0,0 @@ -set(LLVM_LINK_COMPONENTS - Core - ExecutionEngine - MC - MCJIT - Object - OrcJit - Option - RuntimeDyld - Support - native - ) - -add_clang_executable(clang-interpreter - main.cpp - ) - -add_dependencies(clang-interpreter - clang-resource-headers - ) - -clang_target_link_libraries(clang-interpreter - PRIVATE - clangBasic - clangCodeGen - clangDriver - clangFrontend - clangSerialization - ) - -export_executable_symbols(clang-interpreter) - -if (MSVC) - # Is this a CMake bug that even with export_executable_symbols, Windows - # needs to explictly export the type_info vtable - set_property(TARGET clang-interpreter - APPEND_STRING PROPERTY LINK_FLAGS " /EXPORT:??_7type_info@@6B@") -endif() - -function(clang_enable_exceptions TARGET) - # Really have to jump through hoops to enable exception handling independent - # of how LLVM is being built. - if (NOT LLVM_REQUIRES_EH AND NOT LLVM_REQUIRES_RTTI) - if (MSVC) - # /EHs to allow throwing from extern "C" - set(excptnExceptions_ON "/D _HAS_EXCEPTIONS=1 /EHs /wd4714") - set(excptnExceptions_OFF "/D _HAS_EXCEPTIONS=0 /EHs-c-") - set(excptnRTTI_ON "/GR") - set(excptnRTTI_OFF "/GR-") - set(excptnEHRTTIRegEx "(/EHs(-c-?)|_HAS_EXCEPTIONS=(0|1))") - else() - set(excptnExceptions_ON "-fexceptions") - set(excptnExceptions_OFF "-fno-exceptions") - set(excptnRTTI_ON "-frtti") - set(excptnRTTI_OFF "-fno-rtti") - set(excptnEHRTTIRegEx "-f(exceptions|no-exceptions)") - endif() - if (LLVM_REQUIRES_EH) - set(excptnExceptions_DFLT ${excptnExceptions_ON}) - else() - set(excptnExceptions_DFLT ${excptnExceptions_OFF}) - endif() - if (LLVM_REQUIRES_RTTI) - set(excptnRTTI_DFLT ${excptnRTTI_ON}) - else() - set(excptnRTTI_DFLT ${excptnRTTI_OFF}) - endif() - - # Strip the exception & rtti flags from the target - get_property(addedFlags TARGET ${TARGET} PROPERTY COMPILE_FLAGS) - string(REGEX REPLACE ${excptnEHRTTIRegEx} "" editedFlags "${addedFlags}") - string(REPLACE ${excptnRTTI_OFF} "" editedFlags "${editedFlags}") - set_property(TARGET ${TARGET} PROPERTY COMPILE_FLAGS "${editedFlags}") - - get_property(addedFlags TARGET ${TARGET} PROPERTY COMPILE_DEFINITIONS) - string(REGEX REPLACE ${excptnEHRTTIRegEx} "" editedFlags "${addedFlags}") - string(REPLACE ${excptnRTTI_OFF} "" editedFlags "${editedFlags}") - set_property(TARGET ${TARGET} PROPERTY COMPILE_DEFINITIONS "${editedFlags}") - - # Re-add the exception & rtti flags from LLVM - set_property(SOURCE main.cpp APPEND_STRING PROPERTY COMPILE_FLAGS - " ${excptnExceptions_DFLT} ${excptnRTTI_DFLT} ") - set_property(SOURCE Manager.cpp APPEND_STRING PROPERTY COMPILE_FLAGS - " ${excptnExceptions_DFLT} ${excptnRTTI_DFLT} ") - - # Invoke with exceptions & rtti - set_property(SOURCE Invoke.cpp APPEND_STRING PROPERTY COMPILE_FLAGS - " ${excptnExceptions_ON} ${excptnRTTI_ON} ") - - endif() -endfunction(clang_enable_exceptions) - -clang_enable_exceptions(clang-interpreter) Index: clang/examples/clang-interpreter/README.txt =================================================================== --- clang/examples/clang-interpreter/README.txt +++ /dev/null @@ -1,20 +0,0 @@ -This is an example of Clang based interpreter, for executing standalone C/C++ -programs. - -It demonstrates the following features: - 1. Parsing standard compiler command line arguments using the Driver library. - - 2. Constructing a Clang compiler instance, using the appropriate arguments - derived in step #1. - - 3. Invoking the Clang compiler to lex, parse, syntax check, and then generate - LLVM code. - - 4. Use the LLVM JIT functionality to execute the final module. - - 5. Intercepting a Win64 library call to allow throwing and catching exceptions - in and from the JIT. - -The implementation has many limitations and is not designed to be a full fledged -interpreter. It is designed to demonstrate a simple but functional use of the -Clang compiler libraries. Index: clang/examples/clang-interpreter/Test.cxx =================================================================== --- clang/examples/clang-interpreter/Test.cxx +++ /dev/null @@ -1,33 +0,0 @@ -//===-- examples/clang-interpreter/Test.cxx - Clang C Interpreter Example -===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// Example throwing in and from the JIT (particularly on Win64). -// -// ./bin/clang-interpreter /tools/clang/examples/clang-interpreter/Test.cxx - -#include -#include - -static void ThrowerAnError(const char* Name) { - throw std::runtime_error(Name); -} - -int main(int argc, const char** argv) { - for (int I = 0; I < argc; ++I) - printf("arg[%d]='%s'\n", I, argv[I]); - - try { - ThrowerAnError("In JIT"); - } catch (const std::exception& E) { - printf("Caught: '%s'\n", E.what()); - } catch (...) { - printf("Unknown exception\n"); - } - ThrowerAnError("From JIT"); - return 0; -} Index: clang/examples/clang-interpreter/main.cpp =================================================================== --- clang/examples/clang-interpreter/main.cpp +++ /dev/null @@ -1,234 +0,0 @@ -//===-- examples/clang-interpreter/main.cpp - Clang C Interpreter Example -===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/DiagnosticOptions.h" -#include "clang/CodeGen/CodeGenAction.h" -#include "clang/Driver/Compilation.h" -#include "clang/Driver/Driver.h" -#include "clang/Driver/Tool.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/CompilerInvocation.h" -#include "clang/Frontend/FrontendDiagnostic.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ExecutionEngine/ExecutionEngine.h" -#include "llvm/ExecutionEngine/Orc/CompileUtils.h" -#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" -#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" -#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" -#include "llvm/ExecutionEngine/SectionMemoryManager.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Mangler.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Target/TargetMachine.h" - -using namespace clang; -using namespace clang::driver; - -// This function isn't referenced outside its translation unit, but it -// can't use the "static" keyword because its address is used for -// GetMainExecutable (since some platforms don't support taking the -// address of main, and some platforms can't implement GetMainExecutable -// without being given the address of a function in the main executable). -std::string GetExecutablePath(const char *Argv0, void *MainAddr) { - return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); -} - -namespace llvm { -namespace orc { - -class SimpleJIT { -private: - ExecutionSession ES; - std::unique_ptr TM; - const DataLayout DL; - MangleAndInterner Mangle{ES, DL}; - JITDylib &MainJD{ES.createBareJITDylib("
")}; - RTDyldObjectLinkingLayer ObjectLayer{ES, createMemMgr}; - IRCompileLayer CompileLayer{ES, ObjectLayer, - std::make_unique(*TM)}; - - static std::unique_ptr createMemMgr() { - return std::make_unique(); - } - - SimpleJIT( - std::unique_ptr TM, DataLayout DL, - std::unique_ptr ProcessSymbolsGenerator) - : TM(std::move(TM)), DL(std::move(DL)) { - llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); - MainJD.addGenerator(std::move(ProcessSymbolsGenerator)); - } - -public: - ~SimpleJIT() { - if (auto Err = ES.endSession()) - ES.reportError(std::move(Err)); - } - - static Expected> Create() { - auto JTMB = JITTargetMachineBuilder::detectHost(); - if (!JTMB) - return JTMB.takeError(); - - auto TM = JTMB->createTargetMachine(); - if (!TM) - return TM.takeError(); - - auto DL = (*TM)->createDataLayout(); - - auto ProcessSymbolsGenerator = - DynamicLibrarySearchGenerator::GetForCurrentProcess( - DL.getGlobalPrefix()); - - if (!ProcessSymbolsGenerator) - return ProcessSymbolsGenerator.takeError(); - - return std::unique_ptr(new SimpleJIT( - std::move(*TM), std::move(DL), std::move(*ProcessSymbolsGenerator))); - } - - const TargetMachine &getTargetMachine() const { return *TM; } - - Error addModule(ThreadSafeModule M) { - return CompileLayer.add(MainJD, std::move(M)); - } - - Expected findSymbol(const StringRef &Name) { - return ES.lookup({&MainJD}, Mangle(Name)); - } - - Expected getSymbolAddress(const StringRef &Name) { - auto Sym = findSymbol(Name); - if (!Sym) - return Sym.takeError(); - return Sym->getAddress(); - } -}; - -} // end namespace orc -} // end namespace llvm - -llvm::ExitOnError ExitOnErr; - -int main(int argc, const char **argv) { - // This just needs to be some symbol in the binary; C++ doesn't - // allow taking the address of ::main however. - void *MainAddr = (void*) (intptr_t) GetExecutablePath; - std::string Path = GetExecutablePath(argv[0], MainAddr); - IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); - TextDiagnosticPrinter *DiagClient = - new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); - - IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); - DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); - - const std::string TripleStr = llvm::sys::getProcessTriple(); - llvm::Triple T(TripleStr); - - // Use ELF on Windows-32 and MingW for now. -#ifndef CLANG_INTERPRETER_COFF_FORMAT - if (T.isOSBinFormatCOFF()) - T.setObjectFormat(llvm::Triple::ELF); -#endif - - ExitOnErr.setBanner("clang interpreter"); - - Driver TheDriver(Path, T.str(), Diags); - TheDriver.setTitle("clang interpreter"); - TheDriver.setCheckInputsExist(false); - - // FIXME: This is a hack to try to force the driver to do something we can - // recognize. We need to extend the driver library to support this use model - // (basically, exactly one input, and the operation mode is hard wired). - SmallVector Args(argv, argv + argc); - Args.push_back("-fsyntax-only"); - std::unique_ptr C(TheDriver.BuildCompilation(Args)); - if (!C) - return 0; - - // FIXME: This is copied from ASTUnit.cpp; simplify and eliminate. - - // We expect to get back exactly one command job, if we didn't something - // failed. Extract that job from the compilation. - const driver::JobList &Jobs = C->getJobs(); - if (Jobs.size() != 1 || !isa(*Jobs.begin())) { - SmallString<256> Msg; - llvm::raw_svector_ostream OS(Msg); - Jobs.Print(OS, "; ", true); - Diags.Report(diag::err_fe_expected_compiler_job) << OS.str(); - return 1; - } - - const driver::Command &Cmd = cast(*Jobs.begin()); - if (llvm::StringRef(Cmd.getCreator().getName()) != "clang") { - Diags.Report(diag::err_fe_expected_clang_command); - return 1; - } - - // Initialize a compiler invocation object from the clang (-cc1) arguments. - const llvm::opt::ArgStringList &CCArgs = Cmd.getArguments(); - std::unique_ptr CI(new CompilerInvocation); - CompilerInvocation::CreateFromArgs(*CI, CCArgs, Diags); - - // Show the invocation, with -v. - if (CI->getHeaderSearchOpts().Verbose) { - llvm::errs() << "clang invocation:\n"; - Jobs.Print(llvm::errs(), "\n", true); - llvm::errs() << "\n"; - } - - // FIXME: This is copied from cc1_main.cpp; simplify and eliminate. - - // Create a compiler instance to handle the actual work. - CompilerInstance Clang; - Clang.setInvocation(std::move(CI)); - - // Create the compilers actual diagnostics engine. - Clang.createDiagnostics(); - if (!Clang.hasDiagnostics()) - return 1; - - // Infer the builtin include path if unspecified. - if (Clang.getHeaderSearchOpts().UseBuiltinIncludes && - Clang.getHeaderSearchOpts().ResourceDir.empty()) - Clang.getHeaderSearchOpts().ResourceDir = - CompilerInvocation::GetResourcesPath(argv[0], MainAddr); - - // Create and execute the frontend to generate an LLVM bitcode module. - std::unique_ptr Act(new EmitLLVMOnlyAction()); - if (!Clang.ExecuteAction(*Act)) - return 1; - - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - int Res = 255; - std::unique_ptr Ctx(Act->takeLLVMContext()); - std::unique_ptr Module = Act->takeModule(); - - if (Module) { - auto J = ExitOnErr(llvm::orc::SimpleJIT::Create()); - - ExitOnErr(J->addModule( - llvm::orc::ThreadSafeModule(std::move(Module), std::move(Ctx)))); - auto Main = (int (*)(...))ExitOnErr(J->getSymbolAddress("main")); - Res = Main(); - } - - // Shutdown. - llvm::llvm_shutdown(); - - return Res; -} Index: clang/include/clang/Interpreter/Interpreter.h =================================================================== --- clang/include/clang/Interpreter/Interpreter.h +++ clang/include/clang/Interpreter/Interpreter.h @@ -16,6 +16,7 @@ #include "clang/Interpreter/PartialTranslationUnit.h" +#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/Support/Error.h" #include @@ -65,6 +66,8 @@ return Execute(*PTU); return llvm::Error::success(); } + llvm::Expected + getSymbolAddress(llvm::StringRef UnmangledName) const; }; } // namespace clang Index: clang/lib/Interpreter/IncrementalExecutor.h =================================================================== --- clang/lib/Interpreter/IncrementalExecutor.h +++ clang/lib/Interpreter/IncrementalExecutor.h @@ -41,6 +41,8 @@ llvm::Error addModule(std::unique_ptr M); llvm::Error runCtors() const; + llvm::Expected + getSymbolAddress(llvm::StringRef UnmangledName) const; }; } // end namespace clang Index: clang/lib/Interpreter/IncrementalExecutor.cpp =================================================================== --- clang/lib/Interpreter/IncrementalExecutor.cpp +++ clang/lib/Interpreter/IncrementalExecutor.cpp @@ -60,4 +60,12 @@ return Jit->initialize(Jit->getMainJITDylib()); } +llvm::Expected +IncrementalExecutor::getSymbolAddress(llvm::StringRef UnmangledName) const { + auto Sym = Jit->lookup(UnmangledName); + if (!Sym) + return Sym.takeError(); + return Sym->getAddress(); +} + } // end namespace clang Index: clang/lib/Interpreter/Interpreter.cpp =================================================================== --- clang/lib/Interpreter/Interpreter.cpp +++ clang/lib/Interpreter/Interpreter.cpp @@ -35,8 +35,7 @@ using namespace clang; // FIXME: Figure out how to unify with namespace init_convenience from -// tools/clang-import-test/clang-import-test.cpp and -// examples/clang-interpreter/main.cpp +// tools/clang-import-test/clang-import-test.cpp namespace { /// Retrieves the clang CC1 specific flags out of the compilation's jobs. /// \returns NULL on error. @@ -223,3 +222,13 @@ return llvm::Error::success(); } + +llvm::Expected +Interpreter::getSymbolAddress(llvm::StringRef UnmangledName) const { + if (!IncrExecutor) + return llvm::make_error("Operation failed. " + "No execution engine", + std::error_code()); + + return IncrExecutor->getSymbolAddress(UnmangledName); +} Index: clang/test/CMakeLists.txt =================================================================== --- clang/test/CMakeLists.txt +++ clang/test/CMakeLists.txt @@ -96,7 +96,6 @@ Attribute AnnotateFunctions CallSuperAttr - clang-interpreter PrintFunctionNames ) endif () Index: clang/test/Misc/interpreter.c =================================================================== --- clang/test/Misc/interpreter.c +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: clang-interpreter %s | FileCheck %s -// REQUIRES: native, examples - -int printf(const char *, ...); - -int main() { - // CHECK: {{Hello world!}} - printf("Hello world!\n"); - return 0; -} Index: clang/test/lit.cfg.py =================================================================== --- clang/test/lit.cfg.py +++ clang/test/lit.cfg.py @@ -71,7 +71,6 @@ if config.clang_examples: config.available_features.add('examples') - tools.append('clang-interpreter') def have_host_jit_support(): clang_repl_exe = lit.util.which('clang-repl', config.clang_tools_dir) Index: clang/unittests/Interpreter/CMakeLists.txt =================================================================== --- clang/unittests/Interpreter/CMakeLists.txt +++ clang/unittests/Interpreter/CMakeLists.txt @@ -12,3 +12,4 @@ clangInterpreter clangFrontend ) +add_dependencies(ClangReplInterpreterTests clang-resource-headers) Index: clang/unittests/Interpreter/InterpreterTest.cpp =================================================================== --- clang/unittests/Interpreter/InterpreterTest.cpp +++ clang/unittests/Interpreter/InterpreterTest.cpp @@ -14,10 +14,14 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/Support/TargetSelect.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -53,6 +57,92 @@ EXPECT_EQ(1U, DeclsSize(R2.TUPart)); } +// This function isn't referenced outside its translation unit, but it +// can't use the "static" keyword because its address is used for +// GetMainExecutable (since some platforms don't support taking the +// address of main, and some platforms can't implement GetMainExecutable +// without being given the address of a function in the main executable). +std::string GetExecutablePath(const char *Argv0, void *MainAddr) { + return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); +} + +static std::string MakeResourcesPath() { + // Dir is bin/ or lib/, depending on where BinaryPath is. + void *MainAddr = (void *)(intptr_t)GetExecutablePath; + std::string BinaryPath = GetExecutablePath(/*Argv0=*/nullptr, MainAddr); + + // build/tools/clang/unittests/Interpreter/Executable -> build/ + llvm::StringRef Dir = llvm::sys::path::parent_path(BinaryPath); + + Dir = llvm::sys::path::parent_path(Dir); + Dir = llvm::sys::path::parent_path(Dir); + Dir = llvm::sys::path::parent_path(Dir); + Dir = llvm::sys::path::parent_path(Dir); + SmallString<128> P(Dir); + llvm::sys::path::append(P, Twine("lib") + CLANG_LIBDIR_SUFFIX, "clang", + CLANG_VERSION_STRING); + + return std::string(P.str()); +} + +#ifdef _MSC_VER +// Tell the windows linker to export the type_info symbol required by exceptions +#pragma comment(linker, "/export:??_7type_info@@6B@") +#endif // _MSC_VER +TEST(InterpreterTest, CatchException) { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + { + auto J = llvm::orc::LLJITBuilder().create(); + if (!J) { + // The platform does not support JITs. + llvm::consumeError(J.takeError()); + return; + } + } + const char ExceptionCode[] = + R"( +#include +#include + +static void ThrowerAnError(const char* Name) { + throw std::runtime_error(Name); +} + +int main(int argc, const char** argv) { + // FIXME: Somehow when we build this test in release mode argc is not 0. + // printf("%d\n", argc); + // for (int I = 0; I < argc; ++I) + // printf("arg[%d]='%s'\n", I, argv[I]); + + try { + ThrowerAnError("In JIT"); + } catch (const std::exception& E) { + printf("Caught: '%s'\n", E.what()); + } catch (...) { + printf("Unknown exception\n"); + } + ThrowerAnError("From JIT"); + return 0; +} + )"; + std::string ResourceDir = MakeResourcesPath(); + std::unique_ptr Interp = + createInterpreter({"-resource-dir", ResourceDir.c_str()}); + // Adjust the resource-dir + llvm::cantFail(Interp->ParseAndExecute(ExceptionCode)); +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + testing::internal::CaptureStdout(); + auto Main = (int (*)(...))llvm::cantFail(Interp->getSymbolAddress("main")); + // Compiling this file with exceptions on is problematic on some platforms. + // Instead we expect std::terminate to be called upon thrown exception. + EXPECT_DEATH(Main(), "terminate called after throwing an instance of '.*'"); + std::string CapturedStdOut = testing::internal::GetCapturedStdout(); + EXPECT_EQ(CapturedStdOut, "Caught: 'In JIT'\n"); +#endif +} + static std::string DeclToString(Decl *D) { return llvm::cast(D)->getQualifiedNameAsString(); }