[libc][NFC] Move aligned access implementations to separate header
[llvm-project.git] / libc / cmake / modules / LLVMLibCObjectRules.cmake
blob27c73afcc8c60b874eadddbf68acfffbf482a46c
1 set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY")
3 function(_get_common_compile_options output_var flags)
4   list(FIND flags ${FMA_OPT_FLAG} fma)
5   if(${fma} LESS 0)
6     list(FIND flags "${FMA_OPT_FLAG}__ONLY" fma)
7   endif()
8   if((${fma} GREATER -1) AND (LIBC_TARGET_ARCHITECTURE_IS_RISCV64 OR
9                               (LIBC_CPU_FEATURES MATCHES "FMA")))
10     set(ADD_FMA_FLAG TRUE)
11   endif()
13   list(FIND flags ${ROUND_OPT_FLAG} round)
14   if(${round} LESS 0)
15     list(FIND flags "${ROUND_OPT_FLAG}__ONLY" round)
16   endif()
17   if((${round} GREATER -1) AND (LIBC_CPU_FEATURES MATCHES "SSE4_2"))
18     set(ADD_SSE4_2_FLAG TRUE)
19   endif()
21   set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${ARGN})
22   if(LLVM_COMPILER_IS_GCC_COMPATIBLE)
23     list(APPEND compile_options "-fpie")
24     list(APPEND compile_options "-ffreestanding")
25     list(APPEND compile_options "-fno-builtin")
26     list(APPEND compile_options "-fno-exceptions")
27     list(APPEND compile_options "-fno-lax-vector-conversions")
28     list(APPEND compile_options "-fno-unwind-tables")
29     list(APPEND compile_options "-fno-asynchronous-unwind-tables")
30     list(APPEND compile_options "-fno-rtti")
31     list(APPEND compile_options "-Wall")
32     list(APPEND compile_options "-Wextra")
33     list(APPEND compile_options "-Wimplicit-fallthrough")
34     list(APPEND compile_options "-Wwrite-strings")
35     list(APPEND compile_options "-Wextra-semi")
36     if(NOT CMAKE_COMPILER_IS_GNUCXX)
37       list(APPEND compile_options "-Wnewline-eof")
38       list(APPEND compile_options "-Wnonportable-system-include-path")
39       list(APPEND compile_options "-Wstrict-prototypes")
40       list(APPEND compile_options "-Wthread-safety")
41     endif()
42     if(ADD_FMA_FLAG)
43       if(LIBC_TARGET_ARCHITECTURE_IS_X86)
44         list(APPEND compile_options "-mavx2")
45         list(APPEND compile_options "-mfma")
46       elseif(LIBC_TARGET_ARCHITECTURE_IS_RISCV64)
47         list(APPEND compile_options "-D__LIBC_RISCV_USE_FMA")
48       endif()
49     endif()
50     if(ADD_SSE4_2_FLAG)
51       list(APPEND compile_options "-msse4.2")
52     endif()
53   elseif(MSVC)
54     list(APPEND compile_options "/EHs-c-")
55     list(APPEND compile_options "/GR-")
56     if(ADD_FMA_FLAG)
57       list(APPEND compile_options "/arch:AVX2")
58     endif()
59   endif()
60   if (LIBC_TARGET_ARCHITECTURE_IS_GPU)
61     list(APPEND compile_options "-nogpulib")
62     list(APPEND compile_options "-fvisibility=hidden")
63   endif()
64   set(${output_var} ${compile_options} PARENT_SCOPE)
65 endfunction()
67 # Obtains NVPTX specific arguments for compilation.
68 # The PTX feature is primarily based on the CUDA toolchain version. We want to
69 # be able to target NVPTX without an existing CUDA installation, so we need to
70 # set this manually. This simply sets the PTX feature to the minimum required
71 # for the features we wish to use on that target. The minimum PTX features used
72 # here roughly corresponds to the CUDA 9.0 release.
73 # Adjust as needed for desired PTX features.
74 function(get_nvptx_compile_options output_var gpu_arch)
75   set(nvptx_options "")
76   list(APPEND nvptx_options "-march=${gpu_arch}")
77   list(APPEND nvptx_options "-Wno-unknown-cuda-version")
78   if(${gpu_arch} STREQUAL "sm_35")
79     list(APPEND nvptx_options "--cuda-feature=+ptx60")
80   elseif(${gpu_arch} STREQUAL "sm_37")
81     list(APPEND nvptx_options "--cuda-feature=+ptx60")
82   elseif(${gpu_arch} STREQUAL "sm_50")
83     list(APPEND nvptx_options "--cuda-feature=+ptx60")
84   elseif(${gpu_arch} STREQUAL "sm_52")
85     list(APPEND nvptx_options "--cuda-feature=+ptx60")
86   elseif(${gpu_arch} STREQUAL "sm_53")
87     list(APPEND nvptx_options "--cuda-feature=+ptx63")
88   elseif(${gpu_arch} STREQUAL "sm_60")
89     list(APPEND nvptx_options "--cuda-feature=+ptx63")
90   elseif(${gpu_arch} STREQUAL "sm_61")
91     list(APPEND nvptx_options "--cuda-feature=+ptx63")
92   elseif(${gpu_arch} STREQUAL "sm_62")
93     list(APPEND nvptx_options "--cuda-feature=+ptx63")
94   elseif(${gpu_arch} STREQUAL "sm_70")
95     list(APPEND nvptx_options "--cuda-feature=+ptx63")
96   elseif(${gpu_arch} STREQUAL "sm_72")
97     list(APPEND nvptx_options "--cuda-feature=+ptx63")
98   elseif(${gpu_arch} STREQUAL "sm_75")
99     list(APPEND nvptx_options "--cuda-feature=+ptx63")
100   elseif(${gpu_arch} STREQUAL "sm_80")
101     list(APPEND nvptx_options "--cuda-feature=+ptx72")
102   elseif(${gpu_arch} STREQUAL "sm_86")
103     list(APPEND nvptx_options "--cuda-feature=+ptx72")
104   else()
105     message(FATAL_ERROR "Unknown Nvidia GPU architecture '${gpu_arch}'")
106   endif()
108   if(LIBC_CUDA_ROOT)
109     list(APPEND nvptx_options "--cuda-path=${LIBC_CUDA_ROOT}")
110   endif()
111   set(${output_var} ${nvptx_options} PARENT_SCOPE)
112 endfunction()
114 # Builds the object target for the GPU.
115 # This compiles the target for all supported architectures and embeds it into
116 # host binary for installing. The internal target contains the GPU code directly
117 # compiled for a single architecture used internally.
118 # Usage:
119 #     _build_gpu_objects(
120 #       <target_name>
121 #       <internal_target_name>
122 #       SRCS <list of .cpp files>
123 #       HDRS <list of .h files>
124 #       DEPENDS <list of dependencies>
125 #       COMPILE_OPTIONS <optional list of special compile options for this target>
126 #       FLAGS <optional list of flags>
127 #     )
128 function(_build_gpu_objects fq_target_name internal_target_name)
129   cmake_parse_arguments(
130     "ADD_GPU_OBJ"
131     "" # No optional arguments
132     "NAME;CXX_STANDARD" # Single value arguments
133     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS"  # Multi value arguments
134     ${ARGN}
135   )
137   set(include_dirs ${LIBC_BUILD_DIR}/include ${LIBC_SOURCE_DIR} ${LIBC_BUILD_DIR})
138   set(common_compile_options ${ADD_GPU_OBJ_COMPILE_OPTIONS})
139   if(NOT ADD_GPU_OBJ_CXX_STANDARD)
140     set(ADD_GPU_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD})
141   endif()
143   foreach(add_gpu_obj_src ${ADD_GPU_OBJ_SRCS})
144     # The packaged version will be built for every target GPU architecture. We do
145     # this so we can support multiple accelerators on the same machine.
146     foreach(gpu_arch ${LIBC_GPU_ARCHITECTURES})
147       get_filename_component(src_name ${add_gpu_obj_src} NAME)
148       set(gpu_target_name ${fq_target_name}.${src_name}.${gpu_arch})
149       set(compile_options ${ADD_GPU_OBJ_COMPILE_OPTIONS})
150       # Derive the triple from the specified architecture.
151       if("${gpu_arch}" IN_LIST all_amdgpu_architectures)
152         set(gpu_target_triple "amdgcn-amd-amdhsa")
153         list(APPEND compile_options "-mcpu=${gpu_arch}")
154       elseif("${gpu_arch}" IN_LIST all_nvptx_architectures)
155         set(gpu_target_triple "nvptx64-nvidia-cuda")
156         get_nvptx_compile_options(nvptx_options ${gpu_arch})
157         list(APPEND compile_options "${nvptx_options}")
158       else()
159         message(FATAL_ERROR "Unknown GPU architecture '${gpu_arch}'")
160       endif()
161       list(APPEND compile_options "--target=${gpu_target_triple}")
162       list(APPEND compile_options "-emit-llvm")
164       # Build the library for this target architecture. We always emit LLVM-IR for
165       # packaged GPU binaries.
166       add_library(${gpu_target_name}
167         EXCLUDE_FROM_ALL
168         OBJECT
169         ${add_gpu_obj_src}
170         ${ADD_GPU_OBJ_HDRS}
171       )
173       target_compile_options(${gpu_target_name} PRIVATE ${compile_options})
174       target_include_directories(${gpu_target_name} PRIVATE ${include_dirs})
175       target_compile_definitions(${gpu_target_name} PRIVATE LIBC_COPT_PUBLIC_PACKAGING)
176       set_target_properties(
177         ${gpu_target_name}
178         PROPERTIES
179           CXX_STANDARD ${ADD_GPU_OBJ_CXX_STANDARD}
180       )
181       if(ADD_GPU_OBJ_DEPENDS)
182         add_dependencies(${gpu_target_name} ${ADD_GPU_OBJ_DEPENDS})
183       endif()
185       # Append this target to a list of images to package into a single binary.
186       set(input_file $<TARGET_OBJECTS:${gpu_target_name}>)
187       list(APPEND packager_images
188            --image=file=${input_file},arch=${gpu_arch},triple=${gpu_target_triple})
189       list(APPEND gpu_target_names ${gpu_target_name})
190     endforeach()
192     # After building the target for the desired GPUs we must package the output
193     # into a fatbinary, see https://clang.llvm.org/docs/OffloadingDesign.html for
194     # more information.
195     set(packaged_target_name ${fq_target_name}.${src_name}.__gpu__)
196     set(packaged_output_name ${CMAKE_CURRENT_BINARY_DIR}/${fq_target_name}.${src_name}.gpubin)
198     add_custom_command(OUTPUT ${packaged_output_name}
199                        COMMAND ${LIBC_CLANG_OFFLOAD_PACKAGER}
200                                ${packager_images} -o ${packaged_output_name}
201                        DEPENDS ${gpu_target_names} ${add_gpu_obj_src} ${ADD_GPU_OBJ_HDRS}
202                        COMMENT "Packaging LLVM offloading binary")
203     add_custom_target(${packaged_target_name} DEPENDS ${packaged_output_name})
204     list(APPEND packaged_gpu_names ${packaged_target_name})
205     list(APPEND packaged_gpu_binaries ${packaged_output_name})
206   endforeach()
208   # We create an empty 'stub' file for the host to contain the embedded device
209   # code. This will be packaged into 'libcgpu.a'.
210   # TODO: In the future we will want to combine every architecture for a target
211   #       into a single bitcode file and use that. For now we simply build for
212   #       every single one and let the offloading linker handle it.
213   string(FIND ${fq_target_name} "." last_dot_loc REVERSE)
214   math(EXPR name_loc "${last_dot_loc} + 1")
215   string(SUBSTRING ${fq_target_name} ${name_loc} -1 target_name)
216   set(stub_filename "${target_name}.cpp")
217   add_custom_command(
218     OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename}"
219     COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/stubs/
220     COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename}
221     DEPENDS ${gpu_target_names} ${ADD_GPU_OBJ_SRCS} ${ADD_GPU_OBJ_HDRS}
222   )
223   set(stub_target_name ${fq_target_name}.__stub__)
224   add_custom_target(${stub_target_name} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename})
226   add_library(
227     ${fq_target_name}
228     # We want an object library as the objects will eventually get packaged into
229     # an archive (like libcgpu.a).
230     EXCLUDE_FROM_ALL
231     OBJECT
232     ${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename}
233   )
234   target_compile_options(${fq_target_name} BEFORE PRIVATE
235                          ${common_compile_options} -nostdlib)
236   foreach(packaged_gpu_binary ${packaged_gpu_binaries})
237     target_compile_options(${fq_target_name} PRIVATE
238                            "SHELL:-Xclang -fembed-offload-object=${packaged_gpu_binary}")
239   endforeach()
240   target_include_directories(${fq_target_name} PRIVATE ${include_dirs})
241   add_dependencies(${fq_target_name}
242                    ${full_deps_list} ${packaged_gpu_names} ${stub_target_name})
244   # We only build the internal target for a single supported architecture.
245   if(LIBC_GPU_TARGET_ARCHITECTURE_IS_AMDGPU OR
246      LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
247     add_library(
248       ${internal_target_name}
249       EXCLUDE_FROM_ALL
250       OBJECT
251       ${ADD_GPU_OBJ_SRCS}
252       ${ADD_GPU_OBJ_HDRS}
253     )
254     target_compile_options(${internal_target_name} BEFORE PRIVATE
255                            ${common_compile_options} --target=${LIBC_GPU_TARGET_TRIPLE})
256     if(LIBC_GPU_TARGET_ARCHITECTURE_IS_AMDGPU)
257       target_compile_options(${internal_target_name} PRIVATE -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto)
258     elseif(LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
259       get_nvptx_compile_options(nvptx_options ${LIBC_GPU_TARGET_ARCHITECTURE})
260       target_compile_options(${internal_target_name} PRIVATE ${nvptx_options})
261     endif()
262     target_include_directories(${internal_target_name} PRIVATE ${include_dirs})
263     if(full_deps_list)
264       add_dependencies(${internal_target_name} ${full_deps_list})
265     endif()
266   endif()
267 endfunction()
269 # Rule which is essentially a wrapper over add_library to compile a set of
270 # sources to object files.
271 # Usage:
272 #     add_object_library(
273 #       <target_name>
274 #       HDRS <list of header files>
275 #       SRCS <list of source files>
276 #       [ALIAS] <If this object library is an alias for another object library.>
277 #       DEPENDS <list of dependencies; Should be a single item for ALIAS libraries>
278 #       COMPILE_OPTIONS <optional list of special compile options for this target>
279 #       FLAGS <optional list of flags>
280 function(create_object_library fq_target_name)
281   cmake_parse_arguments(
282     "ADD_OBJECT"
283     "ALIAS;NO_GPU_BUNDLE" # optional arguments
284     "CXX_STANDARD" # Single value arguments
285     "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS;FLAGS" # Multivalue arguments
286     ${ARGN}
287   )
289   get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS})
291   if(ADD_OBJECT_ALIAS)
292     if(ADD_OBJECT_SRCS OR ADD_OBJECT_HDRS)
293       message(FATAL_ERROR
294               "${fq_target_name}: object library alias cannot have SRCS and/or HDRS.")
295     endif()
296     list(LENGTH fq_deps_list depends_size)
297     if(NOT ${depends_size} EQUAL 1)
298       message(FATAL_ERROR
299               "${fq_targe_name}: object library alias should have exactly one DEPENDS.")
300     endif()
301     add_library(
302       ${fq_target_name}
303       ALIAS
304       ${fq_deps_list}
305     )
306     return()
307   endif()
309   if(NOT ADD_OBJECT_SRCS)
310     message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.")
311   endif()
313   # The GPU build uses a separate internal file.
314   if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND NOT ${ADD_OBJECT_NO_GPU_BUNDLE})
315     set(internal_target_name ${fq_target_name}.__internal__)
316   else()
317     set(internal_target_name ${fq_target_name})
318   endif()
320   _get_common_compile_options(
321     compile_options
322     "${ADD_OBJECT_FLAGS}"
323     ${ADD_OBJECT_COMPILE_OPTIONS}
324   )
326   # GPU builds require special handling for the objects because we want to
327   # export several different targets at once, e.g. for both Nvidia and AMD.
328   if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND NOT ${ADD_OBJECT_NO_GPU_BUNDLE})
329     _build_gpu_objects(
330       ${fq_target_name}
331       ${internal_target_name}
332       SRCS ${ADD_OBJECT_SRCS}
333       HDRS ${ADD_OBJECT_HDRS}
334       DEPENDS ${fq_deps_list}
335       CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD}
336       COMPILE_OPTIONS ${compile_options}
337     )
338   else()
339     add_library(
340       ${fq_target_name}
341       EXCLUDE_FROM_ALL
342       OBJECT
343       ${ADD_OBJECT_SRCS}
344       ${ADD_OBJECT_HDRS}
345     )
346     target_include_directories(
347       ${fq_target_name}
348       PRIVATE
349         ${LIBC_BUILD_DIR}/include
350         ${LIBC_SOURCE_DIR}
351         ${LIBC_BUILD_DIR}
352     )
353     target_compile_options(${fq_target_name} PRIVATE ${compile_options})
354   endif()
356   if(SHOW_INTERMEDIATE_OBJECTS)
357     message(STATUS "Adding object library ${fq_target_name}")
358     if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
359       foreach(dep IN LISTS ADD_OBJECT_DEPENDS)
360         message(STATUS "  ${fq_target_name} depends on ${dep}")
361       endforeach()
362     endif()
363   endif()
365   if(fq_deps_list)
366     add_dependencies(${fq_target_name} ${fq_deps_list})
367   endif()
369   if(NOT ADD_OBJECT_CXX_STANDARD)
370     set(ADD_OBJECT_CXX_STANDARD ${CMAKE_CXX_STANDARD})
371   endif()
372   set_target_properties(
373     ${fq_target_name}
374     PROPERTIES
375       TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE}
376       CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD}
377       DEPS "${fq_deps_list}"
378       FLAGS "${ADD_OBJECT_FLAGS}"
379   )
381   if(TARGET ${internal_target_name})
382     set_target_properties(
383       ${fq_target_name}
384       PROPERTIES
385         OBJECT_FILES "$<TARGET_OBJECTS:${internal_target_name}>"
386     )
387   endif()
388 endfunction(create_object_library)
390 # Internal function, used by `add_object_library`.
391 function(expand_flags_for_object_library target_name flags)
392   cmake_parse_arguments(
393     "EXPAND_FLAGS"
394     "IGNORE_MARKER" # Optional arguments
395     "" # Single-value arguments
396     "DEPENDS;FLAGS" # Multi-value arguments
397     ${ARGN}
398   )
400   list(LENGTH flags nflags)
401   if(NOT ${nflags})
402     create_object_library(
403       ${target_name}
404       DEPENDS ${EXPAND_FLAGS_DEPENDS}
405       FLAGS ${EXPAND_FLAGS_FLAGS}
406       ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
407     )
408     return()
409   endif()
411   list(GET flags 0 flag)
412   list(REMOVE_AT flags 0)
413   extract_flag_modifier(${flag} real_flag modifier)
415   if(NOT "${modifier}" STREQUAL "NO")
416     expand_flags_for_object_library(
417       ${target_name}
418       "${flags}"
419       DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER
420       FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER
421       "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
422     )
423   endif()
425   if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
426     return()
427   endif()
429   set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
430   list(REMOVE_ITEM NEW_FLAGS ${flag})
431   get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})
433   # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
434   # `flag__ONLY` do not.
435   if("${modifier}" STREQUAL "")
436     set(TARGET_NAME "${target_name}.__NO_${flag}")
437   else()
438     set(TARGET_NAME "${target_name}")
439   endif()
441   expand_flags_for_object_library(
442     ${TARGET_NAME}
443     "${flags}"
444     DEPENDS "${NEW_DEPS}" IGNORE_MARKER
445     FLAGS "${NEW_FLAGS}" IGNORE_MARKER
446     "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
447   )
448 endfunction(expand_flags_for_object_library)
450 function(add_object_library target_name)
451   cmake_parse_arguments(
452     "ADD_TO_EXPAND"
453     "" # Optional arguments
454     "" # Single value arguments
455     "DEPENDS;FLAGS" # Multi-value arguments
456     ${ARGN}
457   )
459   get_fq_target_name(${target_name} fq_target_name)
461   if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
462     message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
463   endif()
465   get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
466   get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
468   list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
469   remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
470   list(SORT flags)
472   if(SHOW_INTERMEDIATE_OBJECTS AND flags)
473     message(STATUS "Object library ${fq_target_name} has FLAGS: ${flags}")
474   endif()
476   expand_flags_for_object_library(
477     ${fq_target_name}
478     "${flags}"
479     DEPENDS "${fq_deps_list}" IGNORE_MARKER
480     FLAGS "${flags}" IGNORE_MARKER
481     ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
482   )
483 endfunction(add_object_library)
485 set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")
487 # A rule for entrypoint object targets.
488 # Usage:
489 #     add_entrypoint_object(
490 #       <target_name>
491 #       [ALIAS|REDIRECTED] # Specified if the entrypoint is redirected or an alias.
492 #       [NAME] <the C name of the entrypoint if different from target_name>
493 #       SRCS <list of .cpp files>
494 #       HDRS <list of .h files>
495 #       DEPENDS <list of dependencies>
496 #       COMPILE_OPTIONS <optional list of special compile options for this target>
497 #       SPECIAL_OBJECTS <optional list of special object targets added by the rule `add_object`>
498 #       FLAGS <optional list of flags>
499 #     )
500 function(create_entrypoint_object fq_target_name)
501   cmake_parse_arguments(
502     "ADD_ENTRYPOINT_OBJ"
503     "ALIAS;REDIRECTED" # Optional argument
504     "NAME;CXX_STANDARD" # Single value arguments
505     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS"  # Multi value arguments
506     ${ARGN}
507   )
509   list(FIND TARGET_ENTRYPOINT_NAME_LIST ${ADD_ENTRYPOINT_OBJ_NAME} entrypoint_name_index)
510   if(${entrypoint_name_index} EQUAL -1)
511     add_custom_target(${fq_target_name})
512     set_target_properties(
513       ${fq_target_name}
514       PROPERTIES
515         "ENTRYPOINT_NAME" ${ADD_ENTRYPOINT_OBJ_NAME}
516         "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
517         "OBJECT_FILE" ""
518         "OBJECT_FILE_RAW" ""
519         "DEPS" ""
520         "SKIPPED" "YES"
521     )
522     if(LIBC_CMAKE_VERBOSE_LOGGING)
523       message(STATUS "Skipping libc entrypoint ${fq_target_name}.")
524     endif()
525     return()
526   endif()
528   if(ADD_ENTRYPOINT_OBJ_ALIAS)
529     # Alias targets help one add aliases to other entrypoint object targets.
530     # One can use alias targets setup OS/machine independent entrypoint targets.
531     list(LENGTH ADD_ENTRYPOINT_OBJ_DEPENDS deps_size)
532     if(NOT (${deps_size} EQUAL "1"))
533       message(FATAL_ERROR "An entrypoint alias should have exactly one dependency.")
534     endif()
535     list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target)
536     get_fq_dep_name(fq_dep_name ${dep_target})
538     if(SHOW_INTERMEDIATE_OBJECTS)
539       message(STATUS "Adding entrypoint object ${fq_target_name} as an alias of"
540               " ${fq_dep_name}")
541     endif()
543     if(NOT TARGET ${fq_dep_name})
544       message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; "
545                       "Target ${target_name} will be ignored.")
546       return()
547     endif()
549     get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE")
550     if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})))
551       message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.")
552     endif()
554     add_custom_target(${fq_target_name})
555     add_dependencies(${fq_target_name} ${fq_dep_name})
556     get_target_property(object_file ${fq_dep_name} "OBJECT_FILE")
557     get_target_property(object_file_raw ${fq_dep_name} "OBJECT_FILE_RAW")
558     set_target_properties(
559       ${fq_target_name}
560       PROPERTIES
561         ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME}
562         TARGET_TYPE ${ENTRYPOINT_OBJ_TARGET_TYPE}
563         IS_ALIAS "YES"
564         OBJECT_FILE ""
565         OBJECT_FILE_RAW ""
566         DEPS "${fq_dep_name}"
567         FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
568     )
569     return()
570   endif()
572   if(NOT ADD_ENTRYPOINT_OBJ_SRCS)
573     message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")
574   endif()
575   if(NOT ADD_ENTRYPOINT_OBJ_HDRS)
576     message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.")
577   endif()
578   if(NOT ADD_ENTRYPOINT_OBJ_CXX_STANDARD)
579     set(ADD_ENTRYPOINT_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD})
580   endif()
582   _get_common_compile_options(
583     common_compile_options
584     "${ADD_ENTRYPOINT_OBJ_FLAGS}"
585     ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS}
586   )
587   set(internal_target_name ${fq_target_name}.__internal__)
588   set(include_dirs ${LIBC_BUILD_DIR}/include ${LIBC_SOURCE_DIR} ${LIBC_BUILD_DIR})
589   get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS})
590   set(full_deps_list ${fq_deps_list} libc.src.__support.common)
592   if(SHOW_INTERMEDIATE_OBJECTS)
593     message(STATUS "Adding entrypoint object ${fq_target_name}")
594     if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
595       foreach(dep IN LISTS ADD_OBJECT_DEPENDS)
596         message(STATUS "  ${fq_target_name} depends on ${dep}")
597       endforeach()
598     endif()
599   endif()
601   # GPU builds require special handling for the objects because we want to
602   # export several different targets at once, e.g. for both Nvidia and AMD.
603   if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
604     _build_gpu_objects(
605       ${fq_target_name}
606       ${internal_target_name}
607       SRCS ${ADD_ENTRYPOINT_OBJ_SRCS}
608       HDRS ${ADD_ENTRYPOINT_OBJ_HDRS}
609       COMPILE_OPTIONS ${common_compile_options}
610       CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}
611       DEPENDS ${full_deps_list}
612       FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
613     )
614   else()
615     add_library(
616       ${internal_target_name}
617       # TODO: We don't need an object library for internal consumption.
618       # A future change should switch this to a normal static library.
619       EXCLUDE_FROM_ALL
620       OBJECT
621       ${ADD_ENTRYPOINT_OBJ_SRCS}
622       ${ADD_ENTRYPOINT_OBJ_HDRS}
623     )
624     target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options})
625     target_include_directories(${internal_target_name} PRIVATE ${include_dirs})
626     add_dependencies(${internal_target_name} ${full_deps_list})
628     add_library(
629       ${fq_target_name}
630       # We want an object library as the objects will eventually get packaged into
631       # an archive (like libc.a).
632       EXCLUDE_FROM_ALL
633       OBJECT
634       ${ADD_ENTRYPOINT_OBJ_SRCS}
635       ${ADD_ENTRYPOINT_OBJ_HDRS}
636     )
637     target_compile_options(${fq_target_name} BEFORE PRIVATE ${common_compile_options} -DLIBC_COPT_PUBLIC_PACKAGING)
638     target_include_directories(${fq_target_name} PRIVATE ${include_dirs})
639     add_dependencies(${fq_target_name} ${full_deps_list})
640   endif()
642   set_target_properties(
643     ${fq_target_name}
644     PROPERTIES
645       ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME}
646       TARGET_TYPE ${ENTRYPOINT_OBJ_TARGET_TYPE}
647       OBJECT_FILE "$<TARGET_OBJECTS:${fq_target_name}>"
648       CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}
649       DEPS "${fq_deps_list}"
650       FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
651   )
653   if(TARGET ${internal_target_name})
654     set_target_properties(
655       ${internal_target_name}
656       PROPERTIES
657         CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}
658         FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
659     )
660     set_target_properties(
661       ${fq_target_name}
662       PROPERTIES
663         # TODO: We don't need to list internal object files if the internal
664         # target is a normal static library.
665         OBJECT_FILE_RAW "$<TARGET_OBJECTS:${internal_target_name}>"
666     )
667   endif()
669   if(LLVM_LIBC_ENABLE_LINTING AND TARGET ${internal_target_name})
670     if(NOT LLVM_LIBC_CLANG_TIDY)
671       message(FATAL_ERROR "Something is wrong!  LLVM_LIBC_ENABLE_LINTING is "
672               "ON but LLVM_LIBC_CLANG_TIDY is not set.")
673     endif()
675     # We only want a second invocation of clang-tidy to run
676     # restrict-system-libc-headers if the compiler-resource-dir was set in
677     # order to prevent false-positives due to a mismatch between the host
678     # compiler and the compiled clang-tidy.
679     if(COMPILER_RESOURCE_DIR)
680       # We run restrict-system-libc-headers with --system-headers to prevent
681       # transitive inclusion through compler provided headers.
682       set(restrict_system_headers_check_invocation
683         COMMAND ${LLVM_LIBC_CLANG_TIDY} --system-headers
684         --checks="-*,llvmlibc-restrict-system-libc-headers"
685         # We explicitly set the resource dir here to match the
686         # resource dir of the host compiler.
687         "--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}"
688         --quiet
689         -p ${PROJECT_BINARY_DIR}
690         ${ADD_ENTRYPOINT_OBJ_SRCS}
691       )
692     else()
693       set(restrict_system_headers_check_invocation
694         COMMAND ${CMAKE_COMMAND} -E echo "Header file check skipped")
695     endif()
697     add_custom_target(
698       ${fq_target_name}.__lint__
699       # --quiet is used to surpress warning statistics from clang-tidy like:
700       #     Suppressed X warnings (X in non-user code).
701       # There seems to be a bug in clang-tidy where by even with --quiet some
702       # messages from clang's own diagnostics engine leak through:
703       #     X warnings generated.
704       # Until this is fixed upstream, we use -fno-caret-diagnostics to surpress
705       # these.
706       COMMAND ${LLVM_LIBC_CLANG_TIDY}
707               "--extra-arg=-fno-caret-diagnostics" --quiet
708               # Path to directory containing compile_commands.json
709               -p ${PROJECT_BINARY_DIR}
710               ${ADD_ENTRYPOINT_OBJ_SRCS}
711       # See above: this might be a second invocation of clang-tidy depending on
712       # the conditions above.
713       ${restrict_system_headers_check_invocation}
714       # We have two options for running commands, add_custom_command and
715       # add_custom_target. We don't want to run the linter unless source files
716       # have changed. add_custom_target explicitly runs everytime therefore we
717       # use add_custom_command. This function requires an output file and since
718       # linting doesn't produce a file, we create a dummy file using a
719       # crossplatform touch.
720       COMMENT "Linting... ${fq_target_name}"
721       DEPENDS ${internal_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}
722       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
723     )
724     add_dependencies(libc-lint ${fq_target_name}.__lint__)
725   endif()
727 endfunction(create_entrypoint_object)
729 # Internal function, used by `add_entrypoint_object`.
730 function(expand_flags_for_entrypoint_object target_name flags)
731   cmake_parse_arguments(
732     "EXPAND_FLAGS"
733     "IGNORE_MARKER" # Optional arguments
734     "" # Single-value arguments
735     "DEPENDS;FLAGS" # Multi-value arguments
736     ${ARGN}
737   )
739   list(LENGTH flags nflags)
740   if(NOT ${nflags})
741     create_entrypoint_object(
742       ${target_name}
743       DEPENDS ${EXPAND_FLAGS_DEPENDS}
744       FLAGS ${EXPAND_FLAGS_FLAGS}
745       ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
746     )
747     return()
748   endif()
750   list(GET flags 0 flag)
751   list(REMOVE_AT flags 0)
752   extract_flag_modifier(${flag} real_flag modifier)
754   if(NOT "${modifier}" STREQUAL "NO")
755     expand_flags_for_entrypoint_object(
756       ${target_name}
757       "${flags}"
758       DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER
759       FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER
760       "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
761     )
762   endif()
764   if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
765     return()
766   endif()
768   set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
769   list(REMOVE_ITEM NEW_FLAGS ${flag})
770   get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})
772   # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
773   # `flag__ONLY` do not.
774   if("${modifier}" STREQUAL "")
775     set(TARGET_NAME "${target_name}.__NO_${flag}")
776   else()
777     set(TARGET_NAME "${target_name}")
778   endif()
780   expand_flags_for_entrypoint_object(
781     ${TARGET_NAME}
782     "${flags}"
783     DEPENDS "${NEW_DEPS}" IGNORE_MARKER
784     FLAGS "${NEW_FLAGS}" IGNORE_MARKER
785     "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
786   )
787 endfunction(expand_flags_for_entrypoint_object)
789 function(add_entrypoint_object target_name)
790   cmake_parse_arguments(
791     "ADD_TO_EXPAND"
792     "" # Optional arguments
793     "NAME" # Single value arguments
794     "DEPENDS;FLAGS" # Multi-value arguments
795     ${ARGN}
796   )
798   get_fq_target_name(${target_name} fq_target_name)
800   if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
801     message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
802   endif()
804   get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
805   get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
807   list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
808   remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
809   list(SORT flags)
811   if(SHOW_INTERMEDIATE_OBJECTS AND flags)
812     message(STATUS "Entrypoint object ${fq_target_name} has FLAGS: ${flags}")
813   endif()
815   if(NOT ADD_TO_EXPAND_NAME)
816     set(ADD_TO_EXPAND_NAME ${target_name})
817   endif()
819   expand_flags_for_entrypoint_object(
820     ${fq_target_name}
821     "${flags}"
822     NAME ${ADD_TO_EXPAND_NAME} IGNORE_MARKER
823     DEPENDS "${fq_deps_list}" IGNORE_MARKER
824     FLAGS "${flags}" IGNORE_MARKER
825     ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
826   )
827 endfunction(add_entrypoint_object)
829 set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT")
831 # A rule for external entrypoint targets.
832 # Usage:
833 #     add_entrypoint_external(
834 #       <target_name>
835 #       DEPENDS <list of dependencies>
836 #     )
837 function(add_entrypoint_external target_name)
838   cmake_parse_arguments(
839     "ADD_ENTRYPOINT_EXT"
840     "" # No optional arguments
841     "" # No single value arguments
842     "DEPENDS"  # Multi value arguments
843     ${ARGN}
844   )
845   get_fq_target_name(${target_name} fq_target_name)
846   set(entrypoint_name ${target_name})
848   add_custom_target(${fq_target_name})
849   set_target_properties(
850     ${fq_target_name}
851     PROPERTIES
852       "ENTRYPOINT_NAME" ${entrypoint_name}
853       "TARGET_TYPE" ${ENTRYPOINT_EXT_TARGET_TYPE}
854       "DEPS" "${ADD_ENTRYPOINT_EXT_DEPENDS}"
855   )
857 endfunction(add_entrypoint_external)
859 # Rule build a redirector object file.
860 function(add_redirector_object target_name)
861   cmake_parse_arguments(
862     "REDIRECTOR_OBJECT"
863     "" # No optional arguments
864     "SRC" # The cpp file in which the redirector is defined.
865     "" # No multivalue arguments
866     ${ARGN}
867   )
868   if(NOT REDIRECTOR_OBJECT_SRC)
869     message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.")
870   endif()
872   add_library(
873     ${target_name}
874     EXCLUDE_FROM_ALL
875     OBJECT
876     ${REDIRECTOR_OBJECT_SRC}
877   )
878   target_compile_options(
879     ${target_name}
880     BEFORE PRIVATE -fPIC ${LIBC_COMPILE_OPTIONS_DEFAULT}
881   )
882 endfunction(add_redirector_object)