diff --git a/clang/docs/ClangFormattedStatus.rst b/clang/docs/ClangFormattedStatus.rst --- a/clang/docs/ClangFormattedStatus.rst +++ b/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` diff --git a/clang/examples/CMakeLists.txt b/clang/examples/CMakeLists.txt --- a/clang/examples/CMakeLists.txt +++ b/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) diff --git a/clang/examples/clang-interpreter/CMakeLists.txt b/clang/examples/clang-interpreter/CMakeLists.txt deleted file mode 100644 --- a/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) diff --git a/clang/examples/clang-interpreter/README.txt b/clang/examples/clang-interpreter/README.txt deleted file mode 100644 --- a/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. diff --git a/clang/examples/clang-interpreter/Test.cxx b/clang/examples/clang-interpreter/Test.cxx deleted file mode 100644 --- a/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; -} 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 @@ -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 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 @@ -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 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 @@ -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 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 @@ -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); +} diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -96,7 +96,6 @@ Attribute AnnotateFunctions CallSuperAttr - clang-interpreter PrintFunctionNames ) endif () diff --git a/clang/test/Misc/interpreter.c b/clang/test/Misc/interpreter.c deleted file mode 100644 --- a/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; -} diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py --- a/clang/test/lit.cfg.py +++ b/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) diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -12,3 +12,8 @@ clangInterpreter clangFrontend ) + +# Exceptions on Windows are not yet supported. +if(NOT WIN32) + add_subdirectory(ExceptionTests) +endif() diff --git a/clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt b/clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt @@ -0,0 +1,18 @@ +# The interpreter can throw an exception from user input. The test binary needs +# to be compiled with exception support to expect and catch the thrown +# exception. +set(LLVM_REQUIRES_EH ON) +set(LLVM_REQUIRES_RTTI ON) + +add_clang_unittest(ClangReplInterpreterExceptionTests + InterpreterExceptionTest.cpp + ) + +llvm_update_compile_flags(ClangReplInterpreterExceptionTests) +target_link_libraries(ClangReplInterpreterExceptionTests PUBLIC + clangAST + clangBasic + clangInterpreter + clangFrontend + ) +add_dependencies(ClangReplInterpreterExceptionTests clang-resource-headers) diff --git a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp @@ -0,0 +1,121 @@ +//===- unittests/Interpreter/InterpreterExceptionTest.cpp -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Unit tests for Clang's Interpreter library. +// +//===----------------------------------------------------------------------===// + +#include "clang/Interpreter/Interpreter.h" + +#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" + +using namespace clang; + +namespace { +using Args = std::vector; +static std::unique_ptr +createInterpreter(const Args &ExtraArgs = {}, + DiagnosticConsumer *Client = nullptr) { + Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; + ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); + auto CI = cantFail(clang::IncrementalCompilerBuilder::create(ClangArgs)); + if (Client) + CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); + return cantFail(clang::Interpreter::create(std::move(CI))); +} + +// 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); + 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()); +} + +TEST(InterpreterTest, CatchException) { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + { + auto J = llvm::orc::LLJITBuilder().create(); + if (!J) { + // The platform does not support JITs. + // We can't use llvm::consumeError as it needs typeinfo for ErrorInfoBase. + auto E = J.takeError(); + (void)E; + return; + } + } + const char ExceptionCode[] = + R"( +#include +#include + +static void ThrowerAnError(const char* Name) { + throw std::runtime_error(Name); +} + +extern "C" int throw_exception() { + 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)); + testing::internal::CaptureStdout(); + auto ThrowException = + (int (*)())llvm::cantFail(Interp->getSymbolAddress("throw_exception")); + EXPECT_THROW(ThrowException(), std::exception); + std::string CapturedStdOut = testing::internal::GetCapturedStdout(); + EXPECT_EQ(CapturedStdOut, "Caught: 'In JIT'\n"); +} + +} // end anonymous namespace 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 @@ -17,8 +17,6 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" -#include "llvm/ADT/ArrayRef.h" - #include "gmock/gmock.h" #include "gtest/gtest.h"