Explorar el Código

FindOpenMP: Add support for CUDA when supported by the toolchain

NVCC supports OpenMP on the host when the host compiler does.
Brad King hace 1 año
padre
commit
8f4cea94da

+ 2 - 0
.gitlab/ci/configure_cuda12.2_nvidia_common.cmake

@@ -1,5 +1,7 @@
 set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "")
 set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "")
 set(CMake_TEST_CUDA_CUPTI "ON" CACHE STRING "")
 set(CMake_TEST_CUDA_CUPTI "ON" CACHE STRING "")
 set(CMake_TEST_CUDA_STANDARDS "03;11;14;17;20" CACHE STRING "")
 set(CMake_TEST_CUDA_STANDARDS "03;11;14;17;20" CACHE STRING "")
+set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_CUDA "ON" CACHE BOOL "")
 
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")

+ 5 - 0
Help/release/dev/FindOpenMP-CUDA.rst

@@ -0,0 +1,5 @@
+FindOpenMP-CUDA
+---------------
+
+* The :module:`FindOpenMP` module gained support for ``CUDA`` when using
+  a CUDA compiler that supports OpenMP on the host.

+ 36 - 8
Modules/FindOpenMP.cmake

@@ -37,6 +37,10 @@ Result Variables
   The module exposes the components ``C``, ``CXX``, and ``Fortran``.
   The module exposes the components ``C``, ``CXX``, and ``Fortran``.
   Each of these controls the various languages to search OpenMP support for.
   Each of these controls the various languages to search OpenMP support for.
 
 
+.. versionadded:: 3.31
+  The ``CUDA`` language component is supported when using a CUDA compiler
+  that supports OpenMP on the host.
+
 Depending on the enabled components the following variables will be set:
 Depending on the enabled components the following variables will be set:
 
 
 ``OpenMP_FOUND``
 ``OpenMP_FOUND``
@@ -48,7 +52,7 @@ Depending on the enabled components the following variables will be set:
   or all enabled languages if no components were specified.
   or all enabled languages if no components were specified.
 
 
 This module will set the following variables per language in your
 This module will set the following variables per language in your
-project, where ``<lang>`` is one of C, CXX, or Fortran:
+project, where ``<lang>`` is one of C, CXX, CUDA, or Fortran:
 
 
 ``OpenMP_<lang>_FOUND``
 ``OpenMP_<lang>_FOUND``
   Variable indicating if OpenMP support for ``<lang>`` was detected.
   Variable indicating if OpenMP support for ``<lang>`` was detected.
@@ -153,7 +157,11 @@ function(_OPENMP_FLAG_CANDIDATES LANG)
     set(OMP_FLAG_Fujitsu "-Kopenmp" "-KOMP")
     set(OMP_FLAG_Fujitsu "-Kopenmp" "-KOMP")
     set(OMP_FLAG_FujitsuClang "-fopenmp" "-Kopenmp")
     set(OMP_FLAG_FujitsuClang "-fopenmp" "-Kopenmp")
 
 
-    set(compiler_id "${CMAKE_${LANG}_COMPILER_ID}")
+    if(CMAKE_${LANG}_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_${LANG}_HOST_COMPILER_ID)
+      set(compiler_id "${CMAKE_${LANG}_HOST_COMPILER_ID}")
+    else()
+      set(compiler_id "${CMAKE_${LANG}_COMPILER_ID}")
+    endif()
 
 
     # If we know the correct flags, use those
     # If we know the correct flags, use those
     if(DEFINED OMP_FLAG_${compiler_id})
     if(DEFINED OMP_FLAG_${compiler_id})
@@ -206,6 +214,9 @@ macro(_OPENMP_PREPARE_SOURCE LANG CONTENT_ID NAME_PREFIX FULLNAME_VAR CONTENT_VA
   elseif("${LANG}" STREQUAL "CXX")
   elseif("${LANG}" STREQUAL "CXX")
     set(${FULLNAME_VAR} "${NAME_PREFIX}.cpp")
     set(${FULLNAME_VAR} "${NAME_PREFIX}.cpp")
     set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}")
     set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}")
+  elseif("${LANG}" STREQUAL "CUDA")
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.cu")
+    set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}")
   elseif("${LANG}" STREQUAL "Fortran")
   elseif("${LANG}" STREQUAL "Fortran")
     set(${FULLNAME_VAR} "${NAME_PREFIX}.F90")
     set(${FULLNAME_VAR} "${NAME_PREFIX}.F90")
     string(CONFIGURE "${OpenMP_Fortran_${CONTENT_ID}}" ${CONTENT_VAR} @ONLY)
     string(CONFIGURE "${OpenMP_Fortran_${CONTENT_ID}}" ${CONTENT_VAR} @ONLY)
@@ -227,11 +238,24 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
     if(OpenMP_${LANG}_INCLUDE_DIR)
     if(OpenMP_${LANG}_INCLUDE_DIR)
       set(_includeDirFlags "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}")
       set(_includeDirFlags "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}")
     endif()
     endif()
+    if(CMAKE_${LANG}_COMPILER_ID STREQUAL "NVIDIA")
+      # With NVCC we drive linking directly through the host compiler, but
+      # without language-wide flags since they may be specific to nvcc.
+      # Pass the candidate OpenMP flag to the host compiler when linking.
+      set(_OpenMP_LINK_OPTIONS "${OPENMP_FLAG}")
+      # Exclude CUDA runtime libraries that we may add ourselves.
+      # See the Compiler/NVIDIA module.  Do not exclude pthread,
+      # as that is typically a dependency of OpenMP too.
+      set(_OpenMP_EXCLUDE_IMPLICIT_LIBS cudart cudart_static cudadevrt rt dl)
+    else()
+      set(_OpenMP_LINK_OPTIONS "")
+      set(_OpenMP_EXCLUDE_IMPLICIT_LIBS "")
+    endif()
     try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}
     try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}
       SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
       SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
       LOG_DESCRIPTION "Detecting ${LANG} OpenMP compiler info"
       LOG_DESCRIPTION "Detecting ${LANG} OpenMP compiler info"
       CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAG}" ${_includeDirFlags}
       CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAG}" ${_includeDirFlags}
-      LINK_OPTIONS ${OpenMP_VERBOSE_OPTIONS}
+      LINK_OPTIONS ${OpenMP_VERBOSE_OPTIONS} ${_OpenMP_LINK_OPTIONS}
       OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT
       OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT
     )
     )
 
 
@@ -282,6 +306,7 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
           string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" _OPENMP_IMPLICIT_LIB_PLAIN_ESC "${_OPENMP_IMPLICIT_LIB_PLAIN}")
           string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" _OPENMP_IMPLICIT_LIB_PLAIN_ESC "${_OPENMP_IMPLICIT_LIB_PLAIN}")
           string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" _OPENMP_IMPLICIT_LIB_PATH_ESC "${_OPENMP_IMPLICIT_LIB}")
           string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" _OPENMP_IMPLICIT_LIB_PATH_ESC "${_OPENMP_IMPLICIT_LIB}")
           if(NOT ( "${_OPENMP_IMPLICIT_LIB}" IN_LIST CMAKE_${LANG}_IMPLICIT_LINK_LIBRARIES
           if(NOT ( "${_OPENMP_IMPLICIT_LIB}" IN_LIST CMAKE_${LANG}_IMPLICIT_LINK_LIBRARIES
+            OR "${_OPENMP_IMPLICIT_LIB}" IN_LIST _OpenMP_EXCLUDE_IMPLICIT_LIBS
             OR "${CMAKE_${LANG}_STANDARD_LIBRARIES}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)"
             OR "${CMAKE_${LANG}_STANDARD_LIBRARIES}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)"
             OR "${CMAKE_${LANG}_LINK_EXECUTABLE}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)" ) )
             OR "${CMAKE_${LANG}_LINK_EXECUTABLE}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)" ) )
             if(_OPENMP_IMPLICIT_LIB_DIR)
             if(_OPENMP_IMPLICIT_LIB_DIR)
@@ -302,6 +327,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
             list(APPEND _OPENMP_LIB_NAMES ${_OPENMP_IMPLICIT_LIB_PLAIN})
             list(APPEND _OPENMP_LIB_NAMES ${_OPENMP_IMPLICIT_LIB_PLAIN})
           endif()
           endif()
         endforeach()
         endforeach()
+        list(REVERSE _OPENMP_LIB_NAMES)
+        list(REMOVE_DUPLICATES _OPENMP_LIB_NAMES)
+        list(REVERSE _OPENMP_LIB_NAMES)
         set("${OPENMP_LIB_NAMES_VAR}" "${_OPENMP_LIB_NAMES}" PARENT_SCOPE)
         set("${OPENMP_LIB_NAMES_VAR}" "${_OPENMP_LIB_NAMES}" PARENT_SCOPE)
       else()
       else()
         # We do not know how to extract implicit OpenMP libraries for this compiler.
         # We do not know how to extract implicit OpenMP libraries for this compiler.
@@ -489,7 +517,7 @@ macro(_OPENMP_SET_VERSION_BY_SPEC_DATE LANG)
   unset(OpenMP_SPEC_DATE_MAP)
   unset(OpenMP_SPEC_DATE_MAP)
 endmacro()
 endmacro()
 
 
-foreach(LANG IN ITEMS C CXX)
+foreach(LANG IN ITEMS C CXX CUDA)
   if(CMAKE_${LANG}_COMPILER_LOADED)
   if(CMAKE_${LANG}_COMPILER_LOADED)
     if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND"
     if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND"
       OR NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND")
       OR NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND")
@@ -555,7 +583,7 @@ if(CMAKE_Fortran_COMPILER_LOADED)
 endif()
 endif()
 
 
 if(NOT OpenMP_FIND_COMPONENTS)
 if(NOT OpenMP_FIND_COMPONENTS)
-  set(OpenMP_FINDLIST C CXX Fortran)
+  set(OpenMP_FINDLIST C CXX CUDA Fortran)
 else()
 else()
   set(OpenMP_FINDLIST ${OpenMP_FIND_COMPONENTS})
   set(OpenMP_FINDLIST ${OpenMP_FIND_COMPONENTS})
 endif()
 endif()
@@ -634,7 +662,7 @@ foreach(LANG IN LISTS OpenMP_FINDLIST)
 endforeach()
 endforeach()
 
 
 unset(_OpenMP_REQ_VARS)
 unset(_OpenMP_REQ_VARS)
-foreach(LANG IN ITEMS C CXX Fortran)
+foreach(LANG IN ITEMS C CXX CUDA Fortran)
   if((NOT OpenMP_FIND_COMPONENTS AND CMAKE_${LANG}_COMPILER_LOADED) OR LANG IN_LIST OpenMP_FIND_COMPONENTS)
   if((NOT OpenMP_FIND_COMPONENTS AND CMAKE_${LANG}_COMPILER_LOADED) OR LANG IN_LIST OpenMP_FIND_COMPONENTS)
     list(APPEND _OpenMP_REQ_VARS "OpenMP_${LANG}_FOUND")
     list(APPEND _OpenMP_REQ_VARS "OpenMP_${LANG}_FOUND")
   endif()
   endif()
@@ -656,8 +684,8 @@ if(CMAKE_Fortran_COMPILER_LOADED AND OpenMP_Fortran_FOUND)
   endif()
   endif()
 endif()
 endif()
 
 
-if(NOT ( CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED OR CMAKE_Fortran_COMPILER_LOADED ))
-  message(SEND_ERROR "FindOpenMP requires the C, CXX or Fortran languages to be enabled")
+if(NOT ( CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED OR CMAKE_CUDA_COMPILER_LOADED OR CMAKE_Fortran_COMPILER_LOADED ))
+  message(SEND_ERROR "FindOpenMP requires the C, CXX, CUDA, or Fortran languages to be enabled")
 endif()
 endif()
 
 
 unset(OpenMP_C_CXX_TEST_SOURCE)
 unset(OpenMP_C_CXX_TEST_SOURCE)

+ 5 - 1
Tests/FindOpenMP/CMakeLists.txt

@@ -1,4 +1,4 @@
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if(CMake_TEST_FindOpenMP_${c})
   if(CMake_TEST_FindOpenMP_${c})
     set(CMake_TEST_FindOpenMP_FLAG_${c} 1)
     set(CMake_TEST_FindOpenMP_FLAG_${c} 1)
   else()
   else()
@@ -16,9 +16,13 @@ add_test(NAME FindOpenMP.Test COMMAND
   --build-options ${build_options}
   --build-options ${build_options}
   -DOpenMP_TEST_C=${CMake_TEST_FindOpenMP_FLAG_C}
   -DOpenMP_TEST_C=${CMake_TEST_FindOpenMP_FLAG_C}
   -DOpenMP_TEST_CXX=${CMake_TEST_FindOpenMP_FLAG_CXX}
   -DOpenMP_TEST_CXX=${CMake_TEST_FindOpenMP_FLAG_CXX}
+  -DOpenMP_TEST_CUDA=${CMake_TEST_FindOpenMP_FLAG_CUDA}
   -DOpenMP_TEST_Fortran=${CMake_TEST_FindOpenMP_FLAG_Fortran}
   -DOpenMP_TEST_Fortran=${CMake_TEST_FindOpenMP_FLAG_Fortran}
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
   )
+if(CMake_TEST_FindOpenMP_FLAG_CUDA)
+  set_property(TEST FindOpenMP.Test APPEND PROPERTY LABELS "CUDA")
+endif()
 if(CMake_TEST_FindOpenMP_FLAG_Fortran)
 if(CMake_TEST_FindOpenMP_FLAG_Fortran)
   set_property(TEST FindOpenMP.Test APPEND PROPERTY LABELS "Fortran")
   set_property(TEST FindOpenMP.Test APPEND PROPERTY LABELS "Fortran")
 endif()
 endif()

+ 8 - 5
Tests/FindOpenMP/Test/CMakeLists.txt

@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.30)
 project(TestFindOpenMP NONE)
 project(TestFindOpenMP NONE)
 include(CTest)
 include(CTest)
 
 
@@ -8,6 +8,9 @@ macro(source_code_mapper_helper LANG_NAME SRC_FILE_NAME)
   elseif("${LANG_NAME}" STREQUAL "CXX")
   elseif("${LANG_NAME}" STREQUAL "CXX")
     configure_file("${SRC_FILE_NAME}.c" "${SRC_FILE_NAME}.cxx" COPYONLY)
     configure_file("${SRC_FILE_NAME}.c" "${SRC_FILE_NAME}.cxx" COPYONLY)
     set(OpenMPTEST_SOURCE_FILE "${SRC_FILE_NAME}.cxx")
     set(OpenMPTEST_SOURCE_FILE "${SRC_FILE_NAME}.cxx")
+  elseif("${LANG_NAME}" STREQUAL "CUDA")
+    configure_file("${SRC_FILE_NAME}.c" "${SRC_FILE_NAME}.cu" COPYONLY)
+    set(OpenMPTEST_SOURCE_FILE "${SRC_FILE_NAME}.cu")
   elseif("${LANG_NAME}" STREQUAL "Fortran")
   elseif("${LANG_NAME}" STREQUAL "Fortran")
     set(OpenMPTEST_SOURCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE_NAME}.f90")
     set(OpenMPTEST_SOURCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE_NAME}.f90")
     if(OpenMP_Fortran_HAVE_OMPLIB_MODULE)
     if(OpenMP_Fortran_HAVE_OMPLIB_MODULE)
@@ -19,7 +22,7 @@ macro(source_code_mapper_helper LANG_NAME SRC_FILE_NAME)
   endif()
   endif()
 endmacro()
 endmacro()
 
 
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if("${OpenMP_TEST_${c}}")
   if("${OpenMP_TEST_${c}}")
     message("Testing ${c}")
     message("Testing ${c}")
     enable_language(${c})
     enable_language(${c})
@@ -41,7 +44,7 @@ if(test_msvc_runtime)
   endif()
   endif()
 endif()
 endif()
 
 
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if(NOT "${OpenMP_TEST_${c}}")
   if(NOT "${OpenMP_TEST_${c}}")
     continue()
     continue()
   endif()
   endif()
@@ -65,11 +68,11 @@ foreach(c C CXX Fortran)
   set_property(TARGET scalprod_${c} PROPERTY LINKER_LANGUAGE ${c})
   set_property(TARGET scalprod_${c} PROPERTY LINKER_LANGUAGE ${c})
 endforeach()
 endforeach()
 
 
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if(NOT "${OpenMP_TEST_${c}}")
   if(NOT "${OpenMP_TEST_${c}}")
     continue()
     continue()
   endif()
   endif()
-  foreach(d C CXX Fortran)
+  foreach(d C CXX CUDA Fortran)
     if(NOT "${OpenMP_TEST_${d}}")
     if(NOT "${OpenMP_TEST_${d}}")
       continue()
       continue()
     endif()
     endif()