[flang-commits] [flang] [flang] Register and lower SECNDS (stubbed implementation) (PR #151878)
Eugene Epshteyn via flang-commits
flang-commits at lists.llvm.org
Mon Aug 25 18:52:09 PDT 2025
https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/151878
>From dae2e998af73945c4f56de83aecba775a5c820b7 Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Fri, 1 Aug 2025 12:18:07 -0400
Subject: [PATCH 1/3] [flang] Register and lower SECNDS (stubbed
implementation)
---
.../include/flang/Optimizer/Builder/IntrinsicCall.h | 2 ++
flang/lib/Evaluate/intrinsics.cpp | 5 +++++
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 12 ++++++++++++
flang/test/Lower/Intrinsics/secnds.f90 | 12 ++++++++++++
4 files changed, 31 insertions(+)
create mode 100644 flang/test/Lower/Intrinsics/secnds.f90
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 2afd50410ae82..ddbdb837b201a 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -405,6 +405,8 @@ struct IntrinsicLibrary {
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genScale(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
+ fir::ExtendedValue genSecnds(mlir::Type resultType,
+ llvm::ArrayRef<fir::ExtendedValue> args);
fir::ExtendedValue genSecond(std::optional<mlir::Type>,
mlir::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSelectedCharKind(mlir::Type,
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index c37a7f908d4d1..d241dff685813 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -921,6 +921,11 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
+ {"secnds",
+ {{"x", TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar,
+ Optionality::required, common::Intent::In}},
+ TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar,
+ IntrinsicClass::impureSubroutine},
{"second", {}, DefaultReal, Rank::scalar},
{"selected_char_kind", {{"name", DefaultChar, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index bfa470dd5690d..7f63fcb979b15 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -864,6 +864,10 @@ static constexpr IntrinsicHandler handlers[]{
{"back", asValue, handleDynamicOptional},
{"kind", asValue}}},
/*isElemental=*/true},
+ {"secnds",
+ &I::genSecnds,
+ {{{"x", asValue}}},
+ /*isElemental=*/false},
{"second",
&I::genSecond,
{{{"time", asAddr}}},
@@ -7813,6 +7817,14 @@ IntrinsicLibrary::genScan(mlir::Type resultType,
return readAndAddCleanUp(resultMutableBox, resultType, "SCAN");
}
+// SECNDS
+// Lowering is registered, runtime not yet implemented
+fir::ExtendedValue
+IntrinsicLibrary::genSecnds(mlir::Type resultType,
+ llvm::ArrayRef<fir::ExtendedValue> args) {
+ TODO(loc, "not yet implemented: SECNDS");
+}
+
// SECOND
fir::ExtendedValue
IntrinsicLibrary::genSecond(std::optional<mlir::Type> resultType,
diff --git a/flang/test/Lower/Intrinsics/secnds.f90 b/flang/test/Lower/Intrinsics/secnds.f90
new file mode 100644
index 0000000000000..105e93ad9301e
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/secnds.f90
@@ -0,0 +1,12 @@
+!---------------------------------------------------------------------
+! RUN: %flang_fc1 -emit-fir %s -o - 2>&1 | FileCheck %s
+! XFAIL: *
+!---------------------------------------------------------------------
+
+program test_secnds
+ real :: x
+ x = secnds(1.0)
+end program
+
+! CHECK: not yet implemented: SECNDS
+
>From c78747564490ee1e47681c1ca328cb5840e4365b Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Tue, 5 Aug 2025 21:19:34 -0400
Subject: [PATCH 2/3] [flang] Register and lower SECNDS intrinsic (stubbed
implementation)
- Updated SECNDS handler to pass `refTime` asAddr instead of asValue, matching the backend runtime contract (`float*`).
- Addressed review feedback in the lowering hook:
* Built the argument list using fir::runtime::createArguments
* Replaced previous call emission with fir::CallOp::create(...)
- Updated the regression test for SECNDS lowering to check argument passing, call signature, and return type.
- Added documentation for SECNDS to Intrinsics.md, including description, usage, and example.
---
flang/docs/Intrinsics.md | 27 +++++++++++++++++
.../Optimizer/Builder/Runtime/Intrinsics.h | 3 ++
flang/lib/Evaluate/intrinsics.cpp | 19 ++++++------
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 14 +++++++--
.../Optimizer/Builder/Runtime/Intrinsics.cpp | 17 +++++++++++
flang/test/Lower/Intrinsics/secnds.f90 | 29 +++++++++++++------
6 files changed, 87 insertions(+), 22 deletions(-)
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index f7da6c889d413..4b000877e7844 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -1123,6 +1123,33 @@ program rename_proc
end program rename_proc
```
+### Non-Standard Intrinsics: SECNDS
+#### Description
+`SECNDS(refTime)` returns the number of seconds since midnight minus a user-supplied reference time `refTime`. If the difference is negative (i.e., the current time is past midnight and refTime was from the previous day), the result wraps around midnight to yield a positive value.
+
+#### Usage and Info
+- **Standard:** GNU extension
+- **Class:** function
+- **Syntax:** result = `SECNDS(refTime)`
+- **Arguments:**
+
+| ARGUMENT | INTENT | TYPE | KIND | Description |
+|-----------|--------|---------------|-------------------------|------------------------------------------|
+| `refTime` | `IN` | `REAL, scalar`| REAL(KIND=4), required | Reference time in seconds since midnight |
+
+- **Return Value:** REAL(KIND=4), scalar — seconds elapsed since `refTime`.
+- **Purity:** Impure. SECNDS references the system clock and may not be invoked from a PURE procedure.
+
+#### Example
+```Fortran
+PROGRAM example_secnds
+ REAL :: refTime, elapsed
+ refTime = SECNDS(0.0)
+ elapsed = SECNDS(refTime)
+ PRINT *, "Elapsed seconds:", elapsed
+END PROGRAM example_secnds
+```
+
### Non-standard Intrinsics: SECOND
This intrinsic is an alias for `CPU_TIME`: supporting both a subroutine and a
function form.
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
index 145ea04e56484..548ee4bb65818 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h
@@ -70,6 +70,9 @@ void genRandomSeed(fir::FirOpBuilder &, mlir::Location, mlir::Value size,
void genRename(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value path1, mlir::Value path2, mlir::Value status);
+mlir::Value genSecnds(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Value refTime);
+
/// generate time runtime call
mlir::Value genTime(fir::FirOpBuilder &builder, mlir::Location loc);
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index d241dff685813..f63dbb8374487 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -922,10 +922,9 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
DefaultingKIND},
KINDInt},
{"secnds",
- {{"x", TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar,
- Optionality::required, common::Intent::In}},
- TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar,
- IntrinsicClass::impureSubroutine},
+ {{"refTime", TypePattern{RealType, KindCode::exactKind, 4},
+ Rank::scalar}},
+ TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar},
{"second", {}, DefaultReal, Rank::scalar},
{"selected_char_kind", {{"name", DefaultChar, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
@@ -1893,7 +1892,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// arguments in a procedure reference.
std::size_t dummyArgPatterns{0};
for (; dummyArgPatterns < maxArguments && dummy[dummyArgPatterns].keyword;
- ++dummyArgPatterns) {
+ ++dummyArgPatterns) {
}
// MAX and MIN (and others that map to them) allow their last argument to
// be repeated indefinitely. The actualForDummy vector is sized
@@ -2697,7 +2696,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// Rearrange the actual arguments into dummy argument order.
ActualArguments rearranged(dummies);
for (std::size_t j{0}; j < dummies; ++j) {
- if (ActualArgument *arg{actualForDummy[j]}) {
+ if (ActualArgument * arg{actualForDummy[j]}) {
rearranged[j] = std::move(*arg);
}
}
@@ -3128,7 +3127,7 @@ IntrinsicProcTable::Implementation::HandleC_F_Pointer(
}
} else if (!IsInteroperableIntrinsicType(
*type, &context.languageFeatures())
- .value_or(true)) {
+ .value_or(true)) {
if (type->category() == TypeCategory::Character &&
type->kind() == 1) {
if (context.languageFeatures().ShouldWarn(
@@ -3634,7 +3633,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
parser::Messages specificBuffer;
auto specificRange{specificFuncs_.equal_range(call.name)};
for (auto specIter{specificRange.first}; specIter != specificRange.second;
- ++specIter) {
+ ++specIter) {
// We only need to check the cases with distinct generic names.
if (const char *genericName{specIter->second->generic}) {
if (auto specificCall{
@@ -3656,13 +3655,13 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
if (context.languageFeatures().IsEnabled(common::LanguageFeature::
UseGenericIntrinsicWhenSpecificDoesntMatch)) {
for (auto specIter{specificRange.first}; specIter != specificRange.second;
- ++specIter) {
+ ++specIter) {
// We only need to check the cases with distinct generic names.
if (const char *genericName{specIter->second->generic}) {
if (specIter->second->useGenericAndForceResultType) {
auto genericRange{genericFuncs_.equal_range(genericName)};
for (auto genIter{genericRange.first}; genIter != genericRange.second;
- ++genIter) {
+ ++genIter) {
if (auto specificCall{
matchOrBufferMessages(*genIter->second, specificBuffer)}) {
// Force the call result type to the specific intrinsic result
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 7f63fcb979b15..0ded152049934 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -866,7 +866,7 @@ static constexpr IntrinsicHandler handlers[]{
/*isElemental=*/true},
{"secnds",
&I::genSecnds,
- {{{"x", asValue}}},
+ {{{"refTime", asAddr}}},
/*isElemental=*/false},
{"second",
&I::genSecond,
@@ -7818,11 +7818,19 @@ IntrinsicLibrary::genScan(mlir::Type resultType,
}
// SECNDS
-// Lowering is registered, runtime not yet implemented
fir::ExtendedValue
IntrinsicLibrary::genSecnds(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args) {
- TODO(loc, "not yet implemented: SECNDS");
+ assert(args.size() == 1 && "SECNDS expects one argument");
+
+ mlir::Value refTime = fir::getBase(args[0]);
+
+ if (!refTime)
+ fir::emitFatalError(loc, "expected REFERENCE TIME parameter");
+
+ mlir::Value result = fir::runtime::genSecnds(builder, loc, refTime);
+
+ return builder.createConvert(loc, resultType, result);
}
// SECOND
diff --git a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
index ee151576ace92..dc61903ddd369 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp
@@ -276,6 +276,23 @@ void fir::runtime::genRename(fir::FirOpBuilder &builder, mlir::Location loc,
fir::CallOp::create(builder, loc, runtimeFunc, args);
}
+mlir::Value fir::runtime::genSecnds(fir::FirOpBuilder &builder,
+ mlir::Location loc, mlir::Value refTime) {
+ auto runtimeFunc =
+ fir::runtime::getRuntimeFunc<mkRTKey(Secnds)>(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);
+}
+
/// generate runtime call to time intrinsic
mlir::Value fir::runtime::genTime(fir::FirOpBuilder &builder,
mlir::Location loc) {
diff --git a/flang/test/Lower/Intrinsics/secnds.f90 b/flang/test/Lower/Intrinsics/secnds.f90
index 105e93ad9301e..5f7dcb077af18 100644
--- a/flang/test/Lower/Intrinsics/secnds.f90
+++ b/flang/test/Lower/Intrinsics/secnds.f90
@@ -1,12 +1,23 @@
-!---------------------------------------------------------------------
-! RUN: %flang_fc1 -emit-fir %s -o - 2>&1 | FileCheck %s
-! XFAIL: *
-!---------------------------------------------------------------------
+! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
-program test_secnds
- real :: x
- x = secnds(1.0)
-end program
+! CHECK-LABEL: func.func @_QPuse_secnds(
+! CHECK-SAME: %arg0: !fir.ref<f32>
+function use_secnds(refTime) result(elapsed)
+ real :: refTime, elapsed
+ elapsed = secnds(refTime)
+end function
-! CHECK: not yet implemented: SECNDS
+! File/line operands (don’t match the actual path/number)
+! CHECK: %[[STRADDR:.*]] = fir.address_of(
+! CHECK: %[[LINE:.*]] = arith.constant {{.*}} : i32
+! CHECK: %[[FNAME8:.*]] = fir.convert %[[STRADDR]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+
+! Important: pass refTime by address and return a value f32
+! CHECK: %[[CALL:.*]] = fir.call @{{.*}}Secnds(%arg0, %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref<f32>, !fir.ref<i8>, i32) -> f32
+
+! Guard against illegal value ->ref conversion of result
+! CHECK-NOT: fir.convert {{.*}} : (f32) -> !fir.ref<f32>
+
+! Function returns an f32 value
+! CHECK: return {{.*}} : f32
>From 7de65bd24c69ca5578910debd22548faedd7f95f Mon Sep 17 00:00:00 2001
From: Sarka Holendova <sarka.holendova at gmail.com>
Date: Sat, 9 Aug 2025 18:42:29 -0400
Subject: [PATCH 3/3] Fix pointer spacing and indentation
---
flang/lib/Evaluate/intrinsics.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index f63dbb8374487..cdfd432234d1c 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -1892,7 +1892,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// arguments in a procedure reference.
std::size_t dummyArgPatterns{0};
for (; dummyArgPatterns < maxArguments && dummy[dummyArgPatterns].keyword;
- ++dummyArgPatterns) {
+ ++dummyArgPatterns) {
}
// MAX and MIN (and others that map to them) allow their last argument to
// be repeated indefinitely. The actualForDummy vector is sized
@@ -2696,7 +2696,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// Rearrange the actual arguments into dummy argument order.
ActualArguments rearranged(dummies);
for (std::size_t j{0}; j < dummies; ++j) {
- if (ActualArgument * arg{actualForDummy[j]}) {
+ if (ActualArgument *arg{actualForDummy[j]}) {
rearranged[j] = std::move(*arg);
}
}
@@ -3127,7 +3127,7 @@ IntrinsicProcTable::Implementation::HandleC_F_Pointer(
}
} else if (!IsInteroperableIntrinsicType(
*type, &context.languageFeatures())
- .value_or(true)) {
+ .value_or(true)) {
if (type->category() == TypeCategory::Character &&
type->kind() == 1) {
if (context.languageFeatures().ShouldWarn(
@@ -3633,7 +3633,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
parser::Messages specificBuffer;
auto specificRange{specificFuncs_.equal_range(call.name)};
for (auto specIter{specificRange.first}; specIter != specificRange.second;
- ++specIter) {
+ ++specIter) {
// We only need to check the cases with distinct generic names.
if (const char *genericName{specIter->second->generic}) {
if (auto specificCall{
@@ -3655,13 +3655,13 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
if (context.languageFeatures().IsEnabled(common::LanguageFeature::
UseGenericIntrinsicWhenSpecificDoesntMatch)) {
for (auto specIter{specificRange.first}; specIter != specificRange.second;
- ++specIter) {
+ ++specIter) {
// We only need to check the cases with distinct generic names.
if (const char *genericName{specIter->second->generic}) {
if (specIter->second->useGenericAndForceResultType) {
auto genericRange{genericFuncs_.equal_range(genericName)};
for (auto genIter{genericRange.first}; genIter != genericRange.second;
- ++genIter) {
+ ++genIter) {
if (auto specificCall{
matchOrBufferMessages(*genIter->second, specificBuffer)}) {
// Force the call result type to the specific intrinsic result
More information about the flang-commits
mailing list