[flang-commits] [flang] cae7beb - [flang-rt] Runtime implementation of extended intrinsic function SECNDS() (#152021)
via flang-commits
flang-commits at lists.llvm.org
Wed Aug 6 13:02:31 PDT 2025
Author: Eugene Epshteyn
Date: 2025-08-06T16:02:27-04:00
New Revision: cae7bebcaa41e4c459e973b9688215f5a57bcb56
URL: https://github.com/llvm/llvm-project/commit/cae7bebcaa41e4c459e973b9688215f5a57bcb56
DIFF: https://github.com/llvm/llvm-project/commit/cae7bebcaa41e4c459e973b9688215f5a57bcb56.diff
LOG: [flang-rt] Runtime implementation of extended intrinsic function SECNDS() (#152021)
Until the compiler part is fully hooked up via
https://github.com/llvm/llvm-project/pull/151878, tested this using
`external`:
```
external secnds
real s1, s2
s1 = secnds(0.0)
print *, "Seconds from midnight:", s1
call sleep(2)
s2 = secnds(s1)
print *, "Seconds from s1", s2
print *, "Seconds from midnight:", secnds(0.0)
end
```
Added:
Modified:
flang-rt/lib/runtime/extensions.cpp
flang/include/flang/Runtime/extensions.h
Removed:
################################################################################
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index f6c39468d5655..a24810b4f344a 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -18,6 +18,7 @@
#include "flang/Runtime/entry-names.h"
#include "flang/Runtime/io-api.h"
#include "flang/Runtime/iostat-consts.h"
+#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstring>
@@ -57,10 +58,76 @@ inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
#include <direct.h>
#endif
-extern "C" {
-
namespace Fortran::runtime {
+// Common implementation that could be used for either SECNDS() or SECNDSD(),
+// which are defined for float or double.
+template <typename T> T SecndsImpl(T *refTime) {
+ static_assert(std::is_same<T, float>::value || std::is_same<T, double>::value,
+ "T must be float or double");
+ constexpr T FAIL_SECNDS{T{-1.0}}; // Failure code for this function
+ // Failure code for time functions that return std::time_t
+ constexpr std::time_t FAIL_TIME{std::time_t{-1}};
+ constexpr std::time_t TIME_UNINITIALIZED{std::time_t{0}};
+ if (!refTime) {
+ return FAIL_SECNDS;
+ }
+ std::time_t now{std::time(nullptr)};
+ if (now == FAIL_TIME) {
+ return FAIL_SECNDS;
+ }
+ // In case we are using a float result, we can only precisely store
+ // 2^24 seconds, which comes out to about 194 days. Thus, need to pick
+ // a starting point, which will allow us to keep the time
diff s as precise
+ // as possible. Given the description of this function, midnight of the
+ // current day is the best starting point.
+ static std::atomic<std::time_t> startingPoint{TIME_UNINITIALIZED};
+ // "Acquire" will give us writes from other threads.
+ std::time_t localStartingPoint{startingPoint.load(std::memory_order_acquire)};
+ // Initialize startingPoint if we haven't initialized it yet or
+ // if we were passed 0.0, which indicates to compute seconds from
+ // current day's midnight.
+ if (localStartingPoint == TIME_UNINITIALIZED || *refTime == 0.0) {
+ // Compute midnight in the current timezone and try to initialize
+ // startingPoint with it. If there are any errors during computation,
+ // exit with error and hope that the other threads have better luck
+ // (or the user retries the call).
+ struct tm timeInfo;
+#ifdef _WIN32
+ if (localtime_s(&timeInfo, &now)) {
+#else
+ if (!localtime_r(&now, &timeInfo)) {
+#endif
+ return FAIL_SECNDS;
+ }
+ // Back to midnight
+ timeInfo.tm_hour = 0;
+ timeInfo.tm_min = 0;
+ timeInfo.tm_sec = 0;
+ localStartingPoint = std::mktime(&timeInfo);
+ if (localStartingPoint == FAIL_TIME) {
+ return FAIL_SECNDS;
+ }
+ INTERNAL_CHECK(localStartingPoint > TIME_UNINITIALIZED);
+ // Attempt to atomically set startingPoint to localStartingPoint
+ std::time_t expected{TIME_UNINITIALIZED};
+ if (startingPoint.compare_exchange_strong(expected, localStartingPoint,
+ std::memory_order_acq_rel, // "Acquire and release" on success
+ std::memory_order_acquire)) { // "Acquire" on failure
+ // startingPoint was set to localStartingPoint
+ } else {
+ // startingPoint was already initialized and its value was loaded
+ // into `expected`. Discard our precomputed midnight value in favor
+ // of the one from startingPoint.
+ localStartingPoint = expected;
+ }
+ }
+ double
diff StartingPoint{std::
diff time(now, localStartingPoint)};
+ return static_cast<T>(
diff StartingPoint) - *refTime;
+}
+
+extern "C" {
+
gid_t RTNAME(GetGID)() {
#ifdef _WIN32
// Group IDs don't exist on Windows, return 1 to avoid errors
@@ -303,6 +370,17 @@ void FORTRAN_PROCEDURE_NAME(qsort)(int *array, int *len, int *isize,
// PERROR(STRING)
void RTNAME(Perror)(const char *str) { perror(str); }
+// GNU extension function SECNDS(refTime)
+float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
+ return SecndsImpl(refTime);
+}
+
+float RTNAME(Secnds)(float *refTime, const char *sourceFile, int line) {
+ Terminator terminator{sourceFile, line};
+ RUNTIME_CHECK(terminator, refTime != nullptr);
+ return FORTRAN_PROCEDURE_NAME(secnds)(refTime);
+}
+
// GNU extension function TIME()
std::int64_t RTNAME(time)() { return time(nullptr); }
@@ -337,5 +415,6 @@ std::int64_t RTNAME(Ftell)(int unitNumber) {
}
} // namespace io
-} // namespace Fortran::runtime
} // extern "C"
+
+} // namespace Fortran::runtime
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index b350204714431..9a100cec9e6b9 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -90,5 +90,9 @@ void RTNAME(Perror)(const char *str);
// MCLOCK -- returns accumulated time in ticks
int FORTRAN_PROCEDURE_NAME(mclock)();
+// GNU extension subroutine SECNDS(refTime)
+float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime);
+float RTNAME(Secnds)(float *refTime, const char *sourceFile, int line);
+
} // extern "C"
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_
More information about the flang-commits
mailing list