[flang] [llvm] [flang-rt] Runtime implementation of extended intrinsic function SECNDS() (PR #152021)
Eugene Epshteyn via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 5 17:59:21 PDT 2025
https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/152021
>From 68a1a813d46719382da3a67a973eb310bccb6e2a Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 16:00:56 -0400
Subject: [PATCH 01/18] [flang-rt] Runtime implementation of extended intrinsic
function SECNDS()
---
flang-rt/lib/runtime/command.cpp | 7 +++++
flang-rt/lib/runtime/extensions.cpp | 39 ++++++++++++++++++++++++
flang/include/flang/Runtime/command.h | 2 ++
flang/include/flang/Runtime/extensions.h | 3 ++
4 files changed, 51 insertions(+)
diff --git a/flang-rt/lib/runtime/command.cpp b/flang-rt/lib/runtime/command.cpp
index a4e8e31ad0274..40233392f2497 100644
--- a/flang-rt/lib/runtime/command.cpp
+++ b/flang-rt/lib/runtime/command.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Runtime/command.h"
+#include "flang/Runtime/extensions.h"
#include "flang-rt/runtime/descriptor.h"
#include "flang-rt/runtime/environment.h"
#include "flang-rt/runtime/stat.h"
@@ -309,6 +310,12 @@ std::int32_t RTNAME(Hostnm)(
return status;
}
+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);
+}
+
std::int32_t RTNAME(PutEnv)(
const char *str, size_t str_length, const char *sourceFile, int line) {
Terminator terminator{sourceFile, line};
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index f6c39468d5655..4751fb24af849 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -303,6 +303,45 @@ 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) {
+ constexpr float FAIL_SECNDS{1.0f};
+ if (!refTime) {
+ return FAIL_SECNDS;
+ }
+ std::time_t now{std::time(nullptr)};
+ if (now == std::time_t{-1}) {
+ return FAIL_SECNDS;
+ }
+ // In float result, we can only precisely store 2^24 seconds, which
+ // comes out to about 194 days. Thus, need to peek a starting point.
+ // Given the description of this function, midnight of the current
+ // day is the best starting point.
+ static time_t startingPoint{0};
+ if (!startingPoint) {
+ struct tm timeInfo;
+#ifdef _WIN32
+ if (localtime_s(&timeInfo, &now)) {
+ return FAIL_SECNDS;
+ }
+#else
+ if (!localtime_r(&now, &timeInfo)) {
+ return FAIL_SECNDS;
+ }
+#endif
+ // Back to midnight
+ timeInfo.tm_hour = 0;
+ timeInfo.tm_min = 0;
+ timeInfo.tm_sec = 0;
+ startingPoint = std::mktime(&timeInfo);
+ if (startingPoint == std::time_t(-1)) {
+ return FAIL_SECNDS;
+ }
+ }
+ double diffStartingPoint = std::difftime(now, startingPoint);
+ return static_cast<float>(diffStartingPoint) - *refTime;
+}
+
// GNU extension function TIME()
std::int64_t RTNAME(time)() { return time(nullptr); }
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index 19b486094da17..21ca1fa8457ee 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -67,6 +67,8 @@ std::int32_t RTNAME(Hostnm)(
std::int32_t RTNAME(PutEnv)(
const char *str, size_t str_length, const char *sourceFile, int line);
+float RTNAME(Secnds)(float* refTime, const char *sourceFile, int line);
+
// Calls unlink()
std::int32_t RTNAME(Unlink)(
const char *path, size_t pathLength, const char *sourceFile, int line);
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index b350204714431..a5bfa8b535286 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -90,5 +90,8 @@ 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);
+
} // extern "C"
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_
>From 716f82d03cb17a493891c2af761cdf4461e5e13a Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 16:01:50 -0400
Subject: [PATCH 02/18] clang-format
---
flang-rt/lib/runtime/command.cpp | 4 ++--
flang-rt/lib/runtime/extensions.cpp | 2 +-
flang/include/flang/Runtime/command.h | 2 +-
flang/include/flang/Runtime/extensions.h | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/flang-rt/lib/runtime/command.cpp b/flang-rt/lib/runtime/command.cpp
index 40233392f2497..ade77e9dd8d0f 100644
--- a/flang-rt/lib/runtime/command.cpp
+++ b/flang-rt/lib/runtime/command.cpp
@@ -7,12 +7,12 @@
//===----------------------------------------------------------------------===//
#include "flang/Runtime/command.h"
-#include "flang/Runtime/extensions.h"
#include "flang-rt/runtime/descriptor.h"
#include "flang-rt/runtime/environment.h"
#include "flang-rt/runtime/stat.h"
#include "flang-rt/runtime/terminator.h"
#include "flang-rt/runtime/tools.h"
+#include "flang/Runtime/extensions.h"
#include <cerrno>
#include <cstdlib>
#include <limits>
@@ -310,7 +310,7 @@ std::int32_t RTNAME(Hostnm)(
return status;
}
-float RTNAME(Secnds)(float* refTime, const char *sourceFile, int line) {
+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);
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 4751fb24af849..a5642b628cbfe 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -304,7 +304,7 @@ void FORTRAN_PROCEDURE_NAME(qsort)(int *array, int *len, int *isize,
void RTNAME(Perror)(const char *str) { perror(str); }
// GNU extension function SECNDS(refTime)
-float FORTRAN_PROCEDURE_NAME(secnds)(float* refTime) {
+float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
constexpr float FAIL_SECNDS{1.0f};
if (!refTime) {
return FAIL_SECNDS;
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index 21ca1fa8457ee..d22c2bc3956fe 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -67,7 +67,7 @@ std::int32_t RTNAME(Hostnm)(
std::int32_t RTNAME(PutEnv)(
const char *str, size_t str_length, const char *sourceFile, int line);
-float RTNAME(Secnds)(float* refTime, const char *sourceFile, int line);
+float RTNAME(Secnds)(float *refTime, const char *sourceFile, int line);
// Calls unlink()
std::int32_t RTNAME(Unlink)(
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index a5bfa8b535286..355808a846f13 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -91,7 +91,7 @@ void RTNAME(Perror)(const char *str);
int FORTRAN_PROCEDURE_NAME(mclock)();
// GNU extension subroutine SECNDS(refTime)
-float FORTRAN_PROCEDURE_NAME(secnds)(float* refTime);
+float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime);
} // extern "C"
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_
>From 8bbe4ebb083f8a707e75318456ad520fdff3213f Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 16:05:06 -0400
Subject: [PATCH 03/18] Fixed a typo
---
flang-rt/lib/runtime/extensions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index a5642b628cbfe..a218456e0cbb4 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -314,7 +314,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
return FAIL_SECNDS;
}
// In float result, we can only precisely store 2^24 seconds, which
- // comes out to about 194 days. Thus, need to peek a starting point.
+ // comes out to about 194 days. Thus, need to pick a starting point.
// Given the description of this function, midnight of the current
// day is the best starting point.
static time_t startingPoint{0};
>From 5f671e74d65d406d3fd576da7fe29dfbcdb904a8 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 16:06:22 -0400
Subject: [PATCH 04/18] Fixed init form
---
flang-rt/lib/runtime/extensions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index a218456e0cbb4..cc3be56019b02 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -338,7 +338,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
return FAIL_SECNDS;
}
}
- double diffStartingPoint = std::difftime(now, startingPoint);
+ double diffStartingPoint{std::difftime(now, startingPoint)};
return static_cast<float>(diffStartingPoint) - *refTime;
}
>From 9c538783b3947458405912de0740d4497b548c40 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 16:08:33 -0400
Subject: [PATCH 05/18] The failure code should be negative
---
flang-rt/lib/runtime/extensions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index cc3be56019b02..01cd13dbf84d5 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -305,7 +305,7 @@ void RTNAME(Perror)(const char *str) { perror(str); }
// GNU extension function SECNDS(refTime)
float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
- constexpr float FAIL_SECNDS{1.0f};
+ constexpr float FAIL_SECNDS{-1.0f};
if (!refTime) {
return FAIL_SECNDS;
}
>From 80b61edbf543a2894466ebc6a9a79265b3674fed Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 16:19:06 -0400
Subject: [PATCH 06/18] Introduce FAIL_TIME
---
flang-rt/lib/runtime/extensions.cpp | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 01cd13dbf84d5..e438d335a6658 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -305,12 +305,14 @@ void RTNAME(Perror)(const char *str) { perror(str); }
// GNU extension function SECNDS(refTime)
float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
- constexpr float FAIL_SECNDS{-1.0f};
+ constexpr float FAIL_SECNDS{-1.0f}; // Failure code for this function
+ // Failure code for time functions that return std::time_t
+ constexpr time_t FAIL_TIME{std::time_t{-1}};
if (!refTime) {
return FAIL_SECNDS;
}
std::time_t now{std::time(nullptr)};
- if (now == std::time_t{-1}) {
+ if (now == FAIL_TIME) {
return FAIL_SECNDS;
}
// In float result, we can only precisely store 2^24 seconds, which
@@ -334,7 +336,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
timeInfo.tm_min = 0;
timeInfo.tm_sec = 0;
startingPoint = std::mktime(&timeInfo);
- if (startingPoint == std::time_t(-1)) {
+ if (startingPoint == FAIL_TIME) {
return FAIL_SECNDS;
}
}
>From 78a8d079aebbd28b60415b8ba383cc377f0c8828 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 23:05:39 -0400
Subject: [PATCH 07/18] Code review: moved RTNAME(Secnds) to extensions.cpp.
Added missing to time_t declarations
---
flang-rt/lib/runtime/command.cpp | 7 -------
flang-rt/lib/runtime/extensions.cpp | 10 ++++++++--
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/flang-rt/lib/runtime/command.cpp b/flang-rt/lib/runtime/command.cpp
index ade77e9dd8d0f..a4e8e31ad0274 100644
--- a/flang-rt/lib/runtime/command.cpp
+++ b/flang-rt/lib/runtime/command.cpp
@@ -12,7 +12,6 @@
#include "flang-rt/runtime/stat.h"
#include "flang-rt/runtime/terminator.h"
#include "flang-rt/runtime/tools.h"
-#include "flang/Runtime/extensions.h"
#include <cerrno>
#include <cstdlib>
#include <limits>
@@ -310,12 +309,6 @@ std::int32_t RTNAME(Hostnm)(
return status;
}
-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);
-}
-
std::int32_t RTNAME(PutEnv)(
const char *str, size_t str_length, const char *sourceFile, int line) {
Terminator terminator{sourceFile, line};
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index e438d335a6658..93054c11848a7 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -307,7 +307,7 @@ void RTNAME(Perror)(const char *str) { perror(str); }
float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
constexpr float FAIL_SECNDS{-1.0f}; // Failure code for this function
// Failure code for time functions that return std::time_t
- constexpr time_t FAIL_TIME{std::time_t{-1}};
+ constexpr std::time_t FAIL_TIME{std::time_t{-1}};
if (!refTime) {
return FAIL_SECNDS;
}
@@ -319,7 +319,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// comes out to about 194 days. Thus, need to pick a starting point.
// Given the description of this function, midnight of the current
// day is the best starting point.
- static time_t startingPoint{0};
+ static std::time_t startingPoint{0};
if (!startingPoint) {
struct tm timeInfo;
#ifdef _WIN32
@@ -344,6 +344,12 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
return static_cast<float>(diffStartingPoint) - *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); }
>From 2f3a274a6d3c4547ab0c52a9f91ebcf7d94408da Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 23:34:48 -0400
Subject: [PATCH 08/18] Attempt at using atomic operations in secnds_() to make
it reentrant
---
flang-rt/lib/runtime/extensions.cpp | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 93054c11848a7..e6e2725427846 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>
@@ -308,6 +309,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
constexpr float FAIL_SECNDS{-1.0f}; // 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}};
+ constexpr std::time_t TIME_INITIALIZING{std::time_t{1}};
if (!refTime) {
return FAIL_SECNDS;
}
@@ -319,8 +322,11 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// comes out to about 194 days. Thus, need to pick a starting point.
// Given the description of this function, midnight of the current
// day is the best starting point.
- static std::time_t startingPoint{0};
- if (!startingPoint) {
+ static std::atomic<std::time_t> startingPoint{TIME_UNINITIALIZED};
+ std::time_t expected{TIME_UNINITIALIZED};
+ std::time_t localStartingPoint{TIME_UNINITIALIZED};
+ if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING)) {
+ // This thread is doing initialization of startingPoint
struct tm timeInfo;
#ifdef _WIN32
if (localtime_s(&timeInfo, &now)) {
@@ -335,12 +341,20 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
timeInfo.tm_hour = 0;
timeInfo.tm_min = 0;
timeInfo.tm_sec = 0;
- startingPoint = std::mktime(&timeInfo);
- if (startingPoint == FAIL_TIME) {
+ std::time_t midnight = std::mktime(&timeInfo);
+ if (midnight == FAIL_TIME) {
return FAIL_SECNDS;
}
+ localStartingPoint = midnight;
+ startingPoint.store(midnight, std::memory_order_release);
+ } else {
+ // This thread is not doing initialization of startingPoint, need to wait
+ // for initialization to complete.
+ while ((localStartingPoint = startingPoint.load(std::memory_order_acquire)) <= TIME_INITIALIZING) {
+ std::this_thread::yield();
+ }
}
- double diffStartingPoint{std::difftime(now, startingPoint)};
+ double diffStartingPoint{std::difftime(now, localStartingPoint)};
return static_cast<float>(diffStartingPoint) - *refTime;
}
>From 9940825dc31baa5962dcf181bb115c1ed3047e3d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 23:35:23 -0400
Subject: [PATCH 09/18] clang-format
---
flang-rt/lib/runtime/extensions.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index e6e2725427846..0070431105163 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -350,7 +350,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
} else {
// This thread is not doing initialization of startingPoint, need to wait
// for initialization to complete.
- while ((localStartingPoint = startingPoint.load(std::memory_order_acquire)) <= TIME_INITIALIZING) {
+ while ((localStartingPoint = startingPoint.load(
+ std::memory_order_acquire)) <= TIME_INITIALIZING) {
std::this_thread::yield();
}
}
>From 7268c8c82948b3eba3b3469e4a6924afbc8d81de Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 4 Aug 2025 23:38:01 -0400
Subject: [PATCH 10/18] Fixed init
---
flang-rt/lib/runtime/extensions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 0070431105163..80df0b8eadf61 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -341,7 +341,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
timeInfo.tm_hour = 0;
timeInfo.tm_min = 0;
timeInfo.tm_sec = 0;
- std::time_t midnight = std::mktime(&timeInfo);
+ std::time_t midnight{std::mktime(&timeInfo)};
if (midnight == FAIL_TIME) {
return FAIL_SECNDS;
}
>From 205818e162adec186a93c835c6c5e7436dd9d284 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 07:21:38 -0400
Subject: [PATCH 11/18] Reset startingPoint to 'uninitialized' on failure
---
flang-rt/lib/runtime/extensions.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 80df0b8eadf61..e15ec6c535fc0 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -330,10 +330,12 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
struct tm timeInfo;
#ifdef _WIN32
if (localtime_s(&timeInfo, &now)) {
+ startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
return FAIL_SECNDS;
}
#else
if (!localtime_r(&now, &timeInfo)) {
+ startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
return FAIL_SECNDS;
}
#endif
>From 59452645984b53d56df401bd142b4afd6d6feb5a Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 14:34:25 -0400
Subject: [PATCH 12/18] Changed currentStartingPoint initialization to use loop
in all cases
---
flang-rt/lib/runtime/extensions.cpp | 80 +++++++++++++++++++----------
1 file changed, 52 insertions(+), 28 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index e15ec6c535fc0..f96584d73a859 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -322,41 +322,65 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// comes out to about 194 days. Thus, need to pick a starting point.
// Given the description of this function, midnight of the current
// day is the best starting point.
+ //
+ // In addition, use atomic operations for thread safety. startingPoint
+ // also acts as a state variable that can take on the following values:
+ // TIME_UNINITIALIZED to indicate that it's not initialized,
+ // TIME_INITIALIZING to indicate that it is being initialized,
+ // any other value to indicate the starting point time.
static std::atomic<std::time_t> startingPoint{TIME_UNINITIALIZED};
- std::time_t expected{TIME_UNINITIALIZED};
std::time_t localStartingPoint{TIME_UNINITIALIZED};
- if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING)) {
- // This thread is doing initialization of startingPoint
- struct tm timeInfo;
+ // Use retry logic to ensure that in case of multiple threads, one thread
+ // will perform initialization and the other threads wait their turn.
+ for (;;) {
+ // "Acquire" will show writes from other threads.
+ std::time_t currentStartingPoint = startingPoint.load(
+ std::memory_order_acquire);
+ if (currentStartingPoint > TIME_INITIALIZING) {
+ // Initialization was already done, use the starting point value
+ localStartingPoint = currentStartingPoint;
+ break;
+ } else if (currentStartingPoint == TIME_INITIALIZING) {
+ // Some other thread is currently initializing
+ std::this_thread::yield();
+ continue;
+ } else if (currentStartingPoint == TIME_UNINITIALIZED) {
+ // Try to start initialization
+ std::time_t expected{TIME_UNINITIALIZED};
+ if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING,
+ std::memory_order_acq_rel, // "Aquire and release" on success
+ std::memory_order_acquire)) { // "Aquire" on failure
+ // This thread is doing initialization of startingPoint
+ struct tm timeInfo;
#ifdef _WIN32
- if (localtime_s(&timeInfo, &now)) {
- startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
- return FAIL_SECNDS;
- }
+ if (localtime_s(&timeInfo, &now)) {
#else
- if (!localtime_r(&now, &timeInfo)) {
- startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
- return FAIL_SECNDS;
- }
+ if (!localtime_r(&now, &timeInfo)) {
#endif
- // Back to midnight
- timeInfo.tm_hour = 0;
- timeInfo.tm_min = 0;
- timeInfo.tm_sec = 0;
- std::time_t midnight{std::mktime(&timeInfo)};
- if (midnight == FAIL_TIME) {
- return FAIL_SECNDS;
- }
- localStartingPoint = midnight;
- startingPoint.store(midnight, std::memory_order_release);
- } else {
- // This thread is not doing initialization of startingPoint, need to wait
- // for initialization to complete.
- while ((localStartingPoint = startingPoint.load(
- std::memory_order_acquire)) <= TIME_INITIALIZING) {
- std::this_thread::yield();
+ // "Relaxed" ensures atomicity, but not ordering
+ startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
+ 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) {
+ startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
+ return FAIL_SECNDS;
+ }
+ // "Release" will make this value available to other threads
+ startingPoint.store(localStartingPoint, std::memory_order_release);
+ } else {
+ // This thread couln't start initialization. Try again.
+ continue;
+ }
}
}
+ if (localStartingPoint <= TIME_INITIALIZING) {
+ return FAIL_SECNDS;
+ }
double diffStartingPoint{std::difftime(now, localStartingPoint)};
return static_cast<float>(diffStartingPoint) - *refTime;
}
>From 5cdb74d455928cd3b790a77a01ec003516480d80 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 14:36:02 -0400
Subject: [PATCH 13/18] clang-format
---
flang-rt/lib/runtime/extensions.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index f96584d73a859..98e41964344e7 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -334,8 +334,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// will perform initialization and the other threads wait their turn.
for (;;) {
// "Acquire" will show writes from other threads.
- std::time_t currentStartingPoint = startingPoint.load(
- std::memory_order_acquire);
+ std::time_t currentStartingPoint =
+ startingPoint.load(std::memory_order_acquire);
if (currentStartingPoint > TIME_INITIALIZING) {
// Initialization was already done, use the starting point value
localStartingPoint = currentStartingPoint;
@@ -348,8 +348,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// Try to start initialization
std::time_t expected{TIME_UNINITIALIZED};
if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING,
- std::memory_order_acq_rel, // "Aquire and release" on success
- std::memory_order_acquire)) { // "Aquire" on failure
+ std::memory_order_acq_rel, // "Aquire and release" on success
+ std::memory_order_acquire)) { // "Aquire" on failure
// This thread is doing initialization of startingPoint
struct tm timeInfo;
#ifdef _WIN32
>From 599713e1cb986d73f6776802649a13301145ebf7 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 16:17:20 -0400
Subject: [PATCH 14/18] Code review feedback
---
flang-rt/lib/runtime/extensions.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 98e41964344e7..ca8d5ac9cbdfa 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -334,8 +334,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// will perform initialization and the other threads wait their turn.
for (;;) {
// "Acquire" will show writes from other threads.
- std::time_t currentStartingPoint =
- startingPoint.load(std::memory_order_acquire);
+ std::time_t currentStartingPoint{startingPoint.load(
+ std::memory_order_acquire)};
if (currentStartingPoint > TIME_INITIALIZING) {
// Initialization was already done, use the starting point value
localStartingPoint = currentStartingPoint;
@@ -348,8 +348,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// Try to start initialization
std::time_t expected{TIME_UNINITIALIZED};
if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING,
- std::memory_order_acq_rel, // "Aquire and release" on success
- std::memory_order_acquire)) { // "Aquire" on failure
+ std::memory_order_acq_rel, // "Acquire and release" on success
+ std::memory_order_acquire)) { // "Acquire" on failure
// This thread is doing initialization of startingPoint
struct tm timeInfo;
#ifdef _WIN32
@@ -372,6 +372,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
}
// "Release" will make this value available to other threads
startingPoint.store(localStartingPoint, std::memory_order_release);
+ break;
} else {
// This thread couln't start initialization. Try again.
continue;
>From 4ae3cd37c31ee283d29c062ef573184197e6d45e Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 16:32:40 -0400
Subject: [PATCH 15/18] clang-format
---
flang-rt/lib/runtime/extensions.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index ca8d5ac9cbdfa..b118046dc0002 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -334,8 +334,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// will perform initialization and the other threads wait their turn.
for (;;) {
// "Acquire" will show writes from other threads.
- std::time_t currentStartingPoint{startingPoint.load(
- std::memory_order_acquire)};
+ std::time_t currentStartingPoint{
+ startingPoint.load(std::memory_order_acquire)};
if (currentStartingPoint > TIME_INITIALIZING) {
// Initialization was already done, use the starting point value
localStartingPoint = currentStartingPoint;
@@ -348,8 +348,8 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// Try to start initialization
std::time_t expected{TIME_UNINITIALIZED};
if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING,
- std::memory_order_acq_rel, // "Acquire and release" on success
- std::memory_order_acquire)) { // "Acquire" on failure
+ std::memory_order_acq_rel, // "Acquire and release" on success
+ std::memory_order_acquire)) { // "Acquire" on failure
// This thread is doing initialization of startingPoint
struct tm timeInfo;
#ifdef _WIN32
>From fdd310a3768cd46585d56837a7da9003ea2653a2 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 18:20:24 -0400
Subject: [PATCH 16/18] Moved RTNAME(Secnds) declaration to extensions.h
---
flang/include/flang/Runtime/command.h | 2 --
flang/include/flang/Runtime/extensions.h | 1 +
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index d22c2bc3956fe..19b486094da17 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -67,8 +67,6 @@ std::int32_t RTNAME(Hostnm)(
std::int32_t RTNAME(PutEnv)(
const char *str, size_t str_length, const char *sourceFile, int line);
-float RTNAME(Secnds)(float *refTime, const char *sourceFile, int line);
-
// Calls unlink()
std::int32_t RTNAME(Unlink)(
const char *path, size_t pathLength, const char *sourceFile, int line);
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index 355808a846f13..9a100cec9e6b9 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -92,6 +92,7 @@ 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_
>From 1cf3a462e561212e98da84caed50ea02927c943c Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 20:54:58 -0400
Subject: [PATCH 17/18] Simplified the implementation based on Peter's
suggestion
---
flang-rt/lib/runtime/extensions.cpp | 94 ++++++++++++-----------------
1 file changed, 38 insertions(+), 56 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index b118046dc0002..092c2e3abd80e 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -310,7 +310,6 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// 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}};
- constexpr std::time_t TIME_INITIALIZING{std::time_t{1}};
if (!refTime) {
return FAIL_SECNDS;
}
@@ -319,68 +318,51 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
return FAIL_SECNDS;
}
// In float result, we can only precisely store 2^24 seconds, which
- // comes out to about 194 days. Thus, need to pick a starting point.
+ // comes out to about 194 days. Thus, need to pick a starting point,
+ // which will allow us to keep the time diffs as precise as possible.
// Given the description of this function, midnight of the current
// day is the best starting point.
- //
- // In addition, use atomic operations for thread safety. startingPoint
- // also acts as a state variable that can take on the following values:
- // TIME_UNINITIALIZED to indicate that it's not initialized,
- // TIME_INITIALIZING to indicate that it is being initialized,
- // any other value to indicate the starting point time.
static std::atomic<std::time_t> startingPoint{TIME_UNINITIALIZED};
- std::time_t localStartingPoint{TIME_UNINITIALIZED};
- // Use retry logic to ensure that in case of multiple threads, one thread
- // will perform initialization and the other threads wait their turn.
- for (;;) {
- // "Acquire" will show writes from other threads.
- std::time_t currentStartingPoint{
- startingPoint.load(std::memory_order_acquire)};
- if (currentStartingPoint > TIME_INITIALIZING) {
- // Initialization was already done, use the starting point value
- localStartingPoint = currentStartingPoint;
- break;
- } else if (currentStartingPoint == TIME_INITIALIZING) {
- // Some other thread is currently initializing
- std::this_thread::yield();
- continue;
- } else if (currentStartingPoint == TIME_UNINITIALIZED) {
- // Try to start initialization
- std::time_t expected{TIME_UNINITIALIZED};
- if (startingPoint.compare_exchange_strong(expected, TIME_INITIALIZING,
- std::memory_order_acq_rel, // "Acquire and release" on success
- std::memory_order_acquire)) { // "Acquire" on failure
- // This thread is doing initialization of startingPoint
- struct tm timeInfo;
+ // "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.0f, which indicates to compute seconds from
+ // current day's midnight.
+ if (localStartingPoint == TIME_UNINITIALIZED || *refTime < 0.5f) {
+ // 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)) {
+ if (localtime_s(&timeInfo, &now)) {
#else
- if (!localtime_r(&now, &timeInfo)) {
+ if (!localtime_r(&now, &timeInfo)) {
#endif
- // "Relaxed" ensures atomicity, but not ordering
- startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
- 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) {
- startingPoint.store(TIME_UNINITIALIZED, std::memory_order_relaxed);
- return FAIL_SECNDS;
- }
- // "Release" will make this value available to other threads
- startingPoint.store(localStartingPoint, std::memory_order_release);
- break;
- } else {
- // This thread couln't start initialization. Try again.
- continue;
- }
+ 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;
}
- }
- if (localStartingPoint <= TIME_INITIALIZING) {
- return FAIL_SECNDS;
}
double diffStartingPoint{std::difftime(now, localStartingPoint)};
return static_cast<float>(diffStartingPoint) - *refTime;
>From aa3d3d4a11e61cd08cda3ca7a15d8584c1343356 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 5 Aug 2025 20:59:02 -0400
Subject: [PATCH 18/18] clang-format
---
flang-rt/lib/runtime/extensions.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index 092c2e3abd80e..bc86917c00828 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -324,8 +324,7 @@ float FORTRAN_PROCEDURE_NAME(secnds)(float *refTime) {
// 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)};
+ 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.0f, which indicates to compute seconds from
// current day's midnight.
More information about the llvm-commits
mailing list