Author: Vassil Vassilev Date: 2021-09-01T05:23:21Z New Revision: 319ce98011742141dad8dd95a2f9de9c0449be5c
URL: https://github.com/llvm/llvm-project/commit/319ce98011742141dad8dd95a2f9de9c0449be5c DIFF: https://github.com/llvm/llvm-project/commit/319ce98011742141dad8dd95a2f9de9c0449be5c.diff LOG: [clang-repl] Re-implement clang-interpreter as a test case. The current infrastructure in lib/Interpreter has a tool, clang-repl, very similar to clang-interpreter which also allows incremental compilation. This patch moves clang-interpreter as a test case and drops it as conditionally built example as we already have clang-repl in place. Differential revision: https://reviews.llvm.org/D107049 Added: clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp Modified: clang/docs/ClangFormattedStatus.rst clang/examples/CMakeLists.txt clang/include/clang/Interpreter/Interpreter.h clang/lib/Interpreter/IncrementalExecutor.cpp clang/lib/Interpreter/IncrementalExecutor.h clang/lib/Interpreter/Interpreter.cpp clang/test/CMakeLists.txt clang/test/lit.cfg.py clang/unittests/Interpreter/CMakeLists.txt clang/unittests/Interpreter/InterpreterTest.cpp Removed: clang/examples/clang-interpreter/CMakeLists.txt clang/examples/clang-interpreter/README.txt clang/examples/clang-interpreter/Test.cxx clang/test/Misc/interpreter.c ################################################################################ diff --git a/clang/docs/ClangFormattedStatus.rst b/clang/docs/ClangFormattedStatus.rst index beca555ebc016..77320cd2b6550 100644 --- a/clang/docs/ClangFormattedStatus.rst +++ b/clang/docs/ClangFormattedStatus.rst @@ -59,11 +59,6 @@ tree in terms of conformance to :doc:`ClangFormat` as of: June 04, 2021 13:01:37 - `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 index 300d8d795c674..8a4139f5d8c11 100644 --- a/clang/examples/CMakeLists.txt +++ b/clang/examples/CMakeLists.txt @@ -3,7 +3,6 @@ if(NOT CLANG_BUILD_EXAMPLES) 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 index 11056aa379ae8..0000000000000 --- 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 index b4f8a935cef83..0000000000000 --- 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 index ed7fc86f9e5fc..0000000000000 --- 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 <src>/tools/clang/examples/clang-interpreter/Test.cxx - -#include <stdexcept> -#include <stdio.h> - -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 index 020cbe2db3d06..9f5b64ce21243 100644 --- 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 <memory> @@ -65,6 +66,8 @@ class Interpreter { return Execute(*PTU); return llvm::Error::success(); } + llvm::Expected<llvm::JITTargetAddress> + getSymbolAddress(llvm::StringRef UnmangledName) const; }; } // namespace clang diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 9a368d9122bcf..230b49167f347 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -60,4 +60,12 @@ llvm::Error IncrementalExecutor::runCtors() const { return Jit->initialize(Jit->getMainJITDylib()); } +llvm::Expected<llvm::JITTargetAddress> +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/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index b4c6ddec10472..b626ebedcdc30 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -41,6 +41,8 @@ class IncrementalExecutor { llvm::Error addModule(std::unique_ptr<llvm::Module> M); llvm::Error runCtors() const; + llvm::Expected<llvm::JITTargetAddress> + getSymbolAddress(llvm::StringRef UnmangledName) const; }; } // end namespace clang diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 937504f34739f..84589b54e87fb 100644 --- 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 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { return llvm::Error::success(); } + +llvm::Expected<llvm::JITTargetAddress> +Interpreter::getSymbolAddress(llvm::StringRef UnmangledName) const { + if (!IncrExecutor) + return llvm::make_error<llvm::StringError>("Operation failed. " + "No execution engine", + std::error_code()); + + return IncrExecutor->getSymbolAddress(UnmangledName); +} diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index 9bf791f93f32e..abc04228ce971 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -96,7 +96,6 @@ if (CLANG_BUILD_EXAMPLES) 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 index 42e1645b7c61b..0000000000000 --- 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 index 3a820943bd0f6..5a9a1abf4531a 100644 --- 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 index ec5e62b989ccf..e79e6d4dbbee5 100644 --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -12,3 +12,8 @@ target_link_libraries(ClangReplInterpreterTests PUBLIC 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 index 0000000000000..305f4a59c0ee1 --- /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 index 0000000000000..fc3740a84e92e --- /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<const char *>; +static std::unique_ptr<Interpreter> +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 <stdexcept> +#include <stdio.h> + +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<Interpreter> 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 index 6ce43748ae049..dc045a40a699d 100644 --- 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" _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits