Reinstate "[clang-repl] Re-implement clang-interpreter as a test case."
Original commit message: " Original commit message: " Original commit message: " Original commit message:" 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. " This patch also ignores ppc due to missing weak symbol for __gxx_personality_v0 which may be a feature request for the jit infrastructure. Also, adds a missing build system dependency to the orc jit. " Additionally, this patch defines a custom exception type and thus avoids the requirement to include header <exception>, making it easier to deploy across systems without standard location of the c++ headers. " This patch also works around PR49692 and finds a way to use llvm::consumeError in rtti mode. " This patch also checks if stl is built with rtti. Differential revision: https://reviews.llvm.org/D107049
This commit is contained in:
parent
d7fbad0dcf
commit
c24a58081b
|
@ -54,11 +54,6 @@ tree in terms of conformance to :doc:`ClangFormat` as of: October 09, 2021 17:11
|
|||
- `1`
|
||||
- `0`
|
||||
- :good:`100%`
|
||||
* - clang/examples/clang-interpreter
|
||||
- `1`
|
||||
- `0`
|
||||
- `1`
|
||||
- :none:`0%`
|
||||
* - clang/examples/PrintFunctionNames
|
||||
- `1`
|
||||
- `0`
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
|
@ -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 @@ public:
|
|||
return Execute(*PTU);
|
||||
return llvm::Error::success();
|
||||
}
|
||||
llvm::Expected<llvm::JITTargetAddress>
|
||||
getSymbolAddress(llvm::StringRef UnmangledName) const;
|
||||
};
|
||||
} // namespace clang
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -41,6 +41,8 @@ public:
|
|||
|
||||
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
|
||||
|
|
|
@ -36,8 +36,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.
|
||||
|
@ -222,3 +221,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);
|
||||
}
|
||||
|
|
|
@ -96,7 +96,6 @@ if (CLANG_BUILD_EXAMPLES)
|
|||
Attribute
|
||||
AnnotateFunctions
|
||||
CallSuperAttr
|
||||
clang-interpreter
|
||||
PluginsOrder
|
||||
PrintFunctionNames
|
||||
)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -71,7 +71,6 @@ tools = [
|
|||
|
||||
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)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
set(LLVM_LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
Core
|
||||
OrcJIT
|
||||
Support
|
||||
)
|
||||
|
||||
add_clang_unittest(ClangReplInterpreterTests
|
||||
|
@ -12,3 +15,8 @@ target_link_libraries(ClangReplInterpreterTests PUBLIC
|
|||
clangInterpreter
|
||||
clangFrontend
|
||||
)
|
||||
|
||||
# Exceptions on Windows are not yet supported.
|
||||
if(NOT WIN32)
|
||||
add_subdirectory(ExceptionTests)
|
||||
endif()
|
||||
|
|
24
clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt
Normal file
24
clang/unittests/Interpreter/ExceptionTests/CMakeLists.txt
Normal file
|
@ -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)
|
|
@ -0,0 +1,138 @@
|
|||
//===- 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 "llvm-c/Error.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)));
|
||||
}
|
||||
|
||||
TEST(InterpreterTest, CatchException) {
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
|
||||
{
|
||||
auto J = llvm::orc::LLJITBuilder().create();
|
||||
if (!J) {
|
||||
// The platform does not support JITs.
|
||||
// Using llvm::consumeError will require typeinfo for ErrorInfoBase, we
|
||||
// can avoid that by going via the C interface.
|
||||
LLVMConsumeError(llvm::wrap(J.takeError()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define Stringify(s) Stringifyx(s)
|
||||
#define Stringifyx(s) #s
|
||||
|
||||
// We define a custom exception to avoid #include-ing the <exception> 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<Interpreter> 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;
|
||||
|
||||
// Check if platform does not support exceptions.
|
||||
{
|
||||
// Force the creation of an incremental executor to call getSymbolAddress.
|
||||
llvm::cantFail(Interp->ParseAndExecute(""));
|
||||
auto Sym = Interp->getSymbolAddress("__cxa_throw");
|
||||
if (!Sym) {
|
||||
LLVMConsumeError(llvm::wrap(Sym.takeError()));
|
||||
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
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user