Skip to content

Commit

Permalink
[libc] Implement timespec_get (llvm#116102)
Browse files Browse the repository at this point in the history
`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.
  • Loading branch information
petrhosek authored Dec 2, 2024
1 parent 0c98776 commit b8daa45
Show file tree
Hide file tree
Showing 18 changed files with 245 additions and 0 deletions.
1 change: 1 addition & 0 deletions libc/config/baremetal/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/baremetal/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions libc/include/llvm-libc-macros/time-macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions libc/newhdrgen/yaml/time.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,14 @@ def StdC : StandardSpec<"stdc"> {
RetValSpec<TimeTType>,
[ArgSpec<TimeTTypePtr>]
>,
FunctionSpec<
"timespec_get",
RetValSpec<IntType>,
[
ArgSpec<StructTimeSpecPtr>,
ArgSpec<IntType>,
]
>,
]
>;

Expand Down
7 changes: 7 additions & 0 deletions libc/src/time/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions libc/src/time/baremetal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
27 changes: 27 additions & 0 deletions libc/src/time/baremetal/timespec_get.cpp
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions libc/src/time/gpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
29 changes: 29 additions & 0 deletions libc/src/time/gpu/timespec_get.cpp
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions libc/src/time/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 45 additions & 0 deletions libc/src/time/linux/timespec_get.cpp
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions libc/src/time/timespec_get.h
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions libc/test/src/time/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 46 additions & 0 deletions libc/test/src/time/timespec_get_test.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit b8daa45

Please sign in to comment.