[flang] Register and lower SECNDS (stubbed implementation) (#151878)

This patch registers and lowers the GNU extension intrinsic `SECNDS` in
Flang.

- Registration and lowering are wired through the intrinsic
infrastructure.
- genSecnds() currently emits a TODO fatal error, marking it as
unimplemented.
- Includes an XFAIL test to exercise the lowering path and reflect
current status.

Fixes https://github.com/llvm/llvm-project/issues/58728

---

CC @eugeneepshteyn @klausler

---------

Co-authored-by: Eugene Epshteyn <eepshteyn@nvidia.com>
This commit is contained in:
Šárka Holendová
2025-08-25 22:31:48 -04:00
committed by GitHub
parent e49946b27f
commit 86bca2963f
7 changed files with 96 additions and 0 deletions

View File

@@ -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.

View File

@@ -407,6 +407,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,

View File

@@ -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);

View File

@@ -921,6 +921,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
{"secnds",
{{"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},

View File

@@ -869,6 +869,10 @@ static constexpr IntrinsicHandler handlers[]{
{"back", asValue, handleDynamicOptional},
{"kind", asValue}}},
/*isElemental=*/true},
{"secnds",
&I::genSecnds,
{{{"refTime", asAddr}}},
/*isElemental=*/false},
{"second",
&I::genSecond,
{{{"time", asAddr}}},
@@ -7864,6 +7868,22 @@ IntrinsicLibrary::genScan(mlir::Type resultType,
return readAndAddCleanUp(resultMutableBox, resultType, "SCAN");
}
// SECNDS
fir::ExtendedValue
IntrinsicLibrary::genSecnds(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args) {
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
fir::ExtendedValue
IntrinsicLibrary::genSecond(std::optional<mlir::Type> resultType,

View File

@@ -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) {

View File

@@ -0,0 +1,23 @@
! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
! 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
! File/line operands (dont 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