Skip to content

[libc] Implement timespec_get #116102

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 2, 2024
Merged

Conversation

petrhosek
Copy link
Member

@petrhosek petrhosek commented Nov 13, 2024

timespec_get is C standard counterpart to POSIX clock_gettime. On Linux we simply use clock_gettime. On baremetal we introduce a new external API __llvm_libc_timespec_get_utc that should be implemented by the vendor.

`timespec_get` is C standard counterpart to POSIX `clock_gettime`. On
Linux we simply use `clock_gettime`. On baremetal we introduce a new
external API `__llvm_libc_time_utc_get` that should be implemented by
the vendor.
@llvmbot
Copy link
Member

llvmbot commented Nov 13, 2024

@llvm/pr-subscribers-libc

Author: Petr Hosek (petrhosek)

Changes

timespec_get is C standard counterpart to POSIX clock_gettime. On Linux we simply use clock_gettime. On baremetal we introduce a new external API __llvm_libc_time_utc_get that should be implemented by the vendor.


Full diff: https://github.com/llvm/llvm-project/pull/116102.diff

18 Files Affected:

  • (modified) libc/config/baremetal/arm/entrypoints.txt (+1)
  • (modified) libc/config/baremetal/riscv/entrypoints.txt (+1)
  • (modified) libc/config/linux/aarch64/entrypoints.txt (+1)
  • (modified) libc/config/linux/riscv/entrypoints.txt (+1)
  • (modified) libc/config/linux/x86_64/entrypoints.txt (+1)
  • (modified) libc/include/llvm-libc-macros/time-macros.h (+2)
  • (modified) libc/newhdrgen/yaml/time.yaml (+7)
  • (modified) libc/src/__support/OSUtil/baremetal/CMakeLists.txt (+2)
  • (added) libc/src/__support/OSUtil/baremetal/time.cpp (+21)
  • (added) libc/src/__support/OSUtil/baremetal/time.h (+21)
  • (modified) libc/src/time/CMakeLists.txt (+7)
  • (added) libc/src/time/baremetal/CMakeLists.txt (+11)
  • (added) libc/src/time/baremetal/timespec_get.cpp (+25)
  • (modified) libc/src/time/linux/CMakeLists.txt (+13)
  • (added) libc/src/time/linux/timespec_get.cpp (+31)
  • (added) libc/src/time/timespec_get.h (+21)
  • (modified) libc/test/src/time/CMakeLists.txt (+10)
  • (added) libc/test/src/time/timespec_get_test.cpp (+32)
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index b85ae1119345dd..9027717acb4dae 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 199a030ee6371e..afae2b3e082be3 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 74ca3742977a5f..effa5b12d87ac4 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -999,6 +999,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 5419462d4f5b3b..5a48baf104159f 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -937,6 +937,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 957e28bd66cc4c..7365b53e8add82 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1082,6 +1082,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 6d49ed484d5d48..02f2f7bfa7051d 100644
--- a/libc/include/llvm-libc-macros/time-macros.h
+++ b/libc/include/llvm-libc-macros/time-macros.h
@@ -7,4 +7,6 @@
 #include "linux/time-macros.h"
 #endif
 
+#define TIME_UTC 1
+
 #endif // LLVM_LIBC_MACROS_TIME_MACROS_H
diff --git a/libc/newhdrgen/yaml/time.yaml b/libc/newhdrgen/yaml/time.yaml
index 69b40bef3160dd..3f745e5ee33868 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/src/__support/OSUtil/baremetal/CMakeLists.txt b/libc/src/__support/OSUtil/baremetal/CMakeLists.txt
index 8f9200ad0bd96e..5b02709315f9b1 100644
--- a/libc/src/__support/OSUtil/baremetal/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/baremetal/CMakeLists.txt
@@ -3,8 +3,10 @@ add_object_library(
   SRCS
     io.cpp
     exit.cpp
+    time.cpp
   HDRS
     io.h
+    time.h
   DEPENDS
     libc.src.__support.common
     libc.src.__support.CPP.string_view
diff --git a/libc/src/__support/OSUtil/baremetal/time.cpp b/libc/src/__support/OSUtil/baremetal/time.cpp
new file mode 100644
index 00000000000000..63f0624b4cda4e
--- /dev/null
+++ b/libc/src/__support/OSUtil/baremetal/time.cpp
@@ -0,0 +1,21 @@
+//===---------- Baremetal implementation of time utils ----------*- 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 "time.h"
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+extern "C" int __llvm_libc_time_utc_get(struct timespec *ts);
+
+int time_utc_get(struct timespec *ts) {
+  return __llvm_libc_time_utc_get(ts);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/OSUtil/baremetal/time.h b/libc/src/__support/OSUtil/baremetal/time.h
new file mode 100644
index 00000000000000..8c3ded377c7376
--- /dev/null
+++ b/libc/src/__support/OSUtil/baremetal/time.h
@@ -0,0 +1,21 @@
+//===---------- Baremetal implementation of time utils ----------*- 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_BAREMETAL_TIME_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_BAREMETAL_TIME_H
+
+#include "include/llvm-libc-types/struct_timespec.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int time_utc_get(struct timespec *ts);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_BAREMETAL_IO_H
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index b3318e7ca87fa5..f18e74a15e6fc2 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 00000000000000..4bddfa691d7c2a
--- /dev/null
+++ b/libc/src/time/baremetal/CMakeLists.txt
@@ -0,0 +1,11 @@
+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.OSUtil.osutil
+)
diff --git a/libc/src/time/baremetal/timespec_get.cpp b/libc/src/time/baremetal/timespec_get.cpp
new file mode 100644
index 00000000000000..93ae4e9c9d1731
--- /dev/null
+++ b/libc/src/time/baremetal/timespec_get.cpp
@@ -0,0 +1,25 @@
+//===-- 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"
+#include "src/__support/OSUtil/baremetal/time.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, timespec_get, (struct timespec *ts, int base)) {
+  if (base != TIME_UTC) {
+    return 0;
+  }
+
+  return time_utc_get(ts);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/linux/CMakeLists.txt b/libc/src/time/linux/CMakeLists.txt
index c15fb44ad5d123..31fd7d1e64c85c 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 00000000000000..fa236cd3eaebee
--- /dev/null
+++ b/libc/src/time/linux/timespec_get.cpp
@@ -0,0 +1,31 @@
+//===-- 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)) {
+  if (base != TIME_UTC) {
+    return 0;
+  }
+
+  auto result = internal::clock_gettime(CLOCK_REALTIME, 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 00000000000000..32ba159a941606
--- /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 "src/__support/macros/config.h"
+#include <time.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 bba01f063fed27..7151526b72b26d 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 00000000000000..5bbd3db651d8a7
--- /dev/null
+++ b/libc/test/src/time/timespec_get_test.cpp
@@ -0,0 +1,32 @@
+//===-- 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 "src/__support/macros/properties/architectures.h"
+#include "src/time/timespec_get.h"
+#include "test/UnitTest/Test.h"
+
+#include <time.h>
+
+TEST(LlvmLibcTimespecGet, Utc) {
+#ifndef LIBC_TARGET_ARCH_IS_GPU
+  timespec ts;
+  int result;
+  result = LIBC_NAMESPACE::timespec_get(&ts, TIME_UTC);
+  ASSERT_EQ(result, TIME_UTC);
+  ASSERT_GT(ts.tv_sec, time_t(0));
+#endif
+}
+
+TEST(LlvmLibcTimespecGet, Unknown) {
+#ifndef LIBC_TARGET_ARCH_IS_GPU
+  timespec ts;
+  int result;
+  result = LIBC_NAMESPACE::timespec_get(&ts, 0);
+  ASSERT_EQ(result, 0);
+#endif
+}

Copy link

github-actions bot commented Nov 13, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@nickdesaulniers nickdesaulniers left a comment

Choose a reason for hiding this comment

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

Thanks for the patch!

int result;
result = LIBC_NAMESPACE::timespec_get(&ts, 0);
ASSERT_EQ(result, 0);
#endif
Copy link
Member

Choose a reason for hiding this comment

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

How come these tests are disabled on the GPU?

Copy link
Member Author

Choose a reason for hiding this comment

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

GPU doesn't support UTC clock, it only implements monotonic clock. I could do the same for TIME_MONOTONIC and for TIME_UTC just return 0. @jhuber6 what do you think?

namespace LIBC_NAMESPACE_DECL {

LLVM_LIBC_FUNCTION(int, timespec_get, (struct timespec * ts, int base)) {
if (base != TIME_UTC) {
Copy link
Member

Choose a reason for hiding this comment

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

Please file a TODO (then link to it from a comment in the sources) about supporting the other specified bases (TIME_MONOTONIC, TIME_ACTIVE, and TIME_THREAD_ACTIVE). Especially since 7.29.2.6.5 recommends:

Because of its wider value range and improved indications on error, timespec_get with time base TIME_ACTIVE should be used instead of clock by new code whenever possible.

Copy link
Member Author

Choose a reason for hiding this comment

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

I wasn't aware of these, looks they were added in the latest standard (and aren't even mentioned on https://en.cppreference.com/w/c/chrono/timespec_get). I implemented all of them for Linux since it was straightforward.

petrhosek added a commit to petrhosek/llvm-project that referenced this pull request Nov 22, 2024
clock_gettime is a POSIX API that may not be available on platforms like
baremetal; timespec_get is the C11 equivalent. This change adds support
for using timespec_get instead of clock_gettime to improve compatibility
with non-POSIX platforms. For now, this is only enabled with LLVM libc
which implemented timespec_get in llvm#116102, but in the future this can be
expanded to other platforms.
Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines +1679 to +1686
FunctionSpec<
"timespec_get",
RetValSpec<IntType>,
[
ArgSpec<StructTimeSpecPtr>,
ArgSpec<IntType>,
]
>,
Copy link
Contributor

Choose a reason for hiding this comment

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

since #117220 is close to landing, it might be best to skip adding anything to the old headergen files.

@petrhosek petrhosek merged commit b8daa45 into llvm:main Dec 2, 2024
7 checks passed
bherrera pushed a commit to misttech/integration that referenced this pull request Dec 6, 2024
…timespec_get is defined

After llvm/llvm-project#116102 we shouldn't need
that workaround anymore.

Original-Bug: 382538608
Original-Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/recipes/+/1169452
Original-Revision: d3ff723f1c9a4cfc412452714d61b8169a4bff36
GitOrigin-RevId: 3f506a9f38779518d8bf23fa7f6d45d87af6ace6
Change-Id: I69d8386c90aaa0ef5c529fc0160a699762f878a2
petrhosek added a commit that referenced this pull request Dec 17, 2024
clock_gettime is a POSIX API that may not be available on platforms like
baremetal; timespec_get is the C11 equivalent. This change adds support
for using timespec_get instead of clock_gettime to improve compatibility
with non-POSIX platforms. For now, this is only enabled with LLVM libc
which implemented timespec_get in #116102, but in the future this can be
expanded to other platforms.

Related to #84879.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants