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/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h index 88c3ada3ff64f..cd73798d71262 100644 --- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h +++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h @@ -407,6 +407,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/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 0f79ba6ed62b6..af453adcb1bf1 100644 --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -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}, diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp index a0d365ced5932..aadc54294e702 100644 --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -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 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 resultType, 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 new file mode 100644 index 0000000000000..5f7dcb077af18 --- /dev/null +++ b/flang/test/Lower/Intrinsics/secnds.f90 @@ -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 +function use_secnds(refTime) result(elapsed) + real :: refTime, elapsed + elapsed = secnds(refTime) +end function + +! 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 +