[clang] a4a2637 - [libc] Add support for creating wrapper headers for offloading in clang

Joseph Huber via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 6 16:11:02 PDT 2023


Author: Joseph Huber
Date: 2023-07-06T18:10:49-05:00
New Revision: a4a26374aa11d48ac6bf65c78c2aaf8f16414287

URL: https://github.com/llvm/llvm-project/commit/a4a26374aa11d48ac6bf65c78c2aaf8f16414287
DIFF: https://github.com/llvm/llvm-project/commit/a4a26374aa11d48ac6bf65c78c2aaf8f16414287.diff

LOG: [libc] Add support for creating wrapper headers for offloading in clang

This is an alternate approach to the patches proposed in D153897 and
D153794. Rather than exporting a single header that can be included on
the GPU in all circumstances, this patch chooses to instead generate a
separate set of headers that only provides the declarations. This can
then be used by external tooling to set up what's on the GPU. This
leaves room for header hacks for offloading languages without needing to
worry about the `libc` implementation.

Currently this generates a set of headers that only contain the
declarations. These will then be installed to a new clang resource
directory called `llvm_libc_wrappers/` which will house the shim code.
We can then automaticlaly include this from `clang` when offloading to
wrap around the headers while specifying what's on the GPU.

Reviewed By: jdoerfert, JonChesterfield

Differential Revision: https://reviews.llvm.org/D154036

Added: 
    clang/lib/Headers/llvm_libc_wrappers/ctype.h
    clang/lib/Headers/llvm_libc_wrappers/inttypes.h
    clang/lib/Headers/llvm_libc_wrappers/llvm-libc-decls/README.txt
    clang/lib/Headers/llvm_libc_wrappers/stdio.h
    clang/lib/Headers/llvm_libc_wrappers/stdlib.h
    clang/lib/Headers/llvm_libc_wrappers/string.h

Modified: 
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Headers/CMakeLists.txt
    clang/test/Driver/gpu-libc-headers.c
    libc/cmake/modules/LLVMLibCHeaderRules.cmake
    libc/include/CMakeLists.txt
    libc/utils/HdrGen/Generator.cpp
    libc/utils/HdrGen/Generator.h
    libc/utils/HdrGen/Main.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 0321da8e43f636..0a78366442767c 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1181,23 +1181,30 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA,
 
   // If we are compiling for a GPU target we want to override the system headers
   // with ones created by the 'libc' project if present.
-  // FIXME: We need to find a way to make these headers compatible with the
-  // host environment so they can be included from offloading languages. For now
-  // these are only active when targeting the GPU with cross-compilation.
   if (!Args.hasArg(options::OPT_nostdinc) &&
       !Args.hasArg(options::OPT_nogpuinc) &&
       !Args.hasArg(options::OPT_nobuiltininc) &&
-      C.getActiveOffloadKinds() == Action::OFK_None &&
       (getToolChain().getTriple().isNVPTX() ||
        getToolChain().getTriple().isAMDGCN())) {
 
-      // Add include/gpu-none-libc/* to our system include path. This lets us use
-      // GPU-specific system headers first.
+    // Without an offloading language we will include these headers directly.
+    // Offloading languages will instead only use the declarations stored in
+    // the resource directory at clang/lib/Headers/llvm_libc_wrappers.
+    if (C.getActiveOffloadKinds() == Action::OFK_None) {
       SmallString<128> P(llvm::sys::path::parent_path(D.InstalledDir));
       llvm::sys::path::append(P, "include");
       llvm::sys::path::append(P, "gpu-none-llvm");
       CmdArgs.push_back("-c-isystem");
       CmdArgs.push_back(Args.MakeArgString(P));
+    } else if (C.getActiveOffloadKinds() == Action::OFK_OpenMP) {
+      // TODO: CUDA / HIP include their own headers for some common functions
+      // implemented here. We'll need to clean those up so they do not conflict.
+      SmallString<128> P(D.ResourceDir);
+      llvm::sys::path::append(P, "include");
+      llvm::sys::path::append(P, "llvm_libc_wrappers");
+      CmdArgs.push_back("-internal-isystem");
+      CmdArgs.push_back(Args.MakeArgString(P));
+    }
   }
 
   // If we are offloading to a target via OpenMP we need to include the

diff  --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index 9d6ac8b7e449a1..5a219e234452a9 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -298,6 +298,14 @@ set(openmp_wrapper_files
   openmp_wrappers/new
 )
 
+set(llvm_libc_wrapper_files
+  llvm_libc_wrappers/stdio.h
+  llvm_libc_wrappers/stdlib.h
+  llvm_libc_wrappers/string.h
+  llvm_libc_wrappers/ctype.h
+  llvm_libc_wrappers/inttypes.h
+)
+
 include(GetClangResourceDir)
 get_clang_resource_dir(output_dir PREFIX ${LLVM_LIBRARY_OUTPUT_INTDIR}/.. SUBDIR include)
 set(out_files)
@@ -333,7 +341,8 @@ endfunction(clang_generate_header)
 
 # Copy header files from the source directory to the build directory
 foreach( f ${files} ${cuda_wrapper_files} ${cuda_wrapper_bits_files}
-           ${ppc_wrapper_files} ${openmp_wrapper_files} ${hlsl_files})
+           ${ppc_wrapper_files} ${openmp_wrapper_files} ${hlsl_files}
+           ${llvm_libc_wrapper_files})
   copy_header_to_output_dir(${CMAKE_CURRENT_SOURCE_DIR} ${f})
 endforeach( f )
 
@@ -427,6 +436,7 @@ add_dependencies("clang-resource-headers"
                  "x86-resource-headers"
                  "opencl-resource-headers"
                  "openmp-resource-headers"
+                 "llvm-libc-resource-headers"
                  "windows-resource-headers"
                  "utility-resource-headers")
 
@@ -453,6 +463,7 @@ add_header_target("x86-resource-headers" "${x86_files}")
 # Other header groupings
 add_header_target("hlsl-resource-headers" ${hlsl_files})
 add_header_target("opencl-resource-headers" ${opencl_files})
+add_header_target("llvm-libc-resource-headers" ${llvm_libc_wrapper_files})
 add_header_target("openmp-resource-headers" ${openmp_wrapper_files})
 add_header_target("windows-resource-headers" ${windows_only_files})
 add_header_target("utility-resource-headers" ${utility_files})
@@ -481,6 +492,11 @@ install(
   DESTINATION ${header_install_dir}/ppc_wrappers
   COMPONENT clang-resource-headers)
 
+install(
+  FILES ${llvm_libc_wrapper_files}
+  DESTINATION ${header_install_dir}/llvm_libc_wrappers
+  COMPONENT clang-resource-headers)
+
 install(
   FILES ${openmp_wrapper_files}
   DESTINATION ${header_install_dir}/openmp_wrappers
@@ -636,6 +652,12 @@ install(
   EXCLUDE_FROM_ALL
   COMPONENT openmp-resource-headers)
 
+install(
+  FILES ${openmp_wrapper_files}
+  DESTINATION ${header_install_dir}/openmp_wrappers
+  EXCLUDE_FROM_ALL
+  COMPONENT openmp-resource-headers)
+
 install(
   FILES ${utility_files}
   DESTINATION ${header_install_dir}

diff  --git a/clang/lib/Headers/llvm_libc_wrappers/ctype.h b/clang/lib/Headers/llvm_libc_wrappers/ctype.h
new file mode 100644
index 00000000000000..e20b7bb58f43b2
--- /dev/null
+++ b/clang/lib/Headers/llvm_libc_wrappers/ctype.h
@@ -0,0 +1,85 @@
+//===-- Wrapper for C standard ctype.h declarations on the GPU ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CLANG_LLVM_LIBC_WRAPPERS_CTYPE_H__
+#define __CLANG_LLVM_LIBC_WRAPPERS_CTYPE_H__
+
+#if !defined(_OPENMP) && !defined(__HIP__) && !defined(__CUDA__)
+#error "This file is for GPU offloading compilation only"
+#endif
+
+#include_next <ctype.h>
+
+#if __has_include(<llvm-libc-decls/ctype.h>)
+
+#if defined(__HIP__) || defined(__CUDA__)
+#define __LIBC_ATTRS __attribute__((device))
+#endif
+
+// The GNU headers like to provide these as macros, we need to undefine them so
+// they do not conflict with the following definitions for the GPU.
+
+#pragma push_macro("isalnum")
+#pragma push_macro("isalpha")
+#pragma push_macro("isblank")
+#pragma push_macro("iscntrl")
+#pragma push_macro("isdigit")
+#pragma push_macro("isgraph")
+#pragma push_macro("islower")
+#pragma push_macro("isprint")
+#pragma push_macro("ispunct")
+#pragma push_macro("isspace")
+#pragma push_macro("isupper")
+#pragma push_macro("isxdigit")
+#pragma push_macro("tolower")
+#pragma push_macro("toupper")
+
+#undef isalnum
+#undef isalpha
+#undef iscntrl
+#undef isdigit
+#undef islower
+#undef isgraph
+#undef isprint
+#undef ispunct
+#undef isspace
+#undef isupper
+#undef isblank
+#undef isxdigit
+#undef tolower
+#undef toupper
+
+#pragma omp begin declare target
+
+#include <llvm-libc-decls/ctype.h>
+
+#pragma omp end declare target
+
+// Restore the original macros when compiling on the host.
+#if !defined(__NVPTX__) && !defined(__AMDGPU__)
+#pragma pop_macro("isalnum")
+#pragma pop_macro("isalpha")
+#pragma pop_macro("isblank")
+#pragma pop_macro("iscntrl")
+#pragma pop_macro("isdigit")
+#pragma pop_macro("isgraph")
+#pragma pop_macro("islower")
+#pragma pop_macro("isprint")
+#pragma pop_macro("ispunct")
+#pragma pop_macro("isspace")
+#pragma pop_macro("isupper")
+#pragma pop_macro("isxdigit")
+#pragma pop_macro("tolower")
+#pragma pop_macro("toupper")
+#endif
+
+#undef __LIBC_ATTRS
+
+#endif
+
+#endif // __CLANG_LLVM_LIBC_WRAPPERS_CTYPE_H__

diff  --git a/clang/lib/Headers/llvm_libc_wrappers/inttypes.h b/clang/lib/Headers/llvm_libc_wrappers/inttypes.h
new file mode 100644
index 00000000000000..415f1e4b7bcab5
--- /dev/null
+++ b/clang/lib/Headers/llvm_libc_wrappers/inttypes.h
@@ -0,0 +1,34 @@
+//===-- Wrapper for C standard inttypes.h declarations on the GPU ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CLANG_LLVM_LIBC_WRAPPERS_INTTYPES_H__
+#define __CLANG_LLVM_LIBC_WRAPPERS_INTTYPES_H__
+
+#if !defined(_OPENMP) && !defined(__HIP__) && !defined(__CUDA__)
+#error "This file is for GPU offloading compilation only"
+#endif
+
+#include_next <inttypes.h>
+
+#if __has_include(<llvm-libc-decls/inttypes.h>)
+
+#if defined(__HIP__) || defined(__CUDA__)
+#define __LIBC_ATTRS __attribute__((device))
+#endif
+
+#pragma omp begin declare target
+
+#include <llvm-libc-decls/inttypes.h>
+
+#pragma omp end declare target
+
+#undef __LIBC_ATTRS
+
+#endif
+
+#endif // __CLANG_LLVM_LIBC_WRAPPERS_INTTYPES_H__

diff  --git a/clang/lib/Headers/llvm_libc_wrappers/llvm-libc-decls/README.txt b/clang/lib/Headers/llvm_libc_wrappers/llvm-libc-decls/README.txt
new file mode 100644
index 00000000000000..e012cd9e293126
--- /dev/null
+++ b/clang/lib/Headers/llvm_libc_wrappers/llvm-libc-decls/README.txt
@@ -0,0 +1,6 @@
+LLVM libc declarations
+======================
+
+This directory will be filled by the `libc` project with declarations that are
+availible on the device. Each declaration will use the `__LIBC_ATTRS` attribute
+to control emission on the device side.

diff  --git a/clang/lib/Headers/llvm_libc_wrappers/stdio.h b/clang/lib/Headers/llvm_libc_wrappers/stdio.h
new file mode 100644
index 00000000000000..51b0f0e3307772
--- /dev/null
+++ b/clang/lib/Headers/llvm_libc_wrappers/stdio.h
@@ -0,0 +1,34 @@
+//===-- Wrapper for C standard stdio.h declarations on the GPU ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
+#define __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__
+
+#if !defined(_OPENMP) && !defined(__HIP__) && !defined(__CUDA__)
+#error "This file is for GPU offloading compilation only"
+#endif
+
+#include_next <stdio.h>
+
+#if __has_include(<llvm-libc-decls/stdio.h>)
+
+#if defined(__HIP__) || defined(__CUDA__)
+#define __LIBC_ATTRS __attribute__((device))
+#endif
+
+#pragma omp begin declare target
+
+#include <llvm-libc-decls/stdio.h>
+
+#pragma omp end declare target
+
+#undef __LIBC_ATTRS
+
+#endif
+
+#endif // __CLANG_LLVM_LIBC_WRAPPERS_STDIO_H__

diff  --git a/clang/lib/Headers/llvm_libc_wrappers/stdlib.h b/clang/lib/Headers/llvm_libc_wrappers/stdlib.h
new file mode 100644
index 00000000000000..9cb2b4e64aa682
--- /dev/null
+++ b/clang/lib/Headers/llvm_libc_wrappers/stdlib.h
@@ -0,0 +1,42 @@
+//===-- Wrapper for C standard stdlib.h declarations on the GPU -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CLANG_LLVM_LIBC_WRAPPERS_STDLIB_H__
+#define __CLANG_LLVM_LIBC_WRAPPERS_STDLIB_H__
+
+#if !defined(_OPENMP) && !defined(__HIP__) && !defined(__CUDA__)
+#error "This file is for GPU offloading compilation only"
+#endif
+
+#include_next <stdlib.h>
+
+#if __has_include(<llvm-libc-decls/stdlib.h>)
+
+#if defined(__HIP__) || defined(__CUDA__)
+#define __LIBC_ATTRS __attribute__((device))
+#endif
+
+#pragma omp begin declare target
+
+// The LLVM C library uses this type so we forward declare it.
+typedef void (*__atexithandler_t)(void);
+
+// Enforce ABI compatibility with the structs used by the LLVM C library.
+_Static_assert(__builtin_offsetof(div_t, quot) == 0, "ABI mismatch!");
+_Static_assert(__builtin_offsetof(ldiv_t, quot) == 0, "ABI mismatch!");
+_Static_assert(__builtin_offsetof(lldiv_t, quot) == 0, "ABI mismatch!");
+
+#include <llvm-libc-decls/stdlib.h>
+
+#pragma omp end declare target
+
+#undef __LIBC_ATTRS
+
+#endif
+
+#endif // __CLANG_LLVM_LIBC_WRAPPERS_STDLIB_H__

diff  --git a/clang/lib/Headers/llvm_libc_wrappers/string.h b/clang/lib/Headers/llvm_libc_wrappers/string.h
new file mode 100644
index 00000000000000..0cab5615ad1300
--- /dev/null
+++ b/clang/lib/Headers/llvm_libc_wrappers/string.h
@@ -0,0 +1,48 @@
+//===-- Wrapper for C standard string.h declarations on the GPU -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CLANG_LLVM_LIBC_WRAPPERS_STRING_H__
+#define __CLANG_LLVM_LIBC_WRAPPERS_STRING_H__
+
+#if !defined(_OPENMP) && !defined(__HIP__) && !defined(__CUDA__)
+#error "This file is for GPU offloading compilation only"
+#endif
+
+// The GNU headers provide non C-standard headers when in C++ mode. Manually
+// undefine it here so that the definitions agree with the C standard for our
+// purposes.
+#ifdef __cplusplus
+extern "C" {
+#pragma push_macro("__cplusplus")
+#undef __cplusplus
+#endif
+
+#include_next <string.h>
+
+#pragma pop_macro("__cplusplus")
+#ifdef __cplusplus
+}
+#endif
+
+#if __has_include(<llvm-libc-decls/string.h>)
+
+#if defined(__HIP__) || defined(__CUDA__)
+#define __LIBC_ATTRS __attribute__((device))
+#endif
+
+#pragma omp begin declare target
+
+#include <llvm-libc-decls/string.h>
+
+#pragma omp end declare target
+
+#undef __LIBC_ATTRS
+
+#endif
+
+#endif // __CLANG_LLVM_LIBC_WRAPPERS_STRING_H__

diff  --git a/clang/test/Driver/gpu-libc-headers.c b/clang/test/Driver/gpu-libc-headers.c
index de030e647154c7..c6361b4b892d39 100644
--- a/clang/test/Driver/gpu-libc-headers.c
+++ b/clang/test/Driver/gpu-libc-headers.c
@@ -1,6 +1,14 @@
 // REQUIRES: nvptx-registered-target
 // REQUIRES: amdgpu-registered-target
 
+// RUN:   %clang -### --target=x86_64-unknown-linux-gnu -fopenmp=libomp --sysroot=./ \
+// RUN:     -fopenmp-targets=amdgcn-amd-amdhsa -Xopenmp-target=amdgcn-amd-amdhsa --offload-arch=gfx908  \
+// RUN:     -nogpulib %s 2>&1 | FileCheck %s --check-prefix=CHECK-HEADERS
+// RUN:   %clang -### --target=x86_64-unknown-linux-gnu -fopenmp=libomp --sysroot=./ \
+// RUN:     -fopenmp-targets=nvptx64-nvidia-cuda -Xopenmp-target=nvptx64-nvidia-cuda --offload-arch=sm_70  \
+// RUN:     -nogpulib %s 2>&1 | FileCheck %s --check-prefix=CHECK-HEADERS
+// CHECK-HEADERS: "-cc1"{{.*}}"-internal-isystem" "{{.*}}include{{.*}}llvm_libc_wrappers"{{.*}}"-isysroot" "./"
+
 // RUN:   %clang -### --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -nogpulib \
 // RUN:     -nogpuinc %s 2>&1 | FileCheck %s --check-prefix=CHECK-HEADERS-DISABLED
 // RUN:   %clang -### --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -nogpulib \

diff  --git a/libc/cmake/modules/LLVMLibCHeaderRules.cmake b/libc/cmake/modules/LLVMLibCHeaderRules.cmake
index 20cf43aafe142a..3e241a24543eba 100644
--- a/libc/cmake/modules/LLVMLibCHeaderRules.cmake
+++ b/libc/cmake/modules/LLVMLibCHeaderRules.cmake
@@ -131,6 +131,23 @@ function(add_gen_header target_name)
             ${hdrgen_deps}
   )
 
+  if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
+    file(MAKE_DIRECTORY ${LIBC_INCLUDE_DIR}/llvm-libc-decls)
+    set(decl_out_file ${LIBC_INCLUDE_DIR}/llvm-libc-decls/${relative_path})
+    add_custom_command(
+      OUTPUT ${decl_out_file}
+      COMMAND ${hdrgen_exe} -o ${decl_out_file}
+              --header ${ADD_GEN_HDR_GEN_HDR} --def ${in_file} --export-decls
+              ${replacement_params} -I ${LIBC_SOURCE_DIR} ${ENTRYPOINT_NAME_LIST_ARG}
+              ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td
+
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${in_file} ${fq_data_files} ${td_includes}
+              ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td
+              ${hdrgen_deps}
+    )
+  endif()
+
   if(ADD_GEN_HDR_DEPENDS)
     get_fq_deps_list(fq_deps_list ${ADD_GEN_HDR_DEPENDS})
     # Dependencies of a add_header target can only be another add_gen_header target
@@ -144,13 +161,14 @@ function(add_gen_header target_name)
   endif()
   add_custom_target(
     ${fq_target_name}
-    DEPENDS ${out_file} ${fq_deps_list}
+    DEPENDS ${out_file} ${fq_deps_list} ${decl_out_file}
   )
 
   set_target_properties(
     ${fq_target_name}
     PROPERTIES
       HEADER_FILE_PATH ${out_file}
+      DECLS_FILE_PATH ${decl_out_file}
       DEPS "${fq_deps_list}"
   )
 endfunction(add_gen_header)

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 02a5f54175e9ac..1e34f4b9004108 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -3,6 +3,11 @@ set(LIBC_INCLUDE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
 
 include(LLVMLibCHeaderRules)
 
+# The GPU build wants to install files in the compiler's resource directory.
+if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
+  include(GetClangResourceDir)
+endif()
+
 add_subdirectory(llvm-libc-macros)
 add_subdirectory(llvm-libc-types)
 
@@ -539,4 +544,21 @@ foreach(target IN LISTS all_install_header_targets)
   install(FILES ${header_file}
           DESTINATION ${LIBC_INSTALL_INCLUDE_DIR}/${nested_dir}
           COMPONENT libc-headers)
+  # The GPU optionally provides the supported declarations externally so
+  # offloading languages like CUDA and OpenMP know what is supported by libc. We
+  # install these in the compiler's resource directory at a preset location.
+  if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
+    get_target_property(decls_file ${target} DECLS_FILE_PATH)
+    if(NOT decls_file)
+      continue()
+    endif()
+    get_clang_resource_dir(resource_dir SUBDIR include)
+    file(RELATIVE_PATH relative_path ${LIBC_INCLUDE_BINARY_DIR} ${decls_file})
+    get_filename_component(nested_dir ${relative_path} DIRECTORY)
+    set(install_dir
+        ${CMAKE_INSTALL_PREFIX}/${resource_dir}/llvm_libc_wrappers/${nested_dir})
+    install(FILES ${decls_file}
+            DESTINATION ${install_dir}
+            COMPONENT libc-headers)
+  endif()
 endforeach()

diff  --git a/libc/utils/HdrGen/Generator.cpp b/libc/utils/HdrGen/Generator.cpp
index cbfb2db6fa4769..24d22680fe525b 100644
--- a/libc/utils/HdrGen/Generator.cpp
+++ b/libc/utils/HdrGen/Generator.cpp
@@ -10,6 +10,7 @@
 
 #include "IncludeFileCommand.h"
 #include "PublicAPICommand.h"
+#include "utils/LibcTableGenUtil/APIIndexer.h"
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -116,4 +117,78 @@ void Generator::generate(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
   }
 }
 
+void Generator::generateDecls(llvm::raw_ostream &OS,
+                              llvm::RecordKeeper &Records) {
+
+  OS << "//===-- C standard declarations for " << StdHeader << " "
+     << std::string(80 - (42 + StdHeader.size()), '-') << "===//\n"
+     << "//\n"
+     << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM "
+        "Exceptions.\n"
+     << "// See https://llvm.org/LICENSE.txt for license information.\n"
+     << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
+     << "//\n"
+     << "//"
+        "===-------------------------------------------------------------------"
+        "---===//\n\n";
+
+  std::string HeaderGuard(StdHeader.size(), '\0');
+  llvm::transform(StdHeader, HeaderGuard.begin(), [](const char C) -> char {
+    return !isalnum(C) ? '_' : llvm::toUpper(C);
+  });
+  OS << "#ifndef __LLVM_LIBC_DECLARATIONS_" << HeaderGuard << "\n"
+     << "#define __LLVM_LIBC_DECLARATIONS_" << HeaderGuard << "\n\n";
+
+  OS << "#ifndef __LIBC_ATTRS\n"
+     << "#define __LIBC_ATTRS\n"
+     << "#endif\n\n";
+
+  OS << "#ifdef __cplusplus\n"
+     << "extern \"C\" {\n"
+     << "#endif\n\n";
+
+  APIIndexer G(StdHeader, Records);
+  for (auto &Name : EntrypointNameList) {
+    // Filter out functions not exported by this header.
+    if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end())
+      continue;
+
+    llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
+    llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
+    llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
+
+    OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
+
+    auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
+    for (size_t i = 0; i < ArgsList.size(); ++i) {
+      llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
+      OS << G.getTypeAsString(ArgType);
+      if (i < ArgsList.size() - 1)
+        OS << ", ";
+    }
+
+    OS << ") __LIBC_ATTRS;\n\n";
+  }
+
+  // Make another pass over entrypoints to emit object declarations.
+  for (const auto &Name : EntrypointNameList) {
+    if (G.ObjectSpecMap.find(Name) == G.ObjectSpecMap.end())
+      continue;
+    llvm::Record *ObjectSpec = G.ObjectSpecMap[Name];
+    auto Type = ObjectSpec->getValueAsString("Type");
+    OS << "extern " << Type << " " << Name << " __LIBC_ATTRS;\n";
+  }
+
+  // Emit a final newline if we emitted any object declarations.
+  if (llvm::any_of(EntrypointNameList, [&](const std::string &Name) {
+        return G.ObjectSpecMap.find(Name) != G.ObjectSpecMap.end();
+      }))
+    OS << "\n";
+
+  OS << "#ifdef __cplusplus\n"
+     << "}\n"
+     << "#endif\n\n";
+  OS << "#endif\n";
+}
+
 } // namespace llvm_libc

diff  --git a/libc/utils/HdrGen/Generator.h b/libc/utils/HdrGen/Generator.h
index eef96ad656f897..76a8a110832112 100644
--- a/libc/utils/HdrGen/Generator.h
+++ b/libc/utils/HdrGen/Generator.h
@@ -52,6 +52,7 @@ class Generator {
         ArgMap(Map) {}
 
   void generate(llvm::raw_ostream &OS, llvm::RecordKeeper &Records);
+  void generateDecls(llvm::raw_ostream &OS, llvm::RecordKeeper &Records);
 };
 
 } // namespace llvm_libc

diff  --git a/libc/utils/HdrGen/Main.cpp b/libc/utils/HdrGen/Main.cpp
index d1487476637062..d3418f206b10e9 100644
--- a/libc/utils/HdrGen/Main.cpp
+++ b/libc/utils/HdrGen/Main.cpp
@@ -32,6 +32,9 @@ llvm::cl::list<std::string> EntrypointNamesOption(
 llvm::cl::list<std::string> ReplacementValues(
     "args", llvm::cl::desc("Command separated <argument name>=<value> pairs."),
     llvm::cl::value_desc("<name=value>[,name=value]"));
+llvm::cl::opt<bool> ExportDecls(
+    "export-decls",
+    llvm::cl::desc("Output a new header containing only the entrypoints."));
 
 void ParseArgValuePairs(std::unordered_map<std::string, std::string> &Map) {
   for (std::string &R : ReplacementValues) {
@@ -48,7 +51,10 @@ bool HeaderGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
   std::unordered_map<std::string, std::string> ArgMap;
   ParseArgValuePairs(ArgMap);
   Generator G(HeaderDefFile, EntrypointNamesOption, StandardHeader, ArgMap);
-  G.generate(OS, Records);
+  if (ExportDecls)
+    G.generateDecls(OS, Records);
+  else
+    G.generate(OS, Records);
 
   return false;
 }


        


More information about the cfe-commits mailing list