[flang] [llvm] [flang] Implement DSECNDS intrinsic (PGI extension) (PR #157573)
Šárka Holendová via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 10 11:00:07 PDT 2025
https://github.com/mlir-maiden updated https://github.com/llvm/llvm-project/pull/157573
>From e5bc137bdf0e85201c46f68fc0819a51920b039c Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Mon, 8 Sep 2025 18:51:02 -0400
Subject: [PATCH 1/3] [flang] Implement DSECNDS intrinsic (PGI extension)
Add support for DSECNDS, the double-precision variant of SECNDS. The implementation mirrors SECNDS, reusing the shared `SecndsImpl<T>` runtime template.
Includes:
- Registration in intrinsics table
- Lowering handler and runtime call wiring
- Hook into shared SecndsImpl in extensions.cpp
- Documentation in Intrinsics.md
- Regression test dsecnds.f90
---
flang-rt/lib/runtime/extensions.cpp | 13 +++++++++-
flang/docs/Intrinsics.md | 26 +++++++++++++++++++
.../flang/Optimizer/Builder/IntrinsicCall.h | 2 ++
.../Optimizer/Builder/Runtime/Intrinsics.h | 4 +++
flang/include/flang/Runtime/extensions.h | 4 +++
flang/lib/Evaluate/intrinsics.cpp | 4 +++
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 21 +++++++++++++++
.../Optimizer/Builder/Runtime/Intrinsics.cpp | 17 ++++++++++++
flang/test/Lower/Intrinsics/dsecnds.f90 | 23 ++++++++++++++++
9 files changed, 113 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Lower/Intrinsics/dsecnds.f90
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index be0eed6f49dc8..2c42597a56541 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -60,7 +60,7 @@ inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
namespace Fortran::runtime {
-// Common implementation that could be used for either SECNDS() or SECNDSD(),
+// Common implementation that could be used for either SECNDS() or DSECNDS(),
// 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,
@@ -381,6 +381,17 @@ float RTNAME(Secnds)(float *refTime, const char *sourceFile, int line) {
return FORTRAN_PROCEDURE_NAME(secnds)(refTime);
}
+// PGI extension function DSECNDS(refTime)
+double FORTRAN_PROCEDURE_NAME(dsecnds)(double *refTime) {
+ return SecndsImpl(refTime);
+}
+
+double RTNAME(Dsecnds)(double *refTime, const char *sourceFile, int line) {
+ Terminator terminator{sourceFile, line};
+ RUNTIME_CHECK(terminator, refTime != nullptr);
+ return FORTRAN_PROCEDURE_NAME(dsecnds)(refTime);
+}
+
// GNU extension function TIME()
std::int64_t RTNAME(time)() { return time(nullptr); }
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index 4b000877e7844..3314d1bcc64a2 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -1149,6 +1149,32 @@ PROGRAM example_secnds
PRINT *, "Elapsed seconds:", elapsed
END PROGRAM example_secnds
```
+### Non-Standard Intrinsics: DSECNDS
+#### Description
+`DSECNDS(refTime)` is the double precision variant of `SECNDS`. It returns the number of seconds
+since midnight minus a user-supplied reference time `refTime`. Uses `REAL(KIND=8)` for higher precision.
+
+#### Usage and Info
+- **Standard:** PGI extension
+- **Class:** function
+- **Syntax:** result = `DSECNDS(refTime)`
+- **Arguments:**
+
+| ARGUMENT | INTENT | TYPE | KIND | Description |
+|-----------|--------|---------------|-------------------------|------------------------------------------|
+| `refTime` | `IN` | `REAL, scalar`| REAL(KIND=8), required | Reference time in seconds since midnight |
+
+- **Return Value:** REAL(KIND=8), scalar — seconds elapsed since `refTime`.
+- **Purity:** Impure
+
+#### Example
+```fortran
+PROGRAM example_dsecnds
+ DOUBLE PRECISION :: refTime
+ refTime = 0.0D0
+ PRINT '(F24.15)', DSECNDS(refTime)
+END PROGRAM example_dsecnds
+```
### Non-standard Intrinsics: SECOND
This intrinsic is an alias for `CPU_TIME`: supporting both a subroutine and a
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index cd73798d71262..038c06b5f1596 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -249,6 +249,8 @@ struct IntrinsicLibrary {
mlir::Value genCosd(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genCospi(mlir::Type, llvm::ArrayRef<mlir::Value>);
void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>);
+ fir::ExtendedValue genDsecnds(mlir::Type resultType,
+ llvm::ArrayRef<fir::ExtendedValue> args);
mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genDotProduct(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
index 548ee4bb65818..71eb7d44b4f2c 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
@@ -44,6 +44,10 @@ void genDateAndTime(fir::FirOpBuilder &, mlir::Location,
std::optional<fir::CharBoxValue> date,
std::optional<fir::CharBoxValue> time,
std::optional<fir::CharBoxValue> zone, mlir::Value values);
+
+mlir::Value genDsecnds(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Value refTime);
+
void genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value values, mlir::Value time);
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index 9a100cec9e6b9..7e4201f15171f 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -28,6 +28,10 @@ typedef std::uint32_t gid_t;
extern "C" {
+// PGI extension function DSECNDS(refTime)
+double FORTRAN_PROCEDURE_NAME(dsecnds)(double *refTime);
+double RTNAME(Dsecnds)(double *refTime, const char *sourceFile, int line);
+
// CALL FLUSH(n) antedates the Fortran 2003 FLUSH statement.
void FORTRAN_PROCEDURE_NAME(flush)(const int &unit);
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index abe53c31210d0..c7f174f7989dd 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -462,6 +462,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"vector_b", AnyNumeric, Rank::vector}},
ResultNumeric, Rank::scalar, IntrinsicClass::transformationalFunction},
{"dprod", {{"x", DefaultReal}, {"y", DefaultReal}}, DoublePrecision},
+ {"dsecnds",
+ {{"refTime", TypePattern{RealType, KindCode::exactKind, 8},
+ Rank::scalar}},
+ TypePattern{RealType, KindCode::exactKind, 8}, Rank::scalar},
{"dshiftl",
{{"i", SameIntOrUnsigned},
{"j", SameIntOrUnsigned, Rank::elementalOrBOZ}, {"shift", AnyInt}},
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index e1c9520592de6..07ec543709764 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -427,6 +427,10 @@ static constexpr IntrinsicHandler handlers[]{
{{{"vector_a", asBox}, {"vector_b", asBox}}},
/*isElemental=*/false},
{"dprod", &I::genDprod},
+ {"dsecnds",
+ &I::genDsecnds,
+ {{{"refTime", asAddr}}},
+ /*isElemental=*/false},
{"dshiftl", &I::genDshiftl},
{"dshiftr", &I::genDshiftr},
{"eoshift",
@@ -3941,6 +3945,23 @@ mlir::Value IntrinsicLibrary::genDprod(mlir::Type resultType,
return mlir::arith::MulFOp::create(builder, loc, a, b);
}
+// DSECNDS
+// Double precision variant of SECNDS (PGI extension)
+fir::ExtendedValue
+IntrinsicLibrary::genDsecnds(mlir::Type resultType,
+ llvm::ArrayRef<fir::ExtendedValue> args) {
+ assert(args.size() == 1 && "DSECNDS expects one argument");
+
+ mlir::Value refTime = fir::getBase(args[0]);
+
+ if (!refTime)
+ fir::emitFatalError(loc, "expected REFERENCE TIME parameter");
+
+ mlir::Value result = fir::runtime::genDsecnds(builder, loc, refTime);
+
+ return builder.createConvert(loc, resultType, result);
+}
+
// DSHIFTL
mlir::Value IntrinsicLibrary::genDshiftl(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args) {
diff --git a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
index dc61903ddd369..e0a4654fc2004 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
@@ -106,6 +106,23 @@ void fir::runtime::genDateAndTime(fir::FirOpBuilder &builder,
fir::CallOp::create(builder, loc, callee, args);
}
+mlir::Value fir::runtime::genDsecnds(fir::FirOpBuilder &builder,
+ mlir::Location loc, mlir::Value refTime) {
+ auto runtimeFunc =
+ fir::runtime::getRuntimeFunc<mkRTKey(Dsecnds)>(loc, builder);
+
+ mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
+
+ mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
+ mlir::Value sourceLine =
+ fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(2));
+
+ llvm::SmallVector<mlir::Value> args = {refTime, sourceFile, sourceLine};
+ args = fir::runtime::createArguments(builder, loc, runtimeFuncTy, args);
+
+ return fir::CallOp::create(builder, loc, runtimeFunc, args).getResult(0);
+}
+
void fir::runtime::genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value values, mlir::Value time) {
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Etime)>(loc, builder);
diff --git a/flang/test/Lower/Intrinsics/dsecnds.f90 b/flang/test/Lower/Intrinsics/dsecnds.f90
new file mode 100644
index 0000000000000..a2655bef55d9d
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/dsecnds.f90
@@ -0,0 +1,23 @@
+! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPuse_dsecnds(
+! CHECK-SAME: %[[arg0:.*]]: !fir.ref<f64>
+function use_dsecnds(refTime) result(elapsed)
+ double precision :: refTime, elapsed
+ elapsed = dsecnds(refTime)
+end function
+
+! Verify filename and line operands are passed into runtime call
+! CHECK: %[[STRADDR:.*]] = fir.address_of(
+! CHECK: %[[LINE:.*]] = arith.constant {{.*}} : i32
+! CHECK: %[[FNAME8:.*]] = fir.convert %[[STRADDR]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+
+! Call the runtime DSECNDS with (refTime, file, line)
+! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%[[arg0]], %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref<f64>, !fir.ref<i8>, i32) -> f64
+
+! Guard: no illegal ref conversion
+! CHECK-NOT: fir.convert {{.*}} : (f64) -> !fir.ref<f64>
+
+! Function returns f64
+! CHECK: return {{.*}} : f64
+
>From 97567ea8c7f043906b6af396ec25a58aa35bda88 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Wed, 10 Sep 2025 12:59:37 -0400
Subject: [PATCH 2/3] [flang] Adjust DSECNDS test to HLFIR and update FileCheck
patterns
---
flang/test/Lower/Intrinsics/dsecnds.f90 | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/flang/test/Lower/Intrinsics/dsecnds.f90 b/flang/test/Lower/Intrinsics/dsecnds.f90
index a2655bef55d9d..909eea6b69a53 100644
--- a/flang/test/Lower/Intrinsics/dsecnds.f90
+++ b/flang/test/Lower/Intrinsics/dsecnds.f90
@@ -1,4 +1,4 @@
-! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
+! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
! CHECK-LABEL: func.func @_QPuse_dsecnds(
! CHECK-SAME: %[[arg0:.*]]: !fir.ref<f64>
@@ -13,11 +13,10 @@ function use_dsecnds(refTime) result(elapsed)
! CHECK: %[[FNAME8:.*]] = fir.convert %[[STRADDR]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
! Call the runtime DSECNDS with (refTime, file, line)
-! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%[[arg0]], %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref<f64>, !fir.ref<i8>, i32) -> f64
+! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%{{.*}}, %{{.*}}, %{{.*}}) {{.*}} : (!fir.ref<f64>, !fir.ref<i8>, i32) -> f64
! Guard: no illegal ref conversion
! CHECK-NOT: fir.convert {{.*}} : (f64) -> !fir.ref<f64>
! Function returns f64
! CHECK: return {{.*}} : f64
-
>From 3f8dc37287f046197e2b6036a481eb368ae56b04 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Wed, 10 Sep 2025 13:59:48 -0400
Subject: [PATCH 3/3] [flang] dsecnds test: reuse captured defs in call (review
feedback)
---
flang/test/Lower/Intrinsics/dsecnds.f90 | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/flang/test/Lower/Intrinsics/dsecnds.f90 b/flang/test/Lower/Intrinsics/dsecnds.f90
index 909eea6b69a53..03814ff60bd80 100644
--- a/flang/test/Lower/Intrinsics/dsecnds.f90
+++ b/flang/test/Lower/Intrinsics/dsecnds.f90
@@ -7,16 +7,27 @@ function use_dsecnds(refTime) result(elapsed)
elapsed = dsecnds(refTime)
end function
-! Verify filename and line operands are passed into runtime call
+! The argument is lowered with hlfir.declare, which returns two results.
+! Capture it here to check that the correct SSA value (%...#0)
+! is passed to the runtime call later
+! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[arg0]] dummy_scope
+
+! The file name and source line are also lowered and passed as runtime arguments
+! Capture the constant line number and convert the file name to i8*.
! CHECK: %[[STRADDR:.*]] = fir.address_of(
! CHECK: %[[LINE:.*]] = arith.constant {{.*}} : i32
! CHECK: %[[FNAME8:.*]] = fir.convert %[[STRADDR]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
-! Call the runtime DSECNDS with (refTime, file, line)
-! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%{{.*}}, %{{.*}}, %{{.*}}) {{.*}} : (!fir.ref<f64>, !fir.ref<i8>, i32) -> f64
+! Verify the runtime call is made with:
+! - the declared refTime value (%[[DECL]]#0)
+! - the converted filename
+! - the source line constant
+! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%[[DECL]]#0, %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref<f64>, !fir.ref<i8>, i32) -> f64
-! Guard: no illegal ref conversion
+! Ensure there is no illegal conversion of a value result into a reference
! CHECK-NOT: fir.convert {{.*}} : (f64) -> !fir.ref<f64>
-! Function returns f64
+! Confirm the function result is returned as a plain f64
! CHECK: return {{.*}} : f64
+
+
More information about the llvm-commits
mailing list