diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp --- a/flang/runtime/time-intrinsic.cpp +++ b/flang/runtime/time-intrinsic.cpp @@ -13,6 +13,8 @@ #include // CPU_TIME (Fortran 2018 16.9.57) +// SYSTEM_CLOCK (Fortran 2018 16.9.168) +// // We can use std::clock() from the 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 @@ // Return some negative value to represent failure. return -1.0; } + +using count_t = + Fortran::runtime::CppTypeFor; + +// 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 +count_t GetSystemClockCount(fallback_implementation) { + std::clock_t timestamp{std::clock()}; + if (timestamp == static_cast(-1)) { + // Return -HUGE() to represent failure. + return -std::numeric_limits::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::max()}; + if constexpr (std::numeric_limits::max() <= max) { + return static_cast(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(timestamp - max * std::floor(timestamp / max)); + } +} + +template +count_t GetSystemClockCountRate(fallback_implementation) { + return CLOCKS_PER_SEC; +} + +template +count_t GetSystemClockCountMax(fallback_implementation) { + static constexpr auto max_clock_t = std::numeric_limits::max(); + static constexpr auto max_count_t = std::numeric_limits::max(); + if constexpr (max_clock_t < max_count_t) { + return static_cast(max_clock_t); + } else { + return max_count_t; + } +} } // anonymous namespace namespace Fortran::runtime { extern "C" { double RTNAME(CpuTime)() { return GetCpuTime(0); } + +CppTypeFor RTNAME(SystemClockCount)() { + return GetSystemClockCount(0); +} + +CppTypeFor RTNAME(SystemClockCountRate)() { + return GetSystemClockCountRate(0); +} + +CppTypeFor RTNAME(SystemClockCountMax)() { + return GetSystemClockCountMax(0); +} } // extern "C" } // namespace Fortran::runtime diff --git a/flang/unittests/Runtime/Time.cpp b/flang/unittests/Runtime/Time.cpp --- a/flang/unittests/Runtime/Time.cpp +++ b/flang/unittests/Runtime/Time.cpp @@ -26,3 +26,33 @@ ASSERT_GE(end, start); } } + +using count_t = CppTypeFor; + +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); + } +}