FindCPU_FEATURES.cmake 12.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

##extract a single line that contains the given pattern (starting from that patter)
function(extract_line LINESTART_PATTERN MULTILINE_STRING OUTPUT_VARIABLE)

STRING(FIND ${MULTILINE_STRING} "${PATTERN}" MATCH_OFFSET)
STRING(SUBSTRING ${MATCH_OFFSET} 0 ${MATCH_OFFSET} MULTILINE_NO_START)
STRING(FIND ${MULTILINE_NO_START} "\n" MULTILINE_RETURN_OFFSET)
STRING(SUBSTRING ${MULTILINE_NO_START} 0 ${MULTILINE_RETURN_OFFSET} MATCHING_LINE)
set(OUTPUT_VARIABLE ${MATCHING_LINE} PARENT_SCOPE)

endfunction(extract_line)

##extract system information from wmic.exe on Windows
function(wmic_get ATTRIBUTENAME RESULTVALUE)
EXEC_PROGRAM("wmic.exe" ARGS "cpu get ${ATTRIBUTENAME}" OUTPUT_VARIABLE WMIC_OUTPUT)
string(REGEX REPLACE " [ ]+" ";" WMIC_OUTPUT_CLEANED ${WMIC_OUTPUT})
set(WMIC_OUTPUT_LIST "${WMIC_OUTPUT_CLEANED};")
list(GET WMIC_OUTPUT_LIST 1 VALUEOFINTEREST)

string(STRIP "${VALUEOFINTEREST}" VALUEOFINTEREST)
set(${RESULTVALUE} ${VALUEOFINTEREST} PARENT_SCOPE)
steinbac's avatar
steinbac committed
22
#message(STATUS "[wmic_get] RESULT = ${VALUEOFINTEREST}")
23 24

endfunction(wmic_get)
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
  EXEC_PROGRAM(cat ARGS "/proc/cpuinfo" OUTPUT_VARIABLE CPUINFO)

  ##FIND VENDOR
  STRING(FIND ${CPUINFO} "vendor" VENDOR_TITLE_OFFSET)
  STRING(SUBSTRING ${CPUINFO} ${VENDOR_TITLE_OFFSET} 50 GUESSED_VENDOR_LINE)
  STRING(REGEX REPLACE "^vendor.*: ([a-zA-Z]+)\n.*" "\\1" VENDOR_TITLE ${GUESSED_VENDOR_LINE})
  message(STATUS "vendor found: ${VENDOR_TITLE}")
  set(CPU_VENDOR "${VENDOR_TITLE}" CACHE STRING "cpu vendor")

  ##FIND MODEL NAME
  STRING(FIND ${CPUINFO} "model name" MODEL_NAME_OFFSET)
  STRING(SUBSTRING ${CPUINFO} ${MODEL_NAME_OFFSET} 100 GUESSED_MODEL_NAME_LINE)
  STRING(FIND ${GUESSED_MODEL_NAME_LINE} "\n" MODEL_NAME_RETURN_OFFSET)
  STRING(SUBSTRING ${GUESSED_MODEL_NAME_LINE} 0 ${MODEL_NAME_RETURN_OFFSET} GUESSED_MODEL_NAME_LINE)

  STRING(REGEX REPLACE "^model name.*: ([a-zA-Z]+.*)$" "\\1" MODEL_NAME ${GUESSED_MODEL_NAME_LINE})
  message(STATUS "model name found: ${MODEL_NAME}")
  set(CPU_MODEL_NAME "${MODEL_NAME}" CACHE STRING "cpu model name")

  ##FIND INSTRUCTIONS
  STRING(REGEX REPLACE "^.*(sse) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "sse" "${SSE_THERE}" SSE_TRUE)
  IF (SSE_TRUE)
    set(SSE_FOUND true CACHE BOOL "SSE available on host")
  ELSE (SSE_TRUE)
    set(SSE_FOUND false CACHE BOOL "SSE available on host")
  ENDIF (SSE_TRUE)

  STRING(REGEX REPLACE "^.*(sse2) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "sse2" "${SSE_THERE}" SSE2_TRUE)
  IF (SSE2_TRUE)
    set(SSE2_FOUND true CACHE BOOL "SSE2 available on host")
  ELSE (SSE2_TRUE)
    set(SSE2_FOUND false CACHE BOOL "SSE2 available on host")
  ENDIF (SSE2_TRUE)

  # /proc/cpuinfo apparently omits sse3 :(
  STRING(REGEX REPLACE "^.*[^s](sse3) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "sse3" "${SSE_THERE}" SSE3_TRUE)
  IF (NOT SSE3_TRUE)
    STRING(REGEX REPLACE "^.*(T2300).*$" "\\1" SSE_THERE ${CPUINFO})
    STRING(COMPARE EQUAL "T2300" "${SSE_THERE}" SSE3_TRUE)
  ENDIF (NOT SSE3_TRUE)

  STRING(REGEX REPLACE "^.* (ssse3) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "ssse3" "${SSE_THERE}" SSSE3_TRUE)
  IF (SSE3_TRUE OR SSSE3_TRUE)
    set(SSE3_FOUND true CACHE BOOL "SSE3 available on host")
  ELSE (SSE3_TRUE OR SSSE3_TRUE)
    set(SSE3_FOUND false CACHE BOOL "SSE3 available on host")
  ENDIF (SSE3_TRUE OR SSSE3_TRUE)
  IF (SSSE3_TRUE)
    set(SSSE3_FOUND true CACHE BOOL "SSSE3 available on host")
  ELSE (SSSE3_TRUE)
    set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host")
  ENDIF (SSSE3_TRUE)

  STRING(REGEX REPLACE "^.* (sse4_1) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "sse4_1" "${SSE_THERE}" SSE41_TRUE)
  IF (SSE41_TRUE)
    set(SSE4_1_FOUND true CACHE BOOL "SSE4.1 available on host")
  ELSE (SSE41_TRUE)
    set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
  ENDIF (SSE41_TRUE)

  STRING(REGEX REPLACE "^.* (sse4_2) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "sse4_2" "${SSE_THERE}" SSE42_TRUE)
  IF (SSE42_TRUE)
    set(SSE4_2_FOUND true CACHE BOOL "SSE4.2 available on host")
  ELSE (SSE42_TRUE)
    set(SSE4_2_FOUND false CACHE BOOL "SSE4.2 available on host")
  ENDIF (SSE42_TRUE)

  if("${CPUINFO}" MATCHES ".*avx .*")
    set(AVX_FOUND true CACHE BOOL "AVX available on host")
  else()
    set(AVX_FOUND false CACHE BOOL "AVX available on host")
  endif()

  if("${CPUINFO}" MATCHES ".*avx2 .*")
    set(AVX2_FOUND true CACHE BOOL "AVX2 available on host")
  else()
    set(AVX2_FOUND false CACHE BOOL "AVX2 available on host")
  endif()

#
  if(EXISTS "/sys/devices/system/cpu/cpu0/cache/index2/size")
    EXEC_PROGRAM(cat ARGS "/sys/devices/system/cpu/cpu0/cache/index2/size" OUTPUT_VARIABLE L2_SIZE_KB_STRING)

    if(${L2_SIZE_KB_STRING} MATCHES ".*[K|k]")
      string(REGEX REPLACE "[K|k]" "" L2_SIZE_KB ${L2_SIZE_KB_STRING})
      set(CPU_L2_SIZE_KB "${L2_SIZE_KB}" CACHE STRING "cpu L2 cache size in kB")
    else()
      if(${L2_SIZE_KB_STRING} MATCHES ".*[M|m]")
        string(REGEX REPLACE "[M|m]" "000" L2_SIZE_KB ${L2_SIZE_KB_STRING})
        set(CPU_L2_SIZE_KB "${L2_SIZE_KB}" CACHE STRING "cpu L2 cache size in kB")
      else()
        message(WARNING "unable to find unit prefix (K|M) in /sys/devices/system/cpu/cpu0/cache/index2/size:${L2_SIZE_KB_STRING} (assuming it's contents as expressed in kB, crossing fingers)")
        set(CPU_L2_SIZE_KB "${L2_SIZE_KB_STRING}" CACHE STRING "cpu L2 cache size in kB")
      endif()

    endif()

  else()
    message("unable to find /sys/devices/system/cpu/cpu0/cache/index2/size")
    set(CPU_L2_SIZE_KB "0" CACHE STRING "cpu L2 cache size in kB")
  endif()


ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
  EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE
    CPUINFO)

140 141 142
  EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.leaf7_features" OUTPUT_VARIABLE
    LEAF7_CPUINFO)

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
  EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.vendor" OUTPUT_VARIABLE
    VENDOR_TITLE)

  EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.brand_string" OUTPUT_VARIABLE
    MODEL_NAME)

  EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.cache.size" OUTPUT_VARIABLE
    CPU_L2_SIZE_KB)

  set(CPU_VENDOR "${VENDOR_TITLE}" CACHE STRING "cpu vendor")
  set(CPU_MODEL_NAME "${MODEL_NAME}" CACHE STRING "cpu model name")

  STRING(REGEX REPLACE "^.*[^S](SSE) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSE" "${SSE_THERE}" SSE_TRUE)
  IF (SSE_TRUE)
    set(SSE_FOUND true CACHE BOOL "SSE2 available on host")
  ELSE (SSE_TRUE)
    set(SSE_FOUND false CACHE BOOL "SSE2 available on host")
  ENDIF (SSE_TRUE)

  STRING(REGEX REPLACE "^.*[^S](SSE2) .*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSE2" "${SSE_THERE}" SSE2_TRUE)
  IF (SSE2_TRUE)
    set(SSE2_FOUND true CACHE BOOL "SSE2 available on host")
  ELSE (SSE2_TRUE)
    set(SSE2_FOUND false CACHE BOOL "SSE2 available on host")
  ENDIF (SSE2_TRUE)

  STRING(REGEX REPLACE "^.*[^S](SSE2).*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSE2" "${SSE_THERE}" SSE2_TRUE)
  IF (SSE2_TRUE)
    set(SSE2_FOUND true CACHE BOOL "SSE2 available on host")
  ELSE (SSE2_TRUE)
    set(SSE2_FOUND false CACHE BOOL "SSE2 available on host")
  ENDIF (SSE2_TRUE)

  STRING(REGEX REPLACE "^.*[^S](SSE3).*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSE3" "${SSE_THERE}" SSE3_TRUE)
  IF (SSE3_TRUE)
    set(SSE3_FOUND true CACHE BOOL "SSE3 available on host")
  ELSE (SSE3_TRUE)
    set(SSE3_FOUND false CACHE BOOL "SSE3 available on host")
  ENDIF (SSE3_TRUE)

  STRING(REGEX REPLACE "^.*(SSSE3).*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSSE3" "${SSE_THERE}" SSSE3_TRUE)
  IF (SSSE3_TRUE)
    set(SSSE3_FOUND true CACHE BOOL "SSSE3 available on host")
  ELSE (SSSE3_TRUE)
    set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host")
  ENDIF (SSSE3_TRUE)

  STRING(REGEX REPLACE "^.*(SSE4.1).*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSE4.1" "${SSE_THERE}" SSE41_TRUE)
  IF (SSE41_TRUE)
    set(SSE4_1_FOUND true CACHE BOOL "SSE4.1 available on host")
  ELSE (SSE41_TRUE)
    set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
  ENDIF (SSE41_TRUE)

203 204 205 206 207 208 209 210 211
  STRING(REGEX REPLACE "^.*(SSE4.2).*$" "\\1" SSE_THERE ${CPUINFO})
  STRING(COMPARE EQUAL "SSE4.2" "${SSE_THERE}" SSE42_TRUE)
  IF (SSE42_TRUE)
    set(SSE4_2_FOUND true CACHE BOOL "SSE4.2 available on host")
  ELSE (SSE42_TRUE)
    set(SSE4_2_FOUND false CACHE BOOL "SSE4.2 available on host")
  ENDIF (SSE42_TRUE)

  if("${CPUINFO}" MATCHES ".*AVX.*")
212 213 214 215 216 217 218 219
    set(AVX_FOUND true CACHE BOOL "AVX available on host")
  else()
    set(AVX_FOUND false CACHE BOOL "AVX available on host")
  endif()

  if("${CPUINFO}" MATCHES ".*AVX2 .*")
    set(AVX2_FOUND true CACHE BOOL "AVX2 available on host")
  else()
220 221 222 223 224
    if("${LEAF7_CPUINFO}" MATCHES ".*AVX2 .*")
      set(AVX2_FOUND true CACHE BOOL "AVX2 available on host")
    else()
      set(AVX2_FOUND false CACHE BOOL "AVX2 available on host")
    endif()
225 226 227
  endif()

ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Windows")
228

229 230 231
  #as an alternative to wmic, use 
  #get_filename_component(_vendor_id "[HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0;VendorIdentifier]" NAME CACHE)
  #get_filename_component(_cpu_id "[HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0;Identifier]" NAME CACHE)
232 233 234 235 236 237 238 239 240
  
  wmic_get("Name" MODEL_NAME)
  string(STRIP ${MODEL_NAME} MODEL_NAME)
  set(CPU_MODEL_NAME "${MODEL_NAME}" CACHE STRING "cpu model name")

  wmic_get("Manufacturer" MODEL_VENDOR)
  set(CPU_VENDOR "${MODEL_VENDOR}" CACHE STRING "cpu model vendor")

  wmic_get("L2CacheSize" L2_CACHE_SIZE)
241 242 243 244 245 246 247
  wmic_get("NumberOfCores" CPU_NPHYS_CORES)

  #dirty hack that I need to validate with other machines in Win7
  if(${CPU_NPHYS_CORES} GREATER 1)
  math(EXPR L2_CACHE_SIZE "${L2_CACHE_SIZE}/${CPU_NPHYS_CORES}")
  endif()
  
248
  set(CPU_L2_SIZE_KB "${L2_CACHE_SIZE}" CACHE STRING "cpu L2 cache size in kB")
249 250 251 252 253 254

  #thanks to the wonderful VC project (https://github.com/VcDevel/Vc)
  include (OptimizeForArchitecture)
  
  OFA_AutodetectHostArchitecture()
  OFA_HandleX86Options()
steinbac's avatar
steinbac committed
255
  
256
    
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
  list(FIND _available_vector_units_list "sse" SSE_INDEX)
  list(FIND _available_vector_units_list "sse2" SSE2_INDEX)
  list(FIND _available_vector_units_list "sse3" SSE3_INDEX)
  list(FIND _available_vector_units_list "ssse3" SSSE3_INDEX)
  list(FIND _available_vector_units_list "sse4.1" SSE4_1_INDEX)
  list(FIND _available_vector_units_list "sse4.2" SSE4_2_INDEX)
  list(FIND _available_vector_units_list "avx" AVX_INDEX)
  list(FIND _available_vector_units_list "avx2" AVX2_INDEX)

  if(${SSE_INDEX} GREATER -1)
    set(SSE_FOUND    true  CACHE BOOL "SSE available on host")
  endif()
  if(${SSE2_INDEX} GREATER -1)
    set(SSE2_FOUND    true  CACHE BOOL "SSE2 available on host")
  endif()
  if(${SSE3_INDEX} GREATER -1)
    set(SSE3_FOUND    true  CACHE BOOL "SSE3 available on host")
  endif()
  if(${SSSE3_INDEX} GREATER -1)
    set(SSSE3_FOUND    true  CACHE BOOL "SSSE3 available on host")
  endif()
  if(${SSE4_1_INDEX} GREATER -1)
    set(SSE4_1_FOUND    true  CACHE BOOL "SSE4.1 available on host")
  endif()
  if(${SSE4_2_INDEX} GREATER -1)
    set(SSE4_2_FOUND    true  CACHE BOOL "SSE4.2 available on host")
  endif()
  if(${AVX_INDEX} GREATER -1)
  set(AVX_FOUND    true  CACHE BOOL "AVX available on host")
  endif()
  if(${AVX2_INDEX} GREATER -1)
    set(AVX2_FOUND    true  CACHE BOOL "AVX2 available on host")
  endif()

  set(SSE_FOUND    true  CACHE BOOL "SSE available on host")
292
  set(SSE2_FOUND   false  CACHE BOOL "SSE2 available on host")
293 294 295
  set(SSE3_FOUND   false CACHE BOOL "SSE3 available on host")
  set(SSSE3_FOUND  false CACHE BOOL "SSSE3 available on host")
  set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
296 297 298
  set(SSE4_2_FOUND false CACHE BOOL "SSE4.2 available on host")
  set(AVX_FOUND false CACHE BOOL "AVX available on host")
  set(AVX2_FOUND false CACHE BOOL "AVX2 available on host")
steinbac's avatar
steinbac committed
299

300 301
ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
message(STATUS "found hardware: ${CPU_VENDOR} ${CPU_MODEL_NAME}")
set(FOUND_FEATURES)

if(SSE_FOUND)
  list(APPEND FOUND_FEATURES "sse")
endif(SSE_FOUND)
if(SSE2_FOUND)
  list(APPEND FOUND_FEATURES "sse2")
endif(SSE2_FOUND)
if( SSE3_FOUND)
list(APPEND FOUND_FEATURES "sse3")
endif( SSE3_FOUND)
if( SSSE3_FOUND)
list(APPEND FOUND_FEATURES "ssse3")
endif( SSSE3_FOUND)
if( SSE4_1_FOUND)
list(APPEND FOUND_FEATURES "sse4.1")
endif( SSE4_1_FOUND)
if( SSE4_2_FOUND)
list(APPEND FOUND_FEATURES "sse4.2")
endif( SSE4_2_FOUND)
if( AVX_FOUND)
list(APPEND FOUND_FEATURES "avx")
endif( AVX_FOUND)
if( AVX2_FOUND)
list(APPEND FOUND_FEATURES "avx2")
endif( AVX2_FOUND)

message(STATUS "found hardware features: ${FOUND_FEATURES}")
331 332

mark_as_advanced(SSE_FOUND SSE2_FOUND SSE3_FOUND SSSE3_FOUND SSE4_1_FOUND SSE4_2_FOUND AVX_FOUND AVX2_FOUND CPU_VENDOR CPU_MODEL_NAME CPU_L2_SIZE_KB)