Skip to content

[libc] Use LIBC_COPT_PUBLIC_PACKAGING for hermetic and integration tests. #79319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 112 additions & 16 deletions libc/cmake/modules/LLVMLibCTestRules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,32 @@
# Usage:
# get_object_files_for_test(<result var>
# <skipped_entrypoints_var>
# INTERNAL <internal>
# <target0> [<target1> ...])
#
# The list of object files is collected in <result_var>.
# If skipped entrypoints were found, then <skipped_entrypoints_var> is
# 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)
Expand All @@ -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")
Expand All @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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.
Expand All @@ -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()
Expand Down Expand Up @@ -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: "
Expand Down Expand Up @@ -493,13 +530,24 @@ 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.
libc.startup.${LIBC_TARGET_OS}.crt1
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
Expand All @@ -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: "
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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})
Expand Down
8 changes: 8 additions & 0 deletions libc/include/errno.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@
#endif

#if !defined(__AMDGPU__) && !defined(__NVPTX__)

#ifdef __cplusplus
extern "C" {
extern thread_local int __llvmlibc_errno;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc/src/errno/libc_errno.cpp line 18 doesn't use indentation here; I think we should remove it here, too.

}
#else
extern _Thread_local int __llvmlibc_errno;
#endif // __cplusplus

#define errno __llvmlibc_errno
#endif

Expand Down
11 changes: 11 additions & 0 deletions libc/src/errno/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
57 changes: 34 additions & 23 deletions libc/src/errno/libc_errno.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 <errno.h>

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;
Loading