Skip to content

Commit d3daa0b

Browse files
rovkajeanPerier
authored andcommitted
[flang] Add default implementation for SYSTEM_CLOCK
Add an implementation for the runtime functions related to SYSTEM_CLOCK. As with CPU_TIME, this is based on std::clock(), which should be available everywhere, but it is highly recommended to add platform-specific implementations for systems where std::clock() behaves poorly (e.g. POSIX). The documentation for std::clock() doesn't specify a maximum value and in fact wrap around behaviour is non-conforming. Therefore, this implementation of SYSTEM_CLOCK is not guaranteed to wrap around either, and after std::clock reaches its maximum value we will likely just return failure rather than wrap around. If this happens often on your system, please add a new platform-specific implementation. We define COUNT_MAX as either the maximum value that can be stored in a std::clock_t or in a 64-bit integer (whichever is smaller), and COUNT_RATE as CLOCKS_PER_SEC. For POSIX systems, the value of CLOCKS_PER_SEC is hardcoded to 10^6 and irrelevant for the values returned by std::clock. Differential Revision: https://reviews.llvm.org/D105969
1 parent 75ba7f3 commit d3daa0b

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

flang/runtime/time-intrinsic.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <ctime>
1414

1515
// CPU_TIME (Fortran 2018 16.9.57)
16+
// SYSTEM_CLOCK (Fortran 2018 16.9.168)
17+
//
1618
// We can use std::clock() from the <ctime> header as a fallback implementation
1719
// that should be available everywhere. This may not provide the best resolution
1820
// and is particularly troublesome on (some?) POSIX systems where CLOCKS_PER_SEC
@@ -68,11 +70,64 @@ double GetCpuTime(preferred_implementation,
6870
// Return some negative value to represent failure.
6971
return -1.0;
7072
}
73+
74+
using count_t =
75+
Fortran::runtime::CppTypeFor<Fortran::common::TypeCategory::Integer, 8>;
76+
77+
// This is the fallback implementation, which should work everywhere. Note that
78+
// in general we can't recover after std::clock has reached its maximum value.
79+
template <typename Unused = void>
80+
count_t GetSystemClockCount(fallback_implementation) {
81+
std::clock_t timestamp{std::clock()};
82+
if (timestamp == static_cast<std::clock_t>(-1)) {
83+
// Return -HUGE() to represent failure.
84+
return -std::numeric_limits<count_t>::max();
85+
}
86+
87+
// If our return type is large enough to hold any value returned by
88+
// std::clock, our work is done. Otherwise, we have to wrap around.
89+
static constexpr auto max{std::numeric_limits<count_t>::max()};
90+
if constexpr (std::numeric_limits<std::clock_t>::max() <= max) {
91+
return static_cast<count_t>(timestamp);
92+
} else {
93+
// Since std::clock_t could be a floating point type, we can't just use the
94+
// % operator, so we have to wrap around manually.
95+
return static_cast<count_t>(timestamp - max * std::floor(timestamp / max));
96+
}
97+
}
98+
99+
template <typename Unused = void>
100+
count_t GetSystemClockCountRate(fallback_implementation) {
101+
return CLOCKS_PER_SEC;
102+
}
103+
104+
template <typename Unused = void>
105+
count_t GetSystemClockCountMax(fallback_implementation) {
106+
static constexpr auto max_clock_t = std::numeric_limits<std::clock_t>::max();
107+
static constexpr auto max_count_t = std::numeric_limits<count_t>::max();
108+
if constexpr (max_clock_t < max_count_t) {
109+
return static_cast<count_t>(max_clock_t);
110+
} else {
111+
return max_count_t;
112+
}
113+
}
71114
} // anonymous namespace
72115

73116
namespace Fortran::runtime {
74117
extern "C" {
75118

76119
double RTNAME(CpuTime)() { return GetCpuTime(0); }
120+
121+
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCount)() {
122+
return GetSystemClockCount(0);
123+
}
124+
125+
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountRate)() {
126+
return GetSystemClockCountRate(0);
127+
}
128+
129+
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountMax)() {
130+
return GetSystemClockCountMax(0);
131+
}
77132
} // extern "C"
78133
} // namespace Fortran::runtime

flang/unittests/RuntimeGTest/Time.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,33 @@ TEST(TimeIntrinsics, CpuTime) {
2626
ASSERT_GE(end, start);
2727
}
2828
}
29+
30+
using count_t = CppTypeFor<TypeCategory::Integer, 8>;
31+
32+
TEST(TimeIntrinsics, SystemClock) {
33+
// We can't really test that we get the "right" result for SYSTEM_CLOCK, but
34+
// we can have a smoke test to see that we get something reasonable on the
35+
// platforms where we expect to support it.
36+
37+
// The value of the count rate and max will vary by platform, but they should
38+
// always be strictly positive if we have a working implementation of
39+
// SYSTEM_CLOCK.
40+
EXPECT_GT(RTNAME(SystemClockCountRate)(), 0);
41+
42+
count_t max{RTNAME(SystemClockCountMax)()};
43+
EXPECT_GT(max, 0);
44+
45+
count_t start{RTNAME(SystemClockCount)()};
46+
EXPECT_GE(start, 0);
47+
EXPECT_LE(start, max);
48+
49+
// Loop until we get a different value from SystemClockCount. If we don't get
50+
// one before we time out, then we should probably look into an implementation
51+
// for SystemClokcCount with a better timer resolution on this platform.
52+
for (count_t end = start; end == start; end = RTNAME(SystemClockCount)()) {
53+
EXPECT_GE(end, 0);
54+
EXPECT_LE(end, max);
55+
56+
EXPECT_GE(end, start);
57+
}
58+
}

0 commit comments

Comments
 (0)