xref: /llvm-project/libc/cmake/modules/LLVMLibCFlagRules.cmake (revision e083a33478b2d570c89d7a9d9f05328b9232c072)
1# In general, a flag is a string provided for supported functions under the
2# multi-valued option `FLAGS`.  It should be one of the following forms:
3#   FLAG_NAME
4#   FLAG_NAME__NO
5#   FLAG_NAME__ONLY
6# A target will inherit all the flags of its upstream dependency.
7#
8# When we create a target `TARGET_NAME` with a flag using (add_header_library,
9# add_object_library, ...), its behavior will depend on the flag form as follow:
10# - FLAG_NAME: The following 2 targets will be generated:
11#     `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property.
12#     `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if
13#        `TARGET_NAME` depends on `DEP` and `DEP` has `FLAG_NAME` in its `FLAGS`
14#        property.
15# - FLAG_NAME__ONLY: Only generate 1 target `TARGET_NAME` that has `FLAG_NAME`
16#     in its `FLAGS` property.
17# - FLAG_NAME__NO: Only generate 1 target `TARGET_NAME` that depends on
18# `DEP.__NO_FLAG_NAME` if `DEP` is in its DEPENDS list and `DEP` has `FLAG_NAME`
19# in its `FLAGS` property.
20#
21# To show all the targets generated, pass SHOW_INTERMEDIATE_OBJECTS=ON to cmake.
22# To show all the targets' dependency and flags, pass
23#   SHOW_INTERMEDIATE_OBJECTS=DEPS to cmake.
24#
25# To completely disable a flag FLAG_NAME expansion, set the variable
26#   SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE in this file.
27
28
29function(extract_flag_modifier input_flag output_flag modifier)
30  if(${input_flag} MATCHES "__NO$")
31    string(REGEX REPLACE "__NO$" "" flag "${input_flag}")
32    set(${output_flag} ${flag} PARENT_SCOPE)
33    set(${modifier} "NO" PARENT_SCOPE)
34  elseif(${input_flag} MATCHES "__ONLY$")
35    string(REGEX REPLACE "__ONLY$" "" flag "${input_flag}")
36    set(${output_flag} ${flag} PARENT_SCOPE)
37    set(${modifier} "ONLY" PARENT_SCOPE)
38  else()
39    set(${output_flag} ${input_flag} PARENT_SCOPE)
40    set(${modifier} "" PARENT_SCOPE)
41  endif()
42endfunction(extract_flag_modifier)
43
44function(remove_duplicated_flags input_flags output_flags)
45  set(out_flags "")
46  foreach(input_flag IN LISTS input_flags)
47    if(NOT input_flag)
48      continue()
49    endif()
50
51    extract_flag_modifier(${input_flag} flag modifier)
52
53    # Check if the flag is skipped.
54    if(${SKIP_FLAG_EXPANSION_${flag}})
55      if("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
56        message(STATUS "  Flag ${flag} is ignored.")
57      endif()
58      continue()
59    endif()
60
61    set(found FALSE)
62    foreach(out_flag IN LISTS out_flags)
63      extract_flag_modifier(${out_flag} o_flag o_modifier)
64      if("${flag}" STREQUAL "${o_flag}")
65        set(found TRUE)
66        break()
67      endif()
68    endforeach()
69    if(NOT found)
70      list(APPEND out_flags ${input_flag})
71    endif()
72  endforeach()
73
74  set(${output_flags} "${out_flags}" PARENT_SCOPE)
75endfunction(remove_duplicated_flags)
76
77# Collect flags from dependency list.  To see which flags come with each
78# dependence, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake.
79function(get_flags_from_dep_list output_list)
80  set(flag_list "")
81  foreach(dep IN LISTS ARGN)
82    if(NOT dep)
83      continue()
84    endif()
85
86    get_fq_dep_name(fq_dep_name ${dep})
87
88    if(NOT TARGET ${fq_dep_name})
89      continue()
90    endif()
91
92    get_target_property(flags ${fq_dep_name} "FLAGS")
93
94    if(flags AND "${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
95      message(STATUS "  FLAGS from dependency ${fq_dep_name} are ${flags}")
96    endif()
97
98    foreach(flag IN LISTS flags)
99      if(flag)
100        list(APPEND flag_list ${flag})
101      endif()
102    endforeach()
103  endforeach(dep)
104
105  list(REMOVE_DUPLICATES flag_list)
106
107  set(${output_list} ${flag_list} PARENT_SCOPE)
108endfunction(get_flags_from_dep_list)
109
110# Given a `flag` without modifier, scan through the list of dependency, append
111# `.__NO_flag` to any target that has `flag` in its FLAGS property.
112function(get_fq_dep_list_without_flag output_list flag)
113  set(fq_dep_no_flag_list "")
114  foreach(dep IN LISTS ARGN)
115    get_fq_dep_name(fq_dep_name ${dep})
116    if(TARGET ${fq_dep_name})
117      get_target_property(dep_flags ${fq_dep_name} "FLAGS")
118      # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
119      # `flag__ONLY` do not.
120      if(${flag} IN_LIST dep_flags)
121        list(APPEND fq_dep_no_flag_list "${fq_dep_name}.__NO_${flag}")
122      else()
123        list(APPEND fq_dep_no_flag_list ${fq_dep_name})
124      endif()
125    else()
126      list(APPEND fq_dep_no_flag_list ${fq_dep_name})
127    endif()
128  endforeach(dep)
129  set(${output_list} ${fq_dep_no_flag_list} PARENT_SCOPE)
130endfunction(get_fq_dep_list_without_flag)
131
132# Check if a `flag` is set
133function(check_flag result flag_name)
134  list(FIND ARGN ${flag_name} has_flag)
135  if(${has_flag} LESS 0)
136    list(FIND ARGN "${flag_name}__ONLY" has_flag)
137  endif()
138  if(${has_flag} GREATER -1)
139    set(${result} TRUE PARENT_SCOPE)
140  else()
141    set(${result} FALSE PARENT_SCOPE)
142  endif()
143endfunction(check_flag)
144
145# Generate all flags' combinations and call the corresponding function provided
146# by `CREATE_TARGET` to create a target for each combination.
147function(expand_flags_for_target target_name flags)
148  cmake_parse_arguments(
149    "EXPAND_FLAGS"
150    "" # Optional arguments
151    "CREATE_TARGET" # Single-value arguments
152    "DEPENDS;FLAGS" # Multi-value arguments
153    ${ARGN}
154  )
155
156  list(LENGTH flags nflags)
157  if(NOT ${nflags})
158    cmake_language(CALL ${EXPAND_FLAGS_CREATE_TARGET}
159      ${target_name}
160      ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
161      DEPENDS ${EXPAND_FLAGS_DEPENDS}
162      FLAGS ${EXPAND_FLAGS_FLAGS}
163    )
164    return()
165  endif()
166
167  list(GET flags 0 flag)
168  list(REMOVE_AT flags 0)
169  extract_flag_modifier(${flag} real_flag modifier)
170
171  if(NOT "${modifier}" STREQUAL "NO")
172    expand_flags_for_target(
173      ${target_name}
174      "${flags}"
175      DEPENDS ${EXPAND_FLAGS_DEPENDS}
176      FLAGS ${EXPAND_FLAGS_FLAGS}
177      CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}
178      ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
179    )
180  endif()
181
182  if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
183    return()
184  endif()
185
186  set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
187  list(REMOVE_ITEM NEW_FLAGS ${flag})
188  get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})
189
190  # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
191  # `flag__ONLY` do not.
192  if("${modifier}" STREQUAL "")
193    set(TARGET_NAME "${target_name}.__NO_${flag}")
194  else()
195    set(TARGET_NAME "${target_name}")
196  endif()
197
198  expand_flags_for_target(
199    ${TARGET_NAME}
200    "${flags}"
201    DEPENDS ${NEW_DEPS}
202    FLAGS ${NEW_FLAGS}
203    CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}
204    ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
205  )
206endfunction(expand_flags_for_target)
207
208# Collect all flags from a target's dependency, and then forward to
209# `expand_flags_for_target to generate all flags' combinations and call
210# the corresponding function provided by `CREATE_TARGET` to create a target for
211# each combination.
212function(add_target_with_flags target_name)
213  cmake_parse_arguments(
214    "ADD_TO_EXPAND"
215    "" # Optional arguments
216    "CREATE_TARGET;" # Single value arguments
217    "DEPENDS;FLAGS;ADD_FLAGS" # Multi-value arguments
218    ${ARGN}
219  )
220
221  if(NOT target_name)
222    message(FATAL_ERROR "Bad target name")
223  endif()
224
225  if(NOT ADD_TO_EXPAND_CREATE_TARGET)
226    message(FATAL_ERROR "Missing function to create targets.  Please specify "
227                        "`CREATE_TARGET <function>`")
228  endif()
229
230  get_fq_target_name(${target_name} fq_target_name)
231
232  if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
233    message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
234  endif()
235
236  get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
237  get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
238
239  # Appending ADD_FLAGS before flags from dependency.
240  if(ADD_TO_EXPAND_ADD_FLAGS)
241    list(APPEND ADD_TO_EXPAND_FLAGS ${ADD_TO_EXPAND_ADD_FLAGS})
242  endif()
243  list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
244  remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
245  list(SORT flags)
246
247  if(SHOW_INTERMEDIATE_OBJECTS AND flags)
248    message(STATUS "Target ${fq_target_name} has FLAGS: ${flags}")
249  endif()
250
251  expand_flags_for_target(
252    ${fq_target_name}
253    "${flags}"
254    DEPENDS "${fq_deps_list}"
255    FLAGS "${flags}"
256    CREATE_TARGET ${ADD_TO_EXPAND_CREATE_TARGET}
257    ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
258  )
259endfunction(add_target_with_flags)
260
261# Special flags
262set(FMA_OPT_FLAG "FMA_OPT")
263set(ROUND_OPT_FLAG "ROUND_OPT")
264# This flag controls whether we use explicit SIMD instructions or not.
265set(EXPLICIT_SIMD_OPT_FLAG "EXPLICIT_SIMD_OPT")
266# This flag controls whether we use compiler builtin functions to implement
267# various basic math operations or not.
268set(MISC_MATH_BASIC_OPS_OPT_FLAG "MISC_MATH_BASIC_OPS_OPT")
269
270# Skip FMA_OPT flag for targets that don't support fma.
271if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "FMA")) OR
272       LIBC_TARGET_ARCHITECTURE_IS_RISCV64))
273  set(SKIP_FLAG_EXPANSION_FMA_OPT TRUE)
274endif()
275
276# Skip EXPLICIT_SIMD_OPT flag for targets that don't support SSE2.
277# Note: one may want to revisit it if they want to control other explicit SIMD
278if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE2")))
279  set(SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT TRUE)
280endif()
281
282# Skip ROUND_OPT flag for targets that don't support rounding instructions. On
283# x86, these are SSE4.1 instructions, but we already had code to check for
284# SSE4.2 support.
285if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE4_2")) OR
286       LIBC_TARGET_ARCHITECTURE_IS_AARCH64 OR LIBC_TARGET_OS_IS_GPU))
287  set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE)
288endif()
289
290# Choose whether time_t is 32- or 64-bit, based on target architecture
291# and config options. This will be used to set a #define during the
292# library build, and also to select the right version of time_t.h for
293# the output headers.
294if(LIBC_TARGET_ARCHITECTURE_IS_ARM AND NOT (LIBC_CONF_TIME_64BIT))
295  # Set time_t to 32 bit for compatibility with glibc, unless
296  # configuration says otherwise
297  set(LIBC_TYPES_TIME_T_IS_32_BIT TRUE)
298else()
299  # Other platforms default to 64-bit time_t
300  set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE)
301endif()
302