aboutsummaryrefslogtreecommitdiff
path: root/libc/cmake/modules/LLVMLibCCheckCpuFeatures.cmake
blob: 86c3822c864c03f9dca0bd3aa23067f9636c3b35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
# ------------------------------------------------------------------------------
# Cpu features definition and flags
# ------------------------------------------------------------------------------

if(${LIBC_TARGET_MACHINE} MATCHES "x86|x86_64")
  set(ALL_CPU_FEATURES SSE SSE2 AVX AVX2 AVX512F)
  list(SORT ALL_CPU_FEATURES)
endif()

# Function to check whether the host supports the provided set of features.
# Usage:
# host_supports(
#   <output variable>
#   <list of cpu features>
# )
function(host_supports output_var features)
  _intersection(a "${HOST_CPU_FEATURES}" "${features}")
  if("${a}" STREQUAL "${features}")
    set(${output_var} TRUE PARENT_SCOPE)
  else()
    unset(${output_var} PARENT_SCOPE)
  endif()
endfunction()

# Function to compute the flags to pass down to the compiler.
# Usage:
# compute_flags(
#   <output variable>
#   MARCH <arch name or "native">
#   REQUIRE <list of mandatory features to enable>
#   REJECT <list of features to disable>
# )
function(compute_flags output_var)
  cmake_parse_arguments(
    "COMPUTE_FLAGS"
    "" # Optional arguments
    "MARCH" # Single value arguments
    "REQUIRE;REJECT" # Multi value arguments
    ${ARGN})
  # Check that features are not required and rejected at the same time.
  if(COMPUTE_FLAGS_REQUIRE AND COMPUTE_FLAGS_REJECT)
    _intersection(var ${COMPUTE_FLAGS_REQUIRE} ${COMPUTE_FLAGS_REJECT})
    if(var)
      message(FATAL_ERROR "Cpu Features REQUIRE and REJECT ${var}")
    endif()
  endif()
  # Generate the compiler flags in `current`.
  if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang|GNU")
    if(COMPUTE_FLAGS_MARCH)
      list(APPEND current "-march=${COMPUTE_FLAGS_MARCH}")
    endif()
    foreach(feature IN LISTS COMPUTE_FLAGS_REQUIRE)
      string(TOLOWER ${feature} lowercase_feature)
      list(APPEND current "-m${lowercase_feature}")
    endforeach()
    foreach(feature IN LISTS COMPUTE_FLAGS_REJECT)
      string(TOLOWER ${feature} lowercase_feature)
      list(APPEND current "-mno-${lowercase_feature}")
    endforeach()
  else()
    # In future, we can extend for other compilers.
    message(FATAL_ERROR "Unkown compiler ${CMAKE_CXX_COMPILER_ID}.")
  endif()
  # Export the list of flags.
  set(${output_var} "${current}" PARENT_SCOPE)
endfunction()

# ------------------------------------------------------------------------------
# Internal helpers and utilities.
# ------------------------------------------------------------------------------

# Computes the intersection between two lists.
function(_intersection output_var list1 list2)
  foreach(element IN LISTS list1)
    if("${list2}" MATCHES "(^|;)${element}(;|$)")
      list(APPEND tmp "${element}")
    endif()
  endforeach()
  set(${output_var} ${tmp} PARENT_SCOPE)
endfunction()

# Generates a cpp file to introspect the compiler defined flags.
function(_generate_check_code)
  foreach(feature IN LISTS ALL_CPU_FEATURES)
    set(DEFINITIONS
        "${DEFINITIONS}
#ifdef __${feature}__
    \"${feature}\",
#endif")
  endforeach()
  configure_file(
    "${LIBC_SOURCE_DIR}/cmake/modules/cpu_features/check_cpu_features.cpp.in"
    "cpu_features/check_cpu_features.cpp" @ONLY)
endfunction()
_generate_check_code()

# Compiles and runs the code generated above with the specified requirements.
# This is helpful to infer which features a particular target supports or if
# a specific features implies other features (e.g. BMI2 implies SSE2 and SSE).
function(_check_defined_cpu_feature output_var)
  cmake_parse_arguments(
    "CHECK_DEFINED"
    "" # Optional arguments
    "MARCH" # Single value arguments
    "REQUIRE;REJECT" # Multi value arguments
    ${ARGN})
  compute_flags(
    flags
    MARCH  ${CHECK_DEFINED_MARCH}
    REQUIRE ${CHECK_DEFINED_REQUIRE}
    REJECT  ${CHECK_DEFINED_REJECT})
  try_run(
    run_result compile_result "${CMAKE_CURRENT_BINARY_DIR}/check_${feature}"
    "${CMAKE_CURRENT_BINARY_DIR}/cpu_features/check_cpu_features.cpp"
    COMPILE_DEFINITIONS ${flags}
    COMPILE_OUTPUT_VARIABLE compile_output
    RUN_OUTPUT_VARIABLE run_output)
  if(${compile_result} AND ("${run_result}" EQUAL 0))
    set(${output_var}
        "${run_output}"
        PARENT_SCOPE)
  else()
    message(FATAL_ERROR "${compile_output}")
  endif()
endfunction()

# Populates the HOST_CPU_FEATURES list.
# Use -march=native only when the compiler supports it.
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
  _check_defined_cpu_feature(HOST_CPU_FEATURES MARCH native)
else()
  _check_defined_cpu_feature(HOST_CPU_FEATURES)
endif()