diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -172,7 +172,7 @@ } def TimeAPI : PublicAPI<"time.h"> { - let Types = ["time_t", "struct tm", "struct timespec", "clockid_t",]; + let Types = ["clock_t", "time_t", "struct tm", "struct timespec", "clockid_t",]; } def ErrnoAPI : PublicAPI<"errno.h"> { diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -443,12 +443,14 @@ # time.h entrypoints libc.src.time.asctime libc.src.time.asctime_r + libc.src.time.clock_gettime + libc.src.time.clock libc.src.time.difftime libc.src.time.gmtime libc.src.time.gmtime_r libc.src.time.mktime libc.src.time.nanosleep - libc.src.time.clock_gettime + libc.src.time.time # unistd.h entrypoints libc.src.unistd.environ diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -93,6 +93,7 @@ .llvm_libc_common_h .llvm-libc-macros.time_macros .llvm-libc-types.time_t + .llvm-libc-types.clock_t .llvm-libc-types.clockid_t .llvm-libc-types.struct_tm .llvm-libc-types.struct_timespec diff --git a/libc/include/llvm-libc-macros/linux/time-macros.h b/libc/include/llvm-libc-macros/linux/time-macros.h --- a/libc/include/llvm-libc-macros/linux/time-macros.h +++ b/libc/include/llvm-libc-macros/linux/time-macros.h @@ -21,4 +21,6 @@ #define CLOCK_REALTIME_ALARM 8 #define CLOCK_BOOTTIME_ALARM 9 +#define CLOCKS_PER_SEC 1000000 + #endif //__LLVM_LIBC_MACROS_LINUX_TIME_MACROS_H diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -16,6 +16,7 @@ add_header(blkcnt_t HDR blkcnt_t.h) add_header(blksize_t HDR blksize_t.h) add_header(cc_t HDR cc_t.h) +add_header(clock_t HDR clock_t.h) add_header(clockid_t HDR clockid_t.h) add_header(cnd_t HDR cnd_t.h) add_header(cookie_io_functions_t HDR cookie_io_functions_t.h DEPENDS .off64_t) diff --git a/libc/include/llvm-libc-types/clock_t.h b/libc/include/llvm-libc-types/clock_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/clock_t.h @@ -0,0 +1,14 @@ +//===-- Definition of clock_t type ----------------------------------------===// +// +// 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_TYPES_CLOCK_T_H__ +#define __LLVM_LIBC_TYPES_CLOCK_T_H__ + +typedef long clock_t; + +#endif // __LLVM_LIBC_TYPES_CLOCK_T_H__ diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -3,6 +3,7 @@ NamedType StructTmType = NamedType<"struct tm">; PtrType StructTmPtr = PtrType; PtrType TimeTTypePtr = PtrType; + NamedType ClockT = NamedType<"clock_t">; NamedType DivTType = NamedType<"div_t">; NamedType LDivTType = NamedType<"ldiv_t">; @@ -917,8 +918,9 @@ "time.h", [], // Macros [ // Types - StructTmType, - TimeTType, + ClockT, + StructTmType, + TimeTType, ], [], // Enumerations [ @@ -935,6 +937,11 @@ ArgSpec, ] >, + FunctionSpec< + "clock", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "difftime", RetValSpec, @@ -961,6 +968,11 @@ RetValSpec, [ArgSpec] >, + FunctionSpec< + "time", + RetValSpec, + [ArgSpec] + >, ] >; diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt --- a/libc/src/time/CMakeLists.txt +++ b/libc/src/time/CMakeLists.txt @@ -1,3 +1,7 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + add_object_library( time_utils SRCS @@ -103,3 +107,17 @@ libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_entrypoint_object( + time + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.time +) + +add_entrypoint_object( + clock + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.clock +) diff --git a/libc/src/time/clock.h b/libc/src/time/clock.h new file mode 100644 --- /dev/null +++ b/libc/src/time/clock.h @@ -0,0 +1,20 @@ +//===-- Implementation header of clock --------------------------*- 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_CLOCK_H +#define LLVM_LIBC_SRC_TIME_CLOCK_H + +#include + +namespace __llvm_libc { + +clock_t clock(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_TIME_CLOCK_H diff --git a/libc/src/time/linux/CMakeLists.txt b/libc/src/time/linux/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/time/linux/CMakeLists.txt @@ -0,0 +1,26 @@ +add_entrypoint_object( + time + SRCS + time.cpp + HDRS + ../time_func.h + DEPENDS + libc.include.time + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + clock + SRCS + clock.cpp + HDRS + ../clock.h + DEPENDS + libc.include.time + libc.include.sys_syscall + libc.src.__support.CPP.limits + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/time/linux/clock.cpp b/libc/src/time/linux/clock.cpp new file mode 100644 --- /dev/null +++ b/libc/src/time/linux/clock.cpp @@ -0,0 +1,49 @@ +//===-- Linux implementation of the clock function ------------------------===// +// +// 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/clock.h" + +#include "src/__support/CPP/limits.h" +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(clock_t, clock, ()) { + struct timespec ts; + long ret_val = __llvm_libc::syscall_impl( + SYS_clock_gettime, CLOCK_PROCESS_CPUTIME_ID, reinterpret_cast(&ts)); + if (ret_val < 0) { + errno = -ret_val; + return clock_t(-1); + } + + // The above syscall gets the CPU time in seconds plus nanoseconds. We should + // make sure that corresponding clocks can actually be represented by clock-t. + // The standard requires that we return clock_t(-1) if we cannot represent + // clocks as a clock_t value. + constexpr clock_t CLOCK_SECS_MAX = + cpp::numeric_limits::max() / CLOCKS_PER_SEC; + if (ts.tv_sec > CLOCK_SECS_MAX) + return clock_t(-1); + if (ts.tv_nsec / 1000000000 > CLOCK_SECS_MAX - ts.tv_sec) + return clock_t(-1); + + // For the integer computation converting tv_nsec to clocks to work + // correctly, we want CLOCKS_PER_SEC to be less than 1000000000. + static_assert(1000000000 > CLOCKS_PER_SEC, + "Expected CLOCKS_PER_SEC to be less than 1000000000."); + return clock_t(ts.tv_sec * CLOCKS_PER_SEC + + ts.tv_nsec / (1000000000 / CLOCKS_PER_SEC)); +} + +} // namespace __llvm_libc diff --git a/libc/src/time/linux/time.cpp b/libc/src/time/linux/time.cpp new file mode 100644 --- /dev/null +++ b/libc/src/time/linux/time.cpp @@ -0,0 +1,35 @@ +//===-- Linux implementation of the time function -------------------------===// +// +// 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/time_func.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(time_t, time, (time_t * tp)) { + // TODO: Use the Linux VDSO to fetch the time and avoid the syscall. + struct timespec ts; + long ret_val = __llvm_libc::syscall_impl(SYS_clock_gettime, CLOCK_REALTIME, + reinterpret_cast(&ts)); + if (ret_val < 0) { + errno = -ret_val; + return -1; + } + + if (tp != nullptr) + *tp = time_t(ts.tv_sec); + return time_t(ts.tv_sec); +} + +} // namespace __llvm_libc diff --git a/libc/src/time/time_func.h b/libc/src/time/time_func.h new file mode 100644 --- /dev/null +++ b/libc/src/time/time_func.h @@ -0,0 +1,22 @@ +//===-- Implementation header of time ---------------------------*- 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_TIME_FUNC_H +#define LLVM_LIBC_SRC_TIME_TIME_FUNC_H + +#include + +// Note this header file is named time_func.h to avoid conflicts with the +// public header file time.h. +namespace __llvm_libc { + +time_t time(time_t *tp); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_TIME_TIME_FUNC_H diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt --- a/libc/test/src/time/CMakeLists.txt +++ b/libc/test/src/time/CMakeLists.txt @@ -105,3 +105,27 @@ libc.include.time libc.src.time.nanosleep ) + +add_libc_unittest( + time_test + SUITE + libc_time_unittests + SRCS + time_test.cpp + DEPENDS + libc.include.errno + libc.include.time + libc.src.time.time +) + +add_libc_unittest( + clock_test + SUITE + libc_time_unittests + SRCS + clock_test.cpp + DEPENDS + libc.include.errno + libc.include.time + libc.src.time.clock +) diff --git a/libc/test/src/time/clock_test.cpp b/libc/test/src/time/clock_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/time/clock_test.cpp @@ -0,0 +1,22 @@ +//===-- Unittests for clock -----------------------------------------------===// +// +// 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/clock.h" +#include "utils/UnitTest/Test.h" + +#include +#include +#include + +TEST(LlvmLibcClockTest, SmokeTest) { + clock_t c1 = __llvm_libc::clock(); + ASSERT_GT(c1, clock_t(0)); + + clock_t c2 = __llvm_libc::clock(); + ASSERT_GE(c2, c1); +} diff --git a/libc/test/src/time/time_test.cpp b/libc/test/src/time/time_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/time/time_test.cpp @@ -0,0 +1,24 @@ +//===-- Unittests for time ------------------------------------------------===// +// +// 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/time_func.h" +#include "utils/UnitTest/Test.h" + +#include +#include +#include + +TEST(LlvmLibcTimeTest, SmokeTest) { + time_t t1; + time_t t2 = __llvm_libc::time(&t1); + ASSERT_EQ(t1, t2); + ASSERT_GT(t1, time_t(0)); + + time_t t3 = __llvm_libc::time(nullptr); + ASSERT_GE(t3, t1); +}