Merge branch release-2016
[alexxy/gromacs.git] / cmake / gmxDetectSimd.cmake
index c0cf2f56ac17db11670d50fc40101c7a3c223233..50088be99f190879bf4d71a9804e27ddba2a99d5 100644 (file)
 
 # - Check the username performing the build, as well as date and time
 #
-# gmx_detect_simd(GMX_SUGGESTED_SIMD)
+# gmx_detect_simd(_suggested_simd_)
 #
-# Try to detect CPU information and suggest SIMD instruction set
+# Try to detect CPU features and suggest a SIMD instruction set
 # that fits the current CPU. This should work on all architectures
 # where we are not cross-compiling; depending on the architecture the
 # detection will either use special assembly instructions (like cpuid),
 # preprocessor defines, or probing /proc/cpuinfo on Linux.
 # 
-# This assumes gmx_detect_target_architecture() has already been run,
-# so that things like GMX_TARGET_X86 are already available.
-# (otherwise we cannot use inline ASM on x86).
-#
-# Sets ${GMX_SUGGESTED_SIMD} in the parent scope if
-# GMX_SIMD is not set (e.g. by the user, or a previous run
-# of CMake).
+# Sets ${suggested_simd} in the parent scope if GMX_SIMD is not set
+# (e.g. by the user, or a previous run of CMake).
 #
 
 # we rely on inline asm support for GNU!
 include(gmxTestInlineASM)
+# Ensure things like GMX_TARGET_X86 are available
+include(gmxDetectTargetArchitecture)
+gmx_detect_target_architecture()
 
+include(gmxDetectCpu)
 function(gmx_suggest_simd _suggested_simd)
-    if(${_suggested_simd})
-        # There's already been a suggestion made, which can't change
-        return()
-    endif()
-
-    # for x86 we need inline asm to use cpuid
-    gmx_test_inline_asm_gcc_x86(GMX_X86_GCC_INLINE_ASM)
-
-    if(GMX_X86_GCC_INLINE_ASM)
-        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM=1")
-    else()
-        set(GCC_INLINE_ASM_DEFINE "-DGMX_X86_GCC_INLINE_ASM=0")
+    if (NOT SUGGEST_SIMD_QUIETLY)
+        message(STATUS "Detecting best SIMD instructions for this CPU")
     endif()
 
-    message(STATUS "Detecting best SIMD instructions for this CPU")
-
-    # Get CPU SIMD properties information
-    if(GMX_TARGET_X86)
-        set(GMX_TARGET_X86_VALUE 1)
-    else()
-        set(GMX_TARGET_X86_VALUE 0)
-    endif()
-    set(_compile_definitions "${GCC_INLINE_ASM_DEFINE} -I${CMAKE_SOURCE_DIR}/src -DGMX_CPUINFO_STANDALONE ${GMX_STDLIB_CXX_FLAGS} -DGMX_TARGET_X86=${GMX_TARGET_X86_VALUE}")
-
     # Prepare a default suggestion
     set(OUTPUT_SIMD "None")
 
-    # We need to execute the binary, so this only works if not cross-compiling.
-    # However, note that we are NOT limited to x86.
-    if(NOT CMAKE_CROSSCOMPILING)
-        # TODO Extract this try_compile to a helper function, because
-        # it duplicates code in gmxSetBuildInformation.cmake
-        set(GMX_DETECTSIMD_BINARY "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/GmxDetectSimd${CMAKE_EXECUTABLE_SUFFIX}")
-        set(LINK_LIBRARIES "${GMX_STDLIB_LIBRARIES}")
-        try_compile(GMX_DETECTSIMD_COMPILED
-            "${CMAKE_CURRENT_BINARY_DIR}"
-            "${CMAKE_CURRENT_SOURCE_DIR}/src/gromacs/hardware/cpuinfo.cpp"
-            COMPILE_DEFINITIONS "${_compile_definitions}"
-            CMAKE_FLAGS "-DLINK_LIBRARIES=${LINK_LIBRARIES}"
-            OUTPUT_VARIABLE GMX_DETECTSIMD_COMPILED_OUTPUT
-            COPY_FILE ${GMX_DETECTSIMD_BINARY})
-        unset(_compile_definitions)
+    # Detect CPU features and place the string in CPU_DETECTION_FEATURES
+    # Note that we are NOT limited to x86.
+    gmx_run_cpu_detection(features)
+
+    if (DEFINED CPU_DETECTION_FEATURES)
+        # Make a concrete suggestion of SIMD level if a feature flag
+        # matches. Make sure that the match strings below work even if
+        # the feature is first or last.
+        set(CPU_DETECTION_FEATURES " ${CPU_DETECTION_FEATURES} ")
 
-        if(GMX_DETECTSIMD_COMPILED)
-            if(NOT DEFINED GMX_DETECTSIMD_RUN)
-                execute_process(COMMAND ${GMX_DETECTSIMD_BINARY} "-features"
-                    RESULT_VARIABLE GMX_DETECTSIMD_RUN
-                    OUTPUT_VARIABLE OUTPUT_TMP
-                    ERROR_QUIET)
-                set(GMX_DETECTSIMD_RUN "${GMX_DETECTSIMD_RUN}" CACHE INTERNAL "Result of running cpuinfo code to detect SIMD support")
-                if(GMX_DETECTSIMD_RUN EQUAL 0)
-                    # Make a concrete suggestion of SIMD level
-                    if(GMX_TARGET_X86)
-                        if(OUTPUT_TMP MATCHES " avx512er ")
-                            set(OUTPUT_SIMD "AVX_512_KNL")
-                        elseif(OUTPUT_TMP MATCHES " avx512f ")
-                            set(OUTPUT_SIMD "AVX_512")
-                        elseif(OUTPUT_TMP MATCHES " avx2 ")
-                            set(OUTPUT_SIMD "AVX2_256")
-                        elseif(OUTPUT_TMP MATCHES " avx ")
-                            if(OUTPUT_TMP MATCHES " fma4 ")
-                                # AMD that works better with avx-128-fma
-                                set(OUTPUT_SIMD "AVX_128_FMA")
-                            else()
-                                # Intel
-                                set(OUTPUT_SIMD "AVX_256")
-                            endif()
-                        elseif(OUTPUT_TMP MATCHES " sse4.1 ")
-                            set(OUTPUT_SIMD "SSE4.1")
-                        elseif(OUTPUT_TMP MATCHES " sse2 ")
-                            set(OUTPUT_SIMD "SSE2")
-                        endif()
-                    else()
-                        if(OUTPUT_TMP MATCHES " vsx ")
-                            set(OUTPUT_SIMD "IBM_VSX")
-                        elseif(OUTPUT_TMP MATCHES " vmx ")
-                            set(OUTPUT_SIMD "IBM_VMX")
-                        elseif(OUTPUT_TMP MATCHES " qpx ")
-                            set(OUTPUT_SIMD "IBM_QPX")
-                        elseif(OUTPUT_TMP MATCHES " neon_asimd ")
-                            set(OUTPUT_SIMD "ARM_NEON_ASIMD")
-                        elseif(OUTPUT_TMP MATCHES " neon " AND NOT GMX_DOUBLE)
-                            set(OUTPUT_SIMD "ARM_NEON")
-                        endif()
-                    endif()
-                    message(STATUS "Detected best SIMD instructions for this CPU - ${OUTPUT_SIMD}")
+        if(GMX_TARGET_X86)
+            if(CPU_DETECTION_FEATURES MATCHES " avx512er ")
+                set(OUTPUT_SIMD "AVX_512_KNL")
+            elseif(CPU_DETECTION_FEATURES MATCHES " avx512f ")
+                set(OUTPUT_SIMD "AVX_512")
+            elseif(CPU_DETECTION_FEATURES MATCHES " avx2 ")
+                if(CPU_DETECTION_FEATURES MATCHES " amd ")
+                    set(OUTPUT_SIMD "AVX2_128")
+                else()
+                    set(OUTPUT_SIMD "AVX2_256")
+                endif()
+            elseif(CPU_DETECTION_FEATURES MATCHES " avx ")
+                if(CPU_DETECTION_FEATURES MATCHES " fma4 ")
+                    # AMD that works better with avx-128-fma
+                    set(OUTPUT_SIMD "AVX_128_FMA")
                 else()
-                    message(WARNING "Cannot run cpuinfo code, which means no SIMD suggestion can be made.")
-                    message(STATUS "Run output: ${OUTPUT_TMP}")
+                    # Intel
+                    set(OUTPUT_SIMD "AVX_256")
                 endif()
+            elseif(CPU_DETECTION_FEATURES MATCHES " sse4.1 ")
+                set(OUTPUT_SIMD "SSE4.1")
+            elseif(CPU_DETECTION_FEATURES MATCHES " sse2 ")
+                set(OUTPUT_SIMD "SSE2")
             endif()
         else()
-            message(WARNING "Cannot compile cpuinfo code, which means no SIMD instructions.")
-            message(STATUS "Compile output: ${GMX_DETECTSIMD_COMPILED_OUTPUT}")
+            if(CPU_DETECTION_FEATURES MATCHES " vsx ")
+                set(OUTPUT_SIMD "IBM_VSX")
+            elseif(CPU_DETECTION_FEATURES MATCHES " vmx ")
+                set(OUTPUT_SIMD "IBM_VMX")
+            elseif(CPU_DETECTION_FEATURES MATCHES " qpx ")
+                set(OUTPUT_SIMD "IBM_QPX")
+            elseif(CPU_DETECTION_FEATURES MATCHES " neon_asimd ")
+                set(OUTPUT_SIMD "ARM_NEON_ASIMD")
+            elseif(CPU_DETECTION_FEATURES MATCHES " neon " AND NOT GMX_DOUBLE)
+                set(OUTPUT_SIMD "ARM_NEON")
+            endif()
+        endif()
+        if (NOT SUGGEST_SIMD_QUIETLY)
+            message(STATUS "Detected best SIMD instructions for this CPU - ${OUTPUT_SIMD}")
         endif()
     else()
-        message(WARNING "Cannot detect SIMD architecture for this cross-compile; you should check it manually.")
+        if (NOT SUGGEST_SIMD_QUIETLY)
+            message(STATUS "Detection for best SIMD instructions failed, using SIMD - ${OUTPUT_SIMD}")
+        endif()
     endif()
 
-    set(${_suggested_simd} "${OUTPUT_SIMD}" CACHE INTERNAL "Suggested SIMD")
+    set(${_suggested_simd} "${OUTPUT_SIMD}" PARENT_SCOPE)
+    set(SUGGEST_SIMD_QUIETLY TRUE CACHE INTERNAL "Be quiet during future construction of SIMD suggestions")
 endfunction()
 
 function(gmx_detect_simd _suggested_simd)
-    if(NOT DEFINED GMX_SIMD)
+    if(GMX_SIMD STREQUAL "AUTO")
         if(GMX_TARGET_BGQ)
+            # BG/Q requires cross-compilation, so needs this
+            # logic. While the qpx feature flag in cpuinfo works, it
+            # can't be returned by cpuinfo running on the build host.
             set(${_suggested_simd} "IBM_QPX")
         elseif(GMX_TARGET_FUJITSU_SPARC64)
             # HPC-ACE is always present. In the future we