diff --git a/.github/workflows/c-cpp-cmake.yml b/.github/workflows/c-cpp-cmake.yml new file mode 100644 index 000000000..9d23cc2a7 --- /dev/null +++ b/.github/workflows/c-cpp-cmake.yml @@ -0,0 +1,124 @@ +name: C/C++ CMake CI + +on: + push: + pull_request: + branches: [ "master" ] + +jobs: + build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + include: + - name: Linux GCC + os: ubuntu-latest + arch: x86_64 + target: native + cmake_args: "" + - name: Linux GCC (no SSE2/AVX2) + os: ubuntu-latest + arch: x86_64 + target: native + cmake_args: "-DUSE_SSE2=OFF -DUSE_AVX2=OFF" + - name: Linux ARM GCC + os: ubuntu-24.04-arm + arch: aarch64 + target: native + cmake_args: "" + - name: Linux ARM GCC (no NEON) + os: ubuntu-24.04-arm + arch: aarch64 + target: native + cmake_args: "-DUSE_NEON=OFF" + - name: Windows MinGW + os: windows-latest + arch: x86_64 + target: native + cmake_args: "" + - name: Windows cross (MinGW) + os: ubuntu-latest + arch: x86_64 + target: windows-cross + cmake_args: "" + - name: macOS ARM Clang + os: macos-latest + arch: arm64 + target: native + cmake_args: "" + + steps: + - name: Configure Windows line endings + if: runner.os == 'Windows' + shell: pwsh + run: git config --global core.autocrlf input + + - uses: actions/checkout@v6 + + - name: Set up MSYS2 + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + mingw-w64-ucrt-x86_64-cmake + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-ninja + + - name: Install Windows cross tools + if: matrix.target == 'windows-cross' + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends --yes mingw-w64 wine + + - name: Build and test + if: runner.os != 'Windows' + shell: bash + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + cmake --version + if test "${{ matrix.target }}" = "windows-cross"; then + cmake -S . -B build \ + -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + -DCMAKE_CROSSCOMPILING_EMULATOR=wine \ + -DCMAKE_EXE_LINKER_FLAGS="-static -static-libgcc -static-libstdc++" \ + ${{ matrix.cmake_args }} + else + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + ${{ matrix.cmake_args }} + fi + cmake --build build --parallel 2 + if test "${{ matrix.target }}" = "windows-cross"; then + WINEARCH=win64 WINEDEBUG=-all WINEPREFIX="$RUNNER_TEMP/wine" \ + ctest --test-dir build --output-on-failure + else + ctest --test-dir build --output-on-failure + fi + cmake --install build --prefix "$RUNNER_TEMP/reflex-install" + + - name: Build and test + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + cmake --version + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + ${{ matrix.cmake_args }} + cmake --build build --parallel 2 + ctest --test-dir build --output-on-failure + cmake --install build --prefix "$RUNNER_TEMP/reflex-install" diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 701d2c862..b45db7aee 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -2,22 +2,123 @@ name: C/C++ CI on: push: - branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: Linux GCC + os: ubuntu-latest + arch: x86_64 + target: native + configure_args: "" + - name: Linux GCC (no SSE2/AVX2) + os: ubuntu-latest + arch: x86_64 + target: native + configure_args: --disable-sse2 --disable-avx2 + - name: Linux ARM GCC + os: ubuntu-24.04-arm + arch: aarch64 + target: native + configure_args: "" + - name: Linux ARM GCC (no NEON) + os: ubuntu-24.04-arm + arch: aarch64 + target: native + configure_args: --disable-neon + - name: Windows MinGW + os: windows-latest + arch: x86_64 + target: native + configure_args: "" + - name: Windows cross (MinGW) + os: ubuntu-latest + arch: x86_64 + target: windows-cross + configure_args: "" + - name: macOS ARM Clang + os: macos-latest + arch: arm64 + target: native + configure_args: "" steps: - - uses: actions/checkout@v3 - - name: Bootstrap autotools - run: autoreconf -i - - name: configure - run: ./configure - - name: make - run: make - - name: make test - run: make test + - name: Configure Windows line endings + if: runner.os == 'Windows' + shell: pwsh + run: git config --global core.autocrlf input + + - uses: actions/checkout@v6 + + - name: Set up MSYS2 + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + autoconf + automake + bison + make + mingw-w64-ucrt-x86_64-gcc + + - name: Install macOS build tools + if: runner.os == 'macOS' + env: + HOMEBREW_NO_AUTO_UPDATE: 1 + run: brew install autoconf automake + + - name: Install Windows cross tools + if: matrix.target == 'windows-cross' + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends --yes mingw-w64 wine + + - name: Build and test + if: runner.os != 'Windows' + shell: bash + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + autoreconf -i + if test "${{ matrix.target }}" = "windows-cross"; then + CC=x86_64-w64-mingw32-gcc \ + CXX=x86_64-w64-mingw32-g++ \ + AR=x86_64-w64-mingw32-ar \ + RANLIB=x86_64-w64-mingw32-ranlib \ + LDFLAGS="-static -static-libgcc -static-libstdc++" \ + ./configure --host=x86_64-w64-mingw32 ${{ matrix.configure_args }} + else + ./configure ${{ matrix.configure_args }} + fi + make -j2 + make -C tests + if test "${{ matrix.target }}" = "windows-cross"; then + WINEARCH=win64 WINEDEBUG=-all WINEPREFIX="$RUNNER_TEMP/wine" \ + wine ./tests/rtest.exe + else + ./tests/rtest + fi + + - name: Build and test + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + autoreconf -i + ./configure ${{ matrix.configure_args }} + make -j2 + make -C tests + ./tests/rtest diff --git a/CMakeLists.txt b/CMakeLists.txt index d10c279d7..a183920a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,33 +2,59 @@ cmake_minimum_required(VERSION 3.15) project(reflex VERSION 6.3.0 LANGUAGES CXX) +# Make the project-specific modules under cmake/ available to include(). set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +# Ask CMake to compile all targets as C++11. set(CMAKE_CXX_STANDARD 11) -# The following setups the simd_* variables -include(SIMDTestAndSetup) +# GNUInstallDirs supplies portable install paths, while CTest defines the +# BUILD_TESTING option and enables test registration when requested. +include(GNUInstallDirs) +include(CTest) -# -# Defining source variables -# +# Clang-cl uses MSVC command-line and binary interfaces despite having a +# different compiler ID. Treat both toolchains alike when selecting targets. +set(REFLEX_MSVC_TOOLCHAIN FALSE) +if(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + set(REFLEX_MSVC_TOOLCHAIN TRUE) +endif() -set(lib_sources - lib/convert.cpp +# The following sets up the simd_* variables. +include(SIMDTestAndSetup) + +# Keep the matcher implementation shared by the full and minimal libraries in +# one list so the two variants do not drift apart. +set(libreflex_common_sources lib/debug.cpp lib/error.cpp lib/input.cpp lib/matcher.cpp - lib/matcher_avx2.cpp - lib/matcher_avx512bw.cpp lib/pattern.cpp - lib/posix.cpp lib/simd.cpp - lib/simd_avx2.cpp - lib/simd_avx512bw.cpp - lib/unicode.cpp lib/utf8.cpp +) +# Use AVX2 flags for AVX2 sources. +if(REFLEX_COMPILER_HAS_AVX2) + set(reflex_avx2_sources lib/matcher_avx2.cpp lib/simd_avx2.cpp) + list(APPEND libreflex_common_sources ${reflex_avx2_sources}) + set_source_files_properties(${reflex_avx2_sources} PROPERTIES COMPILE_OPTIONS "${simd_avx2_flags}") +endif() + +# Use AVX512BW flags for AVX512BW wources. +if(REFLEX_COMPILER_HAS_AVX512BW) + set(reflex_avx512bw_sources lib/matcher_avx512bw.cpp lib/simd_avx512bw.cpp) + list(APPEND libreflex_common_sources ${reflex_avx512bw_sources}) + set_source_files_properties(${reflex_avx512bw_sources} PROPERTIES COMPILE_OPTIONS "${simd_avx512bw_flags}") +endif() + +# The full library adds pattern conversion, POSIX support, and Unicode tables. +set(libreflex_sources + ${libreflex_common_sources} + lib/convert.cpp + lib/posix.cpp + lib/unicode.cpp unicode/block_scripts.cpp unicode/language_scripts.cpp unicode/letter_scripts.cpp @@ -36,74 +62,111 @@ set(lib_sources unicode/composer.cpp ) -set(bin_sources - src/reflex.cpp +# The minimal library contains only the runtime needed by generated scanners. +set(libreflexmin_sources + ${libreflex_common_sources} ) -list(TRANSFORM lib_sources PREPEND ${PROJECT_SOURCE_DIR}/) -list(TRANSFORM bin_sources PREPEND ${PROJECT_SOURCE_DIR}/) +# Only build static libs for MSVC because the public headers do not declare symbols with dllexport/dllimport. +if(REFLEX_MSVC_TOOLCHAIN) + add_library(ReflexLib STATIC) + add_library(ReflexMinLib STATIC) + set(reflex_library_targets ReflexLib) + set(reflexmin_library_targets ReflexMinLib) +else() + add_library(ReflexLib SHARED) + add_library(ReflexLibStatic STATIC) + add_library(ReflexMinLib SHARED) + add_library(ReflexMinLibStatic STATIC) + set(reflex_library_targets ReflexLib ReflexLibStatic) + set(reflexmin_library_targets ReflexMinLib ReflexMinLibStatic) +endif() -# -# Defining targets section -# +# Set the source files contained in each full-library target. +foreach(target IN LISTS reflex_library_targets) + target_sources(${target} PRIVATE ${libreflex_sources}) +endforeach() -add_library(ReflexLib SHARED "") -target_sources(ReflexLib PRIVATE ${lib_sources}) -target_include_directories(ReflexLib PUBLIC - $ - $ -) -target_compile_definitions(ReflexLib PRIVATE ${simd_definitions}) -target_compile_options(ReflexLib PRIVATE ${simd_flags}) - -add_library(ReflexLibStatic STATIC "") -target_sources(ReflexLibStatic PRIVATE ${lib_sources}) -target_include_directories(ReflexLibStatic PUBLIC - $ - $ +# Set the smaller source set contained in each minimal-library target. +foreach(target IN LISTS reflexmin_library_targets) + target_sources(${target} PRIVATE ${libreflexmin_sources}) +endforeach() + +# Gather all library targets so their common build settings remain identical. +set(reflex_all_library_targets + ${reflex_library_targets} + ${reflexmin_library_targets} ) -target_compile_definitions(ReflexLibStatic PRIVATE ${simd_definitions}) -target_compile_options(ReflexLibStatic PRIVATE ${simd_flags}) - -add_executable(Reflex "") -target_sources(Reflex PRIVATE ${bin_sources}) -target_link_libraries(Reflex PRIVATE ReflexLibStatic) -target_compile_definitions(Reflex PRIVATE ${simd_definitions}) -target_compile_options(Reflex PRIVATE ${simd_flags}) - -# Don't user target name as filename instead use lowercase name for backwards compatibility -set_target_properties(ReflexLibStatic PROPERTIES OUTPUT_NAME reflex_static_lib) -set_target_properties(ReflexLib PROPERTIES OUTPUT_NAME reflex_shared_lib) -set_target_properties(Reflex PROPERTIES OUTPUT_NAME reflex) -# -# Exporting targets section -# +# Use source-tree headers while building and installed headers for consumers; +# SIMD definitions and options remain private implementation details. +foreach(target IN LISTS reflex_all_library_targets) + target_include_directories(${target} PUBLIC + $ + $ + ) + target_compile_definitions(${target} PRIVATE ${simd_definitions}) + target_compile_options(${target} PRIVATE ${simd_flags}) +endforeach() + +# Link the generator to the static full library where it has a distinct target; +# on MSVC-style toolchains ReflexLib is already the static library. +add_executable(Reflex src/reflex.cpp) +if(TARGET ReflexLibStatic) + target_link_libraries(Reflex PRIVATE ReflexLibStatic) +else() + target_link_libraries(Reflex PRIVATE ReflexLib) +endif() -include(GNUInstallDirs) +# CTest defines BUILD_TESTING; when enabled, build and register the same rtest +# regression executable used by the Autotools build. +if(BUILD_TESTING) + add_executable(rtest tests/rtest.cpp) + if(TARGET ReflexLibStatic) + target_link_libraries(rtest PRIVATE ReflexLibStatic) + else() + # MSVC-style builds expose ReflexLib itself as the static library because + # the public headers do not provide the exports required by a usable DLL. + target_link_libraries(rtest PRIVATE ReflexLib) + endif() + add_test(NAME rtest COMMAND rtest) +endif() + +# Target names are internal to CMake; give installed artifacts their traditional +# reflex and reflexmin basenames for compatibility with existing consumers. +foreach(target IN LISTS reflex_library_targets) + set_target_properties(${target} PROPERTIES OUTPUT_NAME reflex) +endforeach() + +foreach(target IN LISTS reflexmin_library_targets) + set_target_properties(${target} PROPERTIES OUTPUT_NAME reflexmin) +endforeach() + +set_target_properties(Reflex PROPERTIES OUTPUT_NAME reflex) -install(TARGETS Reflex ReflexLib ReflexLibStatic +# Install the generator and public libraries, and record the library targets for +# the find_package export generated below. +install(TARGETS Reflex ${reflex_all_library_targets} EXPORT ReflexTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) +# Install only the public C++ headers from the source tree. install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/reflex DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h" ) +# Install imported targets such as Reflex::ReflexLib for downstream CMake users. install(EXPORT ReflexTargets NAMESPACE Reflex:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) -# -# Packaging section (find_package support) -# - +# Generate and install the relocatable configuration loaded by +# find_package(Reflex CONFIG). include(CMakePackageConfigHelpers) configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in @@ -111,39 +174,60 @@ configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) +# Do not promise compatibility between releases until the project defines an +# explicit C++ API and ABI compatibility policy. +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/ReflexConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY ExactVersion +) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ReflexConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/ReflexConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) -# -# Packaging section (pkg-config support) -# - -set(prefix "${CMAKE_INSTALL_PREFIX}") -set(exec_prefix "\${prefix}") -set(REFLEX_PKGCONFIG_LIBRARY "reflex_shared_lib") - +# Make relative installations relocatable by deriving their prefix from the +# installed .pc file. Absolute library destinations retain their fixed paths. if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(prefix "${CMAKE_INSTALL_PREFIX}") + set(exec_prefix "\${prefix}") set(libdir "${CMAKE_INSTALL_LIBDIR}") else() + # Anchor both inputs in the native build directory because RELATIVE_PATH + # requires full paths; only their relative structure enters the .pc file. + file(RELATIVE_PATH reflex_pc_prefix_relative + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/pkgconfig" + "${CMAKE_CURRENT_BINARY_DIR}" + ) + set(prefix "\${pcfiledir}/${reflex_pc_prefix_relative}") + set(exec_prefix "\${prefix}") set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") endif() +# GNUInstallDirs also permits the header destination to be absolute. if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") set(includedir "${CMAKE_INSTALL_INCLUDEDIR}") else() set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() -# Compatibility note: CMake currently installs libreflex_shared_lib rather than -# libreflex, so its pkg-config file must use the CMake library name. -configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in +# Reuse the Autotools templates, which expect the project version in VERSION. +set(VERSION "${PROJECT_VERSION}") +configure_file(${PROJECT_SOURCE_DIR}/reflex.pc.in "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" @ONLY ) +configure_file(${PROJECT_SOURCE_DIR}/reflexmin.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/reflexmin.pc" + @ONLY +) + +# Install both pkg-config descriptions alongside the libraries. install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" + "${CMAKE_CURRENT_BINARY_DIR}/reflexmin.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) diff --git a/Makefile.am b/Makefile.am index 87c260d86..6aeb062c6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,7 @@ all-local: cp2bin .PHONY: cp2bin # to copy the reflex binary to reflex/bin -cp2bin: $(top_builddir)/src/reflex +cp2bin: $(top_builddir)/src/reflex$(EXEEXT) -mkdir -p $(top_builddir)/bin -cp -f $< $(top_builddir)/bin @echo @@ -50,5 +50,5 @@ install-data-hook: .PHONY: test -test: $(top_builddir)/src/reflex +test: $(top_builddir)/src/reflex$(EXEEXT) -cd tests; $(MAKE) && ./rtest diff --git a/Makefile.in b/Makefile.in index ef0c6cd0b..32e7236a7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -931,7 +931,7 @@ all-local: cp2bin .PHONY: cp2bin # to copy the reflex binary to reflex/bin -cp2bin: $(top_builddir)/src/reflex +cp2bin: $(top_builddir)/src/reflex$(EXEEXT) -mkdir -p $(top_builddir)/bin -cp -f $< $(top_builddir)/bin @echo @@ -965,7 +965,7 @@ install-data-hook: .PHONY: test -test: $(top_builddir)/src/reflex +test: $(top_builddir)/src/reflex$(EXEEXT) -cd tests; $(MAKE) && ./rtest # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index 5cf14bf57..dbf74ad50 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -1,5 +1,9 @@ +# CMake expands this marker into helpers that make the installed package +# relocatable and provide check_required_components(). @PACKAGE_INIT@ +# Load the imported Reflex:: targets generated by the project's install(EXPORT). include("${CMAKE_CURRENT_LIST_DIR}/ReflexTargets.cmake") -check_required_components(Reflex) \ No newline at end of file +# Honor REQUIRED semantics if consumers request package components later. +check_required_components(Reflex) diff --git a/cmake/SIMDTestAndSetup.cmake b/cmake/SIMDTestAndSetup.cmake index e82891d5e..9d7e4f9ca 100644 --- a/cmake/SIMDTestAndSetup.cmake +++ b/cmake/SIMDTestAndSetup.cmake @@ -1,96 +1,135 @@ -# SIMD intrinsics setup and test. -# Tests whether specifics intrinsics are available and then defines macro definitions -# Expose the simd_definitions and simd_flags when included from another cmake file. +# Test whether the compiler accepts each SIMD implementation with its required +# options. The resulting variables configure baseline and optimized sources. -include(CheckCXXSourceRuns) +# These modules provide compile probes and isolate their temporary flags from +# the rest of the project configuration. +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) +# Options let packagers disable a backend; an enabled backend must still pass +# its compiler probe before it is built. option(USE_AVX512BW "Enable AVX512BW intrinsics (if available)" ON) option(USE_AVX2 "Enable AVX2 intrinsics (if available)" ON) option(USE_SSE2 "Enable SSE2 intrinsics (if available)" ON) option(USE_NEON "Enable NEON intrinsics (if available)" ON) -if (USE_AVX512BW) - check_cxx_source_runs(" - #include +# Select the SIMD option spelling understood by the active compiler interface. +if(REFLEX_MSVC_TOOLCHAIN) + set(reflex_sse2_flag "/arch:SSE2") + set(reflex_avx2_flag "/arch:AVX2") + set(reflex_avx512bw_flag "/arch:AVX512") +else() + set(reflex_sse2_flag "-msse2") + set(reflex_avx2_flag "-mavx2") + set(reflex_avx512bw_flag "-mavx512bw") +endif() + +# Compile rather than run the probe so configuration also works when the build +# machine cannot execute binaries for the target architecture. +if(USE_SSE2) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "${reflex_sse2_flag}") + check_cxx_source_compiles(" + #include int main() { - __m512 n = _mm512_set1_epi8(42); - (void)_mm512_cmpeq_epi8_mask(n, n); - return 0; + __m128i n = _mm_set1_epi8(42); + return _mm_cvtsi128_si32(n) != 42; } - " HAVE_AVX512BW) + " REFLEX_COMPILER_HAS_SSE2) + cmake_pop_check_state() +else() + set(REFLEX_COMPILER_HAS_SSE2 FALSE) endif() -if (USE_AVX2) - check_cxx_source_runs(" +# AVX implementations use SSE2 as their portable runtime fallback, so do not +# enable them when the baseline SSE2 implementation was explicitly disabled. +if(USE_AVX2 AND REFLEX_COMPILER_HAS_SSE2) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "${reflex_avx2_flag}") + check_cxx_source_compiles(" #include int main() { __m256i n = _mm256_set1_epi8(42); - (void)_mm256_movemask_epi8(_mm256_and_si256(n, n)); - return 0; + return _mm256_movemask_epi8(_mm256_and_si256(n, n)) == 0; } - " HAVE_AVX2) + " REFLEX_COMPILER_HAS_AVX2) + cmake_pop_check_state() +else() + set(REFLEX_COMPILER_HAS_AVX2 FALSE) endif() -if (USE_SSE2) - check_cxx_source_runs(" - #include +# The AVX512BW implementation dispatches to AVX2 on machines without AVX512BW, +# so only build it when the AVX2 implementation is also available. +if(USE_AVX512BW AND REFLEX_COMPILER_HAS_AVX2) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "${reflex_avx512bw_flag}") + check_cxx_source_compiles(" + #include int main() { - __m128i n = _mm_set1_epi8(42); - return 0; + __m512i n = _mm512_set1_epi8(42); + return _mm512_cmpeq_epi8_mask(n, n) == 0; } - " HAVE_SSE2) + " REFLEX_COMPILER_HAS_AVX512BW) + cmake_pop_check_state() +else() + set(REFLEX_COMPILER_HAS_AVX512BW FALSE) endif() -if (USE_NEON) - check_cxx_source_runs(" +# Probe NEON only when no x86 backend is available. AArch64 needs no extra +# option, while some 32-bit ARM compilers require -mfpu=neon. +if(USE_NEON AND NOT REFLEX_COMPILER_HAS_SSE2) + cmake_push_check_state(RESET) + check_cxx_source_compiles(" #include int main() { - uint64x2_t n; - uint64_t m = vgetq_lane_u64(n, 0); - return 0; + uint64x2_t n = vdupq_n_u64(42); + return vgetq_lane_u64(n, 0) != 42; } - " HAVE_NEON) + " REFLEX_COMPILER_HAS_NEON) + cmake_pop_check_state() + + if(NOT REFLEX_COMPILER_HAS_NEON AND NOT REFLEX_MSVC_TOOLCHAIN) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "-mfpu=neon") + check_cxx_source_compiles(" + #include + int main() { + uint64x2_t n = vdupq_n_u64(42); + return vgetq_lane_u64(n, 0) != 42; + } + " REFLEX_COMPILER_HAS_NEON_WITH_FLAG) + cmake_pop_check_state() + else() + set(REFLEX_COMPILER_HAS_NEON_WITH_FLAG FALSE) + endif() +else() + set(REFLEX_COMPILER_HAS_NEON FALSE) + set(REFLEX_COMPILER_HAS_NEON_WITH_FLAG FALSE) endif() +# These lists are consumed by the parent CMakeLists.txt when it defines targets. set(simd_definitions "") set(simd_flags "") +set(simd_avx2_flags "") +set(simd_avx512bw_flags "") -if (${HAVE_AVX512BW}) +# Define only the strongest backend macro. Baseline sources stay at SSE2 while +# optimized AVX sources receive the flags required by their specialized code. +if(REFLEX_COMPILER_HAS_AVX512BW) list(APPEND simd_definitions HAVE_AVX512BW) - if (WIN32 AND MSVC) - list(APPEND simd_flags "/arch:AVX512") - else() - list(APPEND simd_flags "-mavx512bw") - endif() -endif() - -if (${HAVE_AVX2}) + list(APPEND simd_flags "${reflex_sse2_flag}") + list(APPEND simd_avx2_flags "${reflex_avx2_flag}") + list(APPEND simd_avx512bw_flags "${reflex_avx512bw_flag}") +elseif(REFLEX_COMPILER_HAS_AVX2) list(APPEND simd_definitions HAVE_AVX2) - if (WIN32 AND MSVC) - list(APPEND simd_flags "/arch:AVX2") - else() - list(APPEND simd_flags "-mavx2") - endif() -endif() - -if (${HAVE_SSE2}) + list(APPEND simd_flags "${reflex_sse2_flag}") + list(APPEND simd_avx2_flags "${reflex_avx2_flag}") +elseif(REFLEX_COMPILER_HAS_SSE2) list(APPEND simd_definitions HAVE_SSE2) - if (WIN32 AND MSVC) - if ("${CMAKE_GENERATOR_PLATFORM}" MATCHES "Win32") - # SSE2 is a given if the system is running x64 - list(APPEND simd_flags "/arch:SSE2") - endif() - else() - list(APPEND simd_flags "-msse2") - endif() -endif() - -if (${HAVE_NEON}) + list(APPEND simd_flags "${reflex_sse2_flag}") +elseif(REFLEX_COMPILER_HAS_NEON) list(APPEND simd_definitions HAVE_NEON) - if ((${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm64|aarch64") OR (WIN32 AND MSVC)) - # Arm64 compilers and MSVC runs NEON by default according to their docs - else() - list(APPEND simd_flags "-march=native" "-mfpu=neon") - endif() +elseif(REFLEX_COMPILER_HAS_NEON_WITH_FLAG) + list(APPEND simd_definitions HAVE_NEON) + list(APPEND simd_flags "-mfpu=neon") endif() - diff --git a/cmake/reflex.pc.in b/cmake/reflex.pc.in deleted file mode 100644 index 0ef7c448c..000000000 --- a/cmake/reflex.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: RE/flex -Description: high-performance C++ regex library and lexical analyzer generator -Version: @PROJECT_VERSION@ -Requires: -Libs: -L${libdir} -l@REFLEX_PKGCONFIG_LIBRARY@ -Cflags: -I${includedir}