diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index b85ae1119345d..9027717acb4da 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -209,6 +209,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.time.gmtime libc.src.time.gmtime_r libc.src.time.mktime + libc.src.time.timespec_get # internal entrypoints libc.startup.baremetal.init diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt index 199a030ee6371..afae2b3e082be 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -205,6 +205,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.time.gmtime libc.src.time.gmtime_r libc.src.time.mktime + libc.src.time.timespec_get # internal entrypoints libc.startup.baremetal.init diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 08b5072499da6..aa0b8ba0490e9 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -1000,6 +1000,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.time.mktime libc.src.time.nanosleep libc.src.time.time + libc.src.time.timespec_get # unistd.h entrypoints libc.src.unistd.__llvm_libc_syscall diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 3baf734d3d819..fedc5002d8c24 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -939,6 +939,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.time.mktime libc.src.time.nanosleep libc.src.time.time + libc.src.time.timespec_get # unistd.h entrypoints libc.src.unistd.__llvm_libc_syscall diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index d0651c06b930a..af7f429561fe0 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1083,6 +1083,7 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.time.mktime libc.src.time.nanosleep libc.src.time.time + libc.src.time.timespec_get # locale.h entrypoints libc.src.locale.localeconv diff --git a/libc/include/llvm-libc-macros/time-macros.h b/libc/include/llvm-libc-macros/time-macros.h index 6d49ed484d5d4..445d8b3e837ed 100644 --- a/libc/include/llvm-libc-macros/time-macros.h +++ b/libc/include/llvm-libc-macros/time-macros.h @@ -7,4 +7,9 @@ #include "linux/time-macros.h" #endif +#define TIME_UTC 1 +#define TIME_MONOTONIC 2 +#define TIME_ACTIVE 3 +#define TIME_THREAD_ACTIVE 4 + #endif // LLVM_LIBC_MACROS_TIME_MACROS_H diff --git a/libc/newhdrgen/yaml/time.yaml b/libc/newhdrgen/yaml/time.yaml index 69b40bef3160d..3f745e5ee3386 100644 --- a/libc/newhdrgen/yaml/time.yaml +++ b/libc/newhdrgen/yaml/time.yaml @@ -96,3 +96,10 @@ functions: return_type: time_t arguments: - type: time_t * + - name: timespec_get + standard: + - stdc + return_type: int + arguments: + - type: struct timespec * + - type: int diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index 4fa057da1cf13..493ca1a6440df 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -1676,6 +1676,14 @@ def StdC : StandardSpec<"stdc"> { RetValSpec, [ArgSpec] >, + FunctionSpec< + "timespec_get", + RetValSpec, + [ + ArgSpec, + ArgSpec, + ] + >, ] >; diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt index b3318e7ca87fa..f18e74a15e6fc 100644 --- a/libc/src/time/CMakeLists.txt +++ b/libc/src/time/CMakeLists.txt @@ -111,6 +111,13 @@ add_entrypoint_object( .${LIBC_TARGET_OS}.time ) +add_entrypoint_object( + timespec_get + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.timespec_get +) + add_entrypoint_object( clock ALIAS diff --git a/libc/src/time/baremetal/CMakeLists.txt b/libc/src/time/baremetal/CMakeLists.txt new file mode 100644 index 0000000000000..bf0d42e265d6f --- /dev/null +++ b/libc/src/time/baremetal/CMakeLists.txt @@ -0,0 +1,10 @@ +add_entrypoint_object( + timespec_get + SRCS + timespec_get.cpp + HDRS + ../timespec_get.h + DEPENDS + libc.hdr.time_macros + libc.hdr.types.struct_timespec +) diff --git a/libc/src/time/baremetal/timespec_get.cpp b/libc/src/time/baremetal/timespec_get.cpp new file mode 100644 index 0000000000000..173fe0e06e3a9 --- /dev/null +++ b/libc/src/time/baremetal/timespec_get.cpp @@ -0,0 +1,27 @@ +//===-- Implementation of timespec_get for baremetal ----------------------===// +// +// 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/time/timespec_get.h" +#include "hdr/time_macros.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +extern "C" bool __llvm_libc_timespec_get_utc(struct timespec *ts); + +LLVM_LIBC_FUNCTION(int, timespec_get, (struct timespec * ts, int base)) { + if (base != TIME_UTC) + return 0; + + if (!__llvm_libc_timespec_get_utc(ts)) + return 0; + return base; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/gpu/CMakeLists.txt b/libc/src/time/gpu/CMakeLists.txt index c9b4562815801..8da5d3a22f5a0 100644 --- a/libc/src/time/gpu/CMakeLists.txt +++ b/libc/src/time/gpu/CMakeLists.txt @@ -44,3 +44,15 @@ add_entrypoint_object( libc.hdr.types.struct_timespec .time_utils ) + +add_entrypoint_object( + timespec_get + SRCS + timespec_get.cpp + HDRS + ../timespec_get.h + DEPENDS + libc.hdr.time_macros + libc.hdr.types.struct_timespec + .time_utils +) diff --git a/libc/src/time/gpu/timespec_get.cpp b/libc/src/time/gpu/timespec_get.cpp new file mode 100644 index 0000000000000..f4ef328a83122 --- /dev/null +++ b/libc/src/time/gpu/timespec_get.cpp @@ -0,0 +1,29 @@ +//===-- Implementation of timespec_get for gpu ----------------------------===// +// +// 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/time/timespec_get.h" +#include "hdr/time_macros.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, timespec_get, (struct timespec * ts, int base)) { + if (base != TIME_MONOTONIC || !ts) + return 0; + + uint64_t ns_per_tick = TICKS_PER_SEC / GPU_CLOCKS_PER_SEC; + uint64_t ticks = gpu::fixed_frequency_clock(); + + ts->tv_nsec = (ticks * ns_per_tick) % TICKS_PER_SEC; + ts->tv_sec = (ticks * ns_per_tick) / TICKS_PER_SEC; + + return base; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/linux/CMakeLists.txt b/libc/src/time/linux/CMakeLists.txt index c15fb44ad5d12..31fd7d1e64c85 100644 --- a/libc/src/time/linux/CMakeLists.txt +++ b/libc/src/time/linux/CMakeLists.txt @@ -11,6 +11,19 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + timespec_get + SRCS + timespec_get.cpp + HDRS + ../timespec_get.h + DEPENDS + libc.hdr.time_macros + libc.hdr.types.struct_timespec + libc.src.__support.time.linux.clock_gettime + libc.src.errno.errno +) + add_entrypoint_object( clock SRCS diff --git a/libc/src/time/linux/timespec_get.cpp b/libc/src/time/linux/timespec_get.cpp new file mode 100644 index 0000000000000..ba9f8eb2e4426 --- /dev/null +++ b/libc/src/time/linux/timespec_get.cpp @@ -0,0 +1,45 @@ +//===-- Implementation of timespec_get for Linux --------------------------===// +// +// 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/time/timespec_get.h" +#include "hdr/time_macros.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/time/linux/clock_gettime.h" +#include "src/errno/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, timespec_get, (struct timespec * ts, int base)) { + clockid_t clockid; + switch (base) { + case TIME_UTC: + clockid = CLOCK_REALTIME; + break; + case TIME_MONOTONIC: + clockid = CLOCK_MONOTONIC; + break; + case TIME_ACTIVE: + clockid = CLOCK_PROCESS_CPUTIME_ID; + break; + case TIME_THREAD_ACTIVE: + clockid = CLOCK_THREAD_CPUTIME_ID; + break; + default: + return 0; + } + + auto result = internal::clock_gettime(clockid, ts); + if (!result.has_value()) { + libc_errno = result.error(); + return 0; + } + return base; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/timespec_get.h b/libc/src/time/timespec_get.h new file mode 100644 index 0000000000000..2a176ff09ce4d --- /dev/null +++ b/libc/src/time/timespec_get.h @@ -0,0 +1,21 @@ +//===-- Implementation header of timespec_get -------------------*- 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_TIME_TIMESPEC_GET_H +#define LLVM_LIBC_SRC_TIME_TIMESPEC_GET_H + +#include "hdr/types/struct_timespec.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int timespec_get(struct timespec *ts, int base); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_TIME_TIMESPEC_GET_H diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt index bba01f063fed2..7151526b72b26 100644 --- a/libc/test/src/time/CMakeLists.txt +++ b/libc/test/src/time/CMakeLists.txt @@ -162,6 +162,16 @@ add_libc_unittest( libc.src.errno.errno ) +add_libc_test( + timespec_get_test + SUITE + libc_time_unittests + SRCS + timespec_get_test.cpp + DEPENDS + libc.src.time.timespec_get +) + add_libc_test( clock_test SUITE diff --git a/libc/test/src/time/timespec_get_test.cpp b/libc/test/src/time/timespec_get_test.cpp new file mode 100644 index 0000000000000..327bfefe934c1 --- /dev/null +++ b/libc/test/src/time/timespec_get_test.cpp @@ -0,0 +1,46 @@ +//===-- Unittests for timespec_get ----------------------------------------===// +// +// 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 "hdr/time_macros.h" +#include "hdr/types/struct_timespec.h" +#include "src/__support/macros/properties/architectures.h" +#include "src/time/timespec_get.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcTimespecGet, Utc) { + timespec ts; + int result; + result = LIBC_NAMESPACE::timespec_get(&ts, TIME_UTC); +#ifdef LIBC_TARGET_ARCH_IS_GPU + ASSERT_EQ(result, 0); +#else + ASSERT_EQ(result, TIME_UTC); + ASSERT_GT(ts.tv_sec, time_t(0)); +#endif +} + +TEST(LlvmLibcTimespecGet, Monotonic) { + timespec ts1, ts2; + int result; + result = LIBC_NAMESPACE::timespec_get(&ts1, TIME_MONOTONIC); + ASSERT_EQ(result, TIME_MONOTONIC); + ASSERT_GT(ts1.tv_sec, time_t(0)); + result = LIBC_NAMESPACE::timespec_get(&ts2, TIME_MONOTONIC); + ASSERT_EQ(result, TIME_MONOTONIC); + ASSERT_GE(ts2.tv_sec, ts1.tv_sec); // The monotonic time should increase. + if (ts2.tv_sec == ts1.tv_sec) { + ASSERT_GE(ts2.tv_nsec, ts1.tv_nsec); + } +} + +TEST(LlvmLibcTimespecGet, Unknown) { + timespec ts; + int result; + result = LIBC_NAMESPACE::timespec_get(&ts, 0); + ASSERT_EQ(result, 0); +}