diff --git a/libc/cmake/modules/LLVMLibCObjectRules.cmake b/libc/cmake/modules/LLVMLibCObjectRules.cmake --- a/libc/cmake/modules/LLVMLibCObjectRules.cmake +++ b/libc/cmake/modules/LLVMLibCObjectRules.cmake @@ -81,6 +81,10 @@ ) get_fq_target_name(${target_name} fq_target_name) + set(entrypoint_name ${target_name}) + if(ADD_ENTRYPOINT_OBJ_NAME) + set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME}) + endif() if(ADD_ENTRYPOINT_OBJ_ALIAS) # Alias targets help one add aliases to other entrypoint object targets. @@ -109,6 +113,7 @@ set_target_properties( ${fq_target_name} PROPERTIES + "ENTRYPOINT_NAME" ${entrypoint_name} "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} "IS_ALIAS" "YES" "OBJECT_FILE" "" @@ -125,11 +130,6 @@ message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.") endif() - set(entrypoint_name ${target_name}) - if(ADD_ENTRYPOINT_OBJ_NAME) - set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME}) - endif() - set(objects_target_name "${fq_target_name}_objects") add_library( @@ -199,6 +199,7 @@ set_target_properties( ${fq_target_name} PROPERTIES + "ENTRYPOINT_NAME" ${entrypoint_name} "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} "OBJECT_FILE" "${object_file}" "OBJECT_FILE_RAW" "${object_file_raw}" @@ -255,7 +256,7 @@ # crossplatform touch. COMMAND "${CMAKE_COMMAND}" -E touch ${lint_timestamp} COMMENT "Linting... ${target_name}" - DEPENDS ${clang-tidy} ${objects_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS} + DEPENDS clang-tidy ${objects_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/libc/lib/CMakeLists.txt b/libc/config/linux/x86_64/entrypoints.txt copy from libc/lib/CMakeLists.txt copy to libc/config/linux/x86_64/entrypoints.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1,7 +1,4 @@ - -add_entrypoint_library( - llvmlibc - DEPENDS +set(LIBC_ENTRYPOINTS # assert.h entrypoints libc.src.assert.__assert_fail @@ -23,11 +20,9 @@ libc.src.stdlib.abort # string.h entrypoints - libc.src.string.bzero libc.src.string.memcpy - libc.src.string.memset - libc.src.string.strcat libc.src.string.strcpy + libc.src.string.strcat libc.src.string.strlen # sys/mman.h entrypoints @@ -35,7 +30,6 @@ libc.src.sys.mman.munmap # threads.h entrypoints - libc.src.threads.call_once libc.src.threads.mtx_init libc.src.threads.mtx_lock libc.src.threads.mtx_unlock @@ -46,28 +40,10 @@ libc.src.unistd.write ) -add_entrypoint_library( - llvmlibm - DEPENDS +set(LIBM_ENTRYPOINTS # math.h entrypoints - libc.src.math.ceil - libc.src.math.ceilf libc.src.math.cosf - libc.src.math.fabs - libc.src.math.fabsf - libc.src.math.floor - libc.src.math.floorf - libc.src.math.expf - libc.src.math.exp2f libc.src.math.round libc.src.math.sincosf libc.src.math.sinf - libc.src.math.trunc - libc.src.math.truncf -) - -add_redirector_library( - llvmlibc_redirectors - DEPENDS - round_redirector -) +) \ No newline at end of file diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt new file mode 100644 --- /dev/null +++ b/libc/config/linux/x86_64/headers.txt @@ -0,0 +1,12 @@ +set(PUBLIC_HEADERS + libc.include.assert_h + libc.include.errno + libc.include.math + libc.include.signal + libc.include.stdio + libc.include.stdlib + libc.include.sys_mman + libc.include.sys_syscall + libc.include.threads + libc.include.unistd +) diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -1,69 +1,16 @@ +include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/entrypoints.txt") +include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/headers.txt") add_entrypoint_library( llvmlibc DEPENDS - # assert.h entrypoints - libc.src.assert.__assert_fail - - # errno.h entrypoints - libc.src.errno.__errno_location - - # signal.h entrypoints - libc.src.signal.raise - libc.src.signal.sigaction - libc.src.signal.sigdelset - libc.src.signal.sigaddset - libc.src.signal.sigemptyset - libc.src.signal.sigprocmask - libc.src.signal.sigfillset - libc.src.signal.signal - - # stdlib.h entrypoints - libc.src.stdlib._Exit - libc.src.stdlib.abort - - # string.h entrypoints - libc.src.string.bzero - libc.src.string.memcpy - libc.src.string.memset - libc.src.string.strcat - libc.src.string.strcpy - libc.src.string.strlen - - # sys/mman.h entrypoints - libc.src.sys.mman.mmap - libc.src.sys.mman.munmap - - # threads.h entrypoints - libc.src.threads.call_once - libc.src.threads.mtx_init - libc.src.threads.mtx_lock - libc.src.threads.mtx_unlock - libc.src.threads.thrd_create - libc.src.threads.thrd_join - - # unistd.h entrypoints - libc.src.unistd.write + ${LIBC_ENTRYPOINTS} ) add_entrypoint_library( llvmlibm DEPENDS - # math.h entrypoints - libc.src.math.ceil - libc.src.math.ceilf - libc.src.math.cosf - libc.src.math.fabs - libc.src.math.fabsf - libc.src.math.floor - libc.src.math.floorf - libc.src.math.expf - libc.src.math.exp2f - libc.src.math.round - libc.src.math.sincosf - libc.src.math.sinf - libc.src.math.trunc - libc.src.math.truncf + ${LIBM_ENTRYPOINTS} ) add_redirector_library( diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -8,3 +8,91 @@ add_subdirectory(sys) add_subdirectory(threads) add_subdirectory(unistd) + +include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/entrypoints.txt") +include("${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/headers.txt") + +set(public_test ${CMAKE_CURRENT_BINARY_DIR}/public_integration_test.cpp) + +set(entrypoints_name_list "") +foreach(entry IN LISTS LIBC_ENTRYPOINTS LIBM_ENTRYPOINTS) + get_target_property(entry_name ${entry} "ENTRYPOINT_NAME") + list(APPEND entrypoints_name_list ${entry_name}) +endforeach() + +# TODO: Remove these when they are added to the TableGen. +list(REMOVE_ITEM entrypoints_name_list "__assert_fail" "__errno_location") +list(TRANSFORM entrypoints_name_list PREPEND "-e=") + +# Generate integration test souce code. +add_custom_command( + OUTPUT ${public_test} + COMMAND $ -o ${public_test} + ${entrypoints_name_list} + -I ${LIBC_SOURCE_DIR} + ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td + + DEPENDS ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td + libc-prototype-testgen ${PUBLIC_HEADERS} + llvmlibc llvmlibm +) + +add_executable( + public_integration_test + EXCLUDE_FROM_ALL + ${public_test} +) +# Blank out default include directories to prevent accidentally including +# system headers or our own internal headers. +set_target_properties( + public_integration_test + PROPERTIES + INCLUDE_DIRECTORIES "" +) +# Only include we need is the include for cpp::IsSame and our generated +# public headers. +target_include_directories( + public_integration_test BEFORE + PRIVATE + "${LIBC_SOURCE_DIR}/utils/CPP" + "${LIBC_BUILD_DIR}/include" +) +target_compile_options( + public_integration_test + PRIVATE + -ffreestanding +) +target_link_options( + public_integration_test + PRIVATE "-nostdlib" +) +set(library_files) +foreach(library_name IN LISTS "llvmlibc;llvmlibm") + get_target_property(library_file ${library_name} "LIBRARY_FILE") + list(APPEND library_files ${library_file}) +endforeach() + +if(COMPILER_RESOURCE_DIR AND LLVM_LIBC_ENABLE_LINTING) + add_custom_target( + public_integration_test-tidy + VERBATIM + COMMAND $ --system-headers + --checks=-*,llvmlibc-restrict-system-libc-headers + "--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}" + --header-filter=.* + --warnings-as-errors=llvmlibc-* + "-config={CheckOptions: [{key: llvmlibc-restrict-system-libc-headers.Includes, value: '-*, linux/*, asm/*.h, asm-generic/*.h'}]}" + --quiet + -p ${PROJECT_BINARY_DIR} + ${public_test} + DEPENDS + clang-tidy ${public_test} + ) + add_dependencies(check-libc public_integration_test-tidy) +endif() + +target_link_libraries(public_integration_test + PRIVATE + ${library_files} +) +add_dependencies(check-libc public_integration_test) diff --git a/libc/utils/HdrGen/CMakeLists.txt b/libc/utils/HdrGen/CMakeLists.txt --- a/libc/utils/HdrGen/CMakeLists.txt +++ b/libc/utils/HdrGen/CMakeLists.txt @@ -11,3 +11,5 @@ PublicAPICommand.cpp PublicAPICommand.h ) + +add_subdirectory(PrototypeTestGen) diff --git a/libc/utils/HdrGen/PrototypeTestGen/.clang-tidy b/libc/utils/HdrGen/PrototypeTestGen/.clang-tidy new file mode 100644 --- /dev/null +++ b/libc/utils/HdrGen/PrototypeTestGen/.clang-tidy @@ -0,0 +1,3 @@ +CheckOptions: + - key: readability-identifier-naming.VariableCase + value: camelBack diff --git a/libc/utils/HdrGen/PrototypeTestGen/CMakeLists.txt b/libc/utils/HdrGen/PrototypeTestGen/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/utils/HdrGen/PrototypeTestGen/CMakeLists.txt @@ -0,0 +1,5 @@ +add_tablegen(libc-prototype-testgen llvm-libc + PrototypeTestGen.cpp + ../PublicAPICommand.cpp + ../Command.cpp +) diff --git a/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp b/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp new file mode 100644 --- /dev/null +++ b/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp @@ -0,0 +1,71 @@ +//===-- PrototypeTestGen.cpp ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "../PublicAPICommand.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/TableGen/Main.h" +#include "llvm/TableGen/Record.h" + +namespace { + +llvm::cl::list + EntrypointNamesOption("e", llvm::cl::desc(""), + llvm::cl::OneOrMore); + +} // anonymous namespace + +bool TestGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &records) { + OS << "#include \"TypeTraits.h\"\n"; + llvm_libc::APIIndexer G(records); + for (const auto &header : G.PublicHeaders) + OS << "#include <" << header << ">\n"; + OS << '\n'; + OS << "int main() {\n"; + for (const auto &entrypoint : EntrypointNamesOption) { + auto match = G.FunctionSpecMap.find(entrypoint); + if (match == G.FunctionSpecMap.end()) { + llvm::errs() << "ERROR: entrypoint '" << entrypoint + << "' could not be found in spec in any public header\n"; + return true; + } + llvm::Record *functionSpec = match->second; + llvm::Record *retValSpec = functionSpec->getValueAsDef("Return"); + std::string returnType = + G.getTypeAsString(retValSpec->getValueAsDef("ReturnType")); + // _Noreturn is an indication for the compiler that a function + // doesn't return, and isn't a type understood by c++ templates. + if (llvm::StringRef(returnType).contains("_Noreturn")) + returnType = "void"; + + OS << " static_assert(__llvm_libc::cpp::IsSame<" << returnType << '('; + auto args = functionSpec->getValueAsListOfDefs("Args"); + for (size_t i = 0, size = args.size(); i < size; ++i) { + llvm::Record *argType = args[i]->getValueAsDef("ArgType"); + OS << G.getTypeAsString(argType); + if (i < size - 1) + OS << ", "; + } + OS << "), decltype(" << entrypoint << ")>::Value, "; + OS << '"' << entrypoint + << " prototype in TableGen does not match public header" << '"'; + OS << ");\n"; + } + + OS << '\n'; + OS << " return 0;\n"; + OS << "}\n\n"; + + return false; +} + +int main(int argc, char *argv[]) { + llvm::cl::ParseCommandLineOptions(argc, argv); + return TableGenMain(argv[0], TestGeneratorMain); +} diff --git a/libc/utils/HdrGen/PublicAPICommand.h b/libc/utils/HdrGen/PublicAPICommand.h --- a/libc/utils/HdrGen/PublicAPICommand.h +++ b/libc/utils/HdrGen/PublicAPICommand.h @@ -25,6 +25,9 @@ } // namespace llvm +using NameToRecordMapping = std::unordered_map; +using NameSet = std::unordered_set; + namespace llvm_libc { class PublicAPICommand : public Command {