20 changes: 19 additions & 1 deletion libc/cmake/modules/LLVMLibCHeaderRules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
22 changes: 22 additions & 0 deletions libc/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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()
75 changes: 75 additions & 0 deletions libc/utils/HdrGen/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions libc/utils/HdrGen/Generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion libc/utils/HdrGen/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down