Index: clang/test/Interpreter/deferred-emit-decls.cpp =================================================================== --- /dev/null +++ clang/test/Interpreter/deferred-emit-decls.cpp @@ -0,0 +1,16 @@ +// REQUIRES: host-supports-jit +// UNSUPPORTED: system-aix +// XFAIL: system-windows +// RUN: cat %s | clang-repl | FileCheck %s +extern "C" int printf(const char *, ...); + +inline int foo() { return 42; } +int r1 = foo(); +auto r3 = printf("foo() = %d\n", r1); +// CHECK: foo() = 42 + +int __attribute__((weak)) bar() { return 1; } +auto r4 = printf("bar() = %d\n", bar()); +// CHECK-NEXT: bar() = 1 + +%quit \ No newline at end of file Index: clang/test/Interpreter/execute.cpp =================================================================== --- clang/test/Interpreter/execute.cpp +++ clang/test/Interpreter/execute.cpp @@ -3,7 +3,6 @@ // RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s // REQUIRES: host-supports-jit // UNSUPPORTED: system-aix -// XFAIL: system-windows // CHECK-DRIVER: i = 10 // RUN: cat %s | clang-repl | FileCheck %s extern "C" int printf(const char *, ...); @@ -16,11 +15,4 @@ auto r2 = printf("S[f=%f, m=0x%llx]\n", s.f, reinterpret_cast(s.m)); // CHECK-NEXT: S[f=1.000000, m=0x0] -inline int foo() { return 42; } -int r3 = foo(); - -int __attribute__((weak)) bar() { return 1; } -auto r4 = printf("bar() = %d\n", bar()); -// CHECK-NEXT: bar() = 1 - %quit Index: clang/test/Interpreter/simple-exception.cpp =================================================================== --- /dev/null +++ clang/test/Interpreter/simple-exception.cpp @@ -0,0 +1,14 @@ +// clang-format off +// REQUIRES: host-supports-jit, host-supports-exception +// UNSUPPORTED: system-aix +// XFAIL: arm, arm64-apple, windows-msvc, windows-gnu +// RUN: cat %s | clang-repl | FileCheck %s +extern "C" int printf(const char *, ...); + +int f() { throw "Simple exception"; return 0; } +int checkException() { try { printf("Running f()\n"); f(); } catch (const char *e) { printf("%s\n", e); } return 0; } +auto r1 = checkException(); +// CHECK: Running f() +// CHECK-NEXT: Simple exception + +%quit \ No newline at end of file Index: clang/test/lit.cfg.py =================================================================== --- clang/test/lit.cfg.py +++ clang/test/lit.cfg.py @@ -89,8 +89,27 @@ return 'true' in clang_repl_out -if have_host_jit_support(): - config.available_features.add('host-supports-jit') +def have_host_exception_support(): + clang_repl_exe = lit.util.which('clang-repl', config.clang_tools_dir) + + if not clang_repl_exe: + print('clang-repl not found') + return False + + try: + clang_repl_cmd = subprocess.Popen( + [clang_repl_exe, '--host-supports-exception'], stdout=subprocess.PIPE) + except OSError: + print('could not exec clang-repl') + return False + + clang_repl_out = clang_repl_cmd.stdout.read().decode('ascii') + clang_repl_cmd.wait() + + return 'true' in clang_repl_out + +if have_host_exception_support(): + config.available_features.add('host-supports-exception') if config.clang_staticanalyzer: config.available_features.add('staticanalyzer') Index: clang/tools/clang-repl/ClangRepl.cpp =================================================================== --- clang/tools/clang-repl/ClangRepl.cpp +++ clang/tools/clang-repl/ClangRepl.cpp @@ -28,6 +28,8 @@ llvm::cl::CommaSeparated); static llvm::cl::opt OptHostSupportsJit("host-supports-jit", llvm::cl::Hidden); +static llvm::cl::opt OptHostSupportsException("host-supports-exception", + llvm::cl::Hidden); static llvm::cl::list OptInputs(llvm::cl::Positional, llvm::cl::desc("[code to run]")); @@ -65,6 +67,35 @@ return Errs ? EXIT_FAILURE : EXIT_SUCCESS; } +// Check if the host environment supports c++ exception handling +// by querying the existence of symbol __cxa_throw. +static bool checkExceptionSupport() { + auto J = llvm::orc::LLJITBuilder().create(); + if (!J) { + llvm::consumeError(J.takeError()); + return false; + } + + auto Interp = clang::Interpreter::create(std::move(CI)); + if (!Interp) { + llvm::consumeError(Interp.takeError()); + return false; + } + + if (auto Err = Interp->ParseAndExecute("")) { + llvm::consumeError(std::move(Err)); + return false; + } + + auto Sym = Interp->getSymbolAddress("__cxa_throw"); + if (!Sym) { + llvm::consumeError(Sym.takeError()); + return false; + } + + return true; +} + llvm::ExitOnError ExitOnErr; int main(int argc, const char **argv) { ExitOnErr.setBanner("clang-repl: "); @@ -87,6 +118,14 @@ return 0; } + if (OptHostSupportsException) { + if (checkExceptionSupport()) + llvm::outs() << "true\n"; + else + llvm::outs() << "false\n"; + return 0; + } + // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It // can replace the boilerplate code for creation of the compiler instance. auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create(ClangArgv)); Index: mypatch.patch =================================================================== --- /dev/null +++ mypatch.patch @@ -0,0 +1,446 @@ +diff --git a/clang/test/Interpreter/deferred-emit-decls.cpp b/clang/test/Interpreter/deferred-emit-decls.cpp +new file mode 100644 +index 000000000000..8bdd4e92c0a9 +--- /dev/null ++++ b/clang/test/Interpreter/deferred-emit-decls.cpp +@@ -0,0 +1,16 @@ ++// REQUIRES: host-supports-jit ++// UNSUPPORTED: system-aix ++// XFAIL: system-windows ++// RUN: cat %s | clang-repl | FileCheck %s ++extern "C" int printf(const char *, ...); ++ ++inline int foo() { return 42; } ++int r1 = foo(); ++auto r3 = printf("foo() = %d\n", r1); ++// CHECK: foo() = 42 ++ ++int __attribute__((weak)) bar() { return 1; } ++auto r4 = printf("bar() = %d\n", bar()); ++// CHECK-NEXT: bar() = 1 ++ ++%quit +\ No newline at end of file +diff --git a/clang/test/Interpreter/execute.cpp b/clang/test/Interpreter/execute.cpp +index b5dad4722c0b..17db58b509bb 100644 +--- a/clang/test/Interpreter/execute.cpp ++++ b/clang/test/Interpreter/execute.cpp +@@ -1,26 +1,18 @@ + // RUN: clang-repl "int x = 10;" "int y=7; err;" "int y = 10;" + // RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \ + // RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s + // REQUIRES: host-supports-jit + // UNSUPPORTED: system-aix +-// XFAIL: system-windows + // CHECK-DRIVER: i = 10 + // RUN: cat %s | clang-repl | FileCheck %s + extern "C" int printf(const char *, ...); + int i = 42; + auto r1 = printf("i = %d\n", i); + // CHECK: i = 42 + + struct S { float f = 1.0; S *m = nullptr;} s; + + auto r2 = printf("S[f=%f, m=0x%llx]\n", s.f, reinterpret_cast(s.m)); + // CHECK-NEXT: S[f=1.000000, m=0x0] + +-inline int foo() { return 42; } +-int r3 = foo(); +- +-int __attribute__((weak)) bar() { return 1; } +-auto r4 = printf("bar() = %d\n", bar()); +-// CHECK-NEXT: bar() = 1 +- + %quit +diff --git a/clang/test/Interpreter/simple-exception.cpp b/clang/test/Interpreter/simple-exception.cpp +new file mode 100644 +index 000000000000..c56fbbb417f0 +--- /dev/null ++++ b/clang/test/Interpreter/simple-exception.cpp +@@ -0,0 +1,14 @@ ++// clang-format off ++// REQUIRES: host-supports-jit, host-supports-exception ++// UNSUPPORTED: system-aix ++// XFAIL: arm, arm64-apple, windows-msvc, windows-gnu ++// RUN: cat %s | clang-repl | FileCheck %s ++extern "C" int printf(const char *, ...); ++ ++int f() { throw "Simple exception"; return 0; } ++int checkException() { try { printf("Running f()\n"); f(); } catch (const char *e) { printf("%s\n", e); } return 0; } ++auto r1 = checkException(); ++// CHECK: Running f() ++// CHECK-NEXT: Simple exception ++ ++%quit +\ No newline at end of file +diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py +index fd86353c8cc3..322e584a4c99 100644 +--- a/clang/test/lit.cfg.py ++++ b/clang/test/lit.cfg.py +@@ -1,266 +1,285 @@ + # -*- Python -*- + + import os + import platform + import re + import subprocess + import tempfile + + import lit.formats + import lit.util + + from lit.llvm import llvm_config + from lit.llvm.subst import ToolSubst + from lit.llvm.subst import FindTool + + # Configuration file for the 'lit' test runner. + + # name: The name of this test suite. + config.name = 'Clang' + + # testFormat: The test format to use to interpret tests. + # + # For now we require '&&' between commands, until they get globally killed and + # the test runner updated. + config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) + + # suffixes: A list of file extensions to treat as test files. + config.suffixes = ['.c', '.cpp', '.i', '.cppm', '.m', '.mm', '.cu', '.hip', '.hlsl', + '.ll', '.cl', '.clcpp', '.s', '.S', '.modulemap', '.test', '.rs', '.ifs', '.rc'] + + # excludes: A list of directories to exclude from the testsuite. The 'Inputs' + # subdirectories contain auxiliary inputs for various tests in their parent + # directories. + config.excludes = ['Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt', 'debuginfo-tests'] + + # test_source_root: The root path where tests are located. + config.test_source_root = os.path.dirname(__file__) + + # test_exec_root: The root path where tests should be run. + config.test_exec_root = os.path.join(config.clang_obj_root, 'test') + + llvm_config.use_default_substitutions() + + llvm_config.use_clang() + + config.substitutions.append( + ('%src_include_dir', config.clang_src_dir + '/include')) + + config.substitutions.append( + ('%target_triple', config.target_triple)) + + config.substitutions.append(('%PATH%', config.environment['PATH'])) + + + # For each occurrence of a clang tool name, replace it with the full path to + # the build directory holding that tool. We explicitly specify the directories + # to search to ensure that we get the tools just built and not some random + # tools that might happen to be in the user's PATH. + tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir] + + tools = [ + 'apinotes-test', 'c-index-test', 'clang-diff', 'clang-format', 'clang-repl', 'clang-offload-packager', + 'clang-tblgen', 'clang-scan-deps', 'opt', 'llvm-ifs', 'yaml2obj', 'clang-linker-wrapper', + ToolSubst('%clang_extdef_map', command=FindTool( + 'clang-extdef-mapping'), unresolved='ignore'), + ToolSubst('%clang_dxc', command=config.clang, + extra_args=['--driver-mode=dxc']), + ] + + if config.clang_examples: + config.available_features.add('examples') + + def have_host_jit_support(): + clang_repl_exe = lit.util.which('clang-repl', config.clang_tools_dir) + + if not clang_repl_exe: + print('clang-repl not found') + return False + + try: + clang_repl_cmd = subprocess.Popen( + [clang_repl_exe, '--host-supports-jit'], stdout=subprocess.PIPE) + except OSError: + print('could not exec clang-repl') + return False + + clang_repl_out = clang_repl_cmd.stdout.read().decode('ascii') + clang_repl_cmd.wait() + + return 'true' in clang_repl_out + +-if have_host_jit_support(): +- config.available_features.add('host-supports-jit') ++def have_host_exception_support(): ++ clang_repl_exe = lit.util.which('clang-repl', config.clang_tools_dir) ++ ++ if not clang_repl_exe: ++ print('clang-repl not found') ++ return False ++ ++ try: ++ clang_repl_cmd = subprocess.Popen( ++ [clang_repl_exe, '--host-supports-exception'], stdout=subprocess.PIPE) ++ except OSError: ++ print('could not exec clang-repl') ++ return False ++ ++ clang_repl_out = clang_repl_cmd.stdout.read().decode('ascii') ++ clang_repl_cmd.wait() ++ ++ return 'true' in clang_repl_out ++ ++if have_host_exception_support(): ++ config.available_features.add('host-supports-exception') + + if config.clang_staticanalyzer: + config.available_features.add('staticanalyzer') + tools.append('clang-check') + + if config.clang_staticanalyzer_z3: + config.available_features.add('z3') + else: + config.available_features.add('no-z3') + + check_analyzer_fixit_path = os.path.join( + config.test_source_root, "Analysis", "check-analyzer-fixit.py") + config.substitutions.append( + ('%check_analyzer_fixit', + '"%s" %s' % (config.python_executable, check_analyzer_fixit_path))) + + llvm_config.add_tool_substitutions(tools, tool_dirs) + + config.substitutions.append( + ('%hmaptool', "'%s' %s" % (config.python_executable, + os.path.join(config.clang_src_dir, 'utils', 'hmaptool', 'hmaptool')))) + + config.substitutions.append( + ('%deps-to-rsp', + '"%s" %s' % (config.python_executable, os.path.join(config.clang_src_dir, 'utils', + 'module-deps-to-rsp.py')))) + + config.substitutions.append(('%host_cc', config.host_cc)) + config.substitutions.append(('%host_cxx', config.host_cxx)) + + + # Plugins (loadable modules) + if config.has_plugins and config.llvm_plugin_ext: + config.available_features.add('plugins') + + if config.clang_default_pie_on_linux: + config.available_features.add('default-pie-on-linux') + + if config.clang_enable_opaque_pointers: + config.available_features.add('enable-opaque-pointers') + + # Set available features we allow tests to conditionalize on. + # + if config.clang_default_cxx_stdlib != '': + config.available_features.add('default-cxx-stdlib-set') + + # As of 2011.08, crash-recovery tests still do not pass on FreeBSD. + if platform.system() not in ['FreeBSD']: + config.available_features.add('crash-recovery') + + # ANSI escape sequences in non-dumb terminal + if platform.system() not in ['Windows']: + config.available_features.add('ansi-escape-sequences') + + # Capability to print utf8 to the terminal. + # Windows expects codepage, unless Wide API. + if platform.system() not in ['Windows']: + config.available_features.add('utf8-capable-terminal') + + # Support for libgcc runtime. Used to rule out tests that require + # clang to run with -rtlib=libgcc. + if platform.system() not in ['Darwin', 'Fuchsia']: + config.available_features.add('libgcc') + + # Case-insensitive file system + + + def is_filesystem_case_insensitive(): + handle, path = tempfile.mkstemp( + prefix='case-test', dir=config.test_exec_root) + isInsensitive = os.path.exists( + os.path.join( + os.path.dirname(path), + os.path.basename(path).upper() + )) + os.close(handle) + os.remove(path) + return isInsensitive + + + if is_filesystem_case_insensitive(): + config.available_features.add('case-insensitive-filesystem') + + # Tests that require the /dev/fd filesystem. + if os.path.exists('/dev/fd/0') and sys.platform not in ['cygwin']: + config.available_features.add('dev-fd-fs') + + # Set on native MS environment. + if re.match(r'.*-(windows-msvc)$', config.target_triple): + config.available_features.add('ms-sdk') + + # [PR8833] LLP64-incompatible tests + if not re.match(r'^x86_64.*-(windows-msvc|windows-gnu)$', config.target_triple): + config.available_features.add('LP64') + + # Tests that are specific to the Apple Silicon macOS. + if re.match(r'^arm64(e)?-apple-(macos|darwin)', config.target_triple): + config.available_features.add('apple-silicon-mac') + + # [PR18856] Depends to remove opened file. On win32, a file could be removed + # only if all handles were closed. + if platform.system() not in ['Windows']: + config.available_features.add('can-remove-opened-file') + + # Features + known_arches = ["x86_64", "mips64", "ppc64", "aarch64"] + if (any(config.target_triple.startswith(x) for x in known_arches)): + config.available_features.add("clang-target-64-bits") + + + + def calculate_arch_features(arch_string): + features = [] + for arch in arch_string.split(): + features.append(arch.lower() + '-registered-target') + return features + + + llvm_config.feature_config( + [('--assertion-mode', {'ON': 'asserts'}), + ('--cxxflags', {r'-D_GLIBCXX_DEBUG\b': 'libstdcxx-safe-mode'}), + ('--targets-built', calculate_arch_features), + ]) + + if lit.util.which('xmllint'): + config.available_features.add('xmllint') + + if config.enable_backtrace: + config.available_features.add('backtrace') + + if config.enable_threads: + config.available_features.add('thread_support') + + # Check if we should allow outputs to console. + run_console_tests = int(lit_config.params.get('enable_console', '0')) + if run_console_tests != 0: + config.available_features.add('console') + + lit.util.usePlatformSdkOnDarwin(config, lit_config) + macOSSDKVersion = lit.util.findPlatformSdkVersionOnMacOS(config, lit_config) + if macOSSDKVersion is not None: + config.available_features.add('macos-sdk-' + str(macOSSDKVersion)) + + if os.path.exists('/etc/gentoo-release'): + config.available_features.add('gentoo') + + if config.enable_shared: + config.available_features.add("enable_shared") + + # Add a vendor-specific feature. + if config.clang_vendor_uti: + config.available_features.add('clang-vendor=' + config.clang_vendor_uti) + + def exclude_unsupported_files_for_aix(dirname): + for filename in os.listdir(dirname): + source_path = os.path.join( dirname, filename) + if os.path.isdir(source_path): + continue + f = open(source_path, 'r', encoding='ISO-8859-1') + try: + data = f.read() + # 64-bit object files are not supported on AIX, so exclude the tests. + if (any(option in data for option in ('-emit-obj', '-fmodule-format=obj', '-fintegrated-as')) and + '64' in config.target_triple): + config.excludes += [ filename ] + finally: + f.close() + + if 'aix' in config.target_triple: + for directory in ('/CodeGenCXX', '/Misc', '/Modules', '/PCH', '/Driver', + '/ASTMerge/anonymous-fields', '/ASTMerge/injected-class-name-decl'): + exclude_unsupported_files_for_aix(config.test_source_root + directory) + +diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp +index 4f673bdcb7cc..424cb3a2034f 100644 +--- a/clang/tools/clang-repl/ClangRepl.cpp ++++ b/clang/tools/clang-repl/ClangRepl.cpp +@@ -1,133 +1,172 @@ + //===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===// + // + // 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 + // + //===----------------------------------------------------------------------===// + // + // This file implements a REPL tool on top of clang. + // + //===----------------------------------------------------------------------===// + + #include "clang/Basic/Diagnostic.h" + #include "clang/Frontend/CompilerInstance.h" + #include "clang/Frontend/FrontendDiagnostic.h" + #include "clang/Interpreter/Interpreter.h" + + #include "llvm/ExecutionEngine/Orc/LLJIT.h" + #include "llvm/LineEditor/LineEditor.h" + #include "llvm/Support/CommandLine.h" + #include "llvm/Support/ManagedStatic.h" // llvm_shutdown + #include "llvm/Support/Signals.h" + #include "llvm/Support/TargetSelect.h" // llvm::Initialize* + + static llvm::cl::list + ClangArgs("Xcc", + llvm::cl::desc("Argument to pass to the CompilerInvocation"), + llvm::cl::CommaSeparated); + static llvm::cl::opt OptHostSupportsJit("host-supports-jit", + llvm::cl::Hidden); ++static llvm::cl::opt OptHostSupportsException("host-supports-exception", ++ llvm::cl::Hidden); + static llvm::cl::list OptInputs(llvm::cl::Positional, + llvm::cl::desc("[code to run]")); + + static void LLVMErrorHandler(void *UserData, const char *Message, + bool GenCrashDiag) { + auto &Diags = *static_cast(UserData); + + Diags.Report(clang::diag::err_fe_error_backend) << Message; + + // Run the interrupt handlers to make sure any special cleanups get done, in + // particular that we remove files registered with RemoveFileOnSignal. + llvm::sys::RunInterruptHandlers(); + + // We cannot recover from llvm errors. When reporting a fatal error, exit + // with status 70 to generate crash diagnostics. For BSD systems this is + // defined as an internal software error. Otherwise, exit with status 1. + + exit(GenCrashDiag ? 70 : 1); + } + + // If we are running with -verify a reported has to be returned as unsuccess. + // This is relevant especially for the test suite. + static int checkDiagErrors(const clang::CompilerInstance *CI) { + unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors(); + if (CI->getDiagnosticOpts().VerifyDiagnostics) { + // If there was an error that came from the verifier we must return 1 as + // an exit code for the process. This will make the test fail as expected. + clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient(); + Client->EndSourceFile(); + Errs = Client->getNumErrors(); + + // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced. + Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor()); + } + return Errs ? EXIT_FAILURE : EXIT_SUCCESS; + } + ++// Check if the host environment supports c++ exception handling ++// by querying the existence of symbol __cxa_throw. ++static bool checkExceptionSupport() { ++ auto J = llvm::orc::LLJITBuilder().create(); ++ i \ No newline at end of file