From e5bc137bdf0e85201c46f68fc0819a51920b039c Mon Sep 17 00:00:00 2001 From: Sarka Holendova 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` 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 T SecndsImpl(T *refTime) { static_assert(std::is_same::value || std::is_same::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 genCospi(mlir::Type, llvm::ArrayRef); void genDateAndTime(llvm::ArrayRef); + fir::ExtendedValue genDsecnds(mlir::Type resultType, + llvm::ArrayRef args); mlir::Value genDim(mlir::Type, llvm::ArrayRef); fir::ExtendedValue genDotProduct(mlir::Type, llvm::ArrayRef); 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 date, std::optional time, std::optional 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 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 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(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 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(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 +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.ref + +! Call the runtime DSECNDS with (refTime, file, line) +! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%[[arg0]], %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref, !fir.ref, i32) -> f64 + +! Guard: no illegal ref conversion +! CHECK-NOT: fir.convert {{.*}} : (f64) -> !fir.ref + +! Function returns f64 +! CHECK: return {{.*}} : f64 + From 97567ea8c7f043906b6af396ec25a58aa35bda88 Mon Sep 17 00:00:00 2001 From: Sarka Holendova 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 @@ -13,11 +13,10 @@ function use_dsecnds(refTime) result(elapsed) ! CHECK: %[[FNAME8:.*]] = fir.convert %[[STRADDR]] : (!fir.ref>) -> !fir.ref ! Call the runtime DSECNDS with (refTime, file, line) -! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%[[arg0]], %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref, !fir.ref, i32) -> f64 +! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%{{.*}}, %{{.*}}, %{{.*}}) {{.*}} : (!fir.ref, !fir.ref, i32) -> f64 ! Guard: no illegal ref conversion ! CHECK-NOT: fir.convert {{.*}} : (f64) -> !fir.ref ! Function returns f64 ! CHECK: return {{.*}} : f64 - From d2d576d6a2a346248c98b82e8e6d599e4989e41b Mon Sep 17 00:00:00 2001 From: Sarka Holendova Date: Wed, 10 Sep 2025 13:59:48 -0400 Subject: [PATCH 3/3] [flang] dsecnds test: reuse captured defs in call (review feedback) --- .../flang/Optimizer/Builder/IntrinsicCall.h | 2 +- .../Optimizer/Builder/Runtime/Intrinsics.h | 2 +- flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 2 +- .../Optimizer/Builder/Runtime/Intrinsics.cpp | 2 +- flang/test/Lower/Intrinsics/dsecnds.f90 | 21 ++++++++++++++----- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h index 038c06b5f1596..3977ac681f83f 100644 --- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h +++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h @@ -250,7 +250,7 @@ struct IntrinsicLibrary { mlir::Value genCospi(mlir::Type, llvm::ArrayRef); void genDateAndTime(llvm::ArrayRef); fir::ExtendedValue genDsecnds(mlir::Type resultType, - llvm::ArrayRef args); + llvm::ArrayRef args); mlir::Value genDim(mlir::Type, llvm::ArrayRef); fir::ExtendedValue genDotProduct(mlir::Type, llvm::ArrayRef); diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h index 71eb7d44b4f2c..7a97172cfbb9a 100644 --- a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h +++ b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h @@ -46,7 +46,7 @@ void genDateAndTime(fir::FirOpBuilder &, mlir::Location, std::optional zone, mlir::Value values); mlir::Value genDsecnds(fir::FirOpBuilder &builder, mlir::Location loc, - mlir::Value refTime); + mlir::Value refTime); void genEtime(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value values, mlir::Value time); diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp index 07ec543709764..1bf006b08a052 100644 --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -3949,7 +3949,7 @@ mlir::Value IntrinsicLibrary::genDprod(mlir::Type resultType, // Double precision variant of SECNDS (PGI extension) fir::ExtendedValue IntrinsicLibrary::genDsecnds(mlir::Type resultType, - llvm::ArrayRef args) { + llvm::ArrayRef args) { assert(args.size() == 1 && "DSECNDS expects one argument"); mlir::Value refTime = fir::getBase(args[0]); diff --git a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp index e0a4654fc2004..110b1b20898c7 100644 --- a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp +++ b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp @@ -107,7 +107,7 @@ void fir::runtime::genDateAndTime(fir::FirOpBuilder &builder, } mlir::Value fir::runtime::genDsecnds(fir::FirOpBuilder &builder, - mlir::Location loc, mlir::Value refTime) { + mlir::Location loc, mlir::Value refTime) { auto runtimeFunc = fir::runtime::getRuntimeFunc(loc, builder); 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.ref -! Call the runtime DSECNDS with (refTime, file, line) -! CHECK: %[[CALL:.*]] = fir.call @_FortranADsecnds(%{{.*}}, %{{.*}}, %{{.*}}) {{.*}} : (!fir.ref, !fir.ref, 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, !fir.ref, 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 -! Function returns f64 +! Confirm the function result is returned as a plain f64 ! CHECK: return {{.*}} : f64 + +