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