[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
This commit is contained in:
Diana Picus 2021-07-12 13:45:44 +00:00
parent 284006079e
commit 0104cc85b1
2 changed files with 85 additions and 0 deletions

View file

@ -13,6 +13,8 @@
#include <ctime>
// CPU_TIME (Fortran 2018 16.9.57)
// SYSTEM_CLOCK (Fortran 2018 16.9.168)
//
// We can use std::clock() from the <ctime> header as a fallback implementation
// that should be available everywhere. This may not provide the best resolution
// and is particularly troublesome on (some?) POSIX systems where CLOCKS_PER_SEC
@ -68,11 +70,64 @@ double GetCpuTime(preferred_implementation,
// Return some negative value to represent failure.
return -1.0;
}
using count_t =
Fortran::runtime::CppTypeFor<Fortran::common::TypeCategory::Integer, 8>;
// This is the fallback implementation, which should work everywhere. Note that
// in general we can't recover after std::clock has reached its maximum value.
template <typename Unused = void>
count_t GetSystemClockCount(fallback_implementation) {
std::clock_t timestamp{std::clock()};
if (timestamp == static_cast<std::clock_t>(-1)) {
// Return -HUGE() to represent failure.
return -std::numeric_limits<count_t>::max();
}
// If our return type is large enough to hold any value returned by
// std::clock, our work is done. Otherwise, we have to wrap around.
static constexpr auto max{std::numeric_limits<count_t>::max()};
if constexpr (std::numeric_limits<std::clock_t>::max() <= max) {
return static_cast<count_t>(timestamp);
} else {
// Since std::clock_t could be a floating point type, we can't just use the
// % operator, so we have to wrap around manually.
return static_cast<count_t>(timestamp - max * std::floor(timestamp / max));
}
}
template <typename Unused = void>
count_t GetSystemClockCountRate(fallback_implementation) {
return CLOCKS_PER_SEC;
}
template <typename Unused = void>
count_t GetSystemClockCountMax(fallback_implementation) {
static constexpr auto max_clock_t = std::numeric_limits<std::clock_t>::max();
static constexpr auto max_count_t = std::numeric_limits<count_t>::max();
if constexpr (max_clock_t < max_count_t) {
return static_cast<count_t>(max_clock_t);
} else {
return max_count_t;
}
}
} // anonymous namespace
namespace Fortran::runtime {
extern "C" {
double RTNAME(CpuTime)() { return GetCpuTime(0); }
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCount)() {
return GetSystemClockCount(0);
}
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountRate)() {
return GetSystemClockCountRate(0);
}
CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountMax)() {
return GetSystemClockCountMax(0);
}
} // extern "C"
} // namespace Fortran::runtime

View file

@ -26,3 +26,33 @@ TEST(TimeIntrinsics, CpuTime) {
ASSERT_GE(end, start);
}
}
using count_t = CppTypeFor<TypeCategory::Integer, 8>;
TEST(TimeIntrinsics, SystemClock) {
// We can't really test that we get the "right" result for SYSTEM_CLOCK, but
// we can have a smoke test to see that we get something reasonable on the
// platforms where we expect to support it.
// The value of the count rate and max will vary by platform, but they should
// always be strictly positive if we have a working implementation of
// SYSTEM_CLOCK.
EXPECT_GT(RTNAME(SystemClockCountRate)(), 0);
count_t max{RTNAME(SystemClockCountMax)()};
EXPECT_GT(max, 0);
count_t start{RTNAME(SystemClockCount)()};
EXPECT_GE(start, 0);
EXPECT_LE(start, max);
// Loop until we get a different value from SystemClockCount. If we don't get
// one before we time out, then we should probably look into an implementation
// for SystemClokcCount with a better timer resolution on this platform.
for (count_t end = start; end == start; end = RTNAME(SystemClockCount)()) {
EXPECT_GE(end, 0);
EXPECT_LE(end, max);
EXPECT_GE(end, start);
}
}