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