diff options
-rw-r--r-- | .circleci/config.yml | 5 | ||||
-rwxr-xr-x | .jenkins/pytorch/build-asan.sh | 16 | ||||
-rwxr-xr-x | .jenkins/pytorch/macos-test.sh | 8 | ||||
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | caffe2/CMakeLists.txt | 7 | ||||
-rw-r--r-- | cmake/Dependencies.cmake | 80 | ||||
-rw-r--r-- | cmake/Modules/FindMKL.cmake | 132 | ||||
-rw-r--r-- | cmake/Modules/FindOpenMP.cmake | 635 | ||||
-rw-r--r-- | cmake/Modules/README.md | 34 | ||||
-rw-r--r-- | cmake/Summary.cmake | 1 | ||||
-rw-r--r-- | modules/detectron/CMakeLists.txt | 8 | ||||
-rw-r--r-- | torch/CMakeLists.txt | 12 |
12 files changed, 848 insertions, 97 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index a15c937f42..a17539fce6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -184,7 +184,7 @@ setup_ci_environment: &setup_ci_environment eval $(aws ecr get-login --region us-east-1 --no-include-email) macos_brew_update: &macos_brew_update - name: Brew update and install moreutils and expect + name: Brew update and install moreutils, expect and libomp no_output_timeout: "1h" command: | set -ex @@ -198,6 +198,7 @@ macos_brew_update: &macos_brew_update brew install moreutils brew link parallel --overwrite brew install expect + brew install libomp ############################################################################## @@ -246,7 +247,7 @@ pytorch_linux_test_defaults: &pytorch_linux_test_defaults <<: *setup_ci_environment - run: name: Test - no_output_timeout: "1h" + no_output_timeout: "90m" command: | set -e export COMMIT_DOCKER_IMAGE=${DOCKER_IMAGE}-${CIRCLE_SHA1} diff --git a/.jenkins/pytorch/build-asan.sh b/.jenkins/pytorch/build-asan.sh index 174b4db099..c29fce0c1a 100755 --- a/.jenkins/pytorch/build-asan.sh +++ b/.jenkins/pytorch/build-asan.sh @@ -14,9 +14,23 @@ clang --version # symbolize=1: Gives us much better errors when things go wrong export ASAN_OPTIONS=detect_leaks=0:symbolize=1 +# FIXME: Remove the hardcoded "-pthread" option. +# With asan build, the cmake thread CMAKE_HAVE_LIBC_CREATE[1] checking will +# succeed because "pthread_create" is in libasan.so. However, libasan doesn't +# have the full pthread implementation. Other advanced pthread functions doesn't +# exist in libasan.so[2]. If we need some pthread advanced functions, we still +# need to link the pthread library. +# This issue is already fixed in cmake 3.13[3]. If we use the newer cmake, we +# could remove this hardcoded option. +# +# [1] https://github.com/Kitware/CMake/blob/8cabaaf054a16ea9c8332ce8e9291bd026b38c62/Modules/FindThreads.cmake#L135 +# [2] https://wiki.gentoo.org/wiki/AddressSanitizer/Problems +# [3] https://github.com/Kitware/CMake/commit/e9a1ddc594de6e6251bf06d732775dae2cabe4c8 +# # TODO: Make the ASAN flags a more unified env var CC="clang" CXX="clang++" LDSHARED="clang --shared" \ - CFLAGS="-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -shared-libasan" \ + CFLAGS="-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -shared-libasan -pthread" \ + CXX_FLAGS="-pthread" \ NO_CUDA=1 USE_MKLDNN=0 \ python setup.py install diff --git a/.jenkins/pytorch/macos-test.sh b/.jenkins/pytorch/macos-test.sh index 265dad5cd0..9ad943605d 100755 --- a/.jenkins/pytorch/macos-test.sh +++ b/.jenkins/pytorch/macos-test.sh @@ -50,6 +50,14 @@ if [ -z "${IN_CIRCLECI}" ]; then 7z x ${IMAGE_COMMIT_TAG}.7z -o"${PYTORCH_ENV_DIR}/miniconda3/lib/python3.6/site-packages" fi +# Test that OpenMP is enabled +pushd test +if [[ ! $(python -c "import torch; print(int(torch.backends.openmp.is_available()))") == "1" ]]; then + echo "Build should have OpenMP enabled, but torch.backends.openmp.is_available() is False" + exit 1 +fi +popd + test_python_all() { echo "Ninja version: $(ninja --version)" python test/run_test.py --verbose diff --git a/CMakeLists.txt b/CMakeLists.txt index 867de519b9..68e2e45bfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) #cmake_policy(SET CMP0022 NEW) #cmake_policy(SET CMP0023 NEW) +# Use compiler ID "AppleClang" instead of "Clang" for XCode. +# Not setting this sometimes makes XCode C compiler gets detected as "Clang", +# even when the C++ one is detected as "AppleClang". +cmake_policy(SET CMP0025 NEW) + # ---[ Project and semantic versioning. project(Caffe2 CXX C) @@ -125,7 +130,7 @@ option(USE_NUMPY "Use NumPy" ON) option(USE_OBSERVERS "Use observers module." OFF) option(USE_OPENCL "Use OpenCL" OFF) option(USE_OPENCV "Use OpenCV" ON) -option(USE_OPENMP "Use OpenMP for parallel code" OFF) +option(USE_OPENMP "Use OpenMP for parallel code" ON) option(USE_PROF "Use profiling" OFF) option(USE_QNNPACK "Use QNNPACK (quantized 8-bit operators)" ON) option(USE_REDIS "Use Redis" OFF) diff --git a/caffe2/CMakeLists.txt b/caffe2/CMakeLists.txt index fe8e04a894..43af0060c1 100644 --- a/caffe2/CMakeLists.txt +++ b/caffe2/CMakeLists.txt @@ -224,6 +224,13 @@ if(NOT BUILD_ATEN_ONLY) endif() endif() +if (USE_OPENMP AND OPENMP_FOUND) + message(STATUS "Caffe2 is compiling with OpenMP. \n" + "OpenMP CXX_FLAGS: ${OpenMP_CXX_FLAGS}. \n" + "OpenMP libraries: ${OpenMP_CXX_LIBRARIES}.") + target_link_libraries(caffe2 PRIVATE ${OpenMP_CXX_LIBRARIES}) +endif() + target_link_libraries(caffe2 PUBLIC c10) target_link_libraries(caffe2 PUBLIC ${Caffe2_PUBLIC_DEPENDENCY_LIBS}) target_link_libraries(caffe2 PRIVATE ${Caffe2_DEPENDENCY_LIBS}) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 2ff910f5aa..7ab15fb5cf 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -107,6 +107,10 @@ elseif(BLAS STREQUAL "MKL") endif() include(${CMAKE_CURRENT_LIST_DIR}/public/mkl.cmake) if(MKL_FOUND) + message(STATUS "MKL libraries: ${MKL_LIBRARIES}") + message(STATUS "MKL include directory: ${MKL_INCLUDE_DIR}") + message(STATUS "MKL OpenMP type: ${MKL_OPENMP_TYPE}") + message(STATUS "MKL OpenMP library: ${MKL_OPENMP_LIBRARY}") include_directories(SYSTEM ${MKL_INCLUDE_DIR}) list(APPEND Caffe2_PUBLIC_DEPENDENCY_LIBS caffe2::mkl) set(CAFFE2_USE_MKL ON) @@ -670,12 +674,45 @@ endif() # ---[ OpenMP if(USE_OPENMP) - find_package(OpenMP) + # OpenMP support? + SET(WITH_OPENMP ON CACHE BOOL "OpenMP support if available?") + + # macOS + GCC + IF (APPLE AND CMAKE_COMPILER_IS_GNUCC) + EXEC_PROGRAM (uname ARGS -v OUTPUT_VARIABLE DARWIN_VERSION) + STRING (REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION}) + MESSAGE (STATUS "macOS Darwin version: ${DARWIN_VERSION}") + IF (DARWIN_VERSION GREATER 9) + SET(APPLE_OPENMP_SUCKS 1) + ENDIF (DARWIN_VERSION GREATER 9) + EXECUTE_PROCESS (COMMAND ${CMAKE_C_COMPILER} -dumpversion + OUTPUT_VARIABLE GCC_VERSION) + IF (APPLE_OPENMP_SUCKS AND GCC_VERSION VERSION_LESS 4.6.2) + MESSAGE(WARNING "Disabling OpenMP (unstable with this version of GCC). " + "Install GCC >= 4.6.2 or change your OS to enable OpenMP.") + add_compile_options(-Wno-unknown-pragmas) + SET(WITH_OPENMP OFF CACHE BOOL "OpenMP support if available?" FORCE) + ENDIF() + ENDIF() + + IF (WITH_OPENMP AND NOT CHECKED_OPENMP) + FIND_PACKAGE(OpenMP QUIET) + SET(CHECKED_OPENMP ON CACHE BOOL "already checked for OpenMP") + + # OPENMP_FOUND is not cached in FindOpenMP.cmake (all other variables are cached) + # see https://github.com/Kitware/CMake/blob/master/Modules/FindOpenMP.cmake + SET(OPENMP_FOUND ${OPENMP_FOUND} CACHE BOOL "OpenMP Support found") + ENDIF() + if(OPENMP_FOUND) - message(STATUS "Adding " ${OpenMP_CXX_FLAGS}) + message(STATUS "Adding OpenMP CXX_FLAGS: " ${OpenMP_CXX_FLAGS}) + IF("${OpenMP_CXX_LIBRARIES}" STREQUAL "") + message(STATUS "No OpenMP library needs to be linked against") + ELSE() + message(STATUS "Will link against OpenMP libraries: ${OpenMP_CXX_LIBRARIES}") + ENDIF() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") else() message(WARNING "Not compiling with OpenMP. Suppress this warning with -DUSE_OPENMP=OFF") caffe2_update_option(USE_OPENMP OFF) @@ -1096,43 +1133,6 @@ if (NOT BUILD_ATEN_MOBILE) STRING(REGEX REPLACE "[-/]DNDEBUG" "" CMAKE_CXX_FLAGS_RELEASE "" ${CMAKE_CXX_FLAGS_RELEASE}) ENDIF() - # OpenMP support? - SET(WITH_OPENMP ON CACHE BOOL "OpenMP support if available?") - - # macOS + GCC - IF (APPLE AND CMAKE_COMPILER_IS_GNUCC) - EXEC_PROGRAM (uname ARGS -v OUTPUT_VARIABLE DARWIN_VERSION) - STRING (REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION}) - MESSAGE (STATUS "macOS Darwin version: ${DARWIN_VERSION}") - IF (DARWIN_VERSION GREATER 9) - SET(APPLE_OPENMP_SUCKS 1) - ENDIF (DARWIN_VERSION GREATER 9) - EXECUTE_PROCESS (COMMAND ${CMAKE_C_COMPILER} -dumpversion - OUTPUT_VARIABLE GCC_VERSION) - IF (APPLE_OPENMP_SUCKS AND GCC_VERSION VERSION_LESS 4.6.2) - MESSAGE(WARNING "Disabling OpenMP (unstable with this version of GCC). " - "Install GCC >= 4.6.2 or change your OS to enable OpenMP.") - add_compile_options(-Wno-unknown-pragmas) - SET(WITH_OPENMP OFF CACHE BOOL "OpenMP support if available?" FORCE) - ENDIF() - ENDIF() - - IF (WITH_OPENMP AND NOT CHECKED_OPENMP) - FIND_PACKAGE(OpenMP) - SET(CHECKED_OPENMP ON CACHE BOOL "already checked for OpenMP") - - # OPENMP_FOUND is not cached in FindOpenMP.cmake (all other variables are cached) - # see https://github.com/Kitware/CMake/blob/master/Modules/FindOpenMP.cmake - SET(OPENMP_FOUND ${OPENMP_FOUND} CACHE BOOL "OpenMP Support found") - ENDIF() - - IF (OPENMP_FOUND) - MESSAGE(STATUS "Compiling with OpenMP support") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - ENDIF() - - SET(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF) FIND_PACKAGE(MAGMA) diff --git a/cmake/Modules/FindMKL.cmake b/cmake/Modules/FindMKL.cmake index fb7a716446..3be472ca0f 100644 --- a/cmake/Modules/FindMKL.cmake +++ b/cmake/Modules/FindMKL.cmake @@ -5,6 +5,8 @@ # MKL_VERSION - best guess of the found mkl version # MKL_INCLUDE_DIR - path to include dir. # MKL_LIBRARIES - list of libraries for base mkl +# MKL_OPENMP_TYPE - OpenMP flavor that the found mkl uses: GNU or Intel +# MKL_OPENMP_LIBRARY - path to the OpenMP library the found mkl uses # MKL_LAPACK_LIBRARIES - list of libraries to add for lapack # MKL_SCALAPACK_LIBRARIES - list of libraries to add for scalapack # MKL_SOLVER_LIBRARIES - list of libraries to add for the solvers @@ -16,6 +18,8 @@ IF (NOT MKL_FOUND) SET(MKL_VERSION) SET(MKL_INCLUDE_DIR) SET(MKL_LIBRARIES) +SET(MKL_OPENMP_TYPE) +SET(MKL_OPENMP_LIBRARY) SET(MKL_LAPACK_LIBRARIES) SET(MKL_SCALAPACK_LIBRARIES) SET(MKL_SOLVER_LIBRARIES) @@ -103,7 +107,7 @@ IF (EXISTS ${INTEL_MKL_DIR}) ENDIF() # Try linking multiple libs -MACRO(CHECK_ALL_LIBRARIES LIBRARIES _name _list _flags) +MACRO(CHECK_ALL_LIBRARIES LIBRARIES OPENMP_TYPE OPENMP_LIBRARY _name _list _flags) # This macro checks for the existence of the combination of libraries given by _list. # If the combination is found, this macro checks whether we can link against that library # combination using the name of a routine given by _name using the linker @@ -116,30 +120,60 @@ MACRO(CHECK_ALL_LIBRARIES LIBRARIES _name _list _flags) # start checking SET(_libraries_work TRUE) SET(${LIBRARIES}) + SET(${OPENMP_TYPE}) + SET(${OPENMP_LIBRARY}) SET(_combined_name) + SET(_openmp_type) + SET(_openmp_library) SET(_paths) - set(__list) - foreach(_elem ${_list}) - if(__list) - set(__list "${__list} - ${_elem}") - else(__list) - set(__list "${_elem}") - endif(__list) - endforeach(_elem) IF (NOT MKL_FIND_QUIETLY) - message(STATUS "Checking for [${__list}]") + set(_str_list) + foreach(_elem ${_list}) + if(_str_list) + set(_str_list "${_str_list} - ${_elem}") + else() + set(_str_list "${_elem}") + endif() + endforeach(_elem) + message(STATUS "Checking for [${_str_list}]") ENDIF () FOREACH(_library ${_list}) SET(_combined_name ${_combined_name}_${_library}) + UNSET(${_prefix}_${_library}_LIBRARY) IF(_libraries_work) - IF(${_library} STREQUAL "gomp") - FIND_PACKAGE(OpenMP) + IF(${_library} MATCHES "omp") + IF(_openmp_type) + MESSAGE(FATAL_ERROR "More than one OpenMP libraries appear in the MKL test: ${_list}") + ELSEIF(${_library} MATCHES "gomp") + SET(_openmp_type "GNU") + # Use FindOpenMP to find gomp + FIND_PACKAGE(OpenMP QUIET) IF(OPENMP_FOUND) - SET(${_prefix}_${_library}_LIBRARY ${OpenMP_C_FLAGS}) + # Test that none of the found library names contains "iomp" (Intel + # OpenMP). This doesn't necessarily mean that we have gomp... but it + # is probably good enough since on gcc we should already have + # OpenMP_CXX_FLAGS="-fopenmp" and OpenMP_CXX_LIB_NAMES="". + SET(_found_gomp true) + FOREACH(_lib_name ${OpenMP_CXX_LIB_NAMES}) + IF (_found_gomp AND "${_lib_name}" MATCHES "iomp") + SET(_found_gomp false) + ENDIF() + ENDFOREACH() + IF(_found_gomp) + SET(${_prefix}_${_library}_LIBRARY ${OpenMP_CXX_FLAGS}) + SET(_openmp_library "${${_prefix}_${_library}_LIBRARY}") + ENDIF() ENDIF(OPENMP_FOUND) - ELSE(${_library} STREQUAL "gomp") + ELSEIF(${_library} MATCHES "iomp") + SET(_openmp_type "Intel") FIND_LIBRARY(${_prefix}_${_library}_LIBRARY NAMES ${_library}) - ENDIF(${_library} STREQUAL "gomp") + SET(_openmp_library "${${_prefix}_${_library}_LIBRARY}") + ELSE() + MESSAGE(FATAL_ERROR "Unknown OpenMP flavor: ${_library}") + ENDIF() + ELSE(${_library} MATCHES "omp") + FIND_LIBRARY(${_prefix}_${_library}_LIBRARY NAMES ${_library}) + ENDIF(${_library} MATCHES "omp") MARK_AS_ADVANCED(${_prefix}_${_library}_LIBRARY) SET(${LIBRARIES} ${${LIBRARIES}} ${${_prefix}_${_library}_LIBRARY}) SET(_libraries_work ${${_prefix}_${_library}_LIBRARY}) @@ -163,6 +197,10 @@ MACRO(CHECK_ALL_LIBRARIES LIBRARIES _name _list _flags) ENDIF(_libraries_work) # Fin IF(_libraries_work) + SET(${OPENMP_TYPE} ${_openmp_type}) + MARK_AS_ADVANCED(${OPENMP_TYPE}) + SET(${OPENMP_LIBRARY} ${_openmp_library}) + MARK_AS_ADVANCED(${OPENMP_LIBRARY}) ELSE (_libraries_work) SET(${LIBRARIES}) MARK_AS_ADVANCED(${LIBRARIES}) @@ -187,48 +225,52 @@ ENDIF(UNIX AND NOT APPLE) IF (NOT MKL_LIBRARIES) SET(MKL_VERSION 1011) ENDIF (NOT MKL_LIBRARIES) -FOREACH(mklrtl ${mklrtls} "") - FOREACH(mkliface ${mklifaces}) - FOREACH(mkl64 ${mkl64s} "") - FOREACH(mklthread ${mklthreads}) - IF (NOT MKL_LIBRARIES AND NOT INTEL_MKL_SEQUENTIAL) - CHECK_ALL_LIBRARIES(MKL_LIBRARIES cblas_sgemm - "mkl_${mkliface}${mkl64};${mklthread};mkl_core;${mklrtl};${mkl_pthread};${mkl_m};${mkl_dl}" "") - ENDIF (NOT MKL_LIBRARIES AND NOT INTEL_MKL_SEQUENTIAL) - ENDFOREACH(mklthread) - ENDFOREACH(mkl64) - ENDFOREACH(mkliface) -ENDFOREACH(mklrtl) + +# First: search for parallelized ones with intel thread lib +IF (NOT INTEL_MKL_SEQUENTIAL) + FOREACH(mklrtl ${mklrtls} "") + FOREACH(mkliface ${mklifaces}) + FOREACH(mkl64 ${mkl64s} "") + FOREACH(mklthread ${mklthreads}) + IF (NOT MKL_LIBRARIES) + CHECK_ALL_LIBRARIES(MKL_LIBRARIES MKL_OPENMP_TYPE MKL_OPENMP_LIBRARY cblas_sgemm + "mkl_${mkliface}${mkl64};${mklthread};mkl_core;${mklrtl};${mkl_pthread};${mkl_m};${mkl_dl}" "") + ENDIF (NOT MKL_LIBRARIES) + ENDFOREACH(mklthread) + ENDFOREACH(mkl64) + ENDFOREACH(mkliface) + ENDFOREACH(mklrtl) +ENDIF (NOT INTEL_MKL_SEQUENTIAL) + +# Second: search for sequential ones +FOREACH(mkliface ${mklifaces}) + FOREACH(mkl64 ${mkl64s} "") + IF (NOT MKL_LIBRARIES) + CHECK_ALL_LIBRARIES(MKL_LIBRARIES MKL_OPENMP_TYPE MKL_OPENMP_LIBRARY cblas_sgemm + "mkl_${mkliface}${mkl64};mkl_sequential;mkl_core;${mkl_m};${mkl_dl}" "") + IF (MKL_LIBRARIES) + SET(mklseq "_sequential") + ENDIF (MKL_LIBRARIES) + ENDIF (NOT MKL_LIBRARIES) + ENDFOREACH(mkl64) +ENDFOREACH(mkliface) + +# First: search for parallelized ones with native pthread lib FOREACH(mklrtl ${mklrtls} "") FOREACH(mkliface ${mklifaces}) FOREACH(mkl64 ${mkl64s} "") IF (NOT MKL_LIBRARIES) - CHECK_ALL_LIBRARIES(MKL_LIBRARIES cblas_sgemm - "mkl_${mkliface}${mkl64};mkl_sequential;mkl_core;${mkl_m};${mkl_dl}" "") - IF (MKL_LIBRARIES) - SET(mklseq "_sequential") - ENDIF (MKL_LIBRARIES) + CHECK_ALL_LIBRARIES(MKL_LIBRARIES MKL_OPENMP_TYPE MKL_OPENMP_LIBRARY cblas_sgemm + "mkl_${mkliface}${mkl64};${mklthread};mkl_core;${mklrtl};pthread;${mkl_m};${mkl_dl}" "") ENDIF (NOT MKL_LIBRARIES) ENDFOREACH(mkl64) ENDFOREACH(mkliface) ENDFOREACH(mklrtl) -FOREACH(mklrtl ${mklrtls} "") - FOREACH(mkliface ${mklifaces}) - FOREACH(mkl64 ${mkl64s} "") - FOREACH(mklthread ${mklthreads}) - IF (NOT MKL_LIBRARIES) - CHECK_ALL_LIBRARIES(MKL_LIBRARIES cblas_sgemm - "mkl_${mkliface}${mkl64};${mklthread};mkl_core;${mklrtl};pthread;${mkl_m};${mkl_dl}" "") - ENDIF (NOT MKL_LIBRARIES) - ENDFOREACH(mklthread) - ENDFOREACH(mkl64) - ENDFOREACH(mkliface) -ENDFOREACH(mklrtl) # Check for older versions IF (NOT MKL_LIBRARIES) SET(MKL_VERSION 900) - CHECK_ALL_LIBRARIES(MKL_LIBRARIES cblas_sgemm + CHECK_ALL_LIBRARIES(MKL_LIBRARIES MKL_OPENMP_TYPE MKL_OPENMP_LIBRARY cblas_sgemm "mkl;guide;pthread;m" "") ENDIF (NOT MKL_LIBRARIES) diff --git a/cmake/Modules/FindOpenMP.cmake b/cmake/Modules/FindOpenMP.cmake new file mode 100644 index 0000000000..5aace85139 --- /dev/null +++ b/cmake/Modules/FindOpenMP.cmake @@ -0,0 +1,635 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindOpenMP +# ---------- +# +# Finds OpenMP support +# +# This module can be used to detect OpenMP support in a compiler. If +# the compiler supports OpenMP, the flags required to compile with +# OpenMP support are returned in variables for the different languages. +# The variables may be empty if the compiler does not need a special +# flag to support OpenMP. +# +# Variables +# ^^^^^^^^^ +# +# The module exposes the components ``C``, ``CXX``, and ``Fortran``. +# Each of these controls the various languages to search OpenMP support for. +# +# Depending on the enabled components the following variables will be set: +# +# ``OpenMP_FOUND`` +# Variable indicating that OpenMP flags for all requested languages have been found. +# If no components are specified, this is true if OpenMP settings for all enabled languages +# were detected. +# ``OpenMP_VERSION`` +# Minimal version of the OpenMP standard detected among the requested languages, +# 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: +# +# ``OpenMP_<lang>_FOUND`` +# Variable indicating if OpenMP support for ``<lang>`` was detected. +# ``OpenMP_<lang>_FLAGS`` +# OpenMP compiler flags for ``<lang>``, separated by spaces. +# +# For linking with OpenMP code written in ``<lang>``, the following +# variables are provided: +# +# ``OpenMP_<lang>_LIB_NAMES`` +# :ref:`;-list <CMake Language Lists>` of libraries for OpenMP programs for ``<lang>``. +# ``OpenMP_<libname>_LIBRARY`` +# Location of the individual libraries needed for OpenMP support in ``<lang>``. +# ``OpenMP_<lang>_LIBRARIES`` +# A list of libraries needed to link with OpenMP code written in ``<lang>``. +# +# Additionally, the module provides :prop_tgt:`IMPORTED` targets: +# +# ``OpenMP::OpenMP_<lang>`` +# Target for using OpenMP from ``<lang>``. +# +# Specifically for Fortran, the module sets the following variables: +# +# ``OpenMP_Fortran_HAVE_OMPLIB_HEADER`` +# Boolean indicating if OpenMP is accessible through ``omp_lib.h``. +# ``OpenMP_Fortran_HAVE_OMPLIB_MODULE`` +# Boolean indicating if OpenMP is accessible through the ``omp_lib`` Fortran module. +# +# The module will also try to provide the OpenMP version variables: +# +# ``OpenMP_<lang>_SPEC_DATE`` +# Date of the OpenMP specification implemented by the ``<lang>`` compiler. +# ``OpenMP_<lang>_VERSION_MAJOR`` +# Major version of OpenMP implemented by the ``<lang>`` compiler. +# ``OpenMP_<lang>_VERSION_MINOR`` +# Minor version of OpenMP implemented by the ``<lang>`` compiler. +# ``OpenMP_<lang>_VERSION`` +# OpenMP version implemented by the ``<lang>`` compiler. +# +# The specification date is formatted as given in the OpenMP standard: +# ``yyyymm`` where ``yyyy`` and ``mm`` represents the year and month of +# the OpenMP specification implemented by the ``<lang>`` compiler. + +cmake_policy(PUSH) +cmake_policy(SET CMP0012 NEW) # if() recognizes numbers and booleans +cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced +cmake_policy(SET CMP0057 NEW) # if IN_LIST + +function(_OPENMP_FLAG_CANDIDATES LANG) + if(NOT OpenMP_${LANG}_FLAG) + unset(OpenMP_FLAG_CANDIDATES) + + set(OMP_FLAG_GNU "-fopenmp") + set(OMP_FLAG_Clang "-fopenmp=libomp" "-fopenmp=libiomp5" "-fopenmp") + + # AppleClang may need a header file, search for omp.h with hints to brew + # default include dir + find_path(__header_dir "omp.h" HINTS "/usr/local/include") + set(OMP_FLAG_AppleClang "-Xpreprocessor -fopenmp" "-Xpreprocessor -fopenmp -I${__header_dir}") + + set(OMP_FLAG_HP "+Oopenmp") + if(WIN32) + set(OMP_FLAG_Intel "-Qopenmp") + elseif(CMAKE_${LANG}_COMPILER_ID STREQUAL "Intel" AND + "${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS "15.0.0.20140528") + set(OMP_FLAG_Intel "-openmp") + else() + set(OMP_FLAG_Intel "-qopenmp") + endif() + set(OMP_FLAG_MIPSpro "-mp") + set(OMP_FLAG_MSVC "-openmp") + set(OMP_FLAG_PathScale "-openmp") + set(OMP_FLAG_NAG "-openmp") + set(OMP_FLAG_Absoft "-openmp") + set(OMP_FLAG_PGI "-mp") + set(OMP_FLAG_Flang "-fopenmp") + set(OMP_FLAG_SunPro "-xopenmp") + set(OMP_FLAG_XL "-qsmp=omp") + # Cray compiler activate OpenMP with -h omp, which is enabled by default. + set(OMP_FLAG_Cray " " "-h omp") + + # If we know the correct flags, use those + if(DEFINED OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}) + set(OpenMP_FLAG_CANDIDATES "${OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}}") + # Fall back to reasonable default tries otherwise + else() + set(OpenMP_FLAG_CANDIDATES "-openmp" "-fopenmp" "-mp" " ") + endif() + set(OpenMP_${LANG}_FLAG_CANDIDATES "${OpenMP_FLAG_CANDIDATES}" PARENT_SCOPE) + else() + set(OpenMP_${LANG}_FLAG_CANDIDATES "${OpenMP_${LANG}_FLAG}" PARENT_SCOPE) + endif() +endfunction() + +# sample openmp source code to test +set(OpenMP_C_CXX_TEST_SOURCE +" +#include <omp.h> +int main(void) { +#ifdef _OPENMP + omp_get_max_threads(); + return 0; +#else + breaks_on_purpose +#endif +} +") + +# in Fortran, an implementation may provide an omp_lib.h header +# or omp_lib module, or both (OpenMP standard, section 3.1) +# Furthmore !$ is the Fortran equivalent of #ifdef _OPENMP (OpenMP standard, 2.2.2) +# Without the conditional compilation, some compilers (e.g. PGI) might compile OpenMP code +# while not actually enabling OpenMP, building code sequentially +set(OpenMP_Fortran_TEST_SOURCE + " + program test + @OpenMP_Fortran_INCLUDE_LINE@ + !$ integer :: n + n = omp_get_num_threads() + end program test + " +) + +function(_OPENMP_WRITE_SOURCE_FILE LANG SRC_FILE_CONTENT_VAR SRC_FILE_NAME SRC_FILE_FULLPATH) + set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMP) + if("${LANG}" STREQUAL "C") + set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.c") + file(WRITE "${SRC_FILE}" "${OpenMP_C_CXX_${SRC_FILE_CONTENT_VAR}}") + elseif("${LANG}" STREQUAL "CXX") + set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.cpp") + file(WRITE "${SRC_FILE}" "${OpenMP_C_CXX_${SRC_FILE_CONTENT_VAR}}") + elseif("${LANG}" STREQUAL "Fortran") + set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.f90") + file(WRITE "${SRC_FILE}_in" "${OpenMP_Fortran_${SRC_FILE_CONTENT_VAR}}") + configure_file("${SRC_FILE}_in" "${SRC_FILE}" @ONLY) + endif() + set(${SRC_FILE_FULLPATH} "${SRC_FILE}" PARENT_SCOPE) +endfunction() + +include(CMakeParseImplicitLinkInfo) + +function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) + _OPENMP_FLAG_CANDIDATES("${LANG}") + _OPENMP_WRITE_SOURCE_FILE("${LANG}" "TEST_SOURCE" OpenMPTryFlag _OPENMP_TEST_SRC) + + unset(OpenMP_VERBOSE_COMPILE_OPTIONS) + if(UNIX) + separate_arguments(OpenMP_VERBOSE_OPTIONS UNIX_COMMAND "${CMAKE_${LANG}_VERBOSE_FLAG}") + else() + separate_arguments(OpenMP_VERBOSE_OPTIONS WINDOWS_COMMAND "${CMAKE_${LANG}_VERBOSE_FLAG}") + endif() + foreach(_VERBOSE_OPTION IN LISTS OpenMP_VERBOSE_OPTIONS) + if(NOT _VERBOSE_OPTION MATCHES "^-Wl,") + list(APPEND OpenMP_VERBOSE_COMPILE_OPTIONS ${_VERBOSE_OPTION}) + endif() + endforeach() + + foreach(OPENMP_FLAG IN LISTS OpenMP_${LANG}_FLAG_CANDIDATES) + set(OPENMP_FLAGS_TEST "${OPENMP_FLAG}") + if(OpenMP_VERBOSE_COMPILE_OPTIONS) + string(APPEND OPENMP_FLAGS_TEST " ${OpenMP_VERBOSE_COMPILE_OPTIONS}") + endif() + string(REGEX REPLACE "[-/=+]" "" OPENMP_PLAIN_FLAG "${OPENMP_FLAG}") + + # NOTE [ Linking both MKL and OpenMP ] + # + # It is crucial not to link two `libomp` libraries together, even when they + # are both Intel or GNU. Otherwise, you will end up with this nasty error, + # and may get incorrect results. + # + # OMP: Error #15: Initializing libomp.dylib, but found libiomp5.dylib + # already initialized. + # + # OMP: Hint This means that multiple copies of the OpenMP runtime have + # been linked into the program. That is dangerous, since it can degrade + # performance or cause incorrect results. The best thing to do is to + # ensure that only a single OpenMP runtime is linked into the process, + # e.g. by avoiding static linking of the OpenMP runtime in any library. As + # an unsafe, unsupported, undocumented workaround you can set the + # environment variable KMP_DUPLICATE_LIB_OK=TRUE to allow the program to + # continue to execute, but that may cause crashes or silently produce + # incorrect results. For more information, please see + # http://openmp.llvm.org/ + # + # So here, before we test each flag combination, we first try directly + # linking against any `libomp` MKL has found (if any). This allows us to + # do sensible things in tricky (yet common) conditions like: + # - using `clang` (so no native GNU OpenMP), and + # - having `brew` `libomp` installed at `/usr/local/`, and + # - having `conda` `mkl` installed at `$HOME/conda/`, with includes a copy + # of `libiomp5`. + # Rather than blindly picking one, we pick what ever `FindMKL.cmake` choses + # to avoid conflicts. + # + # Crucially, we only do so for non-GNU compilers. For GNU ones, + # `FindMKL.cmake` calls `FindOpenMP.cmake` when trying to find `gomp` and + # thus will cause infinite recursion if this is not taken care of. Moreover, + # for them, since the compiler provices the OpenMP library, it is most + # likely that only one viable gomp library can be found in search path by + # `FindOpenMP.cmake`, so the chance of having conflicts is slow. + # + # TODO: refactor to solve this weird dependency where + # - for non-GNU, FindOpenMP.cmake replies on FindMKL.cmake to finish first, but + # - for GNU, FindMKL.cmake replies on FindOpenMP.cmake to finish first. + + if(NOT "${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU") + find_package(MKL QUIET) + if(MKL_FOUND AND (NOT "${MKL_OPENMP_LIBRARY}" STREQUAL "")) + # If we already link OpenMP via MKL, use that. Otherwise at run-time + # OpenMP will complain about being initialized twice (OMP: Error #15), + # can may cause incorrect behavior. + set(OpenMP_libomp_LIBRARY "${MKL_OPENMP_LIBRARY}" CACHE STRING "libomp location for OpenMP") + else() + find_library(OpenMP_libomp_LIBRARY + NAMES omp gomp iomp5 + HINTS ${CMAKE_${LANG}_IMPLICIT_LINK_DIRECTORIES} + DOC "libomp location for OpenMP" + ) + endif() + mark_as_advanced(OpenMP_libomp_LIBRARY) + + if (OpenMP_libomp_LIBRARY) + try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC} + CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" + LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY} + OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT + ) + if(OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}) + set("${OPENMP_FLAG_VAR}" "${OPENMP_FLAG}" PARENT_SCOPE) + set("${OPENMP_LIB_NAMES_VAR}" "libomp" PARENT_SCOPE) + break() + endif() + endif() + endif() + + try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC} + CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" + LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} + OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT + ) + + if(OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}) + set("${OPENMP_FLAG_VAR}" "${OPENMP_FLAG}" PARENT_SCOPE) + + if(CMAKE_${LANG}_VERBOSE_FLAG) + unset(OpenMP_${LANG}_IMPLICIT_LIBRARIES) + unset(OpenMP_${LANG}_IMPLICIT_LINK_DIRS) + unset(OpenMP_${LANG}_IMPLICIT_FWK_DIRS) + unset(OpenMP_${LANG}_LOG_VAR) + + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Detecting ${LANG} OpenMP compiler ABI info compiled with the following output:\n${OpenMP_TRY_COMPILE_OUTPUT}\n\n") + + cmake_parse_implicit_link_info("${OpenMP_TRY_COMPILE_OUTPUT}" + OpenMP_${LANG}_IMPLICIT_LIBRARIES + OpenMP_${LANG}_IMPLICIT_LINK_DIRS + OpenMP_${LANG}_IMPLICIT_FWK_DIRS + OpenMP_${LANG}_LOG_VAR + "${CMAKE_${LANG}_IMPLICIT_OBJECT_REGEX}" + ) + + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Parsed ${LANG} OpenMP implicit link information from above output:\n${OpenMP_${LANG}_LOG_VAR}\n\n") + + unset(_OPENMP_LIB_NAMES) + foreach(_OPENMP_IMPLICIT_LIB IN LISTS OpenMP_${LANG}_IMPLICIT_LIBRARIES) + get_filename_component(_OPENMP_IMPLICIT_LIB_DIR "${_OPENMP_IMPLICIT_LIB}" DIRECTORY) + get_filename_component(_OPENMP_IMPLICIT_LIB_NAME "${_OPENMP_IMPLICIT_LIB}" NAME) + get_filename_component(_OPENMP_IMPLICIT_LIB_PLAIN "${_OPENMP_IMPLICIT_LIB}" NAME_WE) + 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 "${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) + set(OpenMP_${_OPENMP_IMPLICIT_LIB_PLAIN}_LIBRARY "${_OPENMP_IMPLICIT_LIB}" CACHE FILEPATH + "Path to the ${_OPENMP_IMPLICIT_LIB_PLAIN} library for OpenMP") + else() + find_library(OpenMP_${_OPENMP_IMPLICIT_LIB_PLAIN}_LIBRARY + NAMES "${_OPENMP_IMPLICIT_LIB_NAME}" + DOC "Path to the ${_OPENMP_IMPLICIT_LIB_PLAIN} library for OpenMP" + HINTS ${OpenMP_${LANG}_IMPLICIT_LINK_DIRS} + CMAKE_FIND_ROOT_PATH_BOTH + NO_DEFAULT_PATH + ) + endif() + mark_as_advanced(OpenMP_${_OPENMP_IMPLICIT_LIB_PLAIN}_LIBRARY) + list(APPEND _OPENMP_LIB_NAMES ${_OPENMP_IMPLICIT_LIB_PLAIN}) + endif() + endforeach() + set("${OPENMP_LIB_NAMES_VAR}" "${_OPENMP_LIB_NAMES}" PARENT_SCOPE) + else() + # We do not know how to extract implicit OpenMP libraries for this compiler. + # Assume that it handles them automatically, e.g. the Intel Compiler on + # Windows should put the dependency in its object files. + set("${OPENMP_LIB_NAMES_VAR}" "" PARENT_SCOPE) + endif() + break() + elseif((CMAKE_${LANG}_COMPILER_ID STREQUAL "AppleClang") AND + (NOT CMAKE_${LANG}_COMPILER_VERSION VERSION_LESS "7.0")) + + # LLVM 3.7 supports OpenMP 3.1, and continues to add more features to + # support newer OpenMP standards in new versions. + # http://releases.llvm.org/3.7.0/tools/clang/docs/ReleaseNotes.html#openmp-support + # + # Apple Clang 7.0 is the first version based on LLVM 3.7 or later. + # https://en.wikipedia.org/wiki/Xcode#Latest_versions + # + # Check for separate OpenMP library on AppleClang 7+ + find_library(OpenMP_libomp_LIBRARY + NAMES omp gomp iomp5 + HINTS ${CMAKE_${LANG}_IMPLICIT_LINK_DIRECTORIES} + DOC "libomp location for OpenMP" + ) + mark_as_advanced(OpenMP_libomp_LIBRARY) + + if(OpenMP_libomp_LIBRARY) + try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC} + CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" + LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY} + OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT + ) + if(OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}) + set("${OPENMP_FLAG_VAR}" "${OPENMP_FLAG}" PARENT_SCOPE) + set("${OPENMP_LIB_NAMES_VAR}" "libomp" PARENT_SCOPE) + break() + endif() + endif() + else() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Detecting ${LANG} OpenMP failed with the following output:\n${OpenMP_TRY_COMPILE_OUTPUT}\n\n") + endif() + if (NOT ${OpenMP_${LANG}_FIND_QUIETLY}) + message(STATUS "OpenMP try_compile log:\n${OpenMP_TRY_COMPILE_OUTPUT}\n\n") + endif() + set("${OPENMP_LIB_NAMES_VAR}" "NOTFOUND" PARENT_SCOPE) + set("${OPENMP_FLAG_VAR}" "NOTFOUND" PARENT_SCOPE) + endforeach() + + unset(OpenMP_VERBOSE_COMPILE_OPTIONS) +endfunction() + +set(OpenMP_C_CXX_CHECK_VERSION_SOURCE +" +#include <stdio.h> +#include <omp.h> +const char ompver_str[] = { 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'M', + 'P', '-', 'd', 'a', 't', 'e', '[', + ('0' + ((_OPENMP/100000)%10)), + ('0' + ((_OPENMP/10000)%10)), + ('0' + ((_OPENMP/1000)%10)), + ('0' + ((_OPENMP/100)%10)), + ('0' + ((_OPENMP/10)%10)), + ('0' + ((_OPENMP/1)%10)), + ']', '\\0' }; +int main(void) +{ + puts(ompver_str); + return 0; +} +") + +set(OpenMP_Fortran_CHECK_VERSION_SOURCE +" + program omp_ver + @OpenMP_Fortran_INCLUDE_LINE@ + integer, parameter :: zero = ichar('0') + integer, parameter :: ompv = openmp_version + character, dimension(24), parameter :: ompver_str =& + (/ 'I', 'N', 'F', 'O', ':', 'O', 'p', 'e', 'n', 'M', 'P', '-',& + 'd', 'a', 't', 'e', '[',& + char(zero + mod(ompv/100000, 10)),& + char(zero + mod(ompv/10000, 10)),& + char(zero + mod(ompv/1000, 10)),& + char(zero + mod(ompv/100, 10)),& + char(zero + mod(ompv/10, 10)),& + char(zero + mod(ompv/1, 10)), ']' /) + print *, ompver_str + end program omp_ver +") + +function(_OPENMP_GET_SPEC_DATE LANG SPEC_DATE) + _OPENMP_WRITE_SOURCE_FILE("${LANG}" "CHECK_VERSION_SOURCE" OpenMPCheckVersion _OPENMP_TEST_SRC) + + set(BIN_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMP/ompver_${LANG}.bin") + string(REGEX REPLACE "[-/=+]" "" OPENMP_PLAIN_FLAG "${OPENMP_FLAG}") + try_compile(OpenMP_SPECTEST_${LANG}_${OPENMP_PLAIN_FLAG} "${CMAKE_BINARY_DIR}" "${_OPENMP_TEST_SRC}" + CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenMP_${LANG}_FLAGS}" + COPY_FILE ${BIN_FILE} + OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT) + + if(${OpenMP_SPECTEST_${LANG}_${OPENMP_PLAIN_FLAG}}) + file(STRINGS ${BIN_FILE} specstr LIMIT_COUNT 1 REGEX "INFO:OpenMP-date") + set(regex_spec_date ".*INFO:OpenMP-date\\[0*([^]]*)\\].*") + if("${specstr}" MATCHES "${regex_spec_date}") + set(${SPEC_DATE} "${CMAKE_MATCH_1}" PARENT_SCOPE) + endif() + else() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Detecting ${LANG} OpenMP version failed with the following output:\n${OpenMP_TRY_COMPILE_OUTPUT}\n\n") + endif() +endfunction() + +macro(_OPENMP_SET_VERSION_BY_SPEC_DATE LANG) + set(OpenMP_SPEC_DATE_MAP + # Preview versions + "201611=5.0" # OpenMP 5.0 preview 1 + # Combined versions, 2.5 onwards + "201511=4.5" + "201307=4.0" + "201107=3.1" + "200805=3.0" + "200505=2.5" + # C/C++ version 2.0 + "200203=2.0" + # Fortran version 2.0 + "200011=2.0" + # Fortran version 1.1 + "199911=1.1" + # C/C++ version 1.0 (there's no 1.1 for C/C++) + "199810=1.0" + # Fortran version 1.0 + "199710=1.0" + ) + + if(OpenMP_${LANG}_SPEC_DATE) + string(REGEX MATCHALL "${OpenMP_${LANG}_SPEC_DATE}=([0-9]+)\\.([0-9]+)" _version_match "${OpenMP_SPEC_DATE_MAP}") + else() + set(_version_match "") + endif() + if(NOT _version_match STREQUAL "") + set(OpenMP_${LANG}_VERSION_MAJOR ${CMAKE_MATCH_1}) + set(OpenMP_${LANG}_VERSION_MINOR ${CMAKE_MATCH_2}) + set(OpenMP_${LANG}_VERSION "${OpenMP_${LANG}_VERSION_MAJOR}.${OpenMP_${LANG}_VERSION_MINOR}") + else() + unset(OpenMP_${LANG}_VERSION_MAJOR) + unset(OpenMP_${LANG}_VERSION_MINOR) + unset(OpenMP_${LANG}_VERSION) + endif() + unset(_version_match) + unset(OpenMP_SPEC_DATE_MAP) +endmacro() + +foreach(LANG IN ITEMS C CXX) + 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") + _OPENMP_GET_FLAGS("${LANG}" "${LANG}" OpenMP_${LANG}_FLAGS_WORK OpenMP_${LANG}_LIB_NAMES_WORK) + endif() + + set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}" + CACHE STRING "${LANG} compiler flags for OpenMP parallelization") + set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}" + CACHE STRING "${LANG} compiler libraries for OpenMP parallelization") + mark_as_advanced(OpenMP_${LANG}_FLAGS OpenMP_${LANG}_LIB_NAMES) + endif() +endforeach() + +if(CMAKE_Fortran_COMPILER_LOADED) + if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND" + OR NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND" + OR NOT DEFINED OpenMP_Fortran_HAVE_OMPLIB_MODULE) + set(OpenMP_Fortran_INCLUDE_LINE "use omp_lib\n implicit none") + _OPENMP_GET_FLAGS("Fortran" "FortranHeader" OpenMP_Fortran_FLAGS_WORK OpenMP_Fortran_LIB_NAMES_WORK) + if(OpenMP_Fortran_FLAGS_WORK) + set(OpenMP_Fortran_HAVE_OMPLIB_MODULE TRUE CACHE BOOL INTERNAL "") + endif() + + set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}" + CACHE STRING "Fortran compiler flags for OpenMP parallelization") + set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}" + CACHE STRING "Fortran compiler libraries for OpenMP parallelization") + mark_as_advanced(OpenMP_Fortran_FLAGS OpenMP_Fortran_LIB_NAMES) + endif() + + if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND" + OR NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND" + OR NOT DEFINED OpenMP_Fortran_HAVE_OMPLIB_HEADER) + set(OpenMP_Fortran_INCLUDE_LINE "implicit none\n include 'omp_lib.h'") + _OPENMP_GET_FLAGS("Fortran" "FortranModule" OpenMP_Fortran_FLAGS_WORK OpenMP_Fortran_LIB_NAMES_WORK) + if(OpenMP_Fortran_FLAGS_WORK) + set(OpenMP_Fortran_HAVE_OMPLIB_HEADER TRUE CACHE BOOL INTERNAL "") + endif() + + set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}" + CACHE STRING "Fortran compiler flags for OpenMP parallelization") + + set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES}" + CACHE STRING "Fortran compiler libraries for OpenMP parallelization") + endif() + + if(OpenMP_Fortran_HAVE_OMPLIB_MODULE) + set(OpenMP_Fortran_INCLUDE_LINE "use omp_lib\n implicit none") + else() + set(OpenMP_Fortran_INCLUDE_LINE "implicit none\n include 'omp_lib.h'") + endif() +endif() + +if(NOT OpenMP_FIND_COMPONENTS) + set(OpenMP_FINDLIST C CXX Fortran) +else() + set(OpenMP_FINDLIST ${OpenMP_FIND_COMPONENTS}) +endif() + +unset(_OpenMP_MIN_VERSION) + +include(FindPackageHandleStandardArgs) + +foreach(LANG IN LISTS OpenMP_FINDLIST) + if(CMAKE_${LANG}_COMPILER_LOADED) + if (NOT OpenMP_${LANG}_SPEC_DATE AND OpenMP_${LANG}_FLAGS) + _OPENMP_GET_SPEC_DATE("${LANG}" OpenMP_${LANG}_SPEC_DATE_INTERNAL) + set(OpenMP_${LANG}_SPEC_DATE "${OpenMP_${LANG}_SPEC_DATE_INTERNAL}" CACHE + INTERNAL "${LANG} compiler's OpenMP specification date") + _OPENMP_SET_VERSION_BY_SPEC_DATE("${LANG}") + endif() + + set(OpenMP_${LANG}_FIND_QUIETLY ${OpenMP_FIND_QUIETLY}) + set(OpenMP_${LANG}_FIND_REQUIRED ${OpenMP_FIND_REQUIRED}) + set(OpenMP_${LANG}_FIND_VERSION ${OpenMP_FIND_VERSION}) + set(OpenMP_${LANG}_FIND_VERSION_EXACT ${OpenMP_FIND_VERSION_EXACT}) + + set(_OPENMP_${LANG}_REQUIRED_VARS OpenMP_${LANG}_FLAGS) + if("${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND") + set(_OPENMP_${LANG}_REQUIRED_LIB_VARS OpenMP_${LANG}_LIB_NAMES) + else() + foreach(_OPENMP_IMPLICIT_LIB IN LISTS OpenMP_${LANG}_LIB_NAMES) + list(APPEND _OPENMP_${LANG}_REQUIRED_LIB_VARS OpenMP_${_OPENMP_IMPLICIT_LIB}_LIBRARY) + endforeach() + endif() + + find_package_handle_standard_args(OpenMP_${LANG} + REQUIRED_VARS OpenMP_${LANG}_FLAGS ${_OPENMP_${LANG}_REQUIRED_LIB_VARS} + VERSION_VAR OpenMP_${LANG}_VERSION + ) + + if(OpenMP_${LANG}_FOUND) + if(DEFINED OpenMP_${LANG}_VERSION) + if(NOT _OpenMP_MIN_VERSION OR _OpenMP_MIN_VERSION VERSION_GREATER OpenMP_${LANG}_VERSION) + set(_OpenMP_MIN_VERSION OpenMP_${LANG}_VERSION) + endif() + endif() + set(OpenMP_${LANG}_LIBRARIES "") + foreach(_OPENMP_IMPLICIT_LIB IN LISTS OpenMP_${LANG}_LIB_NAMES) + list(APPEND OpenMP_${LANG}_LIBRARIES "${OpenMP_${_OPENMP_IMPLICIT_LIB}_LIBRARY}") + endforeach() + + if(NOT TARGET OpenMP::OpenMP_${LANG}) + add_library(OpenMP::OpenMP_${LANG} INTERFACE IMPORTED) + endif() + if(OpenMP_${LANG}_FLAGS) + if(UNIX) + separate_arguments(_OpenMP_${LANG}_OPTIONS UNIX_COMMAND "${OpenMP_${LANG}_FLAGS}") + else() + separate_arguments(_OpenMP_${LANG}_OPTIONS WINDOWS_COMMAND "${OpenMP_${LANG}_FLAGS}") + endif() + set_property(TARGET OpenMP::OpenMP_${LANG} PROPERTY + INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${_OpenMP_${LANG}_OPTIONS}>") + unset(_OpenMP_${LANG}_OPTIONS) + endif() + if(OpenMP_${LANG}_LIBRARIES) + set_property(TARGET OpenMP::OpenMP_${LANG} PROPERTY + INTERFACE_LINK_LIBRARIES "${OpenMP_${LANG}_LIBRARIES}") + endif() + endif() + endif() +endforeach() + +unset(_OpenMP_REQ_VARS) +foreach(LANG IN ITEMS C CXX 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() +endforeach() + +find_package_handle_standard_args(OpenMP + REQUIRED_VARS ${_OpenMP_REQ_VARS} + VERSION_VAR ${_OpenMP_MIN_VERSION} + HANDLE_COMPONENTS) + +set(OPENMP_FOUND ${OpenMP_FOUND}) + +if(CMAKE_Fortran_COMPILER_LOADED AND OpenMP_Fortran_FOUND) + if(NOT DEFINED OpenMP_Fortran_HAVE_OMPLIB_MODULE) + set(OpenMP_Fortran_HAVE_OMPLIB_MODULE FALSE CACHE BOOL INTERNAL "") + endif() + if(NOT DEFINED OpenMP_Fortran_HAVE_OMPLIB_HEADER) + set(OpenMP_Fortran_HAVE_OMPLIB_HEADER FALSE CACHE BOOL INTERNAL "") + 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") +endif() + +unset(OpenMP_C_CXX_TEST_SOURCE) +unset(OpenMP_Fortran_TEST_SOURCE) +unset(OpenMP_C_CXX_CHECK_VERSION_SOURCE) +unset(OpenMP_Fortran_CHECK_VERSION_SOURCE) +unset(OpenMP_Fortran_INCLUDE_LINE) + +cmake_policy(POP) diff --git a/cmake/Modules/README.md b/cmake/Modules/README.md new file mode 100644 index 0000000000..df1ceb91a9 --- /dev/null +++ b/cmake/Modules/README.md @@ -0,0 +1,34 @@ +This folder contains various custom cmake modules for finding libraries and packages. Details about some of them are listed below. + +### [`FindOpenMP.cmake`](./FindOpenMP.cmake) + +This is modified from [the file included in CMake 3.13 release](https://github.com/Kitware/CMake/blob/05a2ca7f87b9ae73f373e9967fde1ee5210e33af/Modules/FindOpenMP.cmake), with the following changes: + ++ Replace `VERSION_GREATER_EQUAL` with `NOT ... VERSION_LESS` as `VERSION_GREATER_EQUAL` is not supported in CMake 3.5 (our min supported version). + ++ Update the `separate_arguments` commands to not use `NATIVE_COMMAND` which is not supported in CMake 3.5 (our min supported version). + ++ Make it respect the `QUIET` flag so that, when it is set, `try_compile` failures are not reported. + ++ For `AppleClang` compilers, use `-Xpreprocessor` instead of `-Xclang` as the later is not documented. + ++ For `AppleClang` compilers, an extra flag option is tried, which is `-Xpreprocessor -openmp -I${DIR_OF_omp_h}`, where `${DIR_OF_omp_h}` is a obtained using `find_path` on `omp.h` with `brew`'s default include directory as a hint. Without this, the compiler will complain about missing headers as they are not natively included in Apple's LLVM. + ++ For non-GNU compilers, whenever we try a candidate OpenMP flag, first try it with directly linking MKL's `libomp` if it has one. Otherwise, we may end up linking two `libomp`s and end up with this nasty error: + + ``` + OMP: Error #15: Initializing libomp.dylib, but found libiomp5.dylib already + initialized. + + OMP: Hint This means that multiple copies of the OpenMP runtime have been + linked into the program. That is dangerous, since it can degrade performance + or cause incorrect results. The best thing to do is to ensure that only a + single OpenMP runtime is linked into the process, e.g. by avoiding static + linking of the OpenMP runtime in any library. As an unsafe, unsupported, + undocumented workaround you can set the environment variable + KMP_DUPLICATE_LIB_OK=TRUE to allow the program to continue to execute, but + that may cause crashes or silently produce incorrect results. For more + information, please see http://openmp.llvm.org/ + ``` + + See NOTE [ Linking both MKL and OpenMP ] for details. diff --git a/cmake/Summary.cmake b/cmake/Summary.cmake index d687c6dc1f..0793526d64 100644 --- a/cmake/Summary.cmake +++ b/cmake/Summary.cmake @@ -7,6 +7,7 @@ function (caffe2_print_configuration_summary) message(STATUS " CMake command : ${CMAKE_COMMAND}") message(STATUS " System : ${CMAKE_SYSTEM_NAME}") message(STATUS " C++ compiler : ${CMAKE_CXX_COMPILER}") + message(STATUS " C++ compiler id : ${CMAKE_CXX_COMPILER_ID}") message(STATUS " C++ compiler version : ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS " BLAS : ${BLAS}") message(STATUS " CXX flags : ${CMAKE_CXX_FLAGS}") diff --git a/modules/detectron/CMakeLists.txt b/modules/detectron/CMakeLists.txt index 8628c5fdaf..7d4861f574 100644 --- a/modules/detectron/CMakeLists.txt +++ b/modules/detectron/CMakeLists.txt @@ -2,6 +2,10 @@ file(GLOB Detectron_CPU_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cc) file(GLOB Detectron_GPU_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cu) if (BUILD_CAFFE2_OPS) + if (USE_OPENMP AND OPENMP_FOUND) + Set(OpenMP_link ${OpenMP_CXX_LIBRARIES}) + endif() + # Note(ilijar): Since Detectron ops currently have no # CPU implementation, we only build GPU ops for now. if (USE_CUDA) @@ -10,14 +14,14 @@ if (BUILD_CAFFE2_OPS) ${Detectron_CPU_SRCS} ${Detectron_GPU_SRCS}) - target_link_libraries(caffe2_detectron_ops_gpu caffe2_gpu) + target_link_libraries(caffe2_detectron_ops_gpu caffe2_gpu ${OpenMP_link}) install(TARGETS caffe2_detectron_ops_gpu DESTINATION lib) if (MSVC) install(FILES $<TARGET_PDB_FILE:caffe2_detectron_ops_gpu> DESTINATION lib OPTIONAL) endif() elseif(NOT IOS_PLATFORM) add_library(caffe2_detectron_ops SHARED ${Detectron_CPU_SRCS}) - target_link_libraries(caffe2_detectron_ops caffe2) + target_link_libraries(caffe2_detectron_ops caffe2 ${OpenMP_link}) install(TARGETS caffe2_detectron_ops DESTINATION lib) if (MSVC) install(FILES $<TARGET_PDB_FILE:caffe2_detectron_ops> DESTINATION lib OPTIONAL) diff --git a/torch/CMakeLists.txt b/torch/CMakeLists.txt index 2f0beeccf5..347cb3d58c 100644 --- a/torch/CMakeLists.txt +++ b/torch/CMakeLists.txt @@ -314,13 +314,13 @@ endif() target_link_libraries(torch caffe2_library) -find_package(OpenMP) +find_package(OpenMP QUIET) if(OPENMP_FOUND) - if (VERBOSE) - message(STATUS "Compiling with OpenMP") - endif() - target_compile_options(torch INTERFACE -fopenmp) - target_link_libraries(torch -fopenmp) + message(STATUS "pytorch is compiling with OpenMP. \n" + "OpenMP CXX_FLAGS: ${OpenMP_CXX_FLAGS}. \n" + "OpenMP libraries: ${OpenMP_CXX_LIBRARIES}.") + target_compile_options(torch INTERFACE ${OpenMP_CXX_FLAGS}) + target_link_libraries(torch ${OpenMP_CXX_LIBRARIES}) endif() if (NOT NO_API) |