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