1# ------------------------------------------------------------------------------ 2# Cpu features definition and flags 3# ------------------------------------------------------------------------------ 4 5if(${LIBC_TARGET_MACHINE} MATCHES "x86|x86_64") 6 set(ALL_CPU_FEATURES SSE SSE2 AVX AVX2 AVX512F) 7 list(SORT ALL_CPU_FEATURES) 8endif() 9 10# Function to check whether the target CPU supports the provided set of features. 11# Usage: 12# cpu_supports( 13# <output variable> 14# <list of cpu features> 15# ) 16function(cpu_supports output_var features) 17 _intersection(var "${LIBC_CPU_FEATURES}" "${features}") 18 if("${var}" STREQUAL "${features}") 19 set(${output_var} TRUE PARENT_SCOPE) 20 else() 21 unset(${output_var} PARENT_SCOPE) 22 endif() 23endfunction() 24 25# Function to compute the flags to pass down to the compiler. 26# Usage: 27# compute_flags( 28# <output variable> 29# MARCH <arch name or "native"> 30# REQUIRE <list of mandatory features to enable> 31# REJECT <list of features to disable> 32# ) 33function(compute_flags output_var) 34 cmake_parse_arguments( 35 "COMPUTE_FLAGS" 36 "" # Optional arguments 37 "MARCH" # Single value arguments 38 "REQUIRE;REJECT" # Multi value arguments 39 ${ARGN}) 40 # Check that features are not required and rejected at the same time. 41 if(COMPUTE_FLAGS_REQUIRE AND COMPUTE_FLAGS_REJECT) 42 _intersection(var ${COMPUTE_FLAGS_REQUIRE} ${COMPUTE_FLAGS_REJECT}) 43 if(var) 44 message(FATAL_ERROR "Cpu Features REQUIRE and REJECT ${var}") 45 endif() 46 endif() 47 # Generate the compiler flags in `current`. 48 if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang|GNU") 49 if(COMPUTE_FLAGS_MARCH) 50 list(APPEND current "-march=${COMPUTE_FLAGS_MARCH}") 51 endif() 52 foreach(feature IN LISTS COMPUTE_FLAGS_REQUIRE) 53 string(TOLOWER ${feature} lowercase_feature) 54 list(APPEND current "-m${lowercase_feature}") 55 endforeach() 56 foreach(feature IN LISTS COMPUTE_FLAGS_REJECT) 57 string(TOLOWER ${feature} lowercase_feature) 58 list(APPEND current "-mno-${lowercase_feature}") 59 endforeach() 60 else() 61 # In future, we can extend for other compilers. 62 message(FATAL_ERROR "Unkown compiler ${CMAKE_CXX_COMPILER_ID}.") 63 endif() 64 # Export the list of flags. 65 set(${output_var} "${current}" PARENT_SCOPE) 66endfunction() 67 68# ------------------------------------------------------------------------------ 69# Internal helpers and utilities. 70# ------------------------------------------------------------------------------ 71 72# Computes the intersection between two lists. 73function(_intersection output_var list1 list2) 74 foreach(element IN LISTS list1) 75 if("${list2}" MATCHES "(^|;)${element}(;|$)") 76 list(APPEND tmp "${element}") 77 endif() 78 endforeach() 79 set(${output_var} ${tmp} PARENT_SCOPE) 80endfunction() 81 82# Generates a cpp file to introspect the compiler defined flags. 83function(_generate_check_code) 84 foreach(feature IN LISTS ALL_CPU_FEATURES) 85 set(DEFINITIONS 86 "${DEFINITIONS} 87#ifdef __${feature}__ 88 \"${feature}\", 89#endif") 90 endforeach() 91 configure_file( 92 "${LIBC_SOURCE_DIR}/cmake/modules/cpu_features/check_cpu_features.cpp.in" 93 "cpu_features/check_cpu_features.cpp" @ONLY) 94endfunction() 95_generate_check_code() 96 97# Compiles and runs the code generated above with the specified requirements. 98# This is helpful to infer which features a particular target supports or if 99# a specific features implies other features (e.g. BMI2 implies SSE2 and SSE). 100function(_check_defined_cpu_feature output_var) 101 cmake_parse_arguments( 102 "CHECK_DEFINED" 103 "" # Optional arguments 104 "MARCH" # Single value arguments 105 "REQUIRE;REJECT" # Multi value arguments 106 ${ARGN}) 107 compute_flags( 108 flags 109 MARCH ${CHECK_DEFINED_MARCH} 110 REQUIRE ${CHECK_DEFINED_REQUIRE} 111 REJECT ${CHECK_DEFINED_REJECT}) 112 try_run( 113 run_result compile_result "${CMAKE_CURRENT_BINARY_DIR}/check_${feature}" 114 "${CMAKE_CURRENT_BINARY_DIR}/cpu_features/check_cpu_features.cpp" 115 COMPILE_DEFINITIONS ${flags} 116 COMPILE_OUTPUT_VARIABLE compile_output 117 RUN_OUTPUT_VARIABLE run_output) 118 if("${run_result}" EQUAL 0) 119 set(${output_var} 120 "${run_output}" 121 PARENT_SCOPE) 122 elseif(NOT ${compile_result}) 123 message(FATAL_ERROR "Failed to compile: ${compile_output}") 124 else() 125 message(FATAL_ERROR "Failed to run: ${run_output}") 126 endif() 127endfunction() 128 129set(LIBC_CPU_FEATURES "" CACHE PATH "supported CPU features") 130 131if(CMAKE_CROSSCOMPILING) 132 _intersection(cpu_features "${ALL_CPU_FEATURES}" "${LIBC_CPU_FEATURES}") 133 if(NOT "${cpu_features}" STREQUAL "${LIBC_CPU_FEATURES}") 134 message(FATAL_ERROR "Unsupported CPU features: ${cpu_features}") 135 endif() 136 set(LIBC_CPU_FEATURES "${cpu_features}") 137else() 138 # Populates the LIBC_CPU_FEATURES list. 139 # Use -march=native only when the compiler supports it. 140 include(CheckCXXCompilerFlag) 141 CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE) 142 if(COMPILER_SUPPORTS_MARCH_NATIVE) 143 _check_defined_cpu_feature(LIBC_CPU_FEATURES MARCH native) 144 else() 145 _check_defined_cpu_feature(LIBC_CPU_FEATURES) 146 endif() 147endif() 148