107 changes: 97 additions & 10 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.ctype.tolower
libc.src.ctype.toupper

# dlfcn.h entrypoints
libc.src.dlfcn.dlclose
libc.src.dlfcn.dlerror
libc.src.dlfcn.dlopen
libc.src.dlfcn.dlsym

# errno.h entrypoints
libc.src.errno.errno

Expand Down Expand Up @@ -52,6 +58,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.string.mempcpy
libc.src.string.memrchr
libc.src.string.memset
libc.src.string.memset_explicit
libc.src.string.rindex
libc.src.string.stpcpy
libc.src.string.stpncpy
Expand Down Expand Up @@ -180,6 +187,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.qsort_r
libc.src.stdlib.rand
libc.src.stdlib.srand
libc.src.stdlib.strfromd
libc.src.stdlib.strfromf
libc.src.stdlib.strfroml
libc.src.stdlib.strtod
libc.src.stdlib.strtof
libc.src.stdlib.strtol
Expand All @@ -197,6 +207,7 @@ set(TARGET_LIBC_ENTRYPOINTS

# stdio.h entrypoints
libc.src.stdio.fdopen
libc.src.stdio.fileno
libc.src.stdio.fprintf
libc.src.stdio.fscanf
libc.src.stdio.printf
Expand All @@ -211,6 +222,14 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdio.vsnprintf
libc.src.stdio.vsprintf

# sys/epoll.h entrypoints
libc.src.sys.epoll.epoll_create
libc.src.sys.epoll.epoll_create1
libc.src.sys.epoll.epoll_ctl
libc.src.sys.epoll.epoll_pwait
libc.src.sys.epoll.epoll_wait
libc.src.sys.epoll.epoll_pwait2

# sys/mman.h entrypoints
libc.src.sys.mman.madvise
libc.src.sys.mman.mincore
Expand Down Expand Up @@ -261,12 +280,6 @@ set(TARGET_LIBC_ENTRYPOINTS
# sys/auxv.h entrypoints
libc.src.sys.auxv.getauxval

# sys/epoll.h entrypoints
# Disabled due to epoll_wait syscalls not being available on this platform.
# libc.src.sys.epoll.epoll_wait
# libc.src.sys.epoll.epoll_pwait
# libc.src.sys.epoll.epoll_pwait2

# termios.h entrypoints
libc.src.termios.cfgetispeed
libc.src.termios.cfgetospeed
Expand Down Expand Up @@ -296,12 +309,14 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.geteuid
libc.src.unistd.getpid
libc.src.unistd.getppid
libc.src.unistd.gettid
libc.src.unistd.getuid
libc.src.unistd.isatty
libc.src.unistd.link
libc.src.unistd.linkat
libc.src.unistd.lseek
libc.src.unistd.pathconf
libc.src.unistd.pipe
libc.src.unistd.pread
libc.src.unistd.pwrite
libc.src.unistd.read
Expand Down Expand Up @@ -347,6 +362,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.canonicalize
libc.src.math.canonicalizef
libc.src.math.canonicalizel
libc.src.math.cbrt
libc.src.math.cbrtf
libc.src.math.ceil
Expand All @@ -365,6 +383,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.exp10f
libc.src.math.exp2
libc.src.math.exp2f
libc.src.math.exp2m1f
libc.src.math.expf
libc.src.math.expm1
libc.src.math.expm1f
Expand Down Expand Up @@ -492,6 +511,9 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.rintf
libc.src.math.rintl
libc.src.math.round
libc.src.math.roundeven
libc.src.math.roundevenf
libc.src.math.roundevenl
libc.src.math.roundf
libc.src.math.roundl
libc.src.math.scalbn
Expand Down Expand Up @@ -523,8 +545,10 @@ set(TARGET_LIBM_ENTRYPOINTS
if(LIBC_TYPES_HAS_FLOAT128)
list(APPEND TARGET_LIBM_ENTRYPOINTS
# math.h C23 _Float128 entrypoints
libc.src.math.canonicalizef128
libc.src.math.ceilf128
libc.src.math.copysignf128
libc.src.math.dmulf128
libc.src.math.fabsf128
libc.src.math.fdimf128
libc.src.math.floorf128
Expand All @@ -539,6 +563,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.fminimum_numf128
libc.src.math.fminimumf128
libc.src.math.fmodf128
libc.src.math.fmulf128
libc.src.math.frexpf128
libc.src.math.fromfpf128
libc.src.math.fromfpxf128
Expand All @@ -556,7 +581,9 @@ if(LIBC_TYPES_HAS_FLOAT128)
libc.src.math.nextafterf128
libc.src.math.nextdownf128
libc.src.math.nextupf128
libc.src.math.remquof128
libc.src.math.rintf128
libc.src.math.roundevenf128
libc.src.math.roundf128
libc.src.math.scalbnf128
libc.src.math.sqrtf128
Expand All @@ -566,14 +593,47 @@ if(LIBC_TYPES_HAS_FLOAT128)
)
endif()

if(LIBC_COMPILER_HAS_FIXED_POINT)
list(APPEND TARGET_LIBM_ENTRYPOINTS
# stdfix.h _Fract and _Accum entrypoints
libc.src.stdfix.abshk
libc.src.stdfix.abshr
libc.src.stdfix.absk
libc.src.stdfix.abslk
libc.src.stdfix.abslr
libc.src.stdfix.absr
libc.src.stdfix.exphk
libc.src.stdfix.expk
libc.src.stdfix.roundhk
libc.src.stdfix.roundhr
libc.src.stdfix.roundk
libc.src.stdfix.roundlk
libc.src.stdfix.roundlr
libc.src.stdfix.roundr
libc.src.stdfix.rounduhk
libc.src.stdfix.rounduhr
libc.src.stdfix.rounduk
libc.src.stdfix.roundulk
libc.src.stdfix.roundulr
libc.src.stdfix.roundur
libc.src.stdfix.sqrtuhk
libc.src.stdfix.sqrtuhr
libc.src.stdfix.sqrtuk
libc.src.stdfix.sqrtur
libc.src.stdfix.sqrtulr
libc.src.stdfix.uhksqrtus
libc.src.stdfix.uksqrtui
)
endif()

if(LLVM_LIBC_FULL_BUILD)
list(APPEND TARGET_LIBC_ENTRYPOINTS
# compiler entrypoints (no corresponding header)
libc.src.compiler.__stack_chk_fail

# assert.h entrypoints
libc.src.assert.__assert_fail

# compiler entrypoints (no corresponding header)
libc.src.compiler.__stack_chk_fail

# dirent.h entrypoints
libc.src.dirent.closedir
libc.src.dirent.dirfd
Expand All @@ -598,6 +658,12 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_attr_setguardsize
libc.src.pthread.pthread_attr_setstack
libc.src.pthread.pthread_attr_setstacksize
libc.src.pthread.pthread_condattr_destroy
libc.src.pthread.pthread_condattr_getclock
libc.src.pthread.pthread_condattr_getpshared
libc.src.pthread.pthread_condattr_init
libc.src.pthread.pthread_condattr_setclock
libc.src.pthread.pthread_condattr_setpshared
libc.src.pthread.pthread_create
libc.src.pthread.pthread_detach
libc.src.pthread.pthread_equal
Expand All @@ -620,6 +686,21 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_mutexattr_setrobust
libc.src.pthread.pthread_mutexattr_settype
libc.src.pthread.pthread_once
libc.src.pthread.pthread_rwlock_destroy
libc.src.pthread.pthread_rwlock_init
libc.src.pthread.pthread_rwlock_rdlock
libc.src.pthread.pthread_rwlock_timedrdlock
libc.src.pthread.pthread_rwlock_timedwrlock
libc.src.pthread.pthread_rwlock_tryrdlock
libc.src.pthread.pthread_rwlock_trywrlock
libc.src.pthread.pthread_rwlock_unlock
libc.src.pthread.pthread_rwlock_wrlock
libc.src.pthread.pthread_rwlockattr_destroy
libc.src.pthread.pthread_rwlockattr_getkind_np
libc.src.pthread.pthread_rwlockattr_getpshared
libc.src.pthread.pthread_rwlockattr_init
libc.src.pthread.pthread_rwlockattr_setkind_np
libc.src.pthread.pthread_rwlockattr_setpshared
libc.src.pthread.pthread_self
libc.src.pthread.pthread_setname_np
libc.src.pthread.pthread_setspecific
Expand All @@ -643,7 +724,6 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.fgetc
libc.src.stdio.fgetc_unlocked
libc.src.stdio.fgets
libc.src.stdio.fileno
libc.src.stdio.flockfile
libc.src.stdio.fopen
libc.src.stdio.fopencookie
Expand All @@ -652,7 +732,9 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.fread
libc.src.stdio.fread_unlocked
libc.src.stdio.fseek
libc.src.stdio.fseeko
libc.src.stdio.ftell
libc.src.stdio.ftello
libc.src.stdio.funlockfile
libc.src.stdio.fwrite
libc.src.stdio.fwrite_unlocked
Expand All @@ -673,9 +755,11 @@ if(LLVM_LIBC_FULL_BUILD)
# stdlib.h entrypoints
libc.src.stdlib._Exit
libc.src.stdlib.abort
libc.src.stdlib.at_quick_exit
libc.src.stdlib.atexit
libc.src.stdlib.exit
libc.src.stdlib.getenv
libc.src.stdlib.quick_exit

# signal.h entrypoints
libc.src.signal.kill
Expand Down Expand Up @@ -757,6 +841,9 @@ if(LLVM_LIBC_FULL_BUILD)

# sys/select.h entrypoints
libc.src.sys.select.select

# sys/socket.h entrypoints
libc.src.sys.socket.socket
)
endif()

Expand Down
5 changes: 2 additions & 3 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.epoll.epoll_ctl
libc.src.sys.epoll.epoll_pwait
libc.src.sys.epoll.epoll_wait
# TODO: Need to check if pwait2 is available before providing.
# https://github.com/llvm/llvm-project/issues/80060
# libc.src.sys.epoll.epoll_pwait2
libc.src.sys.epoll.epoll_pwait2

# sys/mman.h entrypoints
libc.src.sys.mman.madvise
Expand Down Expand Up @@ -315,6 +313,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.unistd.geteuid
libc.src.unistd.getpid
libc.src.unistd.getppid
libc.src.unistd.gettid
libc.src.unistd.getuid
libc.src.unistd.isatty
libc.src.unistd.link
Expand Down
3 changes: 3 additions & 0 deletions libc/docs/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ to learn about the defaults for your platform and target.
* **"string" options**
- ``LIBC_CONF_MEMSET_X86_USE_SOFTWARE_PREFETCHING``: Inserts prefetch for write instructions (PREFETCHW) for memset on x86 to recover performance when hardware prefetcher is disabled.
- ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen.
* **"unistd" options**
- ``LIBC_CONF_ENABLE_PID_CACHE``: Enable caching mechanism for getpid to avoid syscall (default to true). Please refer to Undefined Behavior documentation for implications.
- ``LIBC_CONF_ENABLE_TID_CACHE``: Enable caching mechanism for gettid to avoid syscall (only effective in fullbuild mode, default to true). Please refer to Undefined Behavior documentation for implications.
23 changes: 23 additions & 0 deletions libc/docs/dev/undefined_behavior.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,26 @@ direction in this case.
Non-const Constant Return Values
--------------------------------
Some libc functions, like ``dlerror()``, return ``char *`` instead of ``const char *`` and then tell the caller they promise not to to modify this value. Any modification of this value is undefined behavior.

Cached ``getpid/gettid``
------------------------
Since version ``2.25``, glibc removes its cache mechanism for ``getpid/gettid``
(See the history section in https://man7.org/linux/man-pages/man2/getpid.2.html).
LLVM's libc still implements the cache as it is useful for fast deadlock detection.
The cache mechanism is also implemented in MUSL and bionic. The tid/pid cache can
be disabled by setting ``LIBC_CONF_ENABLE_TID_CACHE`` and ``LIBC_CONF_ENABLE_PID_CACHE``
to ``false`` respectively.

Unwrapped ``SYS_clone/SYS_fork/SYS_vfork``
------------------------------------------
It is highly discouraged to use unwrapped ``SYS_clone/SYS_fork/SYS_vfork``.
First, calling such syscalls without provided libc wrappers ignores
all the ``pthread_atfork`` entries as libc can no longer detect the ``fork``.
Second, libc relies on the ``fork/clone`` wrappers to correctly maintain cache for
process id and thread id, and other important process-specific states such as the list
of robust mutexes. Third, even if the user is to call ``exec*`` functions immediately,
there can still be other unexpected issues. For instance, there can be signal handlers
inherited from parent process triggered inside the instruction window between ``fork``
and ``exec*``. As libc failed to maintain its internal states correctly, even though the
functions used inside the signal handlers are marked as ``async-signal-safe`` (such as
``getpid``), they will still return wrong values or lead to other even worse situations.
11 changes: 4 additions & 7 deletions libc/include/assert.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,19 @@
// This file may be usefully included multiple times to change assert()'s
// definition based on NDEBUG.


#undef assert
#ifdef NDEBUG
#define assert(e) (void)0
#else

#ifndef __cplusplus
#undef static_assert
#define static_assert _Static_assert
#endif

#undef assert
#ifdef NDEBUG
#define assert(e) (void)0
#else
#ifdef __cplusplus
extern "C"
#endif
_Noreturn void __assert_fail(const char *, const char *, unsigned, const char *) __NOEXCEPT;

#define assert(e) \
((e) ? (void)0 : __assert_fail(#e, __FILE__, __LINE__, __PRETTY_FUNCTION__))
#endif
Expand Down
15 changes: 5 additions & 10 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<PidT>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"gettid",
RetValSpec<PidT>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"getuid",
RetValSpec<UidT>,
Expand Down Expand Up @@ -601,16 +606,6 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"getpid",
RetValSpec<IntType>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"getppid",
RetValSpec<IntType>,
[ArgSpec<VoidType>]
>,
FunctionSpec<
"link",
RetValSpec<IntType>,
Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/File/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ int File::ungetc_unlocked(int c) {
return c;
}

ErrorOr<int> File::seek(long offset, int whence) {
ErrorOr<int> File::seek(off_t offset, int whence) {
FileLock lock(this);
if (prev_op == FileOp::WRITE && pos > 0) {

Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/File/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class File {
return read_unlocked(data, len);
}

ErrorOr<int> seek(long offset, int whence);
ErrorOr<int> seek(off_t offset, int whence);

ErrorOr<off_t> tell();

Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/HashTable/randomness.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ LIBC_INLINE uint64_t next_random_seed() {
entropy[1] = reinterpret_cast<uint64_t>(&state);
#if defined(LIBC_HASHTABLE_USE_GETRANDOM)
int errno_backup = libc_errno;
ssize_t count = sizeof(entropy);
size_t count = sizeof(entropy);
uint8_t *buffer = reinterpret_cast<uint8_t *>(entropy);
while (count > 0) {
ssize_t len = getrandom(buffer, count, 0);
Expand Down
17 changes: 17 additions & 0 deletions libc/src/__support/OSUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,20 @@ add_object_library(
DEPENDS
${target_os_util}
)

if (LIBC_CONF_ENABLE_PID_CACHE)
set(libc_copt_enable_pid_cache 1)
else()
set(libc_copt_enable_pid_cache 0)
endif()

if(TARGET libc.src.__support.OSUtil.${LIBC_TARGET_OS}.pid)
add_object_library(
pid
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.pid
COMPILE_OPTIONS
-DLIBC_COPT_ENABLE_PID_CACHE=${libc_copt_enable_pid_cache}
)
endif()
13 changes: 13 additions & 0 deletions libc/src/__support/OSUtil/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,16 @@ add_object_library(
libc.hdr.types.struct_f_owner_ex
libc.hdr.types.off_t
)

add_object_library(
pid
SRCS
pid.cpp
HDRS
../pid.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.hdr.types.pid_t
libc.include.sys_syscall
)
20 changes: 20 additions & 0 deletions libc/src/__support/OSUtil/linux/pid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===------------ pid_t utilities implementation ----------------*- 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 "src/__support/OSUtil/pid.h"
#include "src/__support/OSUtil/syscall.h"
#include <sys/syscall.h>

namespace LIBC_NAMESPACE_DECL {

pid_t ProcessIdentity::cache = -1;
pid_t ProcessIdentity::get_uncached() {
return syscall_impl<pid_t>(SYS_getpid);
}

} // namespace LIBC_NAMESPACE_DECL
41 changes: 41 additions & 0 deletions libc/src/__support/OSUtil/pid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===------------ pid_t utilities -------------------------------*- 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___SUPPORT_OSUTIL_PID_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H
#include "hdr/types/pid_t.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/optimization.h"

#ifndef LIBC_COPT_ENABLE_PID_CACHE
#define LIBC_COPT_ENABLE_PID_CACHE 1
#endif

namespace LIBC_NAMESPACE_DECL {

class ProcessIdentity {
static LIBC_INLINE_VAR thread_local bool fork_inflight = true;
static pid_t cache;
static pid_t get_uncached();

public:
LIBC_INLINE static void start_fork() { fork_inflight = true; }
LIBC_INLINE static void end_fork() { fork_inflight = false; }
LIBC_INLINE static void refresh_cache() { cache = get_uncached(); }
LIBC_INLINE static pid_t get() {
#if LIBC_COPT_ENABLE_PID_CACHE
if (LIBC_LIKELY(!fork_inflight))
return cache;
#endif
return get_uncached();
}
};

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_PID_H
27 changes: 27 additions & 0 deletions libc/src/__support/threads/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex)
)
endif()

if (LIBC_CONF_ENABLE_TID_CACHE)
set(libc_copt_enable_tid_cache 1)
else()
set(libc_copt_enable_tid_cache 0)
endif()

add_header_library(
thread_common
HDRS
Expand All @@ -54,6 +60,9 @@ add_header_library(
libc.src.__support.CPP.optional
libc.src.__support.CPP.string_view
libc.src.__support.CPP.stringstream
libc.hdr.types.pid_t
COMPILE_OPTIONS
-DLIBC_COPT_ENABLE_TID_CACHE=${libc_copt_enable_tid_cache}
)

if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
Expand Down Expand Up @@ -89,3 +98,21 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar)
.${LIBC_TARGET_OS}.CndVar
)
endif()

set(tid_dep)
if (LLVM_LIBC_FULL_BUILD)
list(APPEND tid_dep libc.src.__support.thread)
else()
list(APPEND tid_dep libc.src.__support.OSUtil.osutil)
list(APPEND tid_dep libc.include.sys_syscall)
endif()

add_header_library(
tid
HDRS
tid.h
DEPENDS
libc.src.__support.common
libc.hdr.types.pid_t
${tid_dep}
)
1 change: 1 addition & 0 deletions libc/src/__support/threads/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_header_library(
libc.src.__support.common
libc.src.__support.OSUtil.osutil
libc.src.__support.CPP.limits
libc.src.__support.threads.tid
COMPILE_OPTIONS
-DLIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT=${LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT}
${monotonicity_flags}
Expand Down
9 changes: 4 additions & 5 deletions libc/src/__support/threads/linux/rwlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/threads/linux/raw_mutex.h"
#include "src/__support/threads/sleep.h"
#include "src/__support/threads/tid.h"

#ifndef LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT
#define LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT 100
Expand Down Expand Up @@ -336,8 +337,6 @@ class RwLock {
LIBC_INLINE Role get_preference() const {
return static_cast<Role>(preference);
}
// TODO: use cached thread id once implemented.
LIBC_INLINE static pid_t gettid() { return syscall_impl<pid_t>(SYS_gettid); }

template <Role role> LIBC_INLINE LockResult try_lock(RwState &old) {
if constexpr (role == Role::Reader) {
Expand All @@ -359,7 +358,7 @@ class RwLock {
if (LIBC_LIKELY(old.compare_exchange_weak_with(
state, old.set_writer_bit(), cpp::MemoryOrder::ACQUIRE,
cpp::MemoryOrder::RELAXED))) {
writer_tid.store(gettid(), cpp::MemoryOrder::RELAXED);
writer_tid.store(gettid_inline(), cpp::MemoryOrder::RELAXED);
return LockResult::Success;
}
// Notice that old is updated by the compare_exchange_weak_with
Expand Down Expand Up @@ -394,7 +393,7 @@ class RwLock {
unsigned spin_count = LIBC_COPT_RWLOCK_DEFAULT_SPIN_COUNT) {
// Phase 1: deadlock detection.
// A deadlock happens if this is a RAW/WAW lock in the same thread.
if (writer_tid.load(cpp::MemoryOrder::RELAXED) == gettid())
if (writer_tid.load(cpp::MemoryOrder::RELAXED) == gettid_inline())
return LockResult::Deadlock;

#if LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
Expand Down Expand Up @@ -520,7 +519,7 @@ class RwLock {
if (old.has_active_writer()) {
// The lock is held by a writer.
// Check if we are the owner of the lock.
if (writer_tid.load(cpp::MemoryOrder::RELAXED) != gettid())
if (writer_tid.load(cpp::MemoryOrder::RELAXED) != gettid_inline())
return LockResult::PermissionDenied;
// clear writer tid.
writer_tid.store(0, cpp::MemoryOrder::RELAXED);
Expand Down
2 changes: 2 additions & 0 deletions libc/src/__support/threads/linux/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,4 +518,6 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
__builtin_unreachable();
}

pid_t Thread::get_uncached_tid() { return syscall_impl<pid_t>(SYS_gettid); }

} // namespace LIBC_NAMESPACE_DECL
37 changes: 31 additions & 6 deletions libc/src/__support/threads/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_THREAD_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_THREAD_H

#ifndef LIBC_COPT_ENABLE_TID_CACHE
#define LIBC_COPT_ENABLE_TID_CACHE 1
#endif

#include "hdr/types/pid_t.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/string_view.h"
Expand Down Expand Up @@ -97,13 +102,13 @@ struct alignas(STACK_ALIGNMENT) ThreadAttributes {
// exits. It will clean up the thread resources once the thread
// exits.
cpp::Atomic<uint32_t> detach_state;
void *stack; // Pointer to the thread stack
unsigned long long stacksize; // Size of the stack
unsigned long long guardsize; // Guard size on stack
uintptr_t tls; // Address to the thread TLS memory
uintptr_t tls_size; // The size of area pointed to by |tls|.
void *stack; // Pointer to the thread stack
size_t stacksize; // Size of the stack
size_t guardsize; // Guard size on stack
uintptr_t tls; // Address to the thread TLS memory
uintptr_t tls_size; // The size of area pointed to by |tls|.
unsigned char owned_stack; // Indicates if the thread owns this stack memory
int tid;
pid_t tid;
ThreadStyle style;
ThreadReturnValue retval;
ThreadAtExitCallbackMgr *atexit_callback_mgr;
Expand Down Expand Up @@ -228,6 +233,26 @@ struct Thread {

// Return the name of the thread in |name|. Return the error number of error.
int get_name(cpp::StringStream &name) const;

static pid_t get_uncached_tid();

LIBC_INLINE void refresh_tid(pid_t cached = -1) {
if (cached >= 0)
this->attrib->tid = cached;
else
this->attrib->tid = get_uncached_tid();
}
LIBC_INLINE void invalidate_tid() { this->attrib->tid = -1; }

LIBC_INLINE pid_t get_tid() {
#if LIBC_COPT_ENABLE_TID_CACHE
if (LIBC_UNLIKELY(this->attrib->tid < 0))
return get_uncached_tid();
return this->attrib->tid;
#else
return get_uncached_tid();
#endif
}
};

extern LIBC_THREAD_LOCAL Thread self;
Expand Down
34 changes: 34 additions & 0 deletions libc/src/__support/threads/tid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- Tid wrapper --------------------------------------------*- 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___SUPPORT_THREADS_TID_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H

// This header is for internal usage which automatically dispatches full build
// and overlay build behaviors.

#include "hdr/types/pid_t.h"
#include "src/__support/common.h"
#ifdef LIBC_FULL_BUILD
#include "src/__support/threads/thread.h"
#else
#include "src/__support/OSUtil/syscall.h"
#include <sys/syscall.h>
#endif // LIBC_FULL_BUILD

namespace LIBC_NAMESPACE_DECL {
LIBC_INLINE pid_t gettid_inline() {
#ifdef LIBC_FULL_BUILD
return self.get_tid();
#else
return syscall_impl<pid_t>(SYS_gettid);
#endif
}
} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_TID_H
2 changes: 1 addition & 1 deletion libc/src/math/docs/add_math_function.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ together with its specifications:
```
- Add function specs to the file:
```
libc/spec/stdc.td
libc/newhdrgen/yaml/math.yaml
```

## Implementation
Expand Down
8 changes: 4 additions & 4 deletions libc/src/stdio/fopencookie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.write == nullptr)
return 0;
return cookie_file->ops.write(cookie_file->cookie,
reinterpret_cast<const char *>(data), size);
return static_cast<size_t>(cookie_file->ops.write(
cookie_file->cookie, reinterpret_cast<const char *>(data), size));
}

FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) {
auto cookie_file = reinterpret_cast<CookieFile *>(f);
if (cookie_file->ops.read == nullptr)
return 0;
return cookie_file->ops.read(cookie_file->cookie,
reinterpret_cast<char *>(data), size);
return static_cast<size_t>(cookie_file->ops.read(
cookie_file->cookie, reinterpret_cast<char *>(data), size));
}

ErrorOr<off_t> CookieFile::cookie_seek(File *f, off_t offset, int whence) {
Expand Down
2 changes: 1 addition & 1 deletion libc/src/sys/auxv/linux/getauxval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ static void initialize_auxv_once(void) {

static AuxEntry read_entry(int fd) {
AuxEntry buf;
ssize_t size = sizeof(AuxEntry);
size_t size = sizeof(AuxEntry);
char *ptr = reinterpret_cast<char *>(&buf);
while (size > 0) {
ssize_t ret = read(fd, ptr, size);
Expand Down
12 changes: 12 additions & 0 deletions libc/src/sys/epoll/linux/epoll_pwait2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,22 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, epoll_pwait2,
(int epfd, struct epoll_event *events, int maxevents,
const struct timespec *timeout, const sigset_t *sigmask)) {
#ifdef SYS_epoll_pwait2
int ret = LIBC_NAMESPACE::syscall_impl<int>(
SYS_epoll_pwait2, epfd, reinterpret_cast<long>(events), maxevents,
reinterpret_cast<long>(timeout), reinterpret_cast<long>(sigmask),
NSIG / 8);
#elif defined(SYS_epoll_pwait)
// Convert nanoseconds to milliseconds, rounding up if there are remaining
// nanoseconds
long timeout_ms = static_cast<long>(timeout->tv_sec * 1000 +
(timeout->tv_nsec + 999999) / 1000000);
int ret = LIBC_NAMESPACE::syscall_impl<int>(
SYS_epoll_pwait, epfd, reinterpret_cast<long>(events), maxevents,
timeout_ms, reinterpret_cast<long>(sigmask), NSIG / 8);
#else
#error "epoll_pwait and epoll_pwait2 syscalls not available."
#endif

// A negative return value indicates an error with the magnitude of the
// value being the error code.
Expand Down
5 changes: 3 additions & 2 deletions libc/src/sys/stat/linux/kernel_statx.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ LIBC_INLINE int statx(int dirfd, const char *__restrict path, int flags,
return -ret;

statbuf->st_dev = MKDEV(xbuf.stx_dev_major, xbuf.stx_dev_minor);
statbuf->st_ino = xbuf.stx_ino;
statbuf->st_ino = static_cast<decltype(statbuf->st_ino)>(xbuf.stx_ino);
statbuf->st_mode = xbuf.stx_mode;
statbuf->st_nlink = xbuf.stx_nlink;
statbuf->st_uid = xbuf.stx_uid;
Expand All @@ -94,7 +94,8 @@ LIBC_INLINE int statx(int dirfd, const char *__restrict path, int flags,
statbuf->st_ctim.tv_sec = xbuf.stx_ctime.tv_sec;
statbuf->st_ctim.tv_nsec = xbuf.stx_ctime.tv_nsec;
statbuf->st_blksize = xbuf.stx_blksize;
statbuf->st_blocks = xbuf.stx_blocks;
statbuf->st_blocks =
static_cast<decltype(statbuf->st_blocks)>(xbuf.stx_blocks);

return 0;
}
Expand Down
10 changes: 10 additions & 0 deletions libc/src/unistd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,13 @@ add_entrypoint_external(
add_entrypoint_external(
opterr
)

add_entrypoint_object(
gettid
SRCS
gettid.cpp
HDRS
gettid.h
DEPENDS
libc.src.__support.threads.tid
)
4 changes: 2 additions & 2 deletions libc/src/unistd/getpid.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
#ifndef LLVM_LIBC_SRC_UNISTD_GETPID_H
#define LLVM_LIBC_SRC_UNISTD_GETPID_H

#include "hdr/types/pid_t.h"
#include "src/__support/macros/config.h"
#include <unistd.h>

namespace LIBC_NAMESPACE_DECL {

pid_t getpid();
pid_t getpid(void);

} // namespace LIBC_NAMESPACE_DECL

Expand Down
17 changes: 17 additions & 0 deletions libc/src/unistd/gettid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//===-- Implementation file for gettid --------------------------*- 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 "src/unistd/gettid.h"
#include "src/__support/common.h"
#include "src/__support/threads/tid.h"

namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(pid_t, gettid, (void)) { return gettid_inline(); }

} // namespace LIBC_NAMESPACE_DECL
21 changes: 21 additions & 0 deletions libc/src/unistd/gettid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for gettid ------------------------*- 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_UNISTD_GETTID_H
#define LLVM_LIBC_SRC_UNISTD_GETTID_H

#include "hdr/types/pid_t.h"
#include "src/__support/common.h"

namespace LIBC_NAMESPACE_DECL {

pid_t gettid(void);

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC_UNISTD_GETTID_H
4 changes: 2 additions & 2 deletions libc/src/unistd/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ add_entrypoint_object(
libc.include.sys_syscall
libc.src.__support.threads.fork_callbacks
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.pid
libc.src.__support.threads.thread
libc.src.errno.errno
)
Expand Down Expand Up @@ -204,8 +205,7 @@ add_entrypoint_object(
../getpid.h
DEPENDS
libc.include.unistd
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.pid
)

add_entrypoint_object(
Expand Down
32 changes: 22 additions & 10 deletions libc/src/unistd/linux/fork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

#include "src/unistd/fork.h"

#include "src/__support/OSUtil/pid.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/fork_callbacks.h"
#include "src/__support/threads/thread.h" // For thread self object

#include "src/errno/libc_errno.h"

#include <signal.h> // For SIGCHLD
#include <sys/syscall.h> // For syscall numbers.

Expand All @@ -25,29 +26,40 @@ namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(pid_t, fork, (void)) {
invoke_prepare_callbacks();

// Invalidate tid/pid cache before fork to avoid post fork signal handler from
// getting wrong values. gettid() is not async-signal-safe, but let's provide
// our best efforts here.
pid_t parent_tid = self.get_tid();
self.invalidate_tid();
ProcessIdentity::start_fork();

#ifdef SYS_fork
pid_t ret = LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_fork);
#elif defined(SYS_clone)
pid_t ret = LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_clone, SIGCHLD, 0);
#else
#error "fork and clone syscalls not available."
#endif
if (ret == 0) {
// Return value is 0 in the child process.
// The child is created with a single thread whose self object will be a
// copy of parent process' thread which called fork. So, we have to fix up
// the child process' self object with the new process' tid.
self.attrib->tid = LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_gettid);
invoke_child_callbacks();
return 0;
}

if (ret < 0) {
// Error case, a child process was not created.
libc_errno = static_cast<int>(-ret);
return -1;
}

// Child process
if (ret == 0) {
self.refresh_tid();
ProcessIdentity::refresh_cache();
ProcessIdentity::end_fork();
invoke_child_callbacks();
return 0;
}

// Parent process
self.refresh_tid(parent_tid);
ProcessIdentity::end_fork();
invoke_parent_callbacks();
return ret;
}
Expand Down
11 changes: 2 additions & 9 deletions libc/src/unistd/linux/getpid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,10 @@
//===----------------------------------------------------------------------===//

#include "src/unistd/getpid.h"

#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/OSUtil/pid.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"

#include <sys/syscall.h> // For syscall numbers.

namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(pid_t, getpid, ()) {
return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_getpid);
}
LLVM_LIBC_FUNCTION(pid_t, getpid, (void)) { return ProcessIdentity::get(); }

} // namespace LIBC_NAMESPACE_DECL
1 change: 1 addition & 0 deletions libc/startup/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ add_object_library(
libc.include.llvm-libc-macros.link_macros
libc.src.__support.threads.thread
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.pid
libc.src.stdlib.exit
libc.src.stdlib.atexit
libc.src.unistd.environ
Expand Down
5 changes: 5 additions & 0 deletions libc/startup/linux/do_start.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "startup/linux/do_start.h"
#include "include/llvm-libc-macros/link-macros.h"
#include "src/__support/OSUtil/pid.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/thread.h"
Expand Down Expand Up @@ -127,6 +128,10 @@ static ThreadAttributes main_thread_attrib;
if (tls.size != 0 && !set_thread_ptr(tls.tp))
syscall_impl<long>(SYS_exit, 1);

// Validate process identity cache (TLS needed).
ProcessIdentity::refresh_cache();
ProcessIdentity::end_fork();

self.attrib = &main_thread_attrib;
main_thread_attrib.atexit_callback_mgr =
internal::get_thread_atexit_callback_mgr();
Expand Down
4 changes: 4 additions & 0 deletions libc/test/integration/src/unistd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ add_integration_test(
libc.src.sys.wait.wait4
libc.src.sys.wait.waitpid
libc.src.unistd.fork
libc.src.unistd.getpid
libc.src.unistd.gettid
libc.src.stdlib.exit
libc.include.sys_syscall
)

if((${LIBC_TARGET_OS} STREQUAL "linux") AND (${LIBC_TARGET_ARCHITECTURE_IS_X86}))
Expand Down
24 changes: 23 additions & 1 deletion libc/test/integration/src/unistd/fork_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/OSUtil/syscall.h"
#include "src/pthread/pthread_atfork.h"
#include "src/signal/raise.h"
#include "src/stdlib/exit.h"
#include "src/sys/wait/wait.h"
#include "src/sys/wait/wait4.h"
#include "src/sys/wait/waitpid.h"
#include "src/unistd/fork.h"

#include "src/unistd/getpid.h"
#include "src/unistd/gettid.h"
#include "test/IntegrationTest/test.h"

#include <errno.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>

Expand Down Expand Up @@ -140,7 +144,25 @@ void fork_with_atfork_callbacks() {
ASSERT_NE(child, DONE);
}

void fork_pid_tid_test() {
pid_t pid = fork();
ASSERT_TRUE(pid >= 0);
ASSERT_EQ(LIBC_NAMESPACE::gettid(),
LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_gettid));
ASSERT_EQ(LIBC_NAMESPACE::getpid(),
LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_getpid));

if (pid == 0) {
LIBC_NAMESPACE::exit(0);
} else {
int status;
LIBC_NAMESPACE::waitpid(pid, &status, 0);
ASSERT_EQ(status, 0);
}
}

TEST_MAIN(int argc, char **argv, char **envp) {
fork_pid_tid_test();
fork_and_wait_normal_exit();
fork_and_wait4_normal_exit();
fork_and_waitpid_normal_exit();
Expand Down
3 changes: 2 additions & 1 deletion libc/test/src/__support/File/platform_file_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ TEST(LlvmLibcPlatformFileTest, CreateAppendSeekAndReadBack) {
constexpr size_t APPEND_TEXT_SIZE = sizeof(APPEND_TEXT) - 1;
ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE).value, APPEND_TEXT_SIZE);

ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END).value(), 0);
ASSERT_EQ(file->seek(-static_cast<off_t>(APPEND_TEXT_SIZE), SEEK_END).value(),
0);
char data[APPEND_TEXT_SIZE + 1];
ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE).value, APPEND_TEXT_SIZE);
data[APPEND_TEXT_SIZE] = '\0';
Expand Down
3 changes: 3 additions & 0 deletions libc/test/src/sys/epoll/linux/epoll_create_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "src/unistd/close.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/Test.h"
#include <sys/syscall.h> // For syscall numbers.

using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;

Expand All @@ -21,6 +22,8 @@ TEST(LlvmLibcEpollCreateTest, Basic) {
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds());
}

#ifdef SYS_epoll_create
TEST(LlvmLibcEpollCreateTest, Fails) {
ASSERT_THAT(LIBC_NAMESPACE::epoll_create(0), Fails(EINVAL));
}
#endif
10 changes: 10 additions & 0 deletions libc/test/src/unistd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,16 @@ add_libc_unittest(
libc.src.unistd.getpid
)

add_libc_unittest(
gettid_test
SUITE
libc_unistd_unittests
SRCS
gettid_test.cpp
DEPENDS
libc.src.unistd.gettid
)

add_libc_unittest(
getppid_test
SUITE
Expand Down
15 changes: 15 additions & 0 deletions libc/test/src/unistd/gettid_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//===-- Unittests for gettid ----------------------------------------------===//
//
// 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/unistd/gettid.h"
#include "test/UnitTest/Test.h"

TEST(LlvmLibcGetTidTest, SmokeTest) {
// gettid always succeeds. So, we just call it as a smoke test.
ASSERT_GT(LIBC_NAMESPACE::gettid(), 0);
}
4 changes: 4 additions & 0 deletions libc/utils/gpu/server/rpc_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ void handle_printf(rpc::Server::Port &port) {
if (cur_section.has_conv && cur_section.conv_name == 's' &&
cur_section.conv_val_ptr) {
strs_to_copy[lane].emplace_back(cur_section.conv_val_ptr);
// Get the minimum size of the string in the case of padding.
char c = '\0';
cur_section.conv_val_ptr = &c;
convert(&writer, cur_section);
} else if (cur_section.has_conv) {
// Ignore conversion errors for the first pass.
convert(&writer, cur_section);
Expand Down
11 changes: 11 additions & 0 deletions libcxx/docs/ImplementationDefinedBehavior.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ Libc++ determines that a stream is Unicode-capable terminal by:
<http://eel.is/c++draft/print.fun#7>`_. This function is used for other
``std::print`` overloads that don't take an ``ostream&`` argument.

`[sf.cmath] <https://wg21.link/sf.cmath>`_ Mathematical Special Functions: Large indices
----------------------------------------------------------------------------------------

Most functions within the Mathematical Special Functions section contain integral indices.
The Standard specifies the result for larger indices as implementation-defined.
Libc++ pursuits reasonable results by choosing the same formulas as for indices below that threshold.
E.g.

- ``std::hermite(unsigned n, T x)`` for ``n >= 128``


Listed in the index of implementation-defined behavior
======================================================

Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/Status/Cxx17.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Paper Status
.. note::

.. [#note-P0067] P0067: ``std::(to|from)_chars`` for integrals has been available since version 7.0. ``std::to_chars`` for ``float`` and ``double`` since version 14.0 ``std::to_chars`` for ``long double`` uses the implementation for ``double``.
.. [#note-P0226] P0226: Progress is tracked `here <https://https://libcxx.llvm.org/Status/SpecialMath.html>`_.
.. [#note-P0607] P0607: The parts of P0607 that are not done are the ``<regex>`` bits.
.. [#note-P0154] P0154: The required macros are only implemented as of clang 19.
.. [#note-P0452] P0452: The changes to ``std::transform_inclusive_scan`` and ``std::transform_exclusive_scan`` have not yet been implemented.
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx17Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"`P0013R1 <https://wg21.link/p0013r1>`__","LWG","Logical type traits rev 2","Kona","|Complete|","3.8"
"","","","","",""
"`P0024R2 <https://wg21.link/P0024R2>`__","LWG","The Parallelism TS Should be Standardized","Jacksonville","|Partial|",""
"`P0226R1 <https://wg21.link/P0226R1>`__","LWG","Mathematical Special Functions for C++17","Jacksonville","",""
"`P0226R1 <https://wg21.link/P0226R1>`__","LWG","Mathematical Special Functions for C++17","Jacksonville","|In Progress| [#note-P0226]_",""
"`P0220R1 <https://wg21.link/P0220R1>`__","LWG","Adopt Library Fundamentals V1 TS Components for C++17","Jacksonville","|Complete|","16.0"
"`P0218R1 <https://wg21.link/P0218R1>`__","LWG","Adopt the File System TS for C++17","Jacksonville","|Complete|","7.0"
"`P0033R1 <https://wg21.link/P0033R1>`__","LWG","Re-enabling shared_from_this","Jacksonville","|Complete|","3.9"
Expand Down
4 changes: 2 additions & 2 deletions libcxx/docs/Status/FormatPaper.csv
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",,,|Nothing To Do|,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::day``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::month``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year``",,Mark de Wever,|Complete|,16.0
Expand All @@ -26,7 +26,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",,Mark de Wever,|Complete|,19.0

"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"
`[thread.thread.id] <https://wg21.link/thread.thread.id>`_,"Formatting ``thread::id``",,Mark de Wever,|Complete|,17.0
Expand Down
35 changes: 35 additions & 0 deletions libcxx/docs/Status/SpecialMath.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.. special-math-status:
======================================================
libc++ Mathematical Special Functions Status (P0226R1)
======================================================

.. include:: ../Helpers/Styles.rst

.. contents::
:local:

Overview
========

This document contains the status of the C++17 mathematical special functions implementation in libc++.
It is used to track both the status of the sub-projects of the effort and who is assigned to these sub-projects.
This avoids duplicating effort.

If you are interested in contributing to this effort, please send a message
to the #libcxx channel in the LLVM discord. Please *do not* start working
on any items below that has already been assigned to someone else.

Sub-projects in the Implementation Effort
=========================================

.. csv-table::
:file: SpecialMathProjects.csv
:header-rows: 1
:widths: auto

Paper and Issue Status
======================

The underlying paper is `Mathematical Special Functions for C++17 (P0226) <https://wg21.link/P0226>`_ and is included in C++17.
Implementation is *In Progress*.
22 changes: 22 additions & 0 deletions libcxx/docs/Status/SpecialMathProjects.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Section,Description,Assignee,Complete
| `[sf.cmath.assoc.laguerre] <https://wg21.link/sf.cmath.assoc.laguerre>`_, std::assoc_laguerre, None, |Not Started|
| `[sf.cmath.assoc.legendre] <https://wg21.link/sf.cmath.assoc.legendre>`_, std::assoc_legendre, None, |Not Started|
| `[sf.cmath.beta] <https://wg21.link/sf.cmath.beta>`_, std::beta, None, |Not Started|
| `[sf.cmath.comp.ellint.1] <https://wg21.link/sf.cmath.comp.ellint.1>`_, std::comp_ellint_1, None, |Not Started|
| `[sf.cmath.comp.ellint.2] <https://wg21.link/sf.cmath.comp.ellint.2>`_, std::comp_ellint_2, None, |Not Started|
| `[sf.cmath.comp.ellint.3] <https://wg21.link/sf.cmath.comp.ellint.3>`_, std::comp_ellint_3, None, |Not Started|
| `[sf.cmath.cyl.bessel.i] <https://wg21.link/sf.cmath.cyl.bessel.i>`_, std::cyl_bessel_i, None, |Not Started|
| `[sf.cmath.cyl.bessel.j] <https://wg21.link/sf.cmath.cyl.bessel.j>`_, std::cyl_bessel_j, None, |Not Started|
| `[sf.cmath.cyl.bessel.k] <https://wg21.link/sf.cmath.cyl.bessel.k>`_, std::cyl_bessel_k, None, |Not Started|
| `[sf.cmath.cyl.neumann] <https://wg21.link/sf.cmath.cyl.neumann>`_, std::cyl_neumann, None, |Not Started|
| `[sf.cmath.ellint.1] <https://wg21.link/sf.cmath.ellint.1>`_, std::ellint_1, None, |Not Started|
| `[sf.cmath.ellint.2] <https://wg21.link/sf.cmath.ellint.2>`_, std::ellint_2, None, |Not Started|
| `[sf.cmath.ellint.3] <https://wg21.link/sf.cmath.ellint.3>`_, std::ellint_3, None, |Not Started|
| `[sf.cmath.expint] <https://wg21.link/sf.cmath.expint>`_, std::expint, None, |Not Started|
| `[sf.cmath.hermite] <https://wg21.link/sf.cmath.hermite>`_, std::hermite, Paul Xi Cao, |Complete|
| `[sf.cmath.laguerre] <https://wg21.link/sf.cmath.laguerre>`_, std::laguerre, None, |Not Started|
| `[sf.cmath.legendre] <https://wg21.link/sf.cmath.legendre>`_, std::legendre, None, |Not Started|
| `[sf.cmath.riemann.zeta] <https://wg21.link/sf.cmath.riemann.zeta>`_, std::riemann_zeta, None, |Not Started|
| `[sf.cmath.sph.bessel] <https://wg21.link/sf.cmath.sph.bessel>`_, std::sph_bessel, None, |Not Started|
| `[sf.cmath.sph.legendre] <https://wg21.link/sf.cmath.sph.legendre>`_, std::sph_legendre, None, |Not Started|
| `[sf.cmath.sph.neumann] <https://wg21.link/sf.cmath.sph.neumann>`_, std::sph_neumann, None, |Not Started|
1 change: 1 addition & 0 deletions libcxx/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Getting Started with libc++
Status/PSTL
Status/Ranges
Status/Spaceship
Status/SpecialMath
Status/Zip


Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ set(files
__math/remainder.h
__math/roots.h
__math/rounding_functions.h
__math/special_functions.h
__math/traits.h
__math/trigonometric_functions.h
__mbstate_t.h
Expand Down
10 changes: 9 additions & 1 deletion libcxx/include/__chrono/convert_to_tm.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
#include <__chrono/year_month.h>
#include <__chrono/year_month_day.h>
#include <__chrono/year_month_weekday.h>
#include <__chrono/zoned_time.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__format/format_error.h>
#include <__memory/addressof.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_specialization.h>
#include <cstdint>
#include <ctime>
#include <limits>
Expand Down Expand Up @@ -178,7 +180,13 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
// Has no time information.
} else if constexpr (same_as<_ChronoT, chrono::local_info>) {
// Has no time information.
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
} else if constexpr (__is_specialization_v<_ChronoT, chrono::zoned_time>) {
return std::__convert_to_tm<_Tm>(
chrono::sys_time<typename _ChronoT::duration>{__value.get_local_time().time_since_epoch()});
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
} else
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");

Expand Down
69 changes: 63 additions & 6 deletions libcxx/include/__chrono/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <__chrono/year_month.h>
#include <__chrono/year_month_day.h>
#include <__chrono/year_month_weekday.h>
#include <__chrono/zoned_time.h>
#include <__concepts/arithmetic.h>
#include <__concepts/same_as.h>
#include <__config>
Expand All @@ -44,6 +45,7 @@
#include <__format/parser_std_format_spec.h>
#include <__format/write_escaped.h>
#include <__memory/addressof.h>
#include <__type_traits/is_specialization.h>
#include <cmath>
#include <ctime>
#include <limits>
Expand Down Expand Up @@ -137,10 +139,24 @@ __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<
__value.fractional_width);
}

# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \
!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
template <class _CharT, class _Duration, class _TimeZonePtr>
_LIBCPP_HIDE_FROM_ABI void
__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {
__formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());
}
# endif

template <class _Tp>
consteval bool __use_fraction() {
if constexpr (__is_time_point<_Tp>)
return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \
!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
# endif
else if constexpr (chrono::__is_duration<_Tp>::value)
return chrono::hh_mm_ss<_Tp>::fractional_width;
else if constexpr (__is_hh_mm_ss<_Tp>)
Expand Down Expand Up @@ -212,8 +228,13 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
if constexpr (same_as<_Tp, chrono::sys_info>)
return {__value.abbrev, __value.offset};
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return __formatter::__convert_to_time_zone(__value.get_info());
# endif
else
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
return {"UTC", chrono::seconds{0}};
}

Expand Down Expand Up @@ -426,7 +447,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -472,7 +498,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -518,7 +549,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -564,7 +600,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -924,7 +965,23 @@ struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT>
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
}
};
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
// Note due to how libc++'s formatters are implemented there is no need to add
// the exposition only local-time-format-t abstraction.
template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>
struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {
public:
using _Base = __formatter_chrono<_CharT>;

template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
}
};
# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
// !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

#endif // if _LIBCPP_STD_VER >= 20

Expand Down
9 changes: 9 additions & 0 deletions libcxx/include/__chrono/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <__chrono/year_month.h>
#include <__chrono/year_month_day.h>
#include <__chrono/year_month_weekday.h>
#include <__chrono/zoned_time.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__format/format_functions.h>
Expand Down Expand Up @@ -302,6 +303,14 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __info) {
_LIBCPP_STATICALLY_WIDEN(_CharT, "{}: {{{}, {}}}"), __result(), __info.first, __info.second);
}

# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
template <class _CharT, class _Traits, class _Duration, class _TimeZonePtr>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const zoned_time<_Duration, _TimeZonePtr>& __tp) {
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T %Z}"), __tp);
}
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

} // namespace chrono
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/__configuration/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
// and WCHAR_MAX. This ABI setting determines whether we should instead track whether the fill
// value has been initialized using a separate boolean, which changes the ABI.
# define _LIBCPP_ABI_IOS_ALLOW_ARBITRARY_FILL_VALUE
// Make a std::pair of trivially copyable types trivially copyable.
// While this technically doesn't change the layout of pair itself, other types may decide to programatically change
// their representation based on whether something is trivially copyable.
# define _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
#elif _LIBCPP_ABI_VERSION == 1
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
// Enable compiling copies of now inline methods into the dylib to support
Expand Down
84 changes: 84 additions & 0 deletions libcxx/include/__math/special_functions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// -*- 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 _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
#define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H

#include <__config>
#include <__math/copysign.h>
#include <__math/traits.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_integral.h>
#include <limits>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 17

template <class _Real>
_LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
// The Hermite polynomial H_n(x).
// The implementation is based on the recurrence formula: H_{n+1}(x) = 2x H_n(x) - 2n H_{n-1}.
// Press, William H., et al. Numerical recipes 3rd edition: The art of scientific computing.
// Cambridge university press, 2007, p. 183.

// NOLINTBEGIN(readability-identifier-naming)
if (__math::isnan(__x))
return __x;

_Real __H_0{1};
if (__n == 0)
return __H_0;

_Real __H_n_prev = __H_0;
_Real __H_n = 2 * __x;
for (unsigned __i = 1; __i < __n; ++__i) {
_Real __H_n_next = 2 * (__x * __H_n - __i * __H_n_prev);
__H_n_prev = __H_n;
__H_n = __H_n_next;
}

if (!__math::isfinite(__H_n)) {
// Overflow occured. Two possible cases:
// n is odd: return infinity of the same sign as x.
// n is even: return +Inf
_Real __inf = std::numeric_limits<_Real>::infinity();
return (__n & 1) ? __math::copysign(__inf, __x) : __inf;
}
return __H_n;
// NOLINTEND(readability-identifier-naming)
}

inline _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, double __x) { return std::__hermite(__n, __x); }

inline _LIBCPP_HIDE_FROM_ABI float hermite(unsigned __n, float __x) {
// use double internally -- float is too prone to overflow!
return static_cast<float>(std::hermite(__n, static_cast<double>(__x)));
}

inline _LIBCPP_HIDE_FROM_ABI long double hermite(unsigned __n, long double __x) { return std::__hermite(__n, __x); }

inline _LIBCPP_HIDE_FROM_ABI float hermitef(unsigned __n, float __x) { return std::hermite(__n, __x); }

inline _LIBCPP_HIDE_FROM_ABI long double hermitel(unsigned __n, long double __x) { return std::hermite(__n, __x); }

template <class _Integer, std::enable_if_t<std::is_integral_v<_Integer>, int> = 0>
_LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
return std::hermite(__n, static_cast<double>(__x));
}

#endif // _LIBCPP_STD_VER >= 17

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
1 change: 1 addition & 0 deletions libcxx/include/__type_traits/datasizeof.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct _FirstPaddingByte<_Tp, true> {
// the use as an extension.
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
template <class _Tp>
inline const size_t __datasizeof_v = offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_);
_LIBCPP_DIAGNOSTIC_POP
Expand Down
46 changes: 41 additions & 5 deletions libcxx/include/__utility/pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <__type_traits/is_implicitly_default_constructible.h>
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_relocatable.h>
Expand Down Expand Up @@ -80,6 +81,38 @@ struct _LIBCPP_TEMPLATE_VIS pair
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
_LIBCPP_HIDE_FROM_ABI pair(pair&&) = default;

// When we are requested for pair to be trivially copyable by the ABI macro, we use defaulted members
// if it is both legal to do it (i.e. no references) and we have a way to actually implement it, which requires
// the __enable_if__ attribute before C++20.
#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
// FIXME: This should really just be a static constexpr variable. It's in a struct to avoid gdb printing the value
// when printing a pair
struct __has_defaulted_members {
static const bool value = !is_reference<first_type>::value && !is_reference<second_type>::value;
};
# if _LIBCPP_STD_VER >= 20
_LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(const pair&)
requires __has_defaulted_members::value
= default;

_LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(pair&&)
requires __has_defaulted_members::value
= default;
# elif __has_attribute(__enable_if__)
_LIBCPP_HIDE_FROM_ABI pair& operator=(const pair&)
__attribute__((__enable_if__(__has_defaulted_members::value, ""))) = default;

_LIBCPP_HIDE_FROM_ABI pair& operator=(pair&&)
__attribute__((__enable_if__(__has_defaulted_members::value, ""))) = default;
# else
# error "_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR isn't supported with this compiler"
# endif
#else
struct __has_defaulted_members {
static const bool value = false;
};
#endif // defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR) && __has_attribute(__enable_if__)

#ifdef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI pair() : first(), second() {}

Expand Down Expand Up @@ -225,7 +258,8 @@ struct _LIBCPP_TEMPLATE_VIS pair
typename __make_tuple_indices<sizeof...(_Args2) >::type()) {}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
operator=(__conditional_t<is_copy_assignable<first_type>::value && is_copy_assignable<second_type>::value,
operator=(__conditional_t<!__has_defaulted_members::value && is_copy_assignable<first_type>::value &&
is_copy_assignable<second_type>::value,
pair,
__nat> const& __p) noexcept(is_nothrow_copy_assignable<first_type>::value &&
is_nothrow_copy_assignable<second_type>::value) {
Expand All @@ -234,10 +268,12 @@ struct _LIBCPP_TEMPLATE_VIS pair
return *this;
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(
__conditional_t<is_move_assignable<first_type>::value && is_move_assignable<second_type>::value, pair, __nat>&&
__p) noexcept(is_nothrow_move_assignable<first_type>::value &&
is_nothrow_move_assignable<second_type>::value) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
operator=(__conditional_t<!__has_defaulted_members::value && is_move_assignable<first_type>::value &&
is_move_assignable<second_type>::value,
pair,
__nat>&& __p) noexcept(is_nothrow_move_assignable<first_type>::value &&
is_nothrow_move_assignable<second_type>::value) {
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
Expand Down
7 changes: 7 additions & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,11 @@ template<class Duration1, class Duration2, class TimeZonePtr>
bool operator==(const zoned_time<Duration1, TimeZonePtr>& x,
const zoned_time<Duration2, TimeZonePtr>& y);

template<class charT, class traits, class Duration, class TimeZonePtr> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
const zoned_time<Duration, TimeZonePtr>& t);

// [time.zone.leap], leap second support
class leap_second { // C++20
public:
Expand Down Expand Up @@ -881,6 +886,8 @@ namespace std {
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
template<class charT> struct formatter<chrono::local_info, charT>; // C++20
template<class Duration, class TimeZonePtr, class charT> // C++20
struct formatter<chrono::zoned_time<Duration, TimeZonePtr>, charT>;
} // namespace std

namespace chrono {
Expand Down
9 changes: 9 additions & 0 deletions libcxx/include/cmath
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ floating_point fmin (arithmetic x, arithmetic y);
float fminf(float x, float y);
long double fminl(long double x, long double y);

double hermite(unsigned n, double x); // C++17
float hermite(unsigned n, float x); // C++17
long double hermite(unsigned n, long double x); // C++17
float hermitef(unsigned n, float x); // C++17
long double hermitel(unsigned n, long double x); // C++17
template <class Integer>
double hermite(unsigned n, Integer x); // C++17

floating_point hypot (arithmetic x, arithmetic y);
float hypotf(float x, float y);
long double hypotl(long double x, long double y);
Expand Down Expand Up @@ -315,6 +323,7 @@ constexpr long double lerp(long double a, long double b, long double t) noexcept
#include <limits>
#include <version>

#include <__math/special_functions.h>
#include <math.h>

#ifndef _LIBCPP_MATH_H
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,7 @@ module std_private_math_modulo [system] { header "__mat
module std_private_math_remainder [system] { header "__math/remainder.h" }
module std_private_math_roots [system] { header "__math/roots.h" }
module std_private_math_rounding_functions [system] { header "__math/rounding_functions.h" }
module std_private_math_special_functions [system] { header "__math/special_functions.h" }
module std_private_math_traits [system] { header "__math/traits.h" }
module std_private_math_trigonometric_functions [system] { header "__math/trigonometric_functions.h" }

Expand Down
6 changes: 5 additions & 1 deletion libcxx/include/vector
Original file line number Diff line number Diff line change
Expand Up @@ -1443,7 +1443,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::shrink_to_fit() _NOE
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
allocator_type& __a = this->__alloc();
__split_buffer<value_type, allocator_type&> __v(size(), size(), __a);
__swap_out_circular_buffer(__v);
// The Standard mandates shrink_to_fit() does not increase the capacity.
// With equal capacity keep the existing buffer. This avoids extra work
// due to swapping the elements.
if (__v.capacity() < capacity())
__swap_out_circular_buffer(__v);
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
Expand Down
2 changes: 2 additions & 0 deletions libcxx/modules/std/cmath.inc
Original file line number Diff line number Diff line change
Expand Up @@ -334,12 +334,14 @@ export namespace std {
using std::expint;
using std::expintf;
using std::expintl;
#endif

// [sf.cmath.hermite], Hermite polynomials
using std::hermite;
using std::hermitef;
using std::hermitel;

#if 0
// [sf.cmath.laguerre], Laguerre polynomials
using std::laguerre;
using std::laguerref;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,13 @@ void test_trivial()
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
#endif // TEST_STD_VER >= 11
#ifndef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(!std::is_trivially_copy_assignable<P>::value, "");
static_assert(!std::is_trivially_move_assignable<P>::value, "");
#else
static_assert(std::is_trivially_copy_assignable<P>::value, "");
static_assert(std::is_trivially_move_assignable<P>::value, "");
#endif
static_assert(std::is_trivially_destructible<P>::value, "");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,42 @@ static_assert(!std::is_trivially_copyable<std::pair<int&, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<int, int&> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<int&, int&> >::value, "");

#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(std::is_trivially_copyable<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<int, char> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<char, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<std::pair<char, char>, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable, int> >::value, "");
#else
static_assert(!std::is_trivially_copyable<std::pair<int, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<int, char> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<char, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<std::pair<char, char>, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable, int> >::value, "");
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR

#if TEST_STD_VER == 03 // Known ABI difference
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_copy_assignment, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_move_assignment, int> >::value, "");
#else
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_copy_assignment, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_move_assignment, int> >::value, "");
#endif

#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
#else
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
#endif

static_assert(std::is_trivially_copy_constructible<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_move_constructible<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_destructible<std::pair<int, int> >::value, "");

#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_move_assignable<std::pair<int, int> >::value, "");
#else
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
static_assert(!std::is_trivially_move_assignable<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_destructible<std::pair<int, int> >::value, "");
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,56 @@ TEST_CONSTEXPR_CXX20 bool tests() {
return true;
}

#if TEST_STD_VER >= 23
template <typename T>
struct increasing_allocator {
using value_type = T;
std::size_t min_elements = 1000;
increasing_allocator() = default;

template <typename U>
constexpr increasing_allocator(const increasing_allocator<U>& other) noexcept : min_elements(other.min_elements) {}

constexpr std::allocation_result<T*> allocate_at_least(std::size_t n) {
if (n < min_elements)
n = min_elements;
min_elements += 1000;
return std::allocator<T>{}.allocate_at_least(n);
}
constexpr T* allocate(std::size_t n) { return allocate_at_least(n).ptr; }
constexpr void deallocate(T* p, std::size_t n) noexcept { std::allocator<T>{}.deallocate(p, n); }
};

template <typename T, typename U>
bool operator==(increasing_allocator<T>, increasing_allocator<U>) {
return true;
}

// https://github.com/llvm/llvm-project/issues/95161
constexpr bool test_increasing_allocator() {
std::vector<int, increasing_allocator<int>> v;
v.push_back(1);
assert(is_contiguous_container_asan_correct(v));
std::size_t capacity = v.capacity();
v.shrink_to_fit();
assert(v.capacity() <= capacity);
assert(v.size() == 1);
assert(is_contiguous_container_asan_correct(v));

return true;
}
#endif // TEST_STD_VER >= 23

int main(int, char**)
{
tests();
tests();
#if TEST_STD_VER > 17
static_assert(tests());
#endif
#if TEST_STD_VER >= 23
test_increasing_allocator();
static_assert(test_increasing_allocator());
#endif

return 0;
}
341 changes: 341 additions & 0 deletions libcxx/test/std/numerics/c.math/hermite.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,341 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14

// <cmath>

// double hermite(unsigned n, double x);
// float hermite(unsigned n, float x);
// long double hermite(unsigned n, long double x);
// float hermitef(unsigned n, float x);
// long double hermitel(unsigned n, long double x);
// template <class Integer>
// double hermite(unsigned n, Integer x);

#include <array>
#include <cassert>
#include <cmath>
#include <limits>
#include <vector>

#include "type_algorithms.h"

inline constexpr unsigned g_max_n = 128;

template <class T>
std::array<T, 11> sample_points() {
return {-12.34, -7.42, -1.0, -0.5, -0.1, 0.0, 0.1, 0.5, 1.0, 5.67, 15.67};
}

template <class Real>
class CompareFloatingValues {
private:
Real abs_tol;
Real rel_tol;

public:
CompareFloatingValues() {
abs_tol = []() -> Real {
if (std::is_same_v<Real, float>)
return 1e-5f;
else if (std::is_same_v<Real, double>)
return 1e-11;
else
return 1e-12l;
}();

rel_tol = abs_tol;
}

bool operator()(Real result, Real expected) const {
if (std::isinf(expected) && std::isinf(result))
return result == expected;

if (std::isnan(expected) || std::isnan(result))
return false;

Real tol = abs_tol + std::abs(expected) * rel_tol;
return std::abs(result - expected) < tol;
}
};

// Roots are taken from
// Salzer, Herbert E., Ruth Zucker, and Ruth Capuano.
// Table of the zeros and weight factors of the first twenty Hermite
// polynomials. US Government Printing Office, 1952.
template <class T>
std::vector<T> get_roots(unsigned n) {
switch (n) {
case 0:
return {};
case 1:
return {T(0)};
case 2:
return {T(0.707106781186548)};
case 3:
return {T(0), T(1.224744871391589)};
case 4:
return {T(0.524647623275290), T(1.650680123885785)};
case 5:
return {T(0), T(0.958572464613819), T(2.020182870456086)};
case 6:
return {T(0.436077411927617), T(1.335849074013697), T(2.350604973674492)};
case 7:
return {T(0), T(0.816287882858965), T(1.673551628767471), T(2.651961356835233)};
case 8:
return {T(0.381186990207322), T(1.157193712446780), T(1.981656756695843), T(2.930637420257244)};
case 9:
return {T(0), T(0.723551018752838), T(1.468553289216668), T(2.266580584531843), T(3.190993201781528)};
case 10:
return {
T(0.342901327223705), T(1.036610829789514), T(1.756683649299882), T(2.532731674232790), T(3.436159118837738)};
case 11:
return {T(0),
T(0.65680956682100),
T(1.326557084494933),
T(2.025948015825755),
T(2.783290099781652),
T(3.668470846559583)};

case 12:
return {T(0.314240376254359),
T(0.947788391240164),
T(1.597682635152605),
T(2.279507080501060),
T(3.020637025120890),
T(3.889724897869782)};

case 13:
return {T(0),
T(0.605763879171060),
T(1.220055036590748),
T(1.853107651601512),
T(2.519735685678238),
T(3.246608978372410),
T(4.101337596178640)};

case 14:
return {T(0.29174551067256),
T(0.87871378732940),
T(1.47668273114114),
T(2.09518325850772),
T(2.74847072498540),
T(3.46265693360227),
T(4.30444857047363)};

case 15:
return {T(0.00000000000000),
T(0.56506958325558),
T(1.13611558521092),
T(1.71999257518649),
T(2.32573248617386),
T(2.96716692790560),
T(3.66995037340445),
T(4.49999070730939)};

case 16:
return {T(0.27348104613815),
T(0.82295144914466),
T(1.38025853919888),
T(1.95178799091625),
T(2.54620215784748),
T(3.17699916197996),
T(3.86944790486012),
T(4.68873893930582)};

case 17:
return {T(0),
T(0.5316330013427),
T(1.0676487257435),
T(1.6129243142212),
T(2.1735028266666),
T(2.7577629157039),
T(3.3789320911415),
T(4.0619466758755),
T(4.8713451936744)};

case 18:
return {T(0.2582677505191),
T(0.7766829192674),
T(1.3009208583896),
T(1.8355316042616),
T(2.3862990891667),
T(2.9613775055316),
T(3.5737690684863),
T(4.2481178735681),
T(5.0483640088745)};

case 19:
return {T(0),
T(0.5035201634239),
T(1.0103683871343),
T(1.5241706193935),
T(2.0492317098506),
T(2.5911337897945),
T(3.1578488183476),
T(3.7621873519640),
T(4.4285328066038),
T(5.2202716905375)};

case 20:
return {T(0.2453407083009),
T(0.7374737285454),
T(1.2340762153953),
T(1.7385377121166),
T(2.2549740020893),
T(2.7888060584281),
T(3.347854567332),
T(3.9447640401156),
T(4.6036824495507),
T(5.3874808900112)};

default: // polynom degree n>20 is unsupported
assert(false);
return {T(-42)};
}
}

template <class Real>
void test() {
{ // checks if NaNs are reported correctly (i.e. output == input for input == NaN)
using nl = std::numeric_limits<Real>;
for (Real NaN : {nl::quiet_NaN(), nl::signaling_NaN()})
for (unsigned n = 0; n < g_max_n; ++n)
assert(std::isnan(std::hermite(n, NaN)));
}

{ // simple sample points for n=0..127 should not produce NaNs.
for (Real x : sample_points<Real>())
for (unsigned n = 0; n < g_max_n; ++n)
assert(!std::isnan(std::hermite(n, x)));
}

{ // checks std::hermite(n, x) for n=0..5 against analytic polynoms
const auto h0 = [](Real) -> Real { return 1; };
const auto h1 = [](Real y) -> Real { return 2 * y; };
const auto h2 = [](Real y) -> Real { return 4 * y * y - 2; };
const auto h3 = [](Real y) -> Real { return y * (8 * y * y - 12); };
const auto h4 = [](Real y) -> Real { return (16 * std::pow(y, 4) - 48 * y * y + 12); };
const auto h5 = [](Real y) -> Real { return y * (32 * std::pow(y, 4) - 160 * y * y + 120); };

for (Real x : sample_points<Real>()) {
const CompareFloatingValues<Real> compare;
assert(compare(std::hermite(0, x), h0(x)));
assert(compare(std::hermite(1, x), h1(x)));
assert(compare(std::hermite(2, x), h2(x)));
assert(compare(std::hermite(3, x), h3(x)));
assert(compare(std::hermite(4, x), h4(x)));
assert(compare(std::hermite(5, x), h5(x)));
}
}

{ // checks std::hermitef for bitwise equality with std::hermite(unsigned, float)
if constexpr (std::is_same_v<Real, float>)
for (unsigned n = 0; n < g_max_n; ++n)
for (float x : sample_points<float>())
assert(std::hermite(n, x) == std::hermitef(n, x));
}

{ // checks std::hermitel for bitwise equality with std::hermite(unsigned, long double)
if constexpr (std::is_same_v<Real, long double>)
for (unsigned n = 0; n < g_max_n; ++n)
for (long double x : sample_points<long double>())
assert(std::hermite(n, x) == std::hermitel(n, x));
}

{ // Checks if the characteristic recurrence relation holds: H_{n+1}(x) = 2x H_n(x) - 2n H_{n-1}(x)
for (Real x : sample_points<Real>()) {
for (unsigned n = 1; n < g_max_n - 1; ++n) {
Real H_next = std::hermite(n + 1, x);
Real H_next_recurrence = 2 * (x * std::hermite(n, x) - n * std::hermite(n - 1, x));

if (std::isinf(H_next))
break;
const CompareFloatingValues<Real> compare;
assert(compare(H_next, H_next_recurrence));
}
}
}

{ // sanity checks: hermite polynoms need to change signs at (simple) roots. checked upto order n<=20.

// root tolerance: must be smaller than the smallest difference between adjacent roots
Real tol = []() -> Real {
if (std::is_same_v<Real, float>)
return 1e-5f;
else if (std::is_same_v<Real, double>)
return 1e-9;
else
return 1e-10l;
}();

const auto is_sign_change = [tol](unsigned n, Real x) -> bool {
return std::hermite(n, x - tol) * std::hermite(n, x + tol) < 0;
};

for (unsigned n = 0; n <= 20u; ++n) {
for (Real x : get_roots<Real>(n)) {
// the roots are symmetric: if x is a root, so is -x
if (x > 0)
assert(is_sign_change(n, -x));
assert(is_sign_change(n, x));
}
}
}

{ // check input infinity is handled correctly
Real inf = std::numeric_limits<Real>::infinity();
for (unsigned n = 1; n < g_max_n; ++n) {
assert(std::hermite(n, +inf) == inf);
assert(std::hermite(n, -inf) == ((n & 1) ? -inf : inf));
}
}

{ // check: if overflow occurs that it is mapped to the correct infinity
if constexpr (std::is_same_v<Real, double>) {
// Q: Why only double?
// A: The numeric values (e.g. overflow threshold `n`) below are different for other types.
static_assert(sizeof(double) == 8);
for (unsigned n = 0; n < g_max_n; ++n) {
// Q: Why n=111 and x=300?
// A: Both are chosen s.t. the first overlow occurs for some `n<g_max_n`.
if (n < 111) {
assert(std::isfinite(std::hermite(n, +300.0)));
assert(std::isfinite(std::hermite(n, -300.0)));
} else {
double inf = std::numeric_limits<double>::infinity();
assert(std::hermite(n, +300.0) == inf);
assert(std::hermite(n, -300.0) == ((n & 1) ? -inf : inf));
}
}
}
}
}

struct TestFloat {
template <class Real>
void operator()() {
test<Real>();
}
};

struct TestInt {
template <class Integer>
void operator()() {
// checks that std::hermite(unsigned, Integer) actually wraps std::hermite(unsigned, double)
for (unsigned n = 0; n < g_max_n; ++n)
for (Integer x : {-42, -7, -5, -1, 0, 1, 5, 7, 42})
assert(std::hermite(n, x) == std::hermite(n, static_cast<double>(x)));
}
};

int main() {
types::for_each(types::floating_point_types(), TestFloat());
types::for_each(types::type_list<short, int, long, long long>(), TestInt());
}
974 changes: 974 additions & 0 deletions libcxx/test/std/time/time.syn/formatter.zoned_time.pass.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cassert>
#include <charconv>
#include <chrono>
#include <format>
#include <string_view>
#include <type_traits>

Expand Down Expand Up @@ -42,13 +43,31 @@ class offset_time_zone {

offset_time_zone* operator->() { return this; }

const offset_time_zone* operator->() const { return this; }

template <class Duration>
std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>
to_sys(const std::chrono::local_time<Duration>& local) const {
return std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>{
local.time_since_epoch() + offset_};
}

template <class Duration>
std::chrono::local_time<std::common_type_t<Duration, std::chrono::seconds>>
to_local(const std::chrono::sys_time<Duration>& sys) const {
return std::chrono::local_time<std::common_type_t<Duration, std::chrono::seconds>>{
sys.time_since_epoch() - offset_};
}

template <class Duration>
std::chrono::sys_info get_info(const std::chrono::sys_time<Duration>&) const {
return {std::chrono::sys_seconds::min(),
std::chrono::sys_seconds::max(),
offset_,
std::chrono::minutes{0},
std::format("{:+03d}s", offset_.count())};
}

private:
std::chrono::seconds offset_;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb

// TODO FMT This test should not require std::to_chars(floating-point)
// XFAIL: availability-fp_to_chars-missing

// XFAIL: libcpp-has-no-experimental-tzdb

// REQUIRES: locale.fr_FR.UTF-8
// REQUIRES: locale.ja_JP.UTF-8

// <chrono>

// template<class charT, class traits, class Duration, class TimeZonePtr>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>& os,
// const zoned_time<Duration, TimeZonePtr>& t);

#include <chrono>
#include <cassert>
#include <sstream>

#include "assert_macros.h"
#include "concat_macros.h"
#include "make_string.h"
#include "platform_support.h" // locale name macros
#include "test_macros.h"
#include "../test_offset_time_zone.h"

#define SV(S) MAKE_STRING_VIEW(CharT, S)

#define TEST_EQUAL(OUT, EXPECTED) \
TEST_REQUIRE(OUT == EXPECTED, \
TEST_WRITE_CONCATENATED( \
"\nExpression ", #OUT, "\nExpected output ", EXPECTED, "\nActual output ", OUT, '\n'));

template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_c_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
std::basic_stringstream<CharT> sstr;
sstr << time_point;
return sstr.str();
}

template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
std::basic_stringstream<CharT> sstr;
const std::locale locale(LOCALE_fr_FR_UTF_8);
sstr.imbue(locale);
sstr << time_point;
return sstr.str();
}

template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
std::basic_stringstream<CharT> sstr;
const std::locale locale(LOCALE_ja_JP_UTF_8);
sstr.imbue(locale);
sstr << time_point;
return sstr.str();
}

template <class CharT>
static void test_c() {
using namespace std::literals::chrono_literals;

{ // Different durations
TEST_EQUAL(stream_c_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
SV("1970-01-01 01:00:00.000000042 +01"));

TEST_EQUAL(stream_c_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
SV("1970-01-01 01:00:00.000042 +01"));

TEST_EQUAL(stream_c_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
SV("1970-01-01 01:00:00.042 +01"));

TEST_EQUAL(
stream_c_locale<CharT>(std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
SV("1970-01-01 01:00:42 +01"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
SV("1970-02-12 01:00:00 +01"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
SV("1970-10-22 01:00:00 +01"));
}

{ // Daylight saving time switches
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.

// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
// ...
// 1 DE CE%sT 1980
// 1 E CE%sT
//
// ...
// R E 1979 1995 - S lastSu 1u 0 -
// R E 1981 ma - Mar lastSu 1u 1 S

// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.

// Start of daylight saving time
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
SV("1986-03-30 01:59:59 CET"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
SV("1986-03-30 03:00:00 CEST"));

// End of daylight saving time
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
SV("1986-09-28 02:59:59 CEST"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
SV("1986-09-28 02:00:00 CET"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
SV("1986-09-28 02:59:59 CET"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
SV("1986-09-28 03:00:00 CET"));
}

{ // offset pointer
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:00 +00s"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
SV("1969-12-31 23:59:18 +42s"));

TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:42 -42s"));
}
}

template <class CharT>
static void test_fr_FR() {
using namespace std::literals::chrono_literals;

{ // Different durations

TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
SV("1970-01-01 01:00:00,000000042 +01"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
SV("1970-01-01 01:00:00,000042 +01"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
SV("1970-01-01 01:00:00,042 +01"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
SV("1970-01-01 01:00:42 +01"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
SV("1970-02-12 01:00:00 +01"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
SV("1970-10-22 01:00:00 +01"));
}

{ // Daylight saving time switches
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.

// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
// ...
// 1 DE CE%sT 1980
// 1 E CE%sT
//
// ...
// R E 1979 1995 - S lastSu 1u 0 -
// R E 1981 ma - Mar lastSu 1u 1 S

// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.

// Start of daylight saving time
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
SV("1986-03-30 01:59:59 CET"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
SV("1986-03-30 03:00:00 CEST"));

// End of daylight saving time
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
SV("1986-09-28 02:59:59 CEST"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
SV("1986-09-28 02:00:00 CET"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
SV("1986-09-28 02:59:59 CET"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
SV("1986-09-28 03:00:00 CET"));
}

{ // offset pointer
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:00 +00s"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
SV("1969-12-31 23:59:18 +42s"));

TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:42 -42s"));
}
}

template <class CharT>
static void test_ja_JP() {
using namespace std::literals::chrono_literals;

{ // Different durations

TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
SV("1970-01-01 01:00:00.000000042 +01"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
SV("1970-01-01 01:00:00.000042 +01"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
SV("1970-01-01 01:00:00.042 +01"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
SV("1970-01-01 01:00:42 +01"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
SV("1970-02-12 01:00:00 +01"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
SV("1970-10-22 01:00:00 +01"));
}

{ // Daylight saving time switches
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.

// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
// ...
// 1 DE CE%sT 1980
// 1 E CE%sT
//
// ...
// R E 1979 1995 - S lastSu 1u 0 -
// R E 1981 ma - Mar lastSu 1u 1 S

// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.

// Start of daylight saving time
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
SV("1986-03-30 01:59:59 CET"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
SV("1986-03-30 03:00:00 CEST"));

// End of daylight saving time
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
SV("1986-09-28 02:59:59 CEST"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
SV("1986-09-28 02:00:00 CET"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
SV("1986-09-28 02:59:59 CET"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
SV("1986-09-28 03:00:00 CET"));
}

{ // offset pointer
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:00 +00s"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
SV("1969-12-31 23:59:18 +42s"));

TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:42 -42s"));
}
}

template <class CharT>
static void test() {
test_c<CharT>();
test_fr_FR<CharT>();
test_ja_JP<CharT>();
}

int main(int, char**) {
test<char>();

#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif

return 0;
}
7 changes: 7 additions & 0 deletions libcxx/utils/libcxx/test/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@
# This declaration is in the ostream header.
ExtraDeclarations["system_error"] = ["std::operator<<"]

# TODO MODULES avoid this work-around
# This is a work-around for the special math functions. They are declared in
# __math/special_functions.h. Adding this as an ExtraHeader works for the std
# module. However these functions are special; they are not available in the
# global namespace.
ExtraDeclarations["cmath"] = ["std::hermite", "std::hermitef", "std::hermitel"]

### ExtraHeader

# Adds extra headers file to scan
Expand Down
14 changes: 2 additions & 12 deletions lld/ELF/ScriptLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,10 @@ StringRef ScriptLexer::peek() {
return tok;
}

StringRef ScriptLexer::peek2() {
skip();
StringRef tok = next();
if (errorCount())
return "";
pos = pos - 2;
return tok;
}

bool ScriptLexer::consume(StringRef tok) {
if (peek() == tok) {
skip();
if (next() == tok)
return true;
}
--pos;
return false;
}

Expand Down
1 change: 0 additions & 1 deletion lld/ELF/ScriptLexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class ScriptLexer {
bool atEOF();
StringRef next();
StringRef peek();
StringRef peek2();
void skip();
bool consume(StringRef tok);
void expect(StringRef expect);
Expand Down
Loading