From dae2e998af73945c4f56de83aecba775a5c820b7 Mon Sep 17 00:00:00 2001 From: Sarka Holendova 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); mlir::Value genScale(mlir::Type, llvm::ArrayRef); fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genSecnds(mlir::Type resultType, + llvm::ArrayRef args); fir::ExtendedValue genSecond(std::optional, mlir::ArrayRef); 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 args) { + TODO(loc, "not yet implemented: SECNDS"); +} + // SECOND fir::ExtendedValue IntrinsicLibrary::genSecond(std::optional 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 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 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 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 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 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 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(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); +} + /// 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 +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.ref + +! Important: pass refTime by address and return a value f32 +! CHECK: %[[CALL:.*]] = fir.call @{{.*}}Secnds(%arg0, %[[FNAME8]], %[[LINE]]) {{.*}} : (!fir.ref, !fir.ref, i32) -> f32 + +! Guard against illegal value ->ref conversion of result +! CHECK-NOT: fir.convert {{.*}} : (f32) -> !fir.ref + +! Function returns an f32 value +! CHECK: return {{.*}} : f32 From 7de65bd24c69ca5578910debd22548faedd7f95f Mon Sep 17 00:00:00 2001 From: Sarka Holendova 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 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 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 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 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