Index: libc/config/linux/x86_64/entrypoints.txt =================================================================== --- libc/config/linux/x86_64/entrypoints.txt +++ libc/config/linux/x86_64/entrypoints.txt @@ -216,6 +216,8 @@ # stdlib.h entrypoints libc.src.stdlib._Exit # libc.src.stdlib.abort + libc.src.stdlib.atexit + libc.src.stdlib.exit # signal.h entrypoints # TODO: Enable signal.h entrypoints after fixing signal.h Index: libc/src/stdlib/CMakeLists.txt =================================================================== --- libc/src/stdlib/CMakeLists.txt +++ libc/src/stdlib/CMakeLists.txt @@ -248,6 +248,34 @@ .${LIBC_TARGET_OS}._Exit ) +if(LLVM_LIBC_INCLUDE_SCUDO OR NOT LLVM_LIBC_FULL_BUILD) + set(vector_dep libc.src.__support.CPP.vector) + add_compile_definitions(LLVM_LIBC_HAS_MALLOC=1) +else() + set(vector_dep "") +endif() + +add_entrypoint_object( + atexit + SRCS + atexit.cpp + HDRS + atexit.h + DEPENDS + ${vector_dep} +) + +add_entrypoint_object( + exit + SRCS + exit.cpp + HDRS + exit.h + DEPENDS + ._Exit + .atexit +) + # add_entrypoint_object( # abort # ALIAS Index: libc/src/stdlib/atexit.h =================================================================== --- /dev/null +++ libc/src/stdlib/atexit.h @@ -0,0 +1,18 @@ +//===-- Implementation header for atexit ------------------------*- C++ -*-===// +// +// 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 LLVM_LIBC_SRC_STDLIB_ATEXIT_H +#define LLVM_LIBC_SRC_STDLIB_ATEXIT_H + +namespace __llvm_libc { + +void atexit(void (*function)()); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDLIB_ATEXIT_H Index: libc/src/stdlib/atexit.cpp =================================================================== --- /dev/null +++ libc/src/stdlib/atexit.cpp @@ -0,0 +1,58 @@ +//===-- Implementation of atexit ------------------------------------------===// +// +// 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 "src/stdlib/atexit.h" +#include "src/__support/common.h" + +#ifdef LLVM_LIBC_HAS_MALLOC +#include "src/__support/CPP/vector.h" +#endif + +namespace __llvm_libc { + +namespace { + +// TOOD should we make cpp::vector like llvm::SmallVector where it will +// allocate at least N before needing dynamic allocation? +#ifdef LLVM_LIBC_HAS_MALLOC +static cpp::vector handlers; +#else +struct { + // POSIX requries at least 32 handlers be possible. + void (*arr[32])(void); + unsigned arr_size = 0; + + unsigned size() const { return arr_size; } + + decltype(*arr) operator[](unsigned i) { return arr[i]; } + + void push_back(void (*func)()) { + if (arr_size == 32) + return; + arr[arr_size++] = func; + } +} handlers; +#endif + +} // namespace + +namespace internal { + +void call_exit_handlers() { + // TODO: implement rbegin() + rend() for cpp::vector + for (int i = handlers.size() - 1; i >= 0; i--) + handlers[i](); +} + +} // namespace internal + +LLVM_LIBC_FUNCTION(void, atexit, (void (*function)(void))) { + handlers.push_back(function); +} + +} // namespace __llvm_libc Index: libc/src/stdlib/exit.h =================================================================== --- /dev/null +++ libc/src/stdlib/exit.h @@ -0,0 +1,20 @@ +//===-- Implementation header for exit --------------------------*- C++ -*-===// +// +// 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 + +#ifndef LLVM_LIBC_SRC_STDLIB_EXIT_H +#define LLVM_LIBC_SRC_STDLIB_EXIT_H + +namespace __llvm_libc { + +void exit(int status); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDLIB_EXIT_H Index: libc/src/stdlib/exit.cpp =================================================================== --- /dev/null +++ libc/src/stdlib/exit.cpp @@ -0,0 +1,24 @@ +//===-- Implementation of exit --------------------------------------------===// +// +// 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 "src/stdlib/exit.h" +#include "src/__support/common.h" +#include "src/stdlib/_Exit.h" + +namespace __llvm_libc { + +namespace internal { +void call_exit_handlers(); +} + +LLVM_LIBC_FUNCTION(void, exit, (int status)) { + internal::call_exit_handlers(); + _Exit(status); +} + +} // namespace __llvm_libc Index: libc/test/src/stdlib/CMakeLists.txt =================================================================== --- libc/test/src/stdlib/CMakeLists.txt +++ libc/test/src/stdlib/CMakeLists.txt @@ -110,35 +110,6 @@ libc.src.stdlib.strtoull ) -if(NOT LLVM_LIBC_FULL_BUILD) - return() -endif() - -add_libc_unittest( - _Exit_test - SUITE - libc_stdlib_unittests - SRCS - _Exit_test.cpp - DEPENDS - libc.include.stdlib - libc.src.stdlib._Exit -) - -# add_libc_unittest( -# abort_test -# SUITE -# libc_stdlib_unittests -# SRCS -# abort_test.cpp -# DEPENDS -# libc.include.stdlib -# libc.include.signal -# libc.src.stdlib.abort -# libc.src.stdlib._Exit -# libc.src.signal.raise -# ) - add_libc_unittest( abs_test SUITE @@ -229,3 +200,51 @@ libc.include.stdlib libc.src.stdlib.qsort ) + +if(NOT LLVM_LIBC_FULL_BUILD) + return() +endif() + +add_libc_unittest( + _Exit_test + SUITE + libc_stdlib_unittests + SRCS + _Exit_test.cpp + DEPENDS + libc.include.stdlib + libc.src.stdlib._Exit + libc.src.stdlib.exit +) + +if(LLVM_LIBC_INCLUDE_SCUDO OR NOT LLVM_LIBC_FULL_BUILD) + add_compile_definitions(LLVM_LIBC_HAS_MALLOC=1) +endif() + +add_libc_unittest( + atexit_test + SUITE + libc_stdlib_unittests + SRCS + atexit_test.cpp + DEPENDS + libc.include.stdlib + libc.src.stdlib._Exit + libc.src.stdlib.exit + libc.src.stdlib.atexit + libc.src.__support.CPP.standalone_cpp +) + +# add_libc_unittest( +# abort_test +# SUITE +# libc_stdlib_unittests +# SRCS +# abort_test.cpp +# DEPENDS +# libc.include.stdlib +# libc.include.signal +# libc.src.stdlib.abort +# libc.src.stdlib._Exit +# libc.src.signal.raise +# ) Index: libc/test/src/stdlib/_Exit_test.cpp =================================================================== --- libc/test/src/stdlib/_Exit_test.cpp +++ libc/test/src/stdlib/_Exit_test.cpp @@ -8,9 +8,13 @@ #include "include/stdlib.h" #include "src/stdlib/_Exit.h" +#include "src/stdlib/exit.h" #include "utils/UnitTest/Test.h" TEST(LlvmLibcStdlib, _Exit) { EXPECT_EXITS([] { __llvm_libc::_Exit(1); }, 1); EXPECT_EXITS([] { __llvm_libc::_Exit(65); }, 65); + + EXPECT_EXITS([] { __llvm_libc::exit(1); }, 1); + EXPECT_EXITS([] { __llvm_libc::exit(65); }, 65); } Index: libc/test/src/stdlib/atexit_test.cpp =================================================================== --- /dev/null +++ libc/test/src/stdlib/atexit_test.cpp @@ -0,0 +1,81 @@ +//===-- Unittests for atexit ----------------------------------------------===// +// +// 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 "src/__support/CPP/Array.h" +#include "src/__support/CPP/Utility.h" +#include "src/stdlib/atexit.h" +#include "src/stdlib/exit.h" +#include "utils/UnitTest/Test.h" + +static int a; +TEST(LlvmLibcAtExit, Basic) { + // In case tests ever run multiple times. + a = 0; + + auto test = [] { + __llvm_libc::atexit(+[] { + if (a != 1) + __builtin_trap(); + }); + __llvm_libc::atexit(+[] { a++; }); + + __llvm_libc::exit(0); + }; + EXPECT_EXITS(test, 0); +} + +TEST(LlvmLibcAtExit, AtExitCallsSysExit) { + auto test = [] { + __llvm_libc::atexit(+[] { _Exit(1); }); + __llvm_libc::exit(0); + }; + EXPECT_EXITS(test, 1); +} + +static int size; +static __llvm_libc::cpp::Array arr; + +template +void register_atexit_handlers(__llvm_libc::cpp::IntegerSequence) { + (__llvm_libc::atexit(+[] { arr[size++] = Ts; }), ...); +} + +template constexpr auto getTest() { + return [] { + __llvm_libc::atexit(+[] { + if (size != count) + __builtin_trap(); + for (int i = 0; i < count; i++) + if (arr[i] != count - 1 - i) + __builtin_trap(); + }); + register_atexit_handlers( + __llvm_libc::cpp::MakeIntegerSequence{}); + __llvm_libc::exit(0); + }; +} + +TEST(LlvmLibcAtExit, ReverseOrder) { + // In case tests ever run multiple times. + size = 0; + + auto test = getTest<32>(); + EXPECT_EXITS(test, 0); +} + +#ifdef LLVM_LIBC_HAS_MALLOC + +TEST(LlvmLibcAtExit, Many) { + // In case tests ever run multiple times. + size = 0; + + auto test = getTest<256>(); + EXPECT_EXITS(test, 0); +} + +#endif