[Reland][Runtimes] Merge 'compile_commands.json' files from runtimes build (#116303)
[llvm-project.git] / libc / cmake / modules / LLVMLibCFlagRules.cmake
blobe5b0e249c90676456b08455bc50ca2ce6a155e5f
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.
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.
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.
25 # To completely disable a flag FLAG_NAME expansion, set the variable
26 #   SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE in this file.
29 function(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()
42 endfunction(extract_flag_modifier)
44 function(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()
51     extract_flag_modifier(${input_flag} flag modifier)
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()
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()
74   set(${output_flags} "${out_flags}" PARENT_SCOPE)
75 endfunction(remove_duplicated_flags)
77 # Collect flags from dependency list.  To see which flags come with each
78 # dependence, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake.
79 function(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()
86     get_fq_dep_name(fq_dep_name ${dep})
88     if(NOT TARGET ${fq_dep_name})
89       continue()
90     endif()
92     get_target_property(flags ${fq_dep_name} "FLAGS")
94     if(flags AND "${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
95       message(STATUS "  FLAGS from dependency ${fq_dep_name} are ${flags}")
96     endif()
98     foreach(flag IN LISTS flags)
99       if(flag)
100         list(APPEND flag_list ${flag})
101       endif()
102     endforeach()
103   endforeach(dep)
105   list(REMOVE_DUPLICATES flag_list)
107   set(${output_list} ${flag_list} PARENT_SCOPE)
108 endfunction(get_flags_from_dep_list)
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.
112 function(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)
130 endfunction(get_fq_dep_list_without_flag)
132 # Check if a `flag` is set
133 function(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()
143 endfunction(check_flag)
145 # Generate all flags' combinations and call the corresponding function provided
146 # by `CREATE_TARGET` to create a target for each combination.
147 function(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   )
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()
167   list(GET flags 0 flag)
168   list(REMOVE_AT flags 0)
169   extract_flag_modifier(${flag} real_flag modifier)
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()
182   if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
183     return()
184   endif()
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})
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()
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   )
206 endfunction(expand_flags_for_target)
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.
212 function(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   )
221   if(NOT target_name)
222     message(FATAL_ERROR "Bad target name")
223   endif()
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()
230   get_fq_target_name(${target_name} fq_target_name)
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()
236   get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
237   get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
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)
247   if(SHOW_INTERMEDIATE_OBJECTS AND flags)
248     message(STATUS "Target ${fq_target_name} has FLAGS: ${flags}")
249   endif()
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   )
259 endfunction(add_target_with_flags)
261 # Special flags
262 set(FMA_OPT_FLAG "FMA_OPT")
263 set(ROUND_OPT_FLAG "ROUND_OPT")
264 # This flag controls whether we use explicit SIMD instructions or not.
265 set(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.
268 set(MISC_MATH_BASIC_OPS_OPT_FLAG "MISC_MATH_BASIC_OPS_OPT")
270 # Skip FMA_OPT flag for targets that don't support fma.
271 if(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)
274 endif()
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
278 if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE2")))
279   set(SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT TRUE)
280 endif()
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.
285 if(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)
288 endif()
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.
294 if(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)
298 else()
299   # Other platforms default to 64-bit time_t
300   set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE)
301 endif()