Author: Haowei Date: 2026-09-23T00:30:36-07:00 New Revision: 45bc9c1acced1f6dfb20daf41a475433572dda8e
URL: https://github.com/llvm/llvm-project/commit/45bc9c1acced1f6dfb20daf41a475433572dda8e DIFF: https://github.com/llvm/llvm-project/commit/45bc9c1acced1f6dfb20daf41a475433572dda8e.diff LOG: Revert "[libc++] Build GoogleBenchmark directly from Lit (#224192)" This reverts commit 1bfe9893f3dc12f1c0f9546564519f02601b2192. Added: libcxx/test/benchmarks/CMakeLists.txt Modified: libcxx/test/CMakeLists.txt libcxx/test/configs/harness-configuration.cfg.in libcxx/utils/libcxx/test/config.py libcxx/utils/libcxx/test/format.py libcxxabi/test/configs/cmake-bridge.cfg.in libunwind/test/configs/cmake-bridge.cfg.in Removed: libcxx/test/benchmarks/lit.local.cfg libcxx/utils/libcxx/test/googlebenchmark.py ################################################################################ diff --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt index 85fa6aacc7796f..8fd34c086a9928 100644 --- a/libcxx/test/CMakeLists.txt +++ b/libcxx/test/CMakeLists.txt @@ -26,6 +26,7 @@ set(SERIALIZED_LIT_PARAMS "# Lit parameters serialized here for llvm-lit to pick serialize_lit_string_param(SERIALIZED_LIT_PARAMS compiler "${CMAKE_CXX_COMPILER}") if (LIBCXX_INCLUDE_BENCHMARKS) + add_subdirectory(benchmarks) set(_libcxx_benchmark_mode "dry-run") else() serialize_lit_string_param(SERIALIZED_LIT_PARAMS enable_benchmarks "no") diff --git a/libcxx/test/benchmarks/CMakeLists.txt b/libcxx/test/benchmarks/CMakeLists.txt new file mode 100644 index 00000000000000..b3f881a008b808 --- /dev/null +++ b/libcxx/test/benchmarks/CMakeLists.txt @@ -0,0 +1,48 @@ +#============================================================================== +# Build Google Benchmark +#============================================================================== + +include(ExternalProject) +set(BENCHMARK_COMPILE_FLAGS + -Wno-unused-command-line-argument + -nostdinc++ + -isystem "${LIBCXX_GENERATED_INCLUDE_DIR}" + -L${LIBCXX_LIBRARY_DIR} + -Wl,-rpath,${LIBCXX_LIBRARY_DIR} + ${SANITIZER_FLAGS} + ) +if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR) + list(APPEND BENCHMARK_COMPILE_FLAGS + -isystem "${LIBCXX_GENERATED_INCLUDE_TARGET_DIR}") +endif() +if (DEFINED LIBCXX_CXX_ABI_LIBRARY_PATH) + list(APPEND BENCHMARK_COMPILE_FLAGS + -L${LIBCXX_CXX_ABI_LIBRARY_PATH} + -Wl,-rpath,${LIBCXX_CXX_ABI_LIBRARY_PATH}) +endif() +split_list(BENCHMARK_COMPILE_FLAGS) + +set(BENCHMARK_CXX_LIBRARIES) +list(APPEND BENCHMARK_CXX_LIBRARIES c++) +if (NOT LIBCXX_ENABLE_SHARED) + list(APPEND BENCHMARK_CXX_LIBRARIES c++abi) +endif() + +ExternalProject_Add(google-benchmark + EXCLUDE_FROM_ALL ON + DEPENDS cxx cxx-headers + PREFIX google-benchmark + SOURCE_DIR ${LLVM_THIRD_PARTY_DIR}/benchmark + INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/google-benchmark + CMAKE_CACHE_ARGS + -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} + -DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM} + -DCMAKE_BUILD_TYPE:STRING=RELEASE + -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> + -DCMAKE_CXX_FLAGS:STRING=${BENCHMARK_COMPILE_FLAGS} + -DBENCHMARK_USE_LIBCXX:BOOL=ON + -DBENCHMARK_ENABLE_TESTING:BOOL=OFF + -DBENCHMARK_CXX_LIBRARIES:STRING=${BENCHMARK_CXX_LIBRARIES}) + +add_dependencies(cxx-test-depends google-benchmark) diff --git a/libcxx/test/benchmarks/lit.local.cfg b/libcxx/test/benchmarks/lit.local.cfg deleted file mode 100644 index 07a15e4ae5df95..00000000000000 --- a/libcxx/test/benchmarks/lit.local.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Build the GoogleBenchmark library using the current Lit configuration so -# that benchmarks can link against it. - -import libcxx.test.googlebenchmark - -if "enable-benchmarks=no" in config.available_features: - config.substitutions.append(("%{benchmark_flags}", "")) -else: - config.substitutions.append( - ("%{benchmark_flags}", libcxx.test.googlebenchmark.prepare(config, lit_config)) - ) diff --git a/libcxx/test/configs/harness-configuration.cfg.in b/libcxx/test/configs/harness-configuration.cfg.in index fab0ee69255014..c527d60c64af5d 100644 --- a/libcxx/test/configs/harness-configuration.cfg.in +++ b/libcxx/test/configs/harness-configuration.cfg.in @@ -26,4 +26,5 @@ config.test_exec_root = os.path.join('@LIBCXX_BINARY_DIR@', 'test') # Add substitutions for bootstrapping the test suite configuration config.substitutions.append(('%{libcxx-dir}', '@LIBCXX_SOURCE_DIR@')) +config.substitutions.append(('%{benchmark_flags}', '-I @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/include -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib64 -l benchmark')) config.substitutions.append(("%{python}", shlex.quote(sys.executable))) diff --git a/libcxx/utils/libcxx/test/config.py b/libcxx/utils/libcxx/test/config.py index c71e81cf9573ac..fd6f1bf5accd67 100644 --- a/libcxx/utils/libcxx/test/config.py +++ b/libcxx/utils/libcxx/test/config.py @@ -51,7 +51,7 @@ def configure(parameters, features, config, lit_config): ) # Print the basic substitutions - for sub in ("%{cxx}", "%{flags}", "%{compile_flags}", "%{link_flags}", "%{exec}"): + for sub in ("%{cxx}", "%{flags}", "%{compile_flags}", "%{link_flags}", "%{benchmark_flags}", "%{exec}"): debug("Using {} substitution: '{}'".format(sub, _getSubstitution(sub, config.substitutions))) # Print all available features diff --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py index 3e24e435e7c07b..2f449e234e6d5e 100644 --- a/libcxx/utils/libcxx/test/format.py +++ b/libcxx/utils/libcxx/test/format.py @@ -31,7 +31,7 @@ def _getTempPaths(test): def _checkBaseSubstitutions(substitutions): substitutions = [s for (s, _) in substitutions] - for s in ["%{cxx}", "%{compile_flags}", "%{link_flags}", "%{flags}", "%{exec}"]: + for s in ["%{cxx}", "%{compile_flags}", "%{link_flags}", "%{benchmark_flags}", "%{flags}", "%{exec}"]: assert s in substitutions, "Required substitution {} was not provided".format(s) def _executeScriptInternal(test, litConfig, commands): @@ -235,6 +235,10 @@ class CxxStandardLibraryTest(lit.formats.FileBasedTest): %{compile_flags} - Flags to use when compiling a test case %{link_flags} - Flags to use when linking a test case %{flags} - Flags to use either when compiling or linking a test case + %{benchmark_flags} - Flags to use when compiling benchmarks. These flags should provide access to + GoogleBenchmark but shouldn't hardcode any optimization level or other settings, + since the benchmarks should be run under the same configuration as the rest of + the test suite. %{exec} - A command to prefix the execution of executables Note that when building an executable (as opposed to only compiling a source @@ -355,13 +359,6 @@ def execute(self, test, litConfig): test.getFullName() ), ) - substitutions = [s for (s, _) in test.config.substitutions] - if "%{benchmark_flags}" not in substitutions: - return lit.Test.Result( - lit.Test.UNRESOLVED, - "Test {} is a benchmark, but the %{{benchmark_flags}} substitution " - "isn't provided by the configuration.".format(test.getFullName()), - ) steps = [ "%dbg(COMPILED WITH) %{cxx} %s %{flags} %{compile_flags} %{benchmark_flags} %{link_flags} -o %t.exe", ] diff --git a/libcxx/utils/libcxx/test/googlebenchmark.py b/libcxx/utils/libcxx/test/googlebenchmark.py deleted file mode 100644 index b5fe708e67f8cc..00000000000000 --- a/libcxx/utils/libcxx/test/googlebenchmark.py +++ /dev/null @@ -1,223 +0,0 @@ -# ===----------------------------------------------------------------------===## -# -# 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 -# -# ===----------------------------------------------------------------------===## - -""" -Support for building GoogleBenchmark from the Lit configuration. - -The benchmarks in the test suite are linked against GoogleBenchmark, which must be -built with the same Standard Library (and more generally with the same ABI-affecting -flags) as the benchmarks themselves. This allows building GoogleBenchmark on-demand -from Lit using the flags of the configuration being tested. - -The result is cached inside the build directory so that subsequent invocations are -cheap. -""" - -import hashlib -import os -import shlex -import subprocess - -import lit.TestRunner - -import libcxx.test.config -import libcxx.test.dsl - -THIS_FILE = os.path.abspath(__file__) -LIBCXX_UTILS = os.path.dirname(os.path.dirname(os.path.dirname(THIS_FILE))) -MONOREPO_ROOT = os.path.dirname(os.path.dirname(LIBCXX_UTILS)) -SOURCE_DIR = os.path.join(MONOREPO_ROOT, "third-party", "benchmark") - -# Flags used by the test suite that must not be used when building GoogleBenchmark. -# Anything that isn't listed here is forwarded verbatim. -# -# -Werror -# Avoid failing GoogleBenchmark's build due to warnings. -# -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -# Only relevant when testing libc++ itself. -# -fmodules, -fcxx-modules, -fmodules-cache-path= -# Modules are irrelevant when building a third-party static library, and sharing a -# module cache with the test suite is undesirable. -# -std= -# GoogleBenchmark sets CMAKE_CXX_STANDARD itself and requires C++17. -_DROPPED_FLAGS = { - "-Werror", - "-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER", - "-fmodules", - "-fcxx-modules", -} - -_DROPPED_FLAG_PREFIXES = ( - "-std=", - "-fmodules-cache-path=", -) - -# Flags that are dropped along with the '-Xclang' that introduces them. -_DROPPED_XCLANG_FLAGS = { - "-fmodules-local-submodule-visibility", -} - - -def _expand(config, string): - """ - Expand the Lit substitutions in the given string, recursively. - """ - (expanded,) = lit.TestRunner.applySubstitutions( - [string], - config.substitutions, - recursion_limit=config.recursiveExpansionLimit, - ) - return expanded - - -def _filterFlags(flags): - """ - Remove the flags that must not be used when building GoogleBenchmark. - """ - result = [] - flags = iter(flags) - for flag in flags: - if flag == "-Xclang": - arg = next(flags, None) - if arg is None: - result.append(flag) - elif arg not in _DROPPED_XCLANG_FLAGS: - result += [flag, arg] - elif flag not in _DROPPED_FLAGS and not flag.startswith(_DROPPED_FLAG_PREFIXES): - result.append(flag) - return result - - -def _getFlags(config, substitutions): - """ - Return the flags contained in the given substitutions, based on the flags used by the - configuration under test. - """ - flags = [] - for substitution in substitutions: - expanded = _expand(config, _getSubstitution(substitution, config)) - flags += shlex.split(expanded) - return _filterFlags(flags) - - -def _splitLibraries(flags): - """ - Split the given link flags into (flags, libraries), where libraries contains the - name of the libraries that were being linked against. - - We can't simply hand the libraries over to CMake as part of CMAKE_CXX_FLAGS, since - CMake puts those flags before the object files on the link line. - """ - result = [] - libraries = [] - flags = iter(flags) - for flag in flags: - if flag == "-l": - library = next(flags, None) - if library is None: - result.append(flag) - else: - libraries.append(library) - elif flag.startswith("-l"): - libraries.append(flag[len("-l") :]) - else: - result.append(flag) - return (result, libraries) - - -def _getSubstitution(substitution, config): - return libcxx.test.config._getSubstitution(substitution, config.substitutions) - - -def _fingerprint(config, flags, libraries): - """ - Return an opaque value identifying this GoogleBenchmark build. - - This changes whenever the compiler is rebuilt or whenever the flags used to build - GoogleBenchmark change. - """ - compiler = libcxx.test.dsl._compilerFingerprint(config) - return hashlib.sha256(repr((compiler, flags, libraries)).encode()).hexdigest()[:16] - - -def _run(litConfig, what, command, cwd): - result = subprocess.run( - command, - cwd=cwd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True, - ) - if result.returncode != 0: - pretty = " ".join(shlex.quote(arg) for arg in command) - litConfig.fatal( - "Failed to {} GoogleBenchmark.\n" - "Command was:\n{}\n\n" - "Output was:\n{}".format(what, pretty, result.stdout) - ) - - -def prepare(config, litConfig): - """ - Make GoogleBenchmark available to the test suite and return the flags required to - build the benchmarks against it. - - GoogleBenchmark is built using the same flags as the rest of the test suite, and the - result is cached inside the build directory. The cache is keyed on the flags being - used, so diff erent Lit configurations do not interfere with each other. - """ - flags, libraries = _splitLibraries( - _getFlags(config, ("%{flags}", "%{compile_flags}", "%{link_flags}")) - ) - root = os.path.join(config.test_exec_root, "__gbench__") - prefix = os.path.join(root, _fingerprint(config, flags, libraries)) - buildDir = os.path.join(prefix, "build") - installDir = os.path.join(prefix, "install") - os.makedirs(root, exist_ok=True) - - cmake = os.environ.get("CMAKE", "cmake") - - if not os.path.exists(os.path.join(buildDir, "CMakeCache.txt")): - litConfig.note("Configuring GoogleBenchmark in {}".format(buildDir)) - compiler = _expand(config, _getSubstitution("%{cxx}", config)) - _run( - litConfig, - "configure", - [ - cmake, - "-S", SOURCE_DIR, - "-B", buildDir, - "-DCMAKE_BUILD_TYPE=Release", - "-DCMAKE_CXX_COMPILER={}".format(compiler), - "-DCMAKE_CXX_FLAGS={}".format(" ".join(flags)), - # Set CMAKE_EXE_LINKER_FLAGS in addition to BENCHMARK_CXX_LIBRARIES since we - # need CMake's own probe executables to have the right linker flags. - "-DCMAKE_EXE_LINKER_FLAGS={}".format(" ".join("-l{}".format(lib) for lib in libraries)), - "-DCMAKE_INSTALL_PREFIX={}".format(installDir), - "-DCMAKE_INSTALL_LIBDIR=lib", - "-DBENCHMARK_CXX_LIBRARIES={}".format(";".join(libraries)), - "-DBENCHMARK_ENABLE_TESTING=OFF", - "-DBENCHMARK_ENABLE_WERROR=OFF", - "-DBENCHMARK_INSTALL_DOCS=OFF", - ], - cwd=root, - ) - - # Always build: GoogleBenchmark is compiled against the headers of the library - # under test, so it must be rebuilt when those change. This is a no-op when - # nothing changed. - _run( - litConfig, - "build", - [cmake, "--build", buildDir, "--target", "install", "--parallel", str(os.cpu_count() or 1)], - cwd=root, - ) - - include = os.path.join(installDir, "include") - lib = os.path.join(installDir, "lib") - return "-isystem {} -L {} -l benchmark".format(include, lib) diff --git a/libcxxabi/test/configs/cmake-bridge.cfg.in b/libcxxabi/test/configs/cmake-bridge.cfg.in index a2fa847f2d6c66..f81dd8afb10915 100644 --- a/libcxxabi/test/configs/cmake-bridge.cfg.in +++ b/libcxxabi/test/configs/cmake-bridge.cfg.in @@ -34,6 +34,7 @@ config.substitutions.append(('%{include}', '@LIBCXXABI_TESTING_INSTALL_PREFIX@/i config.substitutions.append(('%{cxx-include}', '@LIBCXXABI_TESTING_INSTALL_PREFIX@/@LIBCXXABI_INSTALL_INCLUDE_DIR@')) config.substitutions.append(('%{cxx-target-include}', '@LIBCXXABI_TESTING_INSTALL_PREFIX@/@LIBCXXABI_INSTALL_INCLUDE_TARGET_DIR@')) config.substitutions.append(('%{lib}', '@LIBCXXABI_TESTING_INSTALL_PREFIX@/@LIBCXXABI_INSTALL_LIBRARY_DIR@')) +config.substitutions.append(('%{benchmark_flags}', '')) if @LIBCXXABI_USE_LLVM_UNWINDER@: config.substitutions.append(('%{maybe-include-libunwind}', '-I "@LIBCXXABI_LIBUNWIND_INCLUDES_INTERNAL@"')) diff --git a/libunwind/test/configs/cmake-bridge.cfg.in b/libunwind/test/configs/cmake-bridge.cfg.in index 98e7d8d653f5dc..ed0b2afbc0c0d2 100644 --- a/libunwind/test/configs/cmake-bridge.cfg.in +++ b/libunwind/test/configs/cmake-bridge.cfg.in @@ -45,6 +45,7 @@ config.substitutions.append(('%{libcxx}', '@LIBUNWIND_LIBCXX_PATH@')) config.substitutions.append(('%{install-prefix}', '@LIBUNWIND_TESTING_INSTALL_PREFIX@')) config.substitutions.append(('%{include}', '@LIBUNWIND_TESTING_INSTALL_PREFIX@/include')) config.substitutions.append(('%{lib}', '@LIBUNWIND_TESTING_INSTALL_PREFIX@/@LIBUNWIND_INSTALL_LIBRARY_DIR@')) +config.substitutions.append(('%{benchmark_flags}', '')) # Check for objcopy tools objcopy_path = which('llvm-objcopy', '@LLVM_BUILD_BINARY_DIR@/bin') _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
