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/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. @@ -218,3 +217,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 @@ -1,5 +1,8 @@ set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} Core + OrcJIT + Support ) add_clang_unittest(ClangReplInterpreterTests @@ -12,3 +15,8 @@ clangInterpreter clangFrontend ) + +# Exceptions on Windows are not yet supported. +if(NOT WIN32) + add_subdirectory(ExceptionTests) +endif() Index: clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt =================================================================== --- /dev/null +++ clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt @@ -0,0 +1,24 @@ +# The interpreter can throw an exception from user input. The test binary needs +# to be compiled with exception support to catch the thrown exception. +set(LLVM_REQUIRES_EH ON) +set(LLVM_REQUIRES_RTTI ON) + +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Core + OrcJIT + Support + ) + +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) Index: clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp =================================================================== --- /dev/null +++ clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp @@ -0,0 +1,131 @@ +//===- 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/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Basic/TargetInfo.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/ManagedStatic.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))); +} + +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; + } + } + +#define Stringify(s) Stringifyx(s) +#define Stringifyx(s) #s + + // We define a custom exception to avoid #include-ing the header + // which would require this test to know about the libstdc++ location. + // its own header file. +#define CUSTOM_EXCEPTION \ + struct custom_exception { \ + custom_exception(const char *Msg) : Message(Msg) {} \ + const char *Message; \ + }; + + CUSTOM_EXCEPTION; + + std::string ExceptionCode = Stringify(CUSTOM_EXCEPTION); + ExceptionCode += + R"( +extern "C" int printf(const char*, ...); +static void ThrowerAnError(const char* Name) { + throw custom_exception(Name); +} + +extern "C" int throw_exception() { + try { + ThrowerAnError("To be caught in JIT"); + } catch (const custom_exception& E) { + printf("Caught: '%s'\n", E.Message); + } catch (...) { + printf("Unknown exception\n"); + } + ThrowerAnError("To be caught in binary"); + return 0; +} + )"; + std::unique_ptr Interp = createInterpreter(); + // FIXME: Re-enable the excluded target triples. + const clang::CompilerInstance *CI = Interp->getCompilerInstance(); + const llvm::Triple &Triple = CI->getASTContext().getTargetInfo().getTriple(); + // FIXME: PPC fails due to `Symbols not found: [DW.ref.__gxx_personality_v0]` + // The current understanding is that the JIT should emit this symbol if it was + // not (eg. the way passing clang -fPIC does it). + if (Triple.isPPC()) + return; + + // FIXME: ARM fails due to `Not implemented relocation type!` + if (Triple.isARM()) + return; + + // FIXME: libunwind on darwin is broken, see PR49692. + if (Triple.isOSDarwin() && (Triple.getArch() == llvm::Triple::aarch64 || + Triple.getArch() == llvm::Triple::aarch64_32)) + return; + + // FIXME: Hexagon fails due to `No available targets are compatible with + // triple "x86_64-unknown-linux-gnu"` + if (Triple.getArch() == llvm::Triple::hexagon) + return; + + llvm::cantFail(Interp->ParseAndExecute(ExceptionCode)); + testing::internal::CaptureStdout(); + auto ThrowException = + (int (*)())llvm::cantFail(Interp->getSymbolAddress("throw_exception")); + EXPECT_ANY_THROW(ThrowException()); + std::string CapturedStdOut = testing::internal::GetCapturedStdout(); + EXPECT_EQ(CapturedStdOut, "Caught: 'To be caught in JIT'\n"); + + llvm::llvm_shutdown(); +} + +} // end anonymous namespace Index: clang/unittests/Interpreter/InterpreterTest.cpp =================================================================== --- clang/unittests/Interpreter/InterpreterTest.cpp +++ 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"