diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake index 0d0a47b33aaeb..b5948b29970dd 100644 --- a/libc/cmake/modules/LLVMLibCTestRules.cmake +++ b/libc/cmake/modules/LLVMLibCTestRules.cmake @@ -5,6 +5,7 @@ # Usage: # get_object_files_for_test( # +# INTERNAL # [ ...]) # # The list of object files is collected in . @@ -12,11 +13,24 @@ # set to a true value. # targetN is either an "add_entrypoint_target" target or an # "add_object_library" target. +# If INTERNAL is TRUE, then we collect `target.__internal__` for entry points. function(get_object_files_for_test result skipped_entrypoints_list) + cmake_parse_arguments( + GET_OBJ + "" # No optional + "INTERNAL" # Single-value + "" # No multi-value + ${ARGN} + ) set(object_files "") set(skipped_list "") set(checked_list "") - set(unchecked_list "${ARGN}") + set(unchecked_list "${GET_OBJ_UNPARSED_ARGUMENTS}") + + set(check_obj_for_tests "CHECK_OBJ_FOR_TESTS_${GET_OBJ_INTERNAL}") + set(object_files_for_tests "OBJECT_FILES_FOR_TESTS_${GET_OBJ_INTERNAL}") + set(skipped_list_for_tests "SKIPPED_LIST_FOR_TESTS_${GET_OBJ_INTERNAL}") + list(REMOVE_DUPLICATES unchecked_list) foreach(dep IN LISTS unchecked_list) @@ -37,19 +51,23 @@ function(get_object_files_for_test result skipped_entrypoints_list) continue() endif() - get_target_property(dep_checked ${dep} "CHECK_OBJ_FOR_TESTS") + get_target_property(dep_checked ${dep} ${check_obj_for_tests}) if(dep_checked) # Target full dependency has already been checked. Just use the results. - get_target_property(dep_obj ${dep} "OBJECT_FILES_FOR_TESTS") - get_target_property(dep_skip ${dep} "SKIPPED_LIST_FOR_TESTS") + get_target_property(dep_obj ${dep} ${object_files_for_tests}) + get_target_property(dep_skip ${dep} ${skipped_list_for_tests}) else() # Target full dependency hasn't been checked. Recursively check its DEPS. set(dep_obj "${dep}") set(dep_skip "") get_target_property(indirect_deps ${dep} "DEPS") - get_object_files_for_test(dep_obj dep_skip ${indirect_deps}) + get_object_files_for_test( + dep_obj dep_skip + INTERNAL ${GET_OBJ_INTERNAL} + ${indirect_deps} + ) if(${dep_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE}) get_target_property(dep_object_files ${dep} "OBJECT_FILES") @@ -62,7 +80,11 @@ function(get_object_files_for_test result skipped_entrypoints_list) list(APPEND dep_skip ${dep}) list(REMOVE_ITEM dep_obj ${dep}) endif() - get_target_property(object_file_raw ${dep} "OBJECT_FILE_RAW") + if(${GET_OBJ_INTERNAL}) + get_target_property(object_file_raw ${dep} "OBJECT_FILE_RAW") + else() + get_target_property(object_file_raw ${dep} "OBJECT_FILE") + endif() if(object_file_raw) list(APPEND dep_obj ${object_file_raw}) endif() @@ -73,9 +95,9 @@ function(get_object_files_for_test result skipped_entrypoints_list) endif() set_target_properties(${dep} PROPERTIES - OBJECT_FILES_FOR_TESTS "${dep_obj}" - SKIPPED_LIST_FOR_TESTS "${dep_skip}" - CHECK_OBJ_FOR_TESTS "YES" + "${object_files_for_tests}" "${dep_obj}" + "${skipped_list_for_tests}" "${dep_skip}" + "${check_obj_for_tests}" "YES" ) endif() @@ -140,7 +162,10 @@ function(create_libc_unittest fq_target_name) endif() get_object_files_for_test( - link_object_files skipped_entrypoints_list ${fq_deps_list}) + link_object_files skipped_entrypoints_list + INTERNAL TRUE + ${fq_deps_list} + ) if(skipped_entrypoints_list) # If a test is OS/target machine independent, it has to be skipped if the # OS/target machine combination does not provide any dependent entrypoints. @@ -167,6 +192,15 @@ function(create_libc_unittest fq_target_name) return() endif() + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding unit test ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS LIBC_UNITTEST_DEPENDS) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() + if(LIBC_UNITTEST_NO_RUN_POSTBUILD) set(fq_build_target_name ${fq_target_name}) else() @@ -389,7 +423,10 @@ function(add_libc_fuzzer target_name) get_fq_target_name(${target_name} fq_target_name) get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) get_object_files_for_test( - link_object_files skipped_entrypoints_list ${fq_deps_list}) + link_object_files skipped_entrypoints_list + INTERNAL TRUE + ${fq_deps_list} + ) if(skipped_entrypoints_list) if(LIBC_CMAKE_VERBOSE_LOGGING) set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " @@ -493,6 +530,16 @@ function(add_integration_test test_name) get_fq_target_name(${test_name}.libc fq_libc_target_name) get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS}) + + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding integration test ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS fq_deps_list) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() + list(APPEND fq_deps_list # All integration tests use the operating system's startup object with the # integration test object and need to inherit the same dependencies. @@ -500,6 +547,7 @@ function(add_integration_test test_name) libc.test.IntegrationTest.test # We always add the memory functions objects. This is because the # compiler's codegen can emit calls to the C memory functions. + libc.src.stdlib.atexit libc.src.string.bcmp libc.src.string.bzero libc.src.string.memcmp @@ -516,10 +564,28 @@ function(add_integration_test test_name) list(REMOVE_DUPLICATES fq_deps_list) - # TODO: Instead of gathering internal object files from entrypoints, - # collect the object files with public names of entrypoints. + if(LIBC_TARGET_ARCHITECTURE_IS_GPU) + # Hermetic tests for GPUs still need to link against __internal__ targets. + set(internal_targets TRUE) + else() + set(internal_targets FALSE) + endif() + get_object_files_for_test( - link_object_files skipped_entrypoints_list ${fq_deps_list}) + link_object_files skipped_entrypoints_list + INTERNAL ${internal_targets} + ${fq_deps_list} + ) + + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Get objects for test ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS link_object_files) + message(STATUS " ${fq_target_name} need object ${dep}") + endforeach() + endif() + endif() + if(skipped_entrypoints_list) if(LIBC_CMAKE_VERBOSE_LOGGING) set(msg "Skipping integration test ${fq_target_name} as it has missing deps: " @@ -672,6 +738,16 @@ function(add_libc_hermetic_test test_name) get_fq_target_name(${test_name}.libc fq_libc_target_name) get_fq_deps_list(fq_deps_list ${HERMETIC_TEST_DEPENDS}) + + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Adding hermetic test ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS fq_deps_list) + message(STATUS " ${fq_target_name} depends on ${dep}") + endforeach() + endif() + endif() + list(APPEND fq_deps_list # Hermetic tests use the platform's startup object. So, their deps also # have to be collected. @@ -700,11 +776,31 @@ function(add_libc_hermetic_test test_name) list(REMOVE_DUPLICATES fq_deps_list) + if(LIBC_TARGET_ARCHITECTURE_IS_GPU) + # Hermetic tests for GPUs still need to link against __internal__ targets. + set(internal_targets TRUE) + else() + set(internal_targets FALSE) + endif() + # TODO: Instead of gathering internal object files from entrypoints, # collect the object files with public names of entrypoints. get_object_files_for_test( - link_object_files skipped_entrypoints_list ${fq_deps_list}) - if(skipped_entrypoints_list) + link_object_files skipped_entrypoints_list + INTERNAL ${internal_targets} + ${fq_deps_list} + ) + + if(SHOW_INTERMEDIATE_OBJECTS) + message(STATUS "Get objects for test ${fq_target_name}") + if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") + foreach(dep IN LISTS link_object_files) + message(STATUS " ${fq_target_name} need object ${dep}") + endforeach() + endif() + endif() + + if(skipped_entrypoints_list) set(msg "Skipping hermetic test ${fq_target_name} as it has missing deps: " "${skipped_entrypoints_list}.") message(STATUS ${msg}) diff --git a/libc/include/errno.h.def b/libc/include/errno.h.def index d8f79dd47a0d1..6002707deae9b 100644 --- a/libc/include/errno.h.def +++ b/libc/include/errno.h.def @@ -44,7 +44,15 @@ #endif #if !defined(__AMDGPU__) && !defined(__NVPTX__) + +#ifdef __cplusplus +extern "C" { + extern thread_local int __llvmlibc_errno; +} +#else extern _Thread_local int __llvmlibc_errno; +#endif // __cplusplus + #define errno __llvmlibc_errno #endif diff --git a/libc/src/errno/CMakeLists.txt b/libc/src/errno/CMakeLists.txt index e8868dc48c5e9..e19285809e245 100644 --- a/libc/src/errno/CMakeLists.txt +++ b/libc/src/errno/CMakeLists.txt @@ -1,9 +1,20 @@ +# If we are in full build mode, we will provide the errno definition ourselves, +# and if we are in overlay mode, we will just re-use the system's errno. +# We are passing LIBC_FULL_BUILD flag in full build mode so that the +# implementation of libc_errno will know if we are in full build mode or not. +set(full_build_flag "") +if(LLVM_LIBC_FULL_BUILD) + set(full_build_flag "-DLIBC_FULL_BUILD") +endif() + add_entrypoint_object( errno SRCS libc_errno.cpp HDRS libc_errno.h # Include this + COMPILE_OPTIONS + ${full_build_flag} DEPENDS libc.include.errno libc.src.__support.common diff --git a/libc/src/errno/libc_errno.cpp b/libc/src/errno/libc_errno.cpp index c8f0bffd0962e..71ea4a27626a5 100644 --- a/libc/src/errno/libc_errno.cpp +++ b/libc/src/errno/libc_errno.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of errno -------------------------------------------===// +//===-- Implementation of libc_errno --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,32 +9,43 @@ #include "src/__support/macros/attributes.h" #include "src/__support/macros/properties/architectures.h" -namespace LIBC_NAMESPACE { +#include "libc_errno.h" #ifdef LIBC_TARGET_ARCH_IS_GPU -struct ErrnoConsumer { - void operator=(int) {} -}; -#endif +// LIBC_THREAD_LOCAL on GPU currently does nothing. So essentially this is just +// a global errno for gpu to use for now. +extern "C" { +LIBC_THREAD_LOCAL int __llvmlibc_gpu_errno; +} + +void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_gpu_errno = a; } +LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_gpu_errno; } + +#elif !defined(LIBC_COPT_PUBLIC_PACKAGING) +// This mode is for unit testing. We just use our internal errno. +LIBC_THREAD_LOCAL int __llvmlibc_internal_errno; + +void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_internal_errno = a; } +LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_internal_errno; } +#elif defined(LIBC_FULL_BUILD) +// This mode is for public libc archive, hermetic, and integration tests. +// In full build mode, we provide the errno storage ourselves. extern "C" { -#ifdef LIBC_COPT_PUBLIC_PACKAGING -// TODO: Declare __llvmlibc_errno only under LIBC_COPT_PUBLIC_PACKAGING and -// __llvmlibc_internal_errno otherwise. -// In overlay mode, this will be an unused thread local variable as libc_errno -// will resolve to errno from the system libc's errno.h. In full build mode -// however, libc_errno will resolve to this thread local variable via the errno -// macro defined in LLVM libc's public errno.h header file. -// TODO: Use a macro to distinguish full build and overlay build which can be -// used to exclude __llvmlibc_errno under overlay build. -#ifdef LIBC_TARGET_ARCH_IS_GPU -ErrnoConsumer __llvmlibc_errno; -#else LIBC_THREAD_LOCAL int __llvmlibc_errno; -#endif // LIBC_TARGET_ARCH_IS_GPU +} + +void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_errno = a; } +LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_errno; } + #else -LIBC_THREAD_LOCAL int __llvmlibc_internal_errno; -#endif -} // extern "C" +// In overlay mode, we simply use the system errno. +#include + +void LIBC_NAMESPACE::Errno::operator=(int a) { errno = a; } +LIBC_NAMESPACE::Errno::operator int() { return errno; } + +#endif // LIBC_FULL_BUILD -} // namespace LIBC_NAMESPACE +// Define the global `libc_errno` instance. +LIBC_NAMESPACE::Errno libc_errno; diff --git a/libc/src/errno/libc_errno.h b/libc/src/errno/libc_errno.h index fbcd1c3395cd1..143aba5992baf 100644 --- a/libc/src/errno/libc_errno.h +++ b/libc/src/errno/libc_errno.h @@ -12,45 +12,34 @@ #include "src/__support/macros/attributes.h" #include "src/__support/macros/properties/architectures.h" +// TODO: Separate just the definition of errno numbers in +// include/llvm-libc-macros/* and only include that instead of the system +// . #include -// If we are targeting the GPU we currently don't support 'errno'. We simply -// consume it. -#ifdef LIBC_TARGET_ARCH_IS_GPU -namespace LIBC_NAMESPACE { -struct ErrnoConsumer { - void operator=(int) {} -}; -} // namespace LIBC_NAMESPACE -#endif - -// All of the libc runtime and test code should use the "libc_errno" macro. They -// should not refer to the "errno" macro directly. -#ifdef LIBC_COPT_PUBLIC_PACKAGING -#ifdef LIBC_TARGET_ARCH_IS_GPU -extern "C" LIBC_NAMESPACE::ErrnoConsumer __llvmlibc_errno; -#define libc_errno __llvmlibc_errno -#else -// This macro will resolve to errno from the errno.h file included above. Under -// full build, this will be LLVM libc's errno. In overlay build, it will be -// system libc's errno. -#define libc_errno errno -#endif -#else -namespace LIBC_NAMESPACE { +// This header is to be consumed by internal implementations, in which all of +// them should refer to `libc_errno` instead of using `errno` directly from +// header. -// TODO: On the GPU build this will be mapped to a single global value. We need -// to ensure that tests are not run with multiple threads that depend on errno -// until we have true 'thread_local' support on the GPU. -extern "C" LIBC_THREAD_LOCAL int __llvmlibc_internal_errno; +// Unit and hermetic tests should: +// - #include "src/errno/libc_errno.h" +// - NOT #include +// - Only use `libc_errno` in the code +// - Depend on libc.src.errno.errno -// TODO: After all of libc/src and libc/test are switched over to use -// libc_errno, this header file will be "shipped" via an add_entrypoint_object -// target. At which point libc_errno, should point to __llvmlibc_internal_errno -// if LIBC_COPT_PUBLIC_PACKAGING is not defined. -#define libc_errno LIBC_NAMESPACE::__llvmlibc_internal_errno +// Integration tests should: +// - NOT #include "src/errno/libc_errno.h" +// - #include +// - Use regular `errno` in the code +// - Still depend on libc.src.errno.errno +namespace LIBC_NAMESPACE { +struct Errno { + void operator=(int); + operator int(); +}; } // namespace LIBC_NAMESPACE -#endif + +extern LIBC_NAMESPACE::Errno libc_errno; #endif // LLVM_LIBC_SRC_ERRNO_LIBC_ERRNO_H diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt index a4d51fb9a11ee..f56649877a1ae 100644 --- a/libc/src/stdlib/CMakeLists.txt +++ b/libc/src/stdlib/CMakeLists.txt @@ -363,6 +363,13 @@ add_entrypoint_object( libc.src.__support.OSUtil.osutil ) +# Tests for nVidia GPUs need to be compiled with -fno-use-cxa-atexit, so we need +# to export the C symbol `atexit` also for __internal__ targets. +set(atexit_compile_options "") +if(LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX) + set(atexit_compile_options "-DLIBC_ATEXIT_EXPORT_C_SYMBOL") +endif() + add_entrypoint_object( atexit SRCS @@ -371,6 +378,8 @@ add_entrypoint_object( atexit.h CXX_STANDARD 20 # For constinit of the atexit callback list. + COMPILE_OPTIONS + ${atexit_compile_options} DEPENDS libc.src.__support.fixedvector libc.src.__support.blockstore diff --git a/libc/src/stdlib/atexit.cpp b/libc/src/stdlib/atexit.cpp index 10dff42b1be92..769c231e09729 100644 --- a/libc/src/stdlib/atexit.cpp +++ b/libc/src/stdlib/atexit.cpp @@ -84,3 +84,13 @@ LLVM_LIBC_FUNCTION(int, atexit, (StdCAtExitCallback * callback)) { } } // namespace LIBC_NAMESPACE + +#if !defined(LIBC_COPT_PUBLIC_PACKAGING) && defined(LIBC_ATEXIT_EXPORT_C_SYMBOL) +// Tests for nVidia GPUs need to be compiled with -fno-use-cxa-atexit, so we +// need to export the C symbol `atexit` also for __internal__ targets. +extern "C" { +int atexit(LIBC_NAMESPACE::StdCAtExitCallback *callback) { + return LIBC_NAMESPACE::atexit(callback); +} +} +#endif // LIBC_ATEXIT_EXPORT_C_SYMBOL diff --git a/libc/startup/gpu/amdgpu/start.cpp b/libc/startup/gpu/amdgpu/start.cpp index 9d7f04c10b488..dcef719d169d0 100644 --- a/libc/startup/gpu/amdgpu/start.cpp +++ b/libc/startup/gpu/amdgpu/start.cpp @@ -15,12 +15,6 @@ extern "C" int main(int argc, char **argv, char **envp); namespace LIBC_NAMESPACE { -// The AMDGPU architecture provides a fixed frequency clock used for obtaining -// real time. However, the frequency of this clock varies between cards and can -// only be obtained via the driver. The loader will set this so we can use it. -extern "C" [[gnu::visibility("protected")]] uint64_t - [[clang::address_space(4)]] __llvm_libc_clock_freq = 0; - extern "C" uintptr_t __init_array_start[]; extern "C" uintptr_t __init_array_end[]; extern "C" uintptr_t __fini_array_start[]; diff --git a/libc/test/IntegrationTest/test.cpp b/libc/test/IntegrationTest/test.cpp index 3bdbe89a3fb62..b5cdaa7fdf924 100644 --- a/libc/test/IntegrationTest/test.cpp +++ b/libc/test/IntegrationTest/test.cpp @@ -9,47 +9,6 @@ #include #include -// Integration tests rely on the following memory functions. This is because the -// compiler code generation can emit calls to them. We want to map the external -// entrypoint to the internal implementation of the function used for testing. -// This is done manually as not all targets support aliases. - -namespace LIBC_NAMESPACE { - -int bcmp(const void *lhs, const void *rhs, size_t count); -void bzero(void *ptr, size_t count); -int memcmp(const void *lhs, const void *rhs, size_t count); -void *memcpy(void *__restrict, const void *__restrict, size_t); -void *memmove(void *dst, const void *src, size_t count); -void *memset(void *ptr, int value, size_t count); -int atexit(void (*func)(void)); - -} // namespace LIBC_NAMESPACE - -extern "C" { - -int bcmp(const void *lhs, const void *rhs, size_t count) { - return LIBC_NAMESPACE::bcmp(lhs, rhs, count); -} -void bzero(void *ptr, size_t count) { LIBC_NAMESPACE::bzero(ptr, count); } -int memcmp(const void *lhs, const void *rhs, size_t count) { - return LIBC_NAMESPACE::memcmp(lhs, rhs, count); -} -void *memcpy(void *__restrict dst, const void *__restrict src, size_t count) { - return LIBC_NAMESPACE::memcpy(dst, src, count); -} -void *memmove(void *dst, const void *src, size_t count) { - return LIBC_NAMESPACE::memmove(dst, src, count); -} -void *memset(void *ptr, int value, size_t count) { - return LIBC_NAMESPACE::memset(ptr, value, count); -} - -// This is needed if the test was compiled with '-fno-use-cxa-atexit'. -int atexit(void (*func)(void)) { return LIBC_NAMESPACE::atexit(func); } - -} // extern "C" - // Integration tests cannot use the SCUDO standalone allocator as SCUDO pulls // various other parts of the libc. Since SCUDO development does not use // LLVM libc build rules, it is very hard to keep track or pull all that SCUDO diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt index 9baa41874a83d..25c3631834087 100644 --- a/libc/test/UnitTest/CMakeLists.txt +++ b/libc/test/UnitTest/CMakeLists.txt @@ -1,3 +1,12 @@ +# When the clock target is available, it will be used inside the test lib +# to indicate the performance of the tests. +set(clock_target "") +if(libc.src.time.clock IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS) + set(clock_target + libc.src.time.clock + libc.include.llvm-libc-macros.time_macros) +endif() + function(add_unittest_framework_library name) cmake_parse_arguments( "TEST_LIB" @@ -26,29 +35,58 @@ function(add_unittest_framework_library name) ${TEST_LIB_SRCS} ${TEST_LIB_HDRS} ) + set_target_properties(${lib} PROPERTIES + TEST_FRAMEWORK TRUE) target_include_directories(${lib} PUBLIC ${LIBC_SOURCE_DIR}) target_compile_options(${lib} PRIVATE -fno-exceptions -fno-rtti) - if(TARGET libc.src.time.clock) - target_compile_definitions(${lib} PRIVATE TARGET_SUPPORTS_CLOCK) + if(clock_target) + message(STATUS " Clock targets: ${clock_target}") + target_compile_definitions(${lib} PRIVATE LIBC_TEST_USE_CLOCK) endif() endforeach() + target_include_directories(${name}.hermetic PRIVATE ${LIBC_BUILD_DIR}/include) target_compile_options(${name}.hermetic PRIVATE ${LIBC_HERMETIC_TEST_COMPILE_OPTIONS} -ffreestanding -nostdinc++) if(TEST_LIB_DEPENDS) - foreach(dep IN LISTS ${TEST_LIB_DEPENDS}) - if(TARGET ${dep}.unit) - add_dependencies(${name}.unit ${dep}.unit) + set(normal_deps "") + set(framework_deps "") + foreach(dep IN LISTS TEST_LIB_DEPENDS) + if(TARGET ${dep}) + get_target_property(dep_is_framework ${dep} TEST_FRAMEWORK) + if(dep_is_framework) + list(APPEND framework_deps ${dep}) + else() + list(APPEND normal_deps ${dep}) + endif() else() - add_dependencies(${name}.unit ${dep}) - endif() - if(TARGET ${dep}.hermetic) - add_dependencies(${name}.hermetic ${dep}.hermetic) - else() - add_dependencies(${name}.hermetic ${dep}) + list(APPEND normal_deps ${dep}) endif() endforeach() + + foreach(dep IN LISTS framework_deps) + target_link_libraries(${name}.unit ${dep}.unit) + target_link_libraries(${name}.hermetic ${dep}.hermetic) + endforeach() + + get_object_files_for_test( + link_object_files skipped_entrypoints_list + INTERNAL TRUE + ${normal_deps} + ) + + target_link_libraries(${name}.unit ${link_object_files}) + if(LIBC_TARGET_ARCHITECTURE_IS_GPU) + target_link_libraries(${name}.hermetic ${link_object_files}) + else() + get_object_files_for_test( + link_object_files skipped_entrypoints_list + INTERNAL FALSE + ${normal_deps} + ) + target_link_libraries(${name}.hermetic ${link_object_files}) + endif() endif() endfunction() @@ -70,6 +108,7 @@ add_unittest_framework_library( libc.src.__support.CPP.type_traits libc.src.__support.OSUtil.osutil libc.src.__support.uint128 + ${clock_target} ) set(libc_death_test_srcs LibcDeathTestExecutors.cpp) @@ -114,6 +153,7 @@ add_unittest_framework_library( libc.src.__support.FPUtil.fpbits_str libc.src.__support.FPUtil.fenv_impl libc.src.__support.FPUtil.rounding_mode + libc.src.errno.errno ) add_unittest_framework_library( diff --git a/libc/test/UnitTest/HermeticTestUtils.cpp b/libc/test/UnitTest/HermeticTestUtils.cpp index 349c182ff2379..145683976268a 100644 --- a/libc/test/UnitTest/HermeticTestUtils.cpp +++ b/libc/test/UnitTest/HermeticTestUtils.cpp @@ -9,18 +9,6 @@ #include #include -namespace LIBC_NAMESPACE { - -int bcmp(const void *lhs, const void *rhs, size_t count); -void bzero(void *ptr, size_t count); -int memcmp(const void *lhs, const void *rhs, size_t count); -void *memcpy(void *__restrict, const void *__restrict, size_t); -void *memmove(void *dst, const void *src, size_t count); -void *memset(void *ptr, int value, size_t count); -int atexit(void (*func)(void)); - -} // namespace LIBC_NAMESPACE - namespace { // Integration tests cannot use the SCUDO standalone allocator as SCUDO pulls @@ -37,31 +25,6 @@ static uint8_t *ptr = memory; extern "C" { -// Hermetic tests rely on the following memory functions. This is because the -// compiler code generation can emit calls to them. We want to map the external -// entrypoint to the internal implementation of the function used for testing. -// This is done manually as not all targets support aliases. - -int bcmp(const void *lhs, const void *rhs, size_t count) { - return LIBC_NAMESPACE::bcmp(lhs, rhs, count); -} -void bzero(void *ptr, size_t count) { LIBC_NAMESPACE::bzero(ptr, count); } -int memcmp(const void *lhs, const void *rhs, size_t count) { - return LIBC_NAMESPACE::memcmp(lhs, rhs, count); -} -void *memcpy(void *__restrict dst, const void *__restrict src, size_t count) { - return LIBC_NAMESPACE::memcpy(dst, src, count); -} -void *memmove(void *dst, const void *src, size_t count) { - return LIBC_NAMESPACE::memmove(dst, src, count); -} -void *memset(void *ptr, int value, size_t count) { - return LIBC_NAMESPACE::memset(ptr, value, count); -} - -// This is needed if the test was compiled with '-fno-use-cxa-atexit'. -int atexit(void (*func)(void)) { return LIBC_NAMESPACE::atexit(func); } - constexpr uint64_t ALIGNMENT = alignof(uintptr_t); void *malloc(size_t s) { diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp index 201b40a178dca..e447a27bbab8c 100644 --- a/libc/test/UnitTest/LibcTest.cpp +++ b/libc/test/UnitTest/LibcTest.cpp @@ -13,16 +13,10 @@ #include "src/__support/UInt128.h" #include "test/UnitTest/TestLogger.h" -#if __STDC_HOSTED__ -#include -#define LIBC_TEST_USE_CLOCK -#elif defined(TARGET_SUPPORTS_CLOCK) -#include - +#ifdef LIBC_TEST_USE_CLOCK +#include "include/llvm-libc-macros/time-macros.h" // CLOCKS_PER_SEC #include "src/time/clock.h" -extern "C" clock_t clock() noexcept { return LIBC_NAMESPACE::clock(); } -#define LIBC_TEST_USE_CLOCK -#endif +#endif // LIBC_TEST_USE_CLOCK namespace LIBC_NAMESPACE { namespace testing { @@ -126,13 +120,17 @@ int Test::runTests(const char *TestFilter) { continue; } tlog << GREEN << "[ RUN ] " << RESET << TestName << '\n'; - [[maybe_unused]] const auto start_time = clock(); +#ifdef LIBC_TEST_USE_CLOCK + const auto start_time = LIBC_NAMESPACE::clock(); +#endif RunContext Ctx; T->SetUp(); T->setContext(&Ctx); T->Run(); T->TearDown(); - [[maybe_unused]] const auto end_time = clock(); +#ifdef LIBC_TEST_USE_CLOCK + const auto end_time = LIBC_NAMESPACE::clock(); +#endif // LIBC_TEST_USE_CLOCK switch (Ctx.status()) { case RunContext::RunResult::Fail: tlog << RED << "[ FAILED ] " << RESET << TestName << '\n'; diff --git a/libc/test/integration/startup/CMakeLists.txt b/libc/test/integration/startup/CMakeLists.txt index fb5d6bc787cc2..154a6d8a6aa47 100644 --- a/libc/test/integration/startup/CMakeLists.txt +++ b/libc/test/integration/startup/CMakeLists.txt @@ -38,7 +38,11 @@ function(add_startup_test target_name) if(ADD_STARTUP_TEST_DEPENDS) get_fq_deps_list(fq_deps_list ${ADD_STARTUP_TEST_DEPENDS}) add_dependencies(${fq_target_name} ${fq_deps_list}) - get_object_files_for_test(link_object_files has_skipped_entrypoint_list ${fq_deps_list}) + get_object_files_for_test( + link_object_files has_skipped_entrypoint_list + INTERNAL TRUE + ${fq_deps_list} + ) target_link_libraries(${fq_target_name} ${link_object_files}) endif() diff --git a/libc/test/src/errno/errno_test.cpp b/libc/test/src/errno/errno_test.cpp index 33185c2bcf6f5..876ebfc0ac269 100644 --- a/libc/test/src/errno/errno_test.cpp +++ b/libc/test/src/errno/errno_test.cpp @@ -12,5 +12,5 @@ TEST(LlvmLibcErrnoTest, Basic) { int test_val = 123; libc_errno = test_val; - ASSERT_EQ(test_val, libc_errno); + ASSERT_ERRNO_EQ(test_val); } diff --git a/libc/test/src/math/differential_testing/CMakeLists.txt b/libc/test/src/math/differential_testing/CMakeLists.txt index 72bc2f8fb16aa..466c53287a5b1 100644 --- a/libc/test/src/math/differential_testing/CMakeLists.txt +++ b/libc/test/src/math/differential_testing/CMakeLists.txt @@ -27,7 +27,10 @@ function(add_diff_binary target_name) get_fq_target_name(${target_name} fq_target_name) get_fq_deps_list(fq_deps_list ${DIFF_DEPENDS}) get_object_files_for_test( - link_object_files skipped_entrypoints_list ${fq_deps_list}) + link_object_files skipped_entrypoints_list + INTERNAL TRUE + ${fq_deps_list} + ) if(skipped_entrypoints_list) if(LIBC_CMAKE_VERBOSE_LOGGING) set(msg "Will not build ${fq_target_name} as it has missing deps: " diff --git a/libc/test/src/stdlib/StrtolTest.h b/libc/test/src/stdlib/StrtolTest.h index 8f1723b038612..50ed4cca3950e 100644 --- a/libc/test/src/stdlib/StrtolTest.h +++ b/libc/test/src/stdlib/StrtolTest.h @@ -331,8 +331,7 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test { ((is_signed_v && sizeof(ReturnT) == 4) ? T_MAX : ReturnT(0xFFFFFFFF))); - ASSERT_EQ(libc_errno, - is_signed_v && sizeof(ReturnT) == 4 ? ERANGE : 0); + ASSERT_ERRNO_EQ(is_signed_v && sizeof(ReturnT) == 4 ? ERANGE : 0); EXPECT_EQ(str_end - max_32_bit_value, ptrdiff_t(10)); const char *negative_max_32_bit_value = "-0xFFFFFFFF"; @@ -341,8 +340,7 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test { ((is_signed_v && sizeof(ReturnT) == 4) ? T_MIN : -ReturnT(0xFFFFFFFF))); - ASSERT_EQ(libc_errno, - is_signed_v && sizeof(ReturnT) == 4 ? ERANGE : 0); + ASSERT_ERRNO_EQ(is_signed_v && sizeof(ReturnT) == 4 ? ERANGE : 0); EXPECT_EQ(str_end - negative_max_32_bit_value, ptrdiff_t(11)); // Max size for signed 32 bit numbers @@ -368,8 +366,7 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test { (is_signed_v || sizeof(ReturnT) < 8 ? T_MAX : ReturnT(0xFFFFFFFFFFFFFFFF))); - ASSERT_EQ(libc_errno, - (is_signed_v || sizeof(ReturnT) < 8 ? ERANGE : 0)); + ASSERT_ERRNO_EQ((is_signed_v || sizeof(ReturnT) < 8 ? ERANGE : 0)); EXPECT_EQ(str_end - max_64_bit_value, ptrdiff_t(18)); // See the end of CleanBase10Decode for an explanation of how this large @@ -381,8 +378,7 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test { (is_signed_v ? T_MIN : (sizeof(ReturnT) < 8 ? T_MAX : -ReturnT(0xFFFFFFFFFFFFFFFF)))); - ASSERT_EQ(libc_errno, - (is_signed_v || sizeof(ReturnT) < 8 ? ERANGE : 0)); + ASSERT_ERRNO_EQ((is_signed_v || sizeof(ReturnT) < 8 ? ERANGE : 0)); EXPECT_EQ(str_end - negative_max_64_bit_value, ptrdiff_t(19)); // Max size for signed 64 bit numbers diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index 6088289532d77..a0c50abcee98d 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -419,7 +419,18 @@ add_libc_test( ) # Tests all implementations that can run on the target CPU. + +# TODO: For GPUs, hermetic tests still need to get linked against +# __internal__ targets. Update this test generator so that hermetic tests +# work on GPUs again. function(add_libc_multi_impl_test name) + if(LIBC_TARGET_ARCHITECTURE_IS_GPU) + # Hermetic tests for GPUs still need to link against __internal__ targets. + set(unit_test_only "UNIT_TEST_ONLY") + else() + set(unit_test_only "") + endif() + get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations) foreach(fq_config_name IN LISTS fq_implementations) get_target_property(required_cpu_features ${fq_config_name} REQUIRE_CPU_FEATURES) @@ -430,6 +441,7 @@ function(add_libc_multi_impl_test name) string(SUBSTRING ${fq_config_name} ${name_loc} -1 target_name) add_libc_test( ${target_name}_test + ${unit_test_only} SUITE libc-string-tests COMPILE_OPTIONS diff --git a/libc/test/src/sys/mman/linux/madvise_test.cpp b/libc/test/src/sys/mman/linux/madvise_test.cpp index 83c73f5454de1..e45cf19f8913e 100644 --- a/libc/test/src/sys/mman/linux/madvise_test.cpp +++ b/libc/test/src/sys/mman/linux/madvise_test.cpp @@ -23,7 +23,7 @@ TEST(LlvmLibcMadviseTest, NoError) { libc_errno = 0; void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - EXPECT_EQ(0, libc_errno); + ASSERT_ERRNO_SUCCESS(); EXPECT_NE(addr, MAP_FAILED); EXPECT_THAT(LIBC_NAMESPACE::madvise(addr, alloc_size, MADV_RANDOM), diff --git a/libc/test/src/sys/mman/linux/mmap_test.cpp b/libc/test/src/sys/mman/linux/mmap_test.cpp index 9b13b8bd8057f..b996f26db8605 100644 --- a/libc/test/src/sys/mman/linux/mmap_test.cpp +++ b/libc/test/src/sys/mman/linux/mmap_test.cpp @@ -22,7 +22,7 @@ TEST(LlvmLibcMMapTest, NoError) { libc_errno = 0; void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - EXPECT_EQ(0, libc_errno); + ASSERT_ERRNO_SUCCESS(); EXPECT_NE(addr, MAP_FAILED); int *array = reinterpret_cast(addr); diff --git a/libc/test/src/sys/mman/linux/mprotect_test.cpp b/libc/test/src/sys/mman/linux/mprotect_test.cpp index 7127f77714d64..96f625984101d 100644 --- a/libc/test/src/sys/mman/linux/mprotect_test.cpp +++ b/libc/test/src/sys/mman/linux/mprotect_test.cpp @@ -24,7 +24,7 @@ TEST(LlvmLibcMProtectTest, NoError) { libc_errno = 0; void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - EXPECT_EQ(0, libc_errno); + ASSERT_ERRNO_SUCCESS(); EXPECT_NE(addr, MAP_FAILED); int *array = reinterpret_cast(addr); diff --git a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp index 59cf01ac74695..d20db69042b7a 100644 --- a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp +++ b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp @@ -23,7 +23,7 @@ TEST(LlvmLibcPosixMadviseTest, NoError) { libc_errno = 0; void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - EXPECT_EQ(0, libc_errno); + ASSERT_ERRNO_SUCCESS(); EXPECT_NE(addr, MAP_FAILED); EXPECT_EQ(LIBC_NAMESPACE::posix_madvise(addr, alloc_size, POSIX_MADV_RANDOM), diff --git a/libc/test/src/time/asctime_r_test.cpp b/libc/test/src/time/asctime_r_test.cpp index 1abaa135350c1..f3aadbb39de4d 100644 --- a/libc/test/src/time/asctime_r_test.cpp +++ b/libc/test/src/time/asctime_r_test.cpp @@ -27,17 +27,17 @@ static inline char *call_asctime_r(struct tm *tm_data, int year, int month, TEST(LlvmLibcAsctimeR, Nullptr) { char *result; result = LIBC_NAMESPACE::asctime_r(nullptr, nullptr); - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); ASSERT_STREQ(nullptr, result); char buffer[TimeConstants::ASCTIME_BUFFER_SIZE]; result = LIBC_NAMESPACE::asctime_r(nullptr, buffer); - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); ASSERT_STREQ(nullptr, result); struct tm tm_data; result = LIBC_NAMESPACE::asctime_r(&tm_data, nullptr); - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); ASSERT_STREQ(nullptr, result); } diff --git a/libc/test/src/time/asctime_test.cpp b/libc/test/src/time/asctime_test.cpp index 4b5ceb596aa46..169a7463a3037 100644 --- a/libc/test/src/time/asctime_test.cpp +++ b/libc/test/src/time/asctime_test.cpp @@ -22,7 +22,7 @@ static inline char *call_asctime(struct tm *tm_data, int year, int month, TEST(LlvmLibcAsctime, Nullptr) { char *result; result = LIBC_NAMESPACE::asctime(nullptr); - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); ASSERT_STREQ(nullptr, result); } @@ -40,7 +40,7 @@ TEST(LlvmLibcAsctime, InvalidWday) { 0, // sec -1, // wday 0); // yday - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); // Test with wday = 7. call_asctime(&tm_data, @@ -52,7 +52,7 @@ TEST(LlvmLibcAsctime, InvalidWday) { 0, // sec 7, // wday 0); // yday - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); } // Months are from January to December. Test passing invalid value in month. @@ -69,7 +69,7 @@ TEST(LlvmLibcAsctime, InvalidMonth) { 0, // sec 4, // wday 0); // yday - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); // Test with month = 13. call_asctime(&tm_data, @@ -81,7 +81,7 @@ TEST(LlvmLibcAsctime, InvalidMonth) { 0, // sec 4, // wday 0); // yday - ASSERT_EQ(EINVAL, libc_errno); + ASSERT_ERRNO_EQ(EINVAL); } TEST(LlvmLibcAsctime, ValidWeekdays) { @@ -209,6 +209,6 @@ TEST(LlvmLibcAsctime, Max64BitYear) { 50, // sec 2, // wday 50); // yday - ASSERT_EQ(EOVERFLOW, libc_errno); + ASSERT_ERRNO_EQ(EOVERFLOW); ASSERT_STREQ(nullptr, result); } diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt index 8b9a8db374dd4..059cff1cdc0c8 100644 --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -430,9 +430,11 @@ add_libc_unittest( libc.src.unistd.sysconf ) +# getopt_test requires access to set_getopt_state which is only available for +# __internal__ targets, hence the use of UNIT_TEST_ONLY. add_libc_test( getopt_test - HERMETIC_TEST_ONLY # Uses libc's own stderr + UNIT_TEST_ONLY SUITE libc_unistd_unittests SRCS