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