[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