aboutsummaryrefslogtreecommitdiff
path: root/libc/cmake/modules/LLVMLibCFlagRules.cmake
blob: 4bbd21ab569dc758bb153938c30cf9de54063ece (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# In general, a flag is a string provided for supported functions under the
# multi-valued option `FLAGS`.  It should be one of the following forms:
#   FLAG_NAME
#   FLAG_NAME__NO
#   FLAG_NAME__ONLY
# A target will inherit all the flags of its upstream dependency.
#
# When we create a target `TARGET_NAME` with a flag using (add_header_library,
# add_object_library, ...), its behavior will depend on the flag form as follow:
# - FLAG_NAME: The following 2 targets will be generated:
#     `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property.
#     `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if
#        `TARGET_NAME` depends on `DEP` and `DEP` has `FLAG_NAME` in its `FLAGS`
#        property.
# - FLAG_NAME__ONLY: Only generate 1 target `TARGET_NAME` that has `FLAG_NAME`
#     in its `FLAGS` property.
# - FLAG_NAME__NO: Only generate 1 target `TARGET_NAME` that depends on
# `DEP.__NO_FLAG_NAME` if `DEP` is in its DEPENDS list and `DEP` has `FLAG_NAME`
# in its `FLAGS` property.
#
# To show all the targets generated, pass SHOW_INTERMEDIATE_OBJECTS=ON to cmake.
# To show all the targets' dependency and flags, pass
#   SHOW_INTERMEDIATE_OBJECTS=DEPS to cmake.
#
# To completely disable a flag FLAG_NAME expansion, set the variable
#   SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE in this file.


function(extract_flag_modifier input_flag output_flag modifier)
  if(${input_flag} MATCHES "__NO$")
    string(REGEX REPLACE "__NO$" "" flag "${input_flag}")
    set(${output_flag} ${flag} PARENT_SCOPE)
    set(${modifier} "NO" PARENT_SCOPE)
  elseif(${input_flag} MATCHES "__ONLY$")
    string(REGEX REPLACE "__ONLY$" "" flag "${input_flag}")
    set(${output_flag} ${flag} PARENT_SCOPE)
    set(${modifier} "ONLY" PARENT_SCOPE)
  else()
    set(${output_flag} ${input_flag} PARENT_SCOPE)
    set(${modifier} "" PARENT_SCOPE)
  endif()
endfunction(extract_flag_modifier)

function(remove_duplicated_flags input_flags output_flags)
  set(out_flags "")
  foreach(input_flag IN LISTS input_flags)
    if(NOT input_flag)
      continue()
    endif()

    extract_flag_modifier(${input_flag} flag modifier)

    # Check if the flag is skipped.
    if(${SKIP_FLAG_EXPANSION_${flag}})
      if("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
        message(STATUS "  Flag ${flag} is ignored.")
      endif()
      continue()
    endif()

    set(found FALSE)
    foreach(out_flag IN LISTS out_flags)
      extract_flag_modifier(${out_flag} o_flag o_modifier)
      if("${flag}" STREQUAL "${o_flag}")
        set(found TRUE)
        break()
      endif()
    endforeach()
    if(NOT found)
      list(APPEND out_flags ${input_flag})
    endif()
  endforeach()

  set(${output_flags} "${out_flags}" PARENT_SCOPE)
endfunction(remove_duplicated_flags)

# Collect flags from dependency list.  To see which flags come with each
# dependence, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake.
function(get_flags_from_dep_list output_list)
  set(flag_list "")
  foreach(dep IN LISTS ARGN)
    if(NOT dep)
      continue()
    endif()

    get_fq_dep_name(fq_dep_name ${dep})

    if(NOT TARGET ${fq_dep_name})
      continue()
    endif()

    get_target_property(flags ${fq_dep_name} "FLAGS")

    if(flags AND "${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
      message(STATUS "  FLAGS from dependency ${fq_dep_name} are ${flags}")
    endif()

    foreach(flag IN LISTS flags)
      if(flag)
        list(APPEND flag_list ${flag})
      endif()
    endforeach()
  endforeach(dep)

  list(REMOVE_DUPLICATES flag_list)

  set(${output_list} ${flag_list} PARENT_SCOPE)
endfunction(get_flags_from_dep_list)

# Given a `flag` without modifier, scan through the list of dependency, append
# `.__NO_flag` to any target that has `flag` in its FLAGS property.
function(get_fq_dep_list_without_flag output_list flag)
  set(fq_dep_no_flag_list "")
  foreach(dep IN LISTS ARGN)
    get_fq_dep_name(fq_dep_name ${dep})
    if(TARGET ${fq_dep_name})
      get_target_property(dep_flags ${fq_dep_name} "FLAGS")
      # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
      # `flag__ONLY` do not.
      if(${flag} IN_LIST dep_flags)
        list(APPEND fq_dep_no_flag_list "${fq_dep_name}.__NO_${flag}")
      else()
        list(APPEND fq_dep_no_flag_list ${fq_dep_name})
      endif()
    else()
      list(APPEND fq_dep_no_flag_list ${fq_dep_name})
    endif()
  endforeach(dep)
  set(${output_list} ${fq_dep_no_flag_list} PARENT_SCOPE)
endfunction(get_fq_dep_list_without_flag)

# Check if a `flag` is set
function(check_flag result flag_name)
  list(FIND ARGN ${flag_name} has_flag)
  if(${has_flag} LESS 0)
    list(FIND ARGN "${flag_name}__ONLY" has_flag)
  endif()
  if(${has_flag} GREATER -1)
    set(${result} TRUE PARENT_SCOPE)
  else()
    set(${result} FALSE PARENT_SCOPE)
  endif()
endfunction(check_flag)

# Generate all flags' combinations and call the corresponding function provided
# by `CREATE_TARGET` to create a target for each combination.
function(expand_flags_for_target target_name flags)
  cmake_parse_arguments(
    "EXPAND_FLAGS"
    "" # Optional arguments
    "CREATE_TARGET" # Single-value arguments
    "DEPENDS;FLAGS" # Multi-value arguments
    ${ARGN}
  )

  list(LENGTH flags nflags)
  if(NOT ${nflags})
    cmake_language(CALL ${EXPAND_FLAGS_CREATE_TARGET}
      ${target_name}
      ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
      DEPENDS ${EXPAND_FLAGS_DEPENDS}
      FLAGS ${EXPAND_FLAGS_FLAGS}
    )
    return()
  endif()

  list(GET flags 0 flag)
  list(REMOVE_AT flags 0)
  extract_flag_modifier(${flag} real_flag modifier)

  if(NOT "${modifier}" STREQUAL "NO")
    expand_flags_for_target(
      ${target_name}
      "${flags}"
      DEPENDS ${EXPAND_FLAGS_DEPENDS}
      FLAGS ${EXPAND_FLAGS_FLAGS}
      CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}
      ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
    )
  endif()

  if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
    return()
  endif()

  set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
  list(REMOVE_ITEM NEW_FLAGS ${flag})
  get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})

  # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
  # `flag__ONLY` do not.
  if("${modifier}" STREQUAL "")
    set(TARGET_NAME "${target_name}.__NO_${flag}")
  else()
    set(TARGET_NAME "${target_name}")
  endif()

  expand_flags_for_target(
    ${TARGET_NAME}
    "${flags}"
    DEPENDS ${NEW_DEPS}
    FLAGS ${NEW_FLAGS}
    CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}
    ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
  )
endfunction(expand_flags_for_target)

# Collect all flags from a target's dependency, and then forward to
# `expand_flags_for_target to generate all flags' combinations and call
# the corresponding function provided by `CREATE_TARGET` to create a target for
# each combination.
function(add_target_with_flags target_name)
  cmake_parse_arguments(
    "ADD_TO_EXPAND"
    "" # Optional arguments
    "CREATE_TARGET;" # Single value arguments
    "DEPENDS;FLAGS;ADD_FLAGS" # Multi-value arguments
    ${ARGN}
  )

  if(NOT target_name)
    message(FATAL_ERROR "Bad target name")
  endif()

  if(NOT ADD_TO_EXPAND_CREATE_TARGET)
    message(FATAL_ERROR "Missing function to create targets.  Please specify "
                        "`CREATE_TARGET <function>`")
  endif()

  get_fq_target_name(${target_name} fq_target_name)

  if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
    message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
  endif()

  get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
  get_flags_from_dep_list(deps_flag_list ${fq_deps_list})

  # Appending ADD_FLAGS before flags from dependency.
  if(ADD_TO_EXPAND_ADD_FLAGS)
    list(APPEND ADD_TO_EXPAND_FLAGS ${ADD_TO_EXPAND_ADD_FLAGS})
  endif()
  list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
  remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
  list(SORT flags)

  if(SHOW_INTERMEDIATE_OBJECTS AND flags)
    message(STATUS "Target ${fq_target_name} has FLAGS: ${flags}")
  endif()

  expand_flags_for_target(
    ${fq_target_name}
    "${flags}"
    DEPENDS "${fq_deps_list}"
    FLAGS "${flags}"
    CREATE_TARGET ${ADD_TO_EXPAND_CREATE_TARGET}
    ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
  )
endfunction(add_target_with_flags)

# Special flags
set(FMA_OPT_FLAG "FMA_OPT")
set(ROUND_OPT_FLAG "ROUND_OPT")
# This flag controls whether we use explicit SIMD instructions or not.
set(EXPLICIT_SIMD_OPT_FLAG "EXPLICIT_SIMD_OPT")
# This flag controls whether we use compiler builtin functions to implement
# various basic math operations or not.
set(MISC_MATH_BASIC_OPS_OPT_FLAG "MISC_MATH_BASIC_OPS_OPT")

# Skip FMA_OPT flag for targets that don't support fma.
if(NOT DEFINED SKIP_FLAG_EXPANSION_FMA_OPT)
  if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "FMA")) OR
        LIBC_TARGET_ARCHITECTURE_IS_ANY_RISCV))
    set(SKIP_FLAG_EXPANSION_FMA_OPT TRUE)
  endif()
endif()

# Skip EXPLICIT_SIMD_OPT flag for targets that don't support SSE2.
# Note: one may want to revisit it if they want to control other explicit SIMD
if(NOT DEFINED SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT)
  if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE2")))
    set(SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT TRUE)
  endif()
endif()

# Skip ROUND_OPT flag for targets that don't support rounding instructions. On
# x86, these are SSE4.1 instructions, but we already had code to check for
# SSE4.2 support.
if(NOT DEFINED SKIP_FLAG_EXPANSION_ROUND_OPT)
  if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE4_2")) OR
        LIBC_TARGET_ARCHITECTURE_IS_AARCH64 OR LIBC_TARGET_OS_IS_GPU))
    set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE)
  endif()
endif()

# Choose whether time_t is 32- or 64-bit, based on target architecture
# and config options. This will be used to set a #define during the
# library build, and also to select the right version of time_t.h for
# the output headers.
if(LIBC_TARGET_ARCHITECTURE_IS_ARM AND NOT (LIBC_CONF_TIME_64BIT))
  # Set time_t to 32 bit for compatibility with glibc, unless
  # configuration says otherwise
  set(LIBC_TYPES_TIME_T_IS_32_BIT TRUE)
else()
  # Other platforms default to 64-bit time_t
  set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE)
endif()