Browse Source

FindOpenMP: Add support for CUDA when supported by the toolchain

NVCC supports OpenMP on the host when the host compiler does.
Brad King 1 year ago
parent
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_CUPTI "ON" 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")

+ 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``.
   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:
 
 ``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.
 
 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``
   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_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(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")
     set(${FULLNAME_VAR} "${NAME_PREFIX}.cpp")
     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")
     set(${FULLNAME_VAR} "${NAME_PREFIX}.F90")
     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)
       set(_includeDirFlags "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}")
     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}
       SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
       LOG_DESCRIPTION "Detecting ${LANG} OpenMP compiler info"
       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
     )
 
@@ -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_PATH_ESC "${_OPENMP_IMPLICIT_LIB}")
           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}_LINK_EXECUTABLE}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)" ) )
             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})
           endif()
         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)
       else()
         # 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)
 endmacro()
 
-foreach(LANG IN ITEMS C CXX)
+foreach(LANG IN ITEMS C CXX CUDA)
   if(CMAKE_${LANG}_COMPILER_LOADED)
     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")
@@ -555,7 +583,7 @@ if(CMAKE_Fortran_COMPILER_LOADED)
 endif()
 
 if(NOT OpenMP_FIND_COMPONENTS)
-  set(OpenMP_FINDLIST C CXX Fortran)
+  set(OpenMP_FINDLIST C CXX CUDA Fortran)
 else()
   set(OpenMP_FINDLIST ${OpenMP_FIND_COMPONENTS})
 endif()
@@ -634,7 +662,7 @@ foreach(LANG IN LISTS OpenMP_FINDLIST)
 endforeach()
 
 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)
     list(APPEND _OpenMP_REQ_VARS "OpenMP_${LANG}_FOUND")
   endif()
@@ -656,8 +684,8 @@ if(CMAKE_Fortran_COMPILER_LOADED AND OpenMP_Fortran_FOUND)
   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()
 
 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})
     set(CMake_TEST_FindOpenMP_FLAG_${c} 1)
   else()
@@ -16,9 +16,13 @@ add_test(NAME FindOpenMP.Test COMMAND
   --build-options ${build_options}
   -DOpenMP_TEST_C=${CMake_TEST_FindOpenMP_FLAG_C}
   -DOpenMP_TEST_CXX=${CMake_TEST_FindOpenMP_FLAG_CXX}
+  -DOpenMP_TEST_CUDA=${CMake_TEST_FindOpenMP_FLAG_CUDA}
   -DOpenMP_TEST_Fortran=${CMake_TEST_FindOpenMP_FLAG_Fortran}
   --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)
   set_property(TEST FindOpenMP.Test APPEND PROPERTY LABELS "Fortran")
 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)
 include(CTest)
 
@@ -8,6 +8,9 @@ macro(source_code_mapper_helper LANG_NAME SRC_FILE_NAME)
   elseif("${LANG_NAME}" STREQUAL "CXX")
     configure_file("${SRC_FILE_NAME}.c" "${SRC_FILE_NAME}.cxx" COPYONLY)
     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")
     set(OpenMPTEST_SOURCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE_NAME}.f90")
     if(OpenMP_Fortran_HAVE_OMPLIB_MODULE)
@@ -19,7 +22,7 @@ macro(source_code_mapper_helper LANG_NAME SRC_FILE_NAME)
   endif()
 endmacro()
 
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if("${OpenMP_TEST_${c}}")
     message("Testing ${c}")
     enable_language(${c})
@@ -41,7 +44,7 @@ if(test_msvc_runtime)
   endif()
 endif()
 
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if(NOT "${OpenMP_TEST_${c}}")
     continue()
   endif()
@@ -65,11 +68,11 @@ foreach(c C CXX Fortran)
   set_property(TARGET scalprod_${c} PROPERTY LINKER_LANGUAGE ${c})
 endforeach()
 
-foreach(c C CXX Fortran)
+foreach(c C CXX CUDA Fortran)
   if(NOT "${OpenMP_TEST_${c}}")
     continue()
   endif()
-  foreach(d C CXX Fortran)
+  foreach(d C CXX CUDA Fortran)
     if(NOT "${OpenMP_TEST_${d}}")
       continue()
     endif()