Skip to content

Commit e6cf5d2

Browse files
Reapply "[libc][windows] start time API implementation (llvm#117775)" (llvm#118886)
1 parent 63dfe70 commit e6cf5d2

34 files changed

+331
-88
lines changed

libc/config/windows/entrypoints.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ set(TARGET_LIBC_ENTRYPOINTS
9595

9696
# errno.h entrypoints
9797
libc.src.errno.errno
98+
99+
# time.h entrypoints
100+
libc.src.time.time
98101
)
99102

100103
set(TARGET_LIBM_ENTRYPOINTS

libc/hdr/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,18 @@ add_proxy_header_library(
135135
libc.include.llvm-libc-macros.unistd_macros
136136
)
137137

138+
if (WIN32)
139+
set(windows_addtional_time_macros libc.include.llvm-libc-macros.windows.time_macros_ext)
140+
else()
141+
set(windows_addtional_time_macros "")
142+
endif()
143+
138144
add_proxy_header_library(
139145
time_macros
140146
HDRS
141147
time_macros.h
148+
DEPENDS
149+
${windows_addtional_time_macros}
142150
FULL_BUILD_DEPENDS
143151
libc.include.time
144152
libc.include.llvm-libc-macros.time_macros

libc/hdr/time_macros.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@
1919

2020
#endif // LLVM_LIBC_FULL_BUILD
2121

22+
// TODO: For now, on windows, let's always include the extension header.
23+
// We will need to decide how to export this header.
24+
#ifdef _WIN32
25+
#include "include/llvm-libc-macros/windows/time-macros-ext.h"
26+
#endif // _WIN32
27+
2228
#endif // LLVM_LIBC_HDR_TIME_MACROS_H

libc/hdr/types/clockid_t.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
#ifndef LLVM_LIBC_HDR_TYPES_CLOCKID_T_H
1010
#define LLVM_LIBC_HDR_TYPES_CLOCKID_T_H
1111

12-
#ifdef LIBC_FULL_BUILD
12+
// TODO: we will need to decide how to export extension to windows.
13+
#if defined(LIBC_FULL_BUILD) || defined(_WIN32)
1314

1415
#include "include/llvm-libc-types/clockid_t.h"
1516

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
add_header(
2+
time_macros_ext
3+
HDR
4+
time-macros-ext.h
5+
)
6+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//===-- Windows Time Macros Extension -------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_MACROS_WINDOWS_TIME_MACROS_EXT_H
10+
#define LLVM_LIBC_MACROS_WINDOWS_TIME_MACROS_EXT_H
11+
12+
#define CLOCK_MONOTONIC 0
13+
#define CLOCK_REALTIME 1
14+
#define CLOCK_PROCESS_CPUTIME_ID 2
15+
#define CLOCK_THREAD_CPUTIME_ID 3
16+
17+
#endif // LLVM_LIBC_MACROS_WINDOWS_TIME_MACROS_EXT_H

libc/src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_subdirectory(stdio)
1313
add_subdirectory(stdlib)
1414
add_subdirectory(string)
1515
add_subdirectory(wchar)
16+
add_subdirectory(time)
1617

1718
if(${LIBC_TARGET_OS} STREQUAL "linux")
1819
add_subdirectory(dirent)
@@ -40,5 +41,4 @@ add_subdirectory(setjmp)
4041
add_subdirectory(signal)
4142
add_subdirectory(spawn)
4243
add_subdirectory(threads)
43-
add_subdirectory(time)
4444
add_subdirectory(locale)
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
2-
add_subdirectory(${LIBC_TARGET_OS})
3-
endif()
4-
51
add_header_library(
62
units
73
HDRS
@@ -10,3 +6,16 @@ add_header_library(
106
libc.src.__support.common
117
libc.hdr.types.time_t
128
)
9+
10+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
11+
add_subdirectory(${LIBC_TARGET_OS})
12+
else()
13+
return()
14+
endif()
15+
16+
add_object_library(
17+
clock_gettime
18+
ALIAS
19+
DEPENDS
20+
libc.src.__support.time.${LIBC_TARGET_OS}.clock_gettime
21+
)

libc/src/__support/time/linux/clock_gettime.h renamed to libc/src/__support/time/clock_gettime.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,17 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
10-
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_TIME_CLOCK_GETTIME_H
10+
#define LLVM_LIBC_SRC___SUPPORT_TIME_CLOCK_GETTIME_H
1111

1212
#include "hdr/types/clockid_t.h"
1313
#include "hdr/types/struct_timespec.h"
1414
#include "src/__support/error_or.h"
1515

16-
#if defined(SYS_clock_gettime64)
17-
#include <linux/time_types.h>
18-
#endif
19-
2016
namespace LIBC_NAMESPACE_DECL {
2117
namespace internal {
2218
ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts);
2319
} // namespace internal
2420
} // namespace LIBC_NAMESPACE_DECL
2521

26-
#endif // LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_GETTIME_H
22+
#endif // LLVM_LIBC_SRC___SUPPORT_TIME_CLOCK_GETTIME_H
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
add_object_library(
2+
time_utils
3+
SRCS
4+
time_utils.cpp
5+
HDRS
6+
time_utils.h
7+
DEPENDS
8+
libc.hdr.types.clock_t
9+
libc.hdr.time_macros
10+
)
11+
12+
add_object_library(
13+
clock_gettime
14+
SRCS
15+
clock_gettime.cpp
16+
HDRS
17+
../clock_gettime.h
18+
DEPENDS
19+
libc.hdr.types.clockid_t
20+
libc.hdr.types.struct_timespec
21+
.time_utils
22+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===---------- GPU implementation of the clock_gettime function ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/time/clock_gettime.h"
10+
11+
#include "src/__support/common.h"
12+
#include "src/__support/macros/config.h"
13+
#include "src/__support/time/clock_gettime.h"
14+
#include "src/__support/time/gpu/time_utils.h"
15+
16+
namespace LIBC_NAMESPACE_DECL {
17+
namespace internal {
18+
constexpr uint64_t TICKS_PER_SEC = 1000000000UL;
19+
20+
ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
21+
if (clockid != CLOCK_MONOTONIC || !ts)
22+
return cpp::unexpected(-1);
23+
24+
uint64_t ns_per_tick = TICKS_PER_SEC / GPU_CLOCKS_PER_SEC;
25+
uint64_t ticks = gpu::fixed_frequency_clock();
26+
27+
ts->tv_nsec = (ticks * ns_per_tick) % TICKS_PER_SEC;
28+
ts->tv_sec = (ticks * ns_per_tick) / TICKS_PER_SEC;
29+
30+
return 0;
31+
}
32+
} // namespace internal
33+
} // namespace LIBC_NAMESPACE_DECL

libc/src/__support/time/linux/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
add_object_library(
22
clock_gettime
33
HDRS
4-
clock_gettime.h
4+
../clock_gettime.h
55
SRCS
66
clock_gettime.cpp
77
DEPENDS

libc/src/__support/time/linux/clock_conversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#define LLVM_LIBC_SRC___SUPPORT_TIME_LINUX_CLOCK_CONVERSION_H
1111

1212
#include "src/__support/macros/config.h"
13-
#include "src/__support/time/linux/clock_gettime.h"
13+
#include "src/__support/time/clock_gettime.h"
1414
#include "src/__support/time/units.h"
1515

1616
namespace LIBC_NAMESPACE_DECL {

libc/src/__support/time/linux/clock_gettime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include "src/__support/time/linux/clock_gettime.h"
9+
#include "src/__support/time/clock_gettime.h"
1010
#include "hdr/types/clockid_t.h"
1111
#include "hdr/types/struct_timespec.h"
1212
#include "src/__support/OSUtil/linux/vdso.h"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
add_object_library(
2+
clock_gettime
3+
HDRS
4+
../clock_gettime.h
5+
SRCS
6+
clock_gettime.cpp
7+
DEPENDS
8+
libc.hdr.types.struct_timespec
9+
libc.hdr.types.clockid_t
10+
libc.hdr.errno_macros
11+
libc.src.__support.common
12+
libc.src.__support.error_or
13+
libc.src.__support.OSUtil.osutil
14+
libc.src.__support.CPP.atomic
15+
libc.src.__support.CPP.limits
16+
)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//===--- clock_gettime windows implementation -------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "hdr/time_macros.h"
10+
11+
#include "src/__support/CPP/atomic.h"
12+
#include "src/__support/CPP/bit.h"
13+
#include "src/__support/CPP/limits.h"
14+
#include "src/__support/macros/optimization.h"
15+
#include "src/__support/time/clock_gettime.h"
16+
#include "src/__support/time/units.h"
17+
18+
#define WIN32_LEAN_AND_MEAN
19+
#define NOMINMAX
20+
#include <Windows.h>
21+
22+
namespace LIBC_NAMESPACE_DECL {
23+
namespace internal {
24+
static long long get_ticks_per_second() {
25+
static cpp::Atomic<long long> frequency = 0;
26+
// Relaxed ordering is enough. It is okay to record the frequency multiple
27+
// times. The store operation itself is atomic and the value must propagate
28+
// as required by cache coherence.
29+
auto freq = frequency.load(cpp::MemoryOrder::RELAXED);
30+
if (!freq) {
31+
[[clang::uninitialized]] LARGE_INTEGER buffer;
32+
// On systems that run Windows XP or later, the function will always
33+
// succeed and will thus never return zero.
34+
::QueryPerformanceFrequency(&buffer);
35+
frequency.store(buffer.QuadPart, cpp::MemoryOrder::RELAXED);
36+
return buffer.QuadPart;
37+
}
38+
return freq;
39+
}
40+
41+
ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
42+
using namespace time_units;
43+
constexpr unsigned long long HNS_PER_SEC = 1_s_ns / 100ULL;
44+
constexpr long long SEC_LIMIT =
45+
cpp::numeric_limits<decltype(ts->tv_sec)>::max();
46+
ErrorOr<int> ret = 0;
47+
switch (clockid) {
48+
default:
49+
ret = cpp::unexpected(EINVAL);
50+
break;
51+
52+
case CLOCK_MONOTONIC: {
53+
// see
54+
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
55+
// Is the performance counter monotonic (non-decreasing)?
56+
// Yes. QPC does not go backward.
57+
[[clang::uninitialized]] LARGE_INTEGER buffer;
58+
// On systems that run Windows XP or later, the function will always
59+
// succeed and will thus never return zero.
60+
::QueryPerformanceCounter(&buffer);
61+
long long freq = get_ticks_per_second();
62+
long long ticks = buffer.QuadPart;
63+
long long tv_sec = ticks / freq;
64+
long long tv_nsec = (ticks % freq) * 1_s_ns / freq;
65+
if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) {
66+
ret = cpp::unexpected(EOVERFLOW);
67+
break;
68+
}
69+
ts->tv_sec = static_cast<decltype(ts->tv_sec)>(tv_sec);
70+
ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(tv_nsec);
71+
break;
72+
}
73+
case CLOCK_REALTIME: {
74+
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
75+
// GetSystemTimePreciseAsFileTime
76+
// This function is best suited for high-resolution time-of-day
77+
// measurements, or time stamps that are synchronized to UTC
78+
[[clang::uninitialized]] FILETIME file_time;
79+
[[clang::uninitialized]] ULARGE_INTEGER time;
80+
::GetSystemTimePreciseAsFileTime(&file_time);
81+
time.LowPart = file_time.dwLowDateTime;
82+
time.HighPart = file_time.dwHighDateTime;
83+
84+
// adjust to POSIX epoch (from Jan 1, 1601 to Jan 1, 1970)
85+
constexpr unsigned long long POSIX_TIME_SHIFT =
86+
(11644473600ULL * HNS_PER_SEC);
87+
if (LIBC_UNLIKELY(POSIX_TIME_SHIFT > time.QuadPart)) {
88+
ret = cpp::unexpected(EOVERFLOW);
89+
break;
90+
}
91+
time.QuadPart -= (11644473600ULL * HNS_PER_SEC);
92+
unsigned long long tv_sec = time.QuadPart / HNS_PER_SEC;
93+
unsigned long long tv_nsec = (time.QuadPart % HNS_PER_SEC) * 100ULL;
94+
if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) {
95+
ret = cpp::unexpected(EOVERFLOW);
96+
break;
97+
}
98+
ts->tv_sec = static_cast<decltype(ts->tv_sec)>(tv_sec);
99+
ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(tv_nsec);
100+
break;
101+
}
102+
case CLOCK_PROCESS_CPUTIME_ID:
103+
case CLOCK_THREAD_CPUTIME_ID: {
104+
[[clang::uninitialized]] FILETIME creation_time;
105+
[[clang::uninitialized]] FILETIME exit_time;
106+
[[clang::uninitialized]] FILETIME kernel_time;
107+
[[clang::uninitialized]] FILETIME user_time;
108+
bool success;
109+
if (clockid == CLOCK_PROCESS_CPUTIME_ID) {
110+
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
111+
success = ::GetProcessTimes(::GetCurrentProcess(), &creation_time,
112+
&exit_time, &kernel_time, &user_time);
113+
} else {
114+
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes
115+
success = ::GetThreadTimes(::GetCurrentThread(), &creation_time,
116+
&exit_time, &kernel_time, &user_time);
117+
}
118+
if (!success) {
119+
ret = cpp::unexpected(EINVAL);
120+
break;
121+
}
122+
// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
123+
// It is not recommended that you add and subtract values from the FILETIME
124+
// structure to obtain relative times. Instead, you should copy the low- and
125+
// high-order parts of the file time to a ULARGE_INTEGER structure, perform
126+
// 64-bit arithmetic on the QuadPart member, and copy the LowPart and
127+
// HighPart members into the FILETIME structure.
128+
auto kernel_time_hns = cpp::bit_cast<ULARGE_INTEGER>(kernel_time);
129+
auto user_time_hns = cpp::bit_cast<ULARGE_INTEGER>(user_time);
130+
unsigned long long total_time_hns =
131+
kernel_time_hns.QuadPart + user_time_hns.QuadPart;
132+
133+
unsigned long long tv_sec = total_time_hns / HNS_PER_SEC;
134+
unsigned long long tv_nsec = (total_time_hns % HNS_PER_SEC) * 100ULL;
135+
136+
if (LIBC_UNLIKELY(tv_sec > SEC_LIMIT)) {
137+
ret = cpp::unexpected(EOVERFLOW);
138+
break;
139+
}
140+
141+
ts->tv_sec = static_cast<decltype(ts->tv_sec)>(tv_sec);
142+
ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(tv_nsec);
143+
144+
break;
145+
}
146+
}
147+
return ret;
148+
}
149+
} // namespace internal
150+
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)