From bf344532f17388c64be7a0c1cbe1e01deb6edd85 Mon Sep 17 00:00:00 2001 From: Zachary Ferguson Date: Mon, 18 Sep 2023 21:56:55 -0400 Subject: [PATCH] Update FindSIMD CMake --- .github/workflows/continuous.yml | 4 +- .github/workflows/docs.yml | 2 +- .github/workflows/pypi.yml | 4 +- .github/workflows/python.yml | 2 +- CMakeLists.txt | 18 +- cmake/find/FindAVX.cmake | 163 ---------- cmake/find/FindFMA.cmake | 107 ------- cmake/find/FindSIMD.cmake | 514 +++++++++++++++++++++++++++++++ cmake/find/FindSSE.cmake | 300 ------------------ 9 files changed, 527 insertions(+), 587 deletions(-) delete mode 100644 cmake/find/FindAVX.cmake delete mode 100644 cmake/find/FindFMA.cmake create mode 100644 cmake/find/FindSIMD.cmake delete mode 100644 cmake/find/FindSSE.cmake diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index d35ec6886..a66eeb168 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -39,7 +39,7 @@ jobs: name: Linux steps: - name: Checkout repository - uses: actions/checkout@v2.5.0 + uses: actions/checkout@v4.0.0 with: fetch-depth: 10 @@ -94,7 +94,7 @@ jobs: config: [Debug, Release] steps: - name: Checkout repository - uses: actions/checkout@v2.5.0 + uses: actions/checkout@v4.0.0 with: fetch-depth: 10 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index eff26edee..31cf2d68f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4.0.0 with: fetch-depth: 10 diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 4075cb370..3dcd30495 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -37,7 +37,7 @@ jobs: name: macOS steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.0.0 - name: Build wheels uses: pypa/cibuildwheel@v2.12.1 @@ -57,7 +57,7 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.0.0 - name: Build sdist run: pipx run build --sdist diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e66c6e68b..a57b993de 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -32,7 +32,7 @@ jobs: name: macOS steps: - name: Checkout Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4.0.0 with: fetch-depth: 10 diff --git a/CMakeLists.txt b/CMakeLists.txt index 61d35bc53..c3515752f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,18 +188,14 @@ target_link_libraries(ipc_toolkit PRIVATE ipc::toolkit::warnings) ## SIMD support if(IPC_TOOLKIT_WITH_SIMD) - # Figure out SSE level support - message(STATUS "Seaching for SSE...") - find_package(SSE) - # Figure out AVX level support - message(STATUS "Searching for AVX...") - find_package(AVX) - # Figure out FMA level support - message(STATUS "Searching for FMA...") - find_package(FMA) - # Add SSE, AVX, and FMA flags to compiler flags - string(REPLACE " " ";" SIMD_FLAGS "${SSE_FLAGS} ${AVX_FLAGS} ${FMA_FLAGS}") + # Figure out SIMD support + message(STATUS "Testing SIMD capabilities...") + find_package(SIMD) + # Add SIMD flags to compiler flags + message(STATUS "Using SIMD flags: ${SIMD_FLAGS}") target_compile_options(ipc_toolkit PRIVATE ${SIMD_FLAGS}) +else() + message(STATUS "SIMD support disabled") endif() # Use C++17 diff --git a/cmake/find/FindAVX.cmake b/cmake/find/FindAVX.cmake deleted file mode 100644 index b3d1e2c4e..000000000 --- a/cmake/find/FindAVX.cmake +++ /dev/null @@ -1,163 +0,0 @@ -################################################################################ -# Copyright (c) 2016, Project Chrono Development Team -# All rights reserved. -# This file is licensed to you under the BSD 3-Clause "New" or "Revised" License. -# See https://github.com/projectchrono/chrono/blob/develop/LICENSE for details. -################################################################################ -# -# This script checks for the highest level of AVX support on the host -# by compiling and running small C++ programs that use AVX intrinsics. -# -# You can invoke this module using the following command: -# -# FIND_PACKAGE(AVX [major[.minor]] [EXACT] [QUIET|REQUIRED]) -# -# where the version string is one of: -# -# 1.0 for AVX support -# 2.0 for AVX2 support -# -# Note that any ".0" in the above version string is optional. -# -# If any AVX support is detected, the following variables are set: -# -# AVX_FOUND = 1 -# AVX_VERSION = the requested version, if EXACT is true, or -# the highest AVX version found. -# AVX_FLAGS = compile flags for the version of AVX found -# -# If AVX is not supported on the host platform, these variables are -# not set. If QUIET is true, the module does not print a message if -# AVX if missing. If REQUIRED is true, the module produces a fatal -# error if AVX support is missing. -# -set(AVX_FLAGS) -set(AVX_FOUND) -set(DETECTED_AVX_10) -set(DETECTED_AVX_20) - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" OUTPUT_VARIABLE GCC_VERSION_STRING) - if(GCC_VERSION_STRING VERSION_GREATER 4.2 AND NOT APPLE AND NOT CMAKE_CROSSCOMPILING) - SET(AVX_FLAGS "${AVX_FLAGS} -march=native") - message(STATUS "Using CPU native flags for AVX optimization: ${AVX_FLAGS}") - endif() -endif() - -include(CheckCXXSourceRuns) -set(CMAKE_REQUIRED_FLAGS) - - -# Generate a list of AVX versions to test. -if(AVX_FIND_VERSION_EXACT) - if(AVX_FIND_VERSION VERSION_EQUAL "2.0") - set(_AVX_TEST_20 1) - elseif(AVX_FIND_VERSION VERSION_EQUAL "1.0") - set(_AVX_TEST_10 1) - endif() -else() - if(NOT AVX_FIND_VERSION VERSION_GREATER "2.0") - set(_AVX_TEST_20 1) - endif() - if(NOT AVX_FIND_VERSION VERSION_GREATER "1.0") - set(_AVX_TEST_10 1) - endif() -endif() - -# Check for AVX2 support. -if(_AVX_TEST_20) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-mavx2") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(CMAKE_REQUIRED_FLAGS "-xHost") - elseif(MSVC AND NOT CMAKE_CL_64) - set(CMAKE_REQUIRED_FLAGS "/arch:AVX2") - endif() - check_cxx_source_runs(" - #include - int main() - { - __m256i a = _mm256_set_epi32 (-1, 2, -3, 4, -1, 2, -3, 4); - __m256i result = _mm256_abs_epi32 (a); - return 0; - }" DETECTED_AVX_20) -endif() - -# Check for AVX support. -if(_AVX_TEST_10) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-mavx") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(CMAKE_REQUIRED_FLAGS "-xHost") - elseif(MSVC AND NOT CMAKE_CL_64) - set(CMAKE_REQUIRED_FLAGS "/arch:AVX") - endif() - check_cxx_source_runs(" - #include - int main() - { - __m256 a = _mm256_set_ps (-1.0f, 2.0f, -3.0f, 4.0f, -1.0f, 2.0f, -3.0f, 4.0f); - __m256 b = _mm256_set_ps (1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f); - __m256 result = _mm256_add_ps (a, b); - return 0; - }" DETECTED_AVX_10) -endif() - -set(CMAKE_REQUIRED_FLAGS) - - -if(DETECTED_AVX_20) - set(AVX_VERSION "2.0") - set(AVX_STR "2_0") - set(AVX_FOUND 1) -elseif(DETECTED_AVX_10) - set(AVX_VERSION "1.0") - set(AVX_STR "1_0") - set(AVX_FOUND 1) -endif() - - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if(DETECTED_AVX_20) - SET(AVX_FLAGS "${AVX_FLAGS} -mavx2") - elseif(DETECTED_AVX_10) - SET(AVX_FLAGS "${AVX_FLAGS} -mavx") - endif() - # TODO: Check for AVX512 support - SET(AVX_FLAGS "${AVX_FLAGS} -mno-avx512f -mno-avx512pf -mno-avx512er -mno-avx512cd") -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(AVX_FLAGS "-xHost") -elseif(MSVC) - if(DETECTED_AVX_20) - SET(AVX_FLAGS "${AVX_FLAGS} /arch:AVX2") - elseif(DETECTED_AVX_10) - SET(AVX_FLAGS "${AVX_FLAGS} /arch:AVX") - endif() -endif() - -if(AVX_FOUND) - message(STATUS " Found AVX ${AVX_VERSION} extensions, using flags: ${AVX_FLAGS}") -else() - message(STATUS " No AVX support found") - set(AVX_FLAGS "") -endif() - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${AVX_FLAGS}") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${AVX_FLAGS}") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${AVX_FLAGS}") - -return() -#------------------------------------- - -# If no AVX support is found, print an error message. -if(AVX_FIND_VERSION) - set(_AVX_ERROR_MESSAGE "AVX ${AVX_FIND_VERSION} support is not found on this architecture") -else() - set(_AVX_ERROR_MESSAGE "AVX support is not found on this architecture") -endif() - -if(AVX_FIND_REQUIRED) - message(FATAL_ERROR "${_AVX_ERROR_MESSAGE}") -elseif(NOT AVX_FIND_QUIETLY) - message(STATUS "${_AVX_ERROR_MESSAGE}") -endif() diff --git a/cmake/find/FindFMA.cmake b/cmake/find/FindFMA.cmake deleted file mode 100644 index f435c1533..000000000 --- a/cmake/find/FindFMA.cmake +++ /dev/null @@ -1,107 +0,0 @@ -################################################################################ -# Copyright (c) 2016, Project Chrono Development Team -# All rights reserved. -# This file is licensed to you under the BSD 3-Clause "New" or "Revised" License. -# See https://github.com/projectchrono/chrono/blob/develop/LICENSE for details. -################################################################################ -# -# This script checks for the highest level of FMA support on the host -# by compiling and running small C++ programs that uses FMA intrinsics. -# -# You can invoke this module using the following command: -# -# FIND_PACKAGE(FMA [QUIET|REQUIRED]) -# -# If any FMA support is detected, the following variables are set: -# -# FMA_FOUND = 1 -# FMA_FLAGS = compile flags for the version of FMA found -# -# If FMA is not supported on the host platform, these variables are -# not set. If QUIET is true, the module does not print a message if -# FMA if missing. If REQUIRED is true, the module produces a fatal -# error if FMA support is missing. -# -set(FMA_FLAGS) -set(FMA_FOUND) -set(DETECTED_FMA) - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" OUTPUT_VARIABLE GCC_VERSION_STRING) - if(GCC_VERSION_STRING VERSION_GREATER 4.2 AND NOT APPLE AND NOT CMAKE_CROSSCOMPILING) - SET(FMA_FLAGS "${FMA_FLAGS} -march=native") - message(STATUS "Using CPU native flags for FMA optimization: ${FMA_FLAGS}") - endif() -endif() - -include(CheckCXXSourceRuns) -set(CMAKE_REQUIRED_FLAGS) - -# Generate a list of FMA versions to test. -set(_FMA_TEST 1) - -# Check for FMA support. -if(_FMA_TEST) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-mavx2 -mfma") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(CMAKE_REQUIRED_FLAGS "-fma") - elseif(MSVC AND NOT CMAKE_CL_64) - set(CMAKE_REQUIRED_FLAGS "/arch:AVX2") - endif() - check_cxx_source_runs(" - #include - int main() - { - __m256d a = _mm256_set_pd (-1, 2, -3, 4); - __m256d b = _mm256_set_pd (-2, 3, -4, 1); - __m256d c = _mm256_set_pd (-11, 6, 4, -1); - - __m256d result = _mm256_fmsub_pd (a, b, c); - return 0; - }" DETECTED_FMA) -endif() - -set(CMAKE_REQUIRED_FLAGS) - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if(DETECTED_FMA) - SET(FMA_FLAGS "${FMA_FLAGS} -mfma") - SET(FMA_FOUND 1) - endif() -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - if(DETECTED_FMA) - SET(FMA_FLAGS "${FMA_FLAGS} -fma") - SET(FMA_FOUND 1) - endif() -elseif(MSVC) - if(DETECTED_FMA) - SET(FMA_FLAGS "${FMA_FLAGS} /arch:AVX2") - SET(FMA_FOUND 1) - endif() -endif() - -if(FMA_FOUND) - message(STATUS " Found FMA extensions, using flags: ${FMA_FLAGS}") -else() - message(STATUS " No FMA support found") - set(FMA_FLAGS "") -endif() - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FMA_FLAGS}") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${FMA_FLAGS}") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FMA_FLAGS}") - -return() -#----------------------- - -# If no FMA support is found, print an error message. -if(FMA_FIND_REQUIRED) - set(_FMA_ERROR_MESSAGE "FMA support is not found on this architecture") -endif() - -if(FMA_FIND_REQUIRED) - message(FATAL_ERROR "${_FMA_ERROR_MESSAGE}") -elseif(NOT FMA_FIND_QUIETLY) - message(STATUS "${_FMA_ERROR_MESSAGE}") -endif() diff --git a/cmake/find/FindSIMD.cmake b/cmake/find/FindSIMD.cmake new file mode 100644 index 000000000..c3e8bf645 --- /dev/null +++ b/cmake/find/FindSIMD.cmake @@ -0,0 +1,514 @@ +################################################################################ +# Copyright (c) 2022, Project Chrono Development Team +# All rights reserved. +# This file is licensed to you under the BSD 3-Clause "New" or "Revised" License. +# See https://github.com/projectchrono/chrono/blob/main/LICENSE for details. +################################################################################ + +# SPDX-License-Identifier: BSD-3-Clause +# +# This script combines the search routines for various SIMD technologies into one place. +# + + +# This script checks for the highest level of SSE support on the host +# by compiling and running small C++ programs that uses SSE intrinsics. +# +# If any SSE support is detected, the following variables are set: +# +# SSE_FOUND = 1 +# SSE_VERSION = the highest SSE version found. +# SSE_FLAGS = compile flags for the version of SSE found +# +# If SSE is not supported on the host platform, these variables are +# not set. +# +# NOTE: 64-bit x86 architectures provide SSE 2.0 or support by default so it is not tested here. + +function (test_sse_availability) + + set(SSE_FLAGS) + set(SSE_FOUND) + set(DETECTED_SSE_41) + set(DETECTED_SSE_42) + set(DETECTED_SSE_30) + + include(CheckCXXSourceRuns) + set(CMAKE_REQUIRED_FLAGS) + + set(_SSE_TEST_42 1) + set(_SSE_TEST_41 1) + set(_SSE_TEST_30 1) + +# Check for SSE 4.2 support. + if(_SSE_TEST_42) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_REQUIRED_FLAGS "-msse4.2") + endif() + check_cxx_source_runs(" + #include + #include + int main() + { + long long a[2] = { 1, 2 }; + long long b[2] = { -1, 3 }; + long long c[2]; + __m128i va = _mm_loadu_si128((__m128i*)a); + __m128i vb = _mm_loadu_si128((__m128i*)b); + __m128i vc = _mm_cmpgt_epi64(va, vb); + + _mm_storeu_si128((__m128i*)c, vc); + if (c[0] == -1LL && c[1] == 0LL) + return 0; + else + return 1; + }" + DETECTED_SSE_42) + endif() + +# Check for SSE 4.1 support. + if(_SSE_TEST_41) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_REQUIRED_FLAGS "-msse4.1") + endif() + check_cxx_source_runs(" + #include + #include + int main() + { + long long a[2] = { 1, 2 }; + long long b[2] = { -1, 2 }; + long long c[2]; + __m128i va = _mm_loadu_si128((__m128i*)a); + __m128i vb = _mm_loadu_si128((__m128i*)b); + __m128i vc = _mm_cmpeq_epi64(va, vb); + + _mm_storeu_si128((__m128i*)c, vc); + if (c[0] == 0LL && c[1] == -1LL) + return 0; + else + return 1; + }" DETECTED_SSE_41) + endif() + +# Check for SSE 3 support. + if(_SSE_TEST_30) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_REQUIRED_FLAGS "-msse3") + endif() + check_cxx_source_runs(" + #include + #ifdef _WIN32 + #include + #else + #include + #endif + + int main() + { + float a[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; + float b[4] = { 3.0f, 5.0f, 7.0f, 9.0f }; + float c[4]; + + __m128 va = _mm_loadu_ps(a); + __m128 vb = _mm_loadu_ps(b); + __m128 vc = _mm_hadd_ps(va, vb); + + _mm_storeu_ps(c, vc); + if (c[0] == 3.0f && c[1] == 7.0f && c[2] == 8.0f && c[3] == 16.0f) + return 0; + else + return 1; + }" DETECTED_SSE_30) + endif() + + + set(CMAKE_REQUIRED_FLAGS) + + if(DETECTED_SSE_42) + set(SSE_VERSION "4.2") + set(SSE_STR "4_2") + set(SSE_FOUND 1) + elseif(DETECTED_SSE_41) + set(SSE_VERSION "4.1") + set(SSE_STR "4_1") + set(SSE_FOUND 1) + elseif(DETECTED_SSE_30) + set(SSE_VERSION "3.0") + set(SSE_STR "3_0") + set(SSE_FOUND 1) + endif() + + + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + if(DETECTED_SSE_42) + set(SSE_FLAGS "${SSE_FLAGS} -msse4.2 -mfpmath=sse") + endif() + if(DETECTED_SSE_41) + set(SSE_FLAGS "${SSE_FLAGS} -msse4.1 -mfpmath=sse") + endif() + if(DETECTED_SSE_30) + set(SSE_FLAGS "${SSE_FLAGS} -msse3 -mfpmath=sse") + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # clang does not require "-mfpmath" which is automatic + if(DETECTED_SSE_42) + set(SSE_FLAGS "${SSE_FLAGS} -msse4.2") + endif() + if(DETECTED_SSE_41) + set(SSE_FLAGS "${SSE_FLAGS} -msse4.1") + endif() + if(DETECTED_SSE_30) + set(SSE_FLAGS "${SSE_FLAGS} -msse3") + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + set(SSE_FLAGS "-xHost") + elseif(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + endif() + + # Export flags to caller scope + if(SSE_FOUND) + set(SSE_FOUND TRUE PARENT_SCOPE) + set(SSE_FLAGS "${SSE_FLAGS}" PARENT_SCOPE) + set(SSE_VERSION "${SSE_VERSION}" PARENT_SCOPE) + else() + set(SSE_FOUND FALSE PARENT_SCOPE) + set(SSE_FLAGS "") + endif() + + return() + +endfunction() + +# This script checks for the highest level of FMA support on the host +# by compiling and running small C++ programs that uses FMA intrinsics. + +# If any FMA support is detected, the following variables are set: +# +# FMA_FOUND = 1 +# FMA_FLAGS = compile flags for the version of FMA found +# +# If FMA is not supported on the host platform, these variables are +# not set. + +function (test_fma_availability) + set(FMA_FLAGS) + set(FMA_FOUND) + set(DETECTED_FMA) + + include(CheckCXXSourceRuns) + set(CMAKE_REQUIRED_FLAGS) + +# Generate a list of FMA versions to test. + set(_FMA_TEST 1) + +# Check for FMA support. + if(_FMA_TEST) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_REQUIRED_FLAGS "-mavx2 -mfma") + elseif(MSVC AND NOT CMAKE_CL_64) + set(CMAKE_REQUIRED_FLAGS "/arch:AVX2") + endif() + check_cxx_source_runs(" + #include + int main() + { + __m256d a = _mm256_set_pd (-1, 2, -3, 4); + __m256d b = _mm256_set_pd (-2, 3, -4, 1); + __m256d c = _mm256_set_pd (-11, 6, 4, -1); + + __m256d result = _mm256_fmsub_pd (a, b, c); + return 0; + }" DETECTED_FMA) + endif() + + set(CMAKE_REQUIRED_FLAGS) + + if(DETECTED_FMA) + SET(FMA_FOUND 1) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + SET(FMA_FLAGS "${FMA_FLAGS} -mfma") + endif() + endif() + + if(FMA_FOUND) + set(FMA_FOUND TRUE PARENT_SCOPE) + set(FMA_FLAGS "${FMA_FLAGS}" PARENT_SCOPE) + else() + set(FMA_FOUND FALSE PARENT_SCOPE) + set(FMA_FLAGS "") + endif() + + + return() +endfunction() + + +# This script checks for the highest level of AVX support on the host +# by compiling and running small C++ programs that use AVX intrinsics. +# +# If any AVX support is detected, the following variables are set: +# +# AVX_FOUND = 1 +# AVX_VERSION = the requested version, if EXACT is true, or +# the highest AVX version found. +# AVX_FLAGS = compile flags for the version of AVX found +# +# If AVX is not supported on the host platform, these variables are +# not set. + +function(test_avx_availability) + + set(AVX_FLAGS) + set(AVX_FOUND) + set(DETECTED_AVX_10) + set(DETECTED_AVX_20) + + include(CheckCXXSourceRuns) + set(CMAKE_REQUIRED_FLAGS) + + set(_AVX_TEST_20 1) + set(_AVX_TEST_10 1) + +# Check for AVX2 support. + if(_AVX_TEST_20) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_REQUIRED_FLAGS "-mavx2") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + set(CMAKE_REQUIRED_FLAGS "-xHost") + elseif(MSVC AND NOT CMAKE_CL_64) + set(CMAKE_REQUIRED_FLAGS "/arch:AVX2") + endif() + check_cxx_source_runs(" + #include + int main() + { + __m256i a = _mm256_set_epi32 (-1, 2, -3, 4, -1, 2, -3, 4); + __m256i result = _mm256_abs_epi32 (a); + return 0; + }" DETECTED_AVX_20) + endif() + +# Check for AVX support. + if(_AVX_TEST_10) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_REQUIRED_FLAGS "-mavx") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + set(CMAKE_REQUIRED_FLAGS "-xHost") + elseif(MSVC AND NOT CMAKE_CL_64) + set(CMAKE_REQUIRED_FLAGS "/arch:AVX") + endif() + check_cxx_source_runs(" + #include + int main() + { + __m256 a = _mm256_set_ps (-1.0f, 2.0f, -3.0f, 4.0f, -1.0f, 2.0f, -3.0f, 4.0f); + __m256 b = _mm256_set_ps (1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f); + __m256 result = _mm256_add_ps (a, b); + return 0; + }" DETECTED_AVX_10) + endif() + + set(CMAKE_REQUIRED_FLAGS) + + + if(DETECTED_AVX_20) + set(AVX_VERSION "2.0") + set(AVX_STR "2_0") + set(AVX_FOUND 1) + elseif(DETECTED_AVX_10) + set(AVX_VERSION "1.0") + set(AVX_STR "1_0") + set(AVX_FOUND 1) + endif() + + + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if(DETECTED_AVX_20) + SET(AVX_FLAGS "${AVX_FLAGS} -mavx2") + endif() + if(DETECTED_AVX_10) + SET(AVX_FLAGS "${AVX_FLAGS} -mavx") + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + set(AVX_FLAGS "-xHost") + elseif(MSVC) + if(DETECTED_AVX_20) + SET(AVX_FLAGS "${AVX_FLAGS} /arch:AVX2") + endif() + if(DETECTED_AVX_10) + SET(AVX_FLAGS "${AVX_FLAGS} /arch:AVX") + endif() + endif() + + if(AVX_FOUND) + set(AVX_FOUND TRUE PARENT_SCOPE) + set(AVX_FLAGS "${AVX_FLAGS}" PARENT_SCOPE) + set(AVX_VERSION "${AVX_VERSION}" PARENT_SCOPE) + else() + set(AVX_FOUND FALSE PARENT_SCOPE) + set(AVX_FLAGS "") + endif() + + + return() + +endfunction() + +# This script checks for the highest level of NEON support on the host +# by compiling and running small C++ programs that uses NEON intrinsics. +# +# If any NEON support is detected, the following variables are set: +# +# NEON_FOUND = 1 +# NEON_VERSION = 2_0 (assumes Advanced SIMD 2.0) +# NEON_FLAGS = compile flags for the version of NEON found +# +# If NEON is not supported on the host platform, these variables are +# not set. +# + +function(test_neon_availability) + + set(NEON_FLAGS) + set(NEON_FOUND) + set(DETECTED_NEON) + + include(CheckCXXSourceRuns) + + set(CMAKE_REQUIRED_FLAGS "-march=armv8-a") +# Check for NEON support. + check_cxx_source_runs(" +#include + int main() + { + float64_t a[2] = { 1., 2. }; + float64_t b[2] = { -1., 3. }; + float64_t c[2]; + + float64x2_t va = vld1q_f64(&a[0]); + float64x2_t vb = vld1q_f64(&b[0]); + float64x2_t vc = vaddq_f64(va, vb); + vst1q_f64(&c[0], vc); + + if (c[0] == 0. && c[1] == 5.) + return 0; + else + return 0; + } + " DETECTED_NEON) + + set(CMAKE_REQUIRED_FLAGS) + + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if(DETECTED_NEON) + SET(NEON_FLAGS "-march=armv8-a") + set(NEON_STR "2_0") + SET(NEON_FOUND 1) + else() + # Setting -ffloat-store to alleviate 32bit vs 64bit discrepancies on non-SIMD platforms. + set(NEON_FLAGS "-ffloat-store") + endif() + endif() + + if(NEON_FOUND) + set(NEON_FOUND TRUE PARENT_SCOPE) + set(NEON_FLAGS "${NEON_FLAGS}" PARENT_SCOPE) + else() + set(NEON_FOUND FALSE PARENT_SCOPE) + set(NEON_FLAGS "") + endif() + + return() + +endfunction() + + + +### +# +# Perform SIMD checks as defined above +# +### + +set(SIMD_FLAGS "") + +set(SIMD_SSE "FALSE" CACHE STRING "Any detected SSE SIMD version, else FALSE") +set(SIMD_AVX "FALSE" CACHE STRING "Any detected AVX SIMD version, else FALSE") +set(SIMD_FMA ${FMA_FOUND} CACHE BOOL "Whether AVX2 FMA extensions were a detected SIMD feature") +set(SIMD_NEON ${NEON_FOUND} CACHE BOOL "Whether NEON was a detected SIMD feature") + +# Check availability of SSE instructions +test_sse_availability() +if (SSE_FOUND) + if (NOT ${SIMD_FIND_QUIETLY}) + message(STATUS "Target supports SSE instructions") + endif() + + set(SIMD_FLAGS "${SIMD_FLAGS} ${SSE_FLAGS}") + set(SIMD_SSE "${SSE_VERSION}") +endif() + +test_avx_availability() +if (AVX_FOUND) + if (NOT ${SIMD_FIND_QUIETLY}) + message(STATUS "Target supports AVX instructions") + endif() + + set(SIMD_FLAGS "${SIMD_FLAGS} ${AVX_FLAGS}") + set(SIMD_AVX "${AVX_VERSION}") +endif() + +test_fma_availability() +if (FMA_FOUND) + if (NOT ${SIMD_FIND_QUIETLY}) + message(STATUS "Target supports AVX2 FMA instructions") + endif() + + set(SIMD_FLAGS "${SIMD_FLAGS} ${FMA_FLAGS}") +endif() + +test_neon_availability() +if (NEON_FOUND) + if (NOT ${SIMD_FIND_QUIETLY}) + message(STATUS "Target supports NEON instructions") + endif() + + set(SIMD_FLAGS "${SIMD_FLAGS} ${NEON_FLAGS}") +endif() + +# Determine whether to use SIMD flags or automatic detection +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" OUTPUT_VARIABLE GCC_VERSION_STRING) + if(GCC_VERSION_STRING VERSION_GREATER 4.2 AND NOT APPLE AND NOT CMAKE_CROSSCOMPILING) + SET(SIMD_FLAGS "-march=native") + if (NOT SIMD_FIND_QUIETLY) + message(STATUS "Using automatic native flag for SIMD optimization") + endif() + endif() +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CROSSCOMPILING) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" OUTPUT_VARIABLE CLANG_VERSION_STRING) + if(CLANG_VERSION_STRING VERSION_GREATER_EQUAL 15.0 AND NOT CMAKE_CROSSCOMPILING) + SET(SIMD_FLAGS "-march=native") + if (NOT SIMD_FIND_QUIETLY) + message(STATUS "Using automatic native flag for SIMD optimization") + endif() + elseif(CMAKE_HOST_UNIX) + execute_process(COMMAND uname -m OUTPUT_VARIABLE UNIX_MACHINE_ARCH) + if(UNIX_MACHINE_ARCH MATCHES "x86_64|x86|amd64") + SET(SIMD_FLAGS "-march=native") + if (NOT SIMD_FIND_QUIETLY) + message(STATUS "Using automatic native flag for SIMD optimization") + endif() + endif() + endif() +endif() + +string(STRIP "${SIMD_FLAGS}" SIMD_FLAGS) +string(REPLACE " " ";" SIMD_FLAGS "${SIMD_FLAGS}") + +set(SIMD_C_FLAGS "${SIMD_FLAGS}" CACHE STRING "Flags used for compiling C programs with SIMD support") +set(SIMD_CXX_FLAGS "${SIMD_FLAGS}" CACHE STRING "Flags used for compiling C++ programs with SIMD support") + +mark_as_advanced(SIMD_SSE SIMD_AVX SIMD_FMA SIMD_NEON SIMD_C_FLAGS SIMD_CXX_FLAGS) + diff --git a/cmake/find/FindSSE.cmake b/cmake/find/FindSSE.cmake deleted file mode 100644 index 51c1ae8ab..000000000 --- a/cmake/find/FindSSE.cmake +++ /dev/null @@ -1,300 +0,0 @@ -################################################################################ -# Copyright (c) 2016, Project Chrono Development Team -# All rights reserved. -# This file is licensed to you under the BSD 3-Clause "New" or "Revised" License. -# See https://github.com/projectchrono/chrono/blob/develop/LICENSE for details. -################################################################################ -# -# This script checks for the highest level of SSE support on the host -# by compiling and running small C++ programs that uses SSE intrinsics. -# -# You can invoke this module using the following command: -# -# FIND_PACKAGE(SSE [major[.minor]] [EXACT] [QUIET|REQUIRED]) -# -# where the version string is one of: -# -# 1.0 for SSE support -# 2.0 for SSE2 support -# 3.0 for SSE3 support -# 3.1 for SSSE3 support -# 4.1 for SSE 4.1 support -# 4.2 for SSE 4.2 support -# -# Note that any ".0" in the above version string is optional. -# -# If any SSE support is detected, the following variables are set: -# -# SSE_FOUND = 1 -# SSE_VERSION = the requested version, if EXACT is true, or -# the highest SSE version found. -# SSE_FLAGS = compile flags for the version of SSE found -# -# If SSE is not supported on the host platform, these variables are -# not set. If QUIET is true, the module does not print a message if -# SSE if missing. If REQUIRED is true, the module produces a fatal -# error if SSE support is missing. -# -set(SSE_FLAGS) -set(SSE_FOUND) -set(DETECTED_SSE_41) -set(DETECTED_SSE_42) -set(DETECTED_SSE_10) -set(DETECTED_SSE_20) -set(DETECTED_SSE_30) - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" OUTPUT_VARIABLE GCC_VERSION_STRING) - if(GCC_VERSION_STRING VERSION_GREATER 4.2 AND NOT APPLE AND NOT CMAKE_CROSSCOMPILING) - SET(SSE_FLAGS "${SSE_FLAGS} -march=native") - message(STATUS "Using CPU native flags for SSE optimization: ${SSE_FLAGS}") - endif() -endif() - -include(CheckCXXSourceRuns) -set(CMAKE_REQUIRED_FLAGS) - - -# Generate a list of SSE versions to test. -if(SSE_FIND_VERSION_EXACT) - if(SSE_FIND_VERSION VERSION_EQUAL "4.2") - set(_SSE_TEST_42 1) - elseif(SSE_FIND_VERSION VERSION_EQUAL "4.1") - set(_SSE_TEST_41 1) - elseif(SSE_FIND_VERSION VERSION_EQUAL "3.0") - set(_SSE_TEST_30 1) - elseif(SSE_FIND_VERSION VERSION_EQUAL "2.0") - set(_SSE_TEST_20 1) - elseif(SSE_FIND_VERSION VERSION_EQUAL "1.0") - set(_SSE_TEST_10 1) - endif() -else() - if(NOT SSE_FIND_VERSION VERSION_GREATER "4.2") - set(_SSE_TEST_42 1) - endif() - if(NOT SSE_FIND_VERSION VERSION_GREATER "4.1") - set(_SSE_TEST_41 1) - endif() - if(NOT SSE_FIND_VERSION VERSION_GREATER "3.0") - set(_SSE_TEST_30 1) - endif() - if(NOT SSE_FIND_VERSION VERSION_GREATER "2.0") - set(_SSE_TEST_20 1) - endif() - if(NOT SSE_FIND_VERSION VERSION_GREATER "1.0") - set(_SSE_TEST_10 1) - endif() -endif() - - -# Check for SSE 4.2 support. -if(_SSE_TEST_42) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-msse4.2") - endif() - check_cxx_source_runs(" - #include - #include - int main() - { - long long a[2] = { 1, 2 }; - long long b[2] = { -1, 3 }; - long long c[2]; - __m128i va = _mm_loadu_si128((__m128i*)a); - __m128i vb = _mm_loadu_si128((__m128i*)b); - __m128i vc = _mm_cmpgt_epi64(va, vb); - - _mm_storeu_si128((__m128i*)c, vc); - if (c[0] == -1LL && c[1] == 0LL) - return 0; - else - return 1; - }" - DETECTED_SSE_42) -endif() - -# Check for SSE 4.1 support. -if(_SSE_TEST_41) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-msse4.1") - endif() - check_cxx_source_runs(" - #include - #include - int main() - { - long long a[2] = { 1, 2 }; - long long b[2] = { -1, 2 }; - long long c[2]; - __m128i va = _mm_loadu_si128((__m128i*)a); - __m128i vb = _mm_loadu_si128((__m128i*)b); - __m128i vc = _mm_cmpeq_epi64(va, vb); - - _mm_storeu_si128((__m128i*)c, vc); - if (c[0] == 0LL && c[1] == -1LL) - return 0; - else - return 1; - }" DETECTED_SSE_41) -endif() - -# Check for SSE 3 support. -if(_SSE_TEST_30) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-msse3") - endif() - check_cxx_source_runs(" - #include - #ifdef _WIN32 - #include - #else - #include - #endif - - int main() - { - float a[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; - float b[4] = { 3.0f, 5.0f, 7.0f, 9.0f }; - float c[4]; - - __m128 va = _mm_loadu_ps(a); - __m128 vb = _mm_loadu_ps(b); - __m128 vc = _mm_hadd_ps(va, vb); - - _mm_storeu_ps(c, vc); - if (c[0] == 3.0f && c[1] == 7.0f && c[2] == 8.0f && c[3] == 16.0f) - return 0; - else - return 1; - }" DETECTED_SSE_30) -endif() - -# Check for SSE2 support. -if(_SSE_TEST_20) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-msse2") - elseif(MSVC AND NOT CMAKE_CL_64) - set(CMAKE_REQUIRED_FLAGS "/arch:SSE2") - endif() - check_cxx_source_runs(" - #include - int main() - { - int a[4] = { 1, 2, 3, 4 }; - int b[4] = { 3, 6, -4, -4 }; - int c[4]; - - __m128i va = _mm_loadu_si128((__m128i*)a); - __m128i vb = _mm_loadu_si128((__m128i*)b); - __m128i vc = _mm_add_epi32(va, vb); - - _mm_storeu_si128((__m128i*)c, vc); - if (c[0] == 4 && c[1] == 8 && c[2] == -1 && c[3] == 0) - return 0; - else - return 1; - }" DETECTED_SSE_20) -endif() - -# Check for SSE support. -if(_SSE_TEST_10) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_REQUIRED_FLAGS "-msse") - elseif(MSVC AND NOT CMAKE_CL_64) - set(CMAKE_REQUIRED_FLAGS "/arch:SSE") - endif() - check_cxx_source_runs(" - #include - int main() - { - float a[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; - float b[4] = { 2.0f, 3.0f, 4.0f, 5.0f }; - float c[4]; - __m128 va = _mm_loadu_ps(a); - __m128 vb = _mm_loadu_ps(b); - __m128 vc = _mm_add_ps(va, vb); - - _mm_storeu_ps(c, vc); - if (c[0] == 3.0f && c[1] == 5.0f && c[2] == 7.0f && c[3] == 9.0f) - return 0; - else - return 1; - }" DETECTED_SSE_10) -endif() - -set(CMAKE_REQUIRED_FLAGS) - -if(DETECTED_SSE_42) - set(SSE_VERSION "4.2") - set(SSE_STR "4_2") - set(SSE_FOUND 1) -elseif(DETECTED_SSE_41) - set(SSE_VERSION "4.1") - set(SSE_STR "4_1") - set(SSE_FOUND 1) -elseif(DETECTED_SSE_30) - set(SSE_VERSION "3.0") - set(SSE_STR "3_0") - set(SSE_FOUND 1) -elseif(DETECTED_SSE_20) - set(SSE_VERSION "2.0") - set(SSE_STR "2_0") - set(SSE_FOUND 1) -elseif(DETECTED_SSE_10) - set(SSE_VERSION "1.0") - set(SSE_STR "1_0") - set(SSE_FOUND 1) -endif() - - -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if(DETECTED_SSE_42) - set(SSE_FLAGS "${SSE_FLAGS} -msse4.2 -mfpmath=sse") - elseif(DETECTED_SSE_41) - set(SSE_FLAGS "${SSE_FLAGS} -msse4.1 -mfpmath=sse") - elseif(DETECTED_SSE_30) - set(SSE_FLAGS "${SSE_FLAGS} -msse3 -mfpmath=sse") - elseif(DETECTED_SSE_20) - set(SSE_FLAGS "${SSE_FLAGS} -msse2 -mfpmath=sse") - elseif(DETECTED_SSE_10) - set(SSE_FLAGS "${SSE_FLAGS} -msse -mfpmath=sse") - else() - # Setting -ffloat-store to alleviate 32bit vs 64bit discrepancies on non-SSE platforms. - set(SSE_FLAGS "-ffloat-store") - endif() -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(SSE_FLAGS "-xHost") -elseif(MSVC) - if(DETECTED_SSE_20) - set(SSE_FLAGS "${SSE_FLAGS} /arch:SSE2") - elseif(DETECTED_SSE_10) - set(SSE_FLAGS "${SSE_FLAGS} /arch:SSE") - endif() -endif() - -if(SSE_FOUND) - message(STATUS " Found SSE ${SSE_VERSION} extensions, using flags: ${SSE_FLAGS}") -else() - message(STATUS " No SSE support found") - set(SSE_FLAGS "") -endif() - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${SSE_FLAGS}") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${SSE_FLAGS}") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${SSE_FLAGS}") - -return() -#------------------------------------- - -# If no SSE support is found, print an error message. -if(SSE_FIND_VERSION) - set(_SSE_ERROR_MESSAGE "SSE ${SSE_FIND_VERSION} support is not found on this architecture") -else() - set(_SSE_ERROR_MESSAGE "SSE support is not found on this architecture") -endif() - -if(SSE_FIND_REQUIRED) - message(FATAL_ERROR "${_SSE_ERROR_MESSAGE}") -elseif(NOT SSE_FIND_QUIETLY) - message(STATUS "${_SSE_ERROR_MESSAGE}") -endif()