Skip to content

Commit dc8d70a

Browse files
authored
[reland][flang] Add ETIME runtime and lowering intrinsics implementation (llvm#92571)
This is same as llvm#90578 with an added fix. This PR updated tests of etime intrinsic due to Lowering changes for assigning dummy_scope to hlfir.declare. Referring to llvm#92472 and llvm#90989
1 parent d74bc82 commit dc8d70a

File tree

13 files changed

+384
-5
lines changed

13 files changed

+384
-5
lines changed

etime-function.mlir

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<"dlti.endianness", "little">, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
2+
func.func @_QPetime_test(%arg0: !fir.ref<!fir.array<2xf32>> {fir.bindc_name = "values"}, %arg1: !fir.ref<f32> {fir.bindc_name = "time"}) {
3+
%c9_i32 = arith.constant 9 : i32
4+
%c2 = arith.constant 2 : index
5+
%0 = fir.alloca f32
6+
%1 = fir.declare %arg1 {uniq_name = "_QFetime_testEtime"} : (!fir.ref<f32>) -> !fir.ref<f32>
7+
%2 = fir.shape %c2 : (index) -> !fir.shape<1>
8+
%3 = fir.declare %arg0(%2) {uniq_name = "_QFetime_testEvalues"} : (!fir.ref<!fir.array<2xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<2xf32>>
9+
%4 = fir.embox %3(%2) : (!fir.ref<!fir.array<2xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<2xf32>>
10+
%5 = fir.embox %0 : (!fir.ref<f32>) -> !fir.box<f32>
11+
%6 = fir.address_of(@_QQclX116781708dcf8f012d7ec1e40d743d97) : !fir.ref<!fir.char<1,71>>
12+
%7 = fir.convert %4 : (!fir.box<!fir.array<2xf32>>) -> !fir.box<none>
13+
%8 = fir.convert %5 : (!fir.box<f32>) -> !fir.box<none>
14+
%9 = fir.convert %6 : (!fir.ref<!fir.char<1,71>>) -> !fir.ref<i8>
15+
%10 = fir.call @_FortranAEtime(%7, %8, %9, %c9_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
16+
%11 = fir.load %0 : !fir.ref<f32>
17+
fir.store %11 to %1 : !fir.ref<f32>
18+
return
19+
}
20+
func.func private @_FortranAEtime(!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none attributes {fir.runtime}
21+
fir.global linkonce @_QQclX116781708dcf8f012d7ec1e40d743d97 constant : !fir.char<1,71> {
22+
%0 = fir.string_lit "/home/jump/llvm-project/flang/test/Lower/Intrinsics/etime-function.f90\00"(71) : !fir.char<1,71>
23+
fir.has_value %0 : !fir.char<1,71>
24+
}
25+
}

flang/docs/Intrinsics.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,3 +916,55 @@ used in constant expressions have currently no folding support at all.
916916
- If a condition occurs that would assign a nonzero value to `CMDSTAT` but the `CMDSTAT` variable is not present, error termination is initiated.
917917
- On POSIX-compatible systems, the child process (async process) will be terminated with no effect on the parent process (continues).
918918
- On Windows, error termination is not initiated.
919+
920+
### Non-Standard Intrinsics: ETIME
921+
922+
#### Description
923+
`ETIME(VALUES, TIME)` returns the number of seconds of runtime since the start of the process’s execution in *TIME*. *VALUES* returns the user and system components of this time in `VALUES(1)` and `VALUES(2)` respectively. *TIME* is equal to `VALUES(1) + VALUES(2)`.
924+
925+
On some systems, the underlying timings are represented using types with sufficiently small limits that overflows (wrap around) are possible, such as 32-bit types. Therefore, the values returned by this intrinsic might be, or become, negative, or numerically less than previous values, during a single run of the compiled program.
926+
927+
This intrinsic is provided in both subroutine and function forms; however, only one form can be used in any given program unit.
928+
929+
*VALUES* and *TIME* are `INTENT(OUT)` and provide the following:
930+
931+
932+
| | |
933+
|---------------|-----------------------------------|
934+
| `VALUES(1)` | User time in seconds. |
935+
| `VALUES(2)` | System time in seconds. |
936+
| `TIME` | Run time since start in seconds. |
937+
938+
#### Usage and Info
939+
940+
- **Standard:** GNU extension
941+
- **Class:** Subroutine, function
942+
- **Syntax:** `CALL ETIME(VALUES, TIME)`
943+
- **Arguments:**
944+
- **Return value** Elapsed time in seconds since the start of program execution.
945+
946+
| Argument | Description |
947+
|------------|-----------------------------------------------------------------------|
948+
| `VALUES` | The type shall be REAL(4), DIMENSION(2). |
949+
| `TIME` | The type shall be REAL(4). |
950+
951+
#### Example
952+
Here is an example usage from [Gfortran ETIME](https://gcc.gnu.org/onlinedocs/gfortran/ETIME.html)
953+
```Fortran
954+
program test_etime
955+
integer(8) :: i, j
956+
real, dimension(2) :: tarray
957+
real :: result
958+
call ETIME(tarray, result)
959+
print *, result
960+
print *, tarray(1)
961+
print *, tarray(2)
962+
do i=1,100000000 ! Just a delay
963+
j = i * i - i
964+
end do
965+
call ETIME(tarray, result)
966+
print *, result
967+
print *, tarray(1)
968+
print *, tarray(2)
969+
end program test_etime
970+
```

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ struct IntrinsicLibrary {
222222
fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
223223
void genExit(llvm::ArrayRef<fir::ExtendedValue>);
224224
void genExecuteCommandLine(mlir::ArrayRef<fir::ExtendedValue> args);
225+
fir::ExtendedValue genEtime(std::optional<mlir::Type>,
226+
mlir::ArrayRef<fir::ExtendedValue> args);
225227
mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>);
226228
fir::ExtendedValue genExtendsTypeOf(mlir::Type,
227229
llvm::ArrayRef<fir::ExtendedValue>);
@@ -400,8 +402,10 @@ struct IntrinsicLibrary {
400402
using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
401403
using ExtendedGenerator = decltype(&IntrinsicLibrary::genLenTrim);
402404
using SubroutineGenerator = decltype(&IntrinsicLibrary::genDateAndTime);
403-
using Generator =
404-
std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>;
405+
/// The generator for intrinsic that has both function and subroutine form.
406+
using DualGenerator = decltype(&IntrinsicLibrary::genEtime);
407+
using Generator = std::variant<ElementalGenerator, ExtendedGenerator,
408+
SubroutineGenerator, DualGenerator>;
405409

406410
/// All generators can be outlined. This will build a function named
407411
/// "fir."+ <generic name> + "." + <result type code> and generate the
@@ -442,6 +446,10 @@ struct IntrinsicLibrary {
442446
llvm::ArrayRef<mlir::Value> args);
443447
mlir::Value invokeGenerator(SubroutineGenerator generator,
444448
llvm::ArrayRef<mlir::Value> args);
449+
mlir::Value invokeGenerator(DualGenerator generator,
450+
llvm::ArrayRef<mlir::Value> args);
451+
mlir::Value invokeGenerator(DualGenerator generator, mlir::Type resultType,
452+
llvm::ArrayRef<mlir::Value> args);
445453

446454
/// Get pointer to unrestricted intrinsic. Generate the related unrestricted
447455
/// intrinsic if it is not defined yet.

flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ void genDateAndTime(fir::FirOpBuilder &, mlir::Location,
4444
std::optional<fir::CharBoxValue> date,
4545
std::optional<fir::CharBoxValue> time,
4646
std::optional<fir::CharBoxValue> zone, mlir::Value values);
47+
void genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
48+
mlir::Value values, mlir::Value time);
4749

4850
void genRandomInit(fir::FirOpBuilder &, mlir::Location, mlir::Value repeatable,
4951
mlir::Value imageDistinct);

flang/include/flang/Runtime/time-intrinsic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ void RTNAME(DateAndTime)(char *date, std::size_t dateChars, char *time,
4343
const char *source = nullptr, int line = 0,
4444
const Descriptor *values = nullptr);
4545

46+
void RTNAME(Etime)(const Descriptor *values, const Descriptor *time,
47+
const char *sourceFile, int line);
48+
4649
} // extern "C"
4750
} // namespace Fortran::runtime
4851
#endif // FORTRAN_RUNTIME_TIME_INTRINSIC_H_

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
454454
{"erf", {{"x", SameReal}}, SameReal},
455455
{"erfc", {{"x", SameReal}}, SameReal},
456456
{"erfc_scaled", {{"x", SameReal}}, SameReal},
457+
{"etime",
458+
{{"values", TypePattern{RealType, KindCode::exactKind, 4}, Rank::vector,
459+
Optionality::required, common::Intent::Out}},
460+
TypePattern{RealType, KindCode::exactKind, 4}},
457461
{"exp", {{"x", SameFloating}}, SameFloating},
458462
{"exp", {{"x", SameFloating}}, SameFloating},
459463
{"exponent", {{"x", AnyReal}}, DefaultInt},
@@ -1342,6 +1346,12 @@ static const IntrinsicInterface intrinsicSubroutine[]{
13421346
{"values", AnyInt, Rank::vector, Optionality::optional,
13431347
common::Intent::Out}},
13441348
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1349+
{"etime",
1350+
{{"values", TypePattern{RealType, KindCode::exactKind, 4}, Rank::vector,
1351+
Optionality::required, common::Intent::Out},
1352+
{"time", TypePattern{RealType, KindCode::exactKind, 4},
1353+
Rank::scalar, Optionality::required, common::Intent::Out}},
1354+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
13451355
{"execute_command_line",
13461356
{{"command", DefaultChar, Rank::scalar},
13471357
{"wait", AnyLogical, Rank::scalar, Optionality::optional},
@@ -2484,6 +2494,7 @@ class IntrinsicProcTable::Implementation {
24842494
bool IsIntrinsic(const std::string &) const;
24852495
bool IsIntrinsicFunction(const std::string &) const;
24862496
bool IsIntrinsicSubroutine(const std::string &) const;
2497+
bool IsDualIntrinsic(const std::string &) const;
24872498

24882499
IntrinsicClass GetIntrinsicClass(const std::string &) const;
24892500
std::string GetGenericIntrinsicName(const std::string &) const;
@@ -2545,6 +2556,17 @@ bool IntrinsicProcTable::Implementation::IsIntrinsic(
25452556
const std::string &name) const {
25462557
return IsIntrinsicFunction(name) || IsIntrinsicSubroutine(name);
25472558
}
2559+
bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
2560+
const std::string &name) const {
2561+
// Collection for some intrinsics with function and subroutine form,
2562+
// in order to pass the semantic check.
2563+
static const std::string dualIntrinsic[]{{"etime"}};
2564+
2565+
return std::find_if(std::begin(dualIntrinsic), std::end(dualIntrinsic),
2566+
[&name](const std::string &dualName) {
2567+
return dualName == name;
2568+
}) != std::end(dualIntrinsic);
2569+
}
25482570

25492571
IntrinsicClass IntrinsicProcTable::Implementation::GetIntrinsicClass(
25502572
const std::string &name) const {
@@ -3083,7 +3105,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
30833105
return specificCall;
30843106
}
30853107
}
3086-
if (IsIntrinsicFunction(call.name)) {
3108+
if (IsIntrinsicFunction(call.name) && !IsDualIntrinsic(call.name)) {
30873109
context.messages().Say(
30883110
"Cannot use intrinsic function '%s' as a subroutine"_err_en_US,
30893111
call.name);
@@ -3218,7 +3240,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
32183240
}
32193241

32203242
if (specificBuffer.empty() && genericBuffer.empty() &&
3221-
IsIntrinsicSubroutine(call.name)) {
3243+
IsIntrinsicSubroutine(call.name) && !IsDualIntrinsic(call.name)) {
32223244
context.messages().Say(
32233245
"Cannot use intrinsic subroutine '%s' as a function"_err_en_US,
32243246
call.name);

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "flang/Optimizer/Builder/Runtime/Stop.h"
3636
#include "flang/Optimizer/Builder/Runtime/Transformational.h"
3737
#include "flang/Optimizer/Builder/Todo.h"
38+
#include "flang/Optimizer/Dialect/FIROps.h"
3839
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
3940
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
4041
#include "flang/Optimizer/Support/FatalError.h"
@@ -49,6 +50,7 @@
4950
#include "llvm/Support/Debug.h"
5051
#include "llvm/Support/MathExtras.h"
5152
#include "llvm/Support/raw_ostream.h"
53+
#include <mlir/IR/Value.h>
5254
#include <optional>
5355

5456
#define DEBUG_TYPE "flang-lower-intrinsic"
@@ -222,6 +224,10 @@ static constexpr IntrinsicHandler handlers[]{
222224
{"boundary", asBox, handleDynamicOptional},
223225
{"dim", asValue}}},
224226
/*isElemental=*/false},
227+
{"etime",
228+
&I::genEtime,
229+
{{{"values", asBox}, {"time", asBox}}},
230+
/*isElemental=*/false},
225231
{"execute_command_line",
226232
&I::genExecuteCommandLine,
227233
{{{"command", asBox},
@@ -1682,6 +1688,24 @@ IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>(
16821688
return mlir::Value();
16831689
}
16841690

1691+
template <>
1692+
fir::ExtendedValue
1693+
IntrinsicLibrary::genElementalCall<IntrinsicLibrary::DualGenerator>(
1694+
DualGenerator generator, llvm::StringRef name, mlir::Type resultType,
1695+
llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
1696+
assert(resultType.getImpl() && "expect elemental intrinsic to be functions");
1697+
1698+
for (const fir::ExtendedValue &arg : args)
1699+
if (!arg.getUnboxed() && !arg.getCharBox())
1700+
// fir::emitFatalError(loc, "nonscalar intrinsic argument");
1701+
crashOnMissingIntrinsic(loc, name);
1702+
if (outline)
1703+
return outlineInExtendedWrapper(generator, name, resultType, args);
1704+
1705+
return std::invoke(generator, *this, std::optional<mlir::Type>{resultType},
1706+
args);
1707+
}
1708+
16851709
static fir::ExtendedValue
16861710
invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
16871711
const IntrinsicHandler &handler,
@@ -1725,6 +1749,22 @@ invokeHandler(IntrinsicLibrary::SubroutineGenerator generator,
17251749
return mlir::Value{};
17261750
}
17271751

1752+
static fir::ExtendedValue
1753+
invokeHandler(IntrinsicLibrary::DualGenerator generator,
1754+
const IntrinsicHandler &handler,
1755+
std::optional<mlir::Type> resultType,
1756+
llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
1757+
IntrinsicLibrary &lib) {
1758+
if (handler.isElemental)
1759+
return lib.genElementalCall(generator, handler.name, mlir::Type{}, args,
1760+
outline);
1761+
if (outline)
1762+
return lib.outlineInExtendedWrapper(generator, handler.name, resultType,
1763+
args);
1764+
1765+
return std::invoke(generator, lib, resultType, args);
1766+
}
1767+
17281768
std::pair<fir::ExtendedValue, bool>
17291769
IntrinsicLibrary::genIntrinsicCall(llvm::StringRef specificName,
17301770
std::optional<mlir::Type> resultType,
@@ -1820,6 +1860,34 @@ IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator,
18201860
return {};
18211861
}
18221862

1863+
mlir::Value
1864+
IntrinsicLibrary::invokeGenerator(DualGenerator generator,
1865+
llvm::ArrayRef<mlir::Value> args) {
1866+
llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1867+
for (mlir::Value arg : args)
1868+
extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1869+
std::invoke(generator, *this, std::optional<mlir::Type>{}, extendedArgs);
1870+
return {};
1871+
}
1872+
1873+
mlir::Value
1874+
IntrinsicLibrary::invokeGenerator(DualGenerator generator,
1875+
mlir::Type resultType,
1876+
llvm::ArrayRef<mlir::Value> args) {
1877+
llvm::SmallVector<fir::ExtendedValue> extendedArgs;
1878+
for (mlir::Value arg : args)
1879+
extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
1880+
1881+
if (resultType.getImpl() == nullptr) {
1882+
// TODO:
1883+
assert(false && "result type is null");
1884+
}
1885+
1886+
auto extendedResult = std::invoke(
1887+
generator, *this, std::optional<mlir::Type>{resultType}, extendedArgs);
1888+
return toValue(extendedResult, builder, loc);
1889+
}
1890+
18231891
//===----------------------------------------------------------------------===//
18241892
// Intrinsic Procedure Mangling
18251893
//===----------------------------------------------------------------------===//
@@ -3235,6 +3303,37 @@ void IntrinsicLibrary::genExecuteCommandLine(
32353303
exitstatBox, cmdstatBox, cmdmsgBox);
32363304
}
32373305

3306+
// ETIME
3307+
fir::ExtendedValue
3308+
IntrinsicLibrary::genEtime(std::optional<mlir::Type> resultType,
3309+
llvm::ArrayRef<fir::ExtendedValue> args) {
3310+
assert((args.size() == 2 && !resultType.has_value()) ||
3311+
(args.size() == 1 && resultType.has_value()));
3312+
3313+
mlir::Value values = fir::getBase(args[0]);
3314+
if (resultType.has_value()) {
3315+
// function form
3316+
if (!values)
3317+
fir::emitFatalError(loc, "expected VALUES parameter");
3318+
3319+
auto timeAddr = builder.createTemporary(loc, *resultType);
3320+
auto timeBox = builder.createBox(loc, timeAddr);
3321+
fir::runtime::genEtime(builder, loc, values, timeBox);
3322+
return builder.create<fir::LoadOp>(loc, timeAddr);
3323+
} else {
3324+
// subroutine form
3325+
mlir::Value time = fir::getBase(args[1]);
3326+
if (!values)
3327+
fir::emitFatalError(loc, "expected VALUES parameter");
3328+
if (!time)
3329+
fir::emitFatalError(loc, "expected TIME parameter");
3330+
3331+
fir::runtime::genEtime(builder, loc, values, time);
3332+
return {};
3333+
}
3334+
return {};
3335+
}
3336+
32383337
// EXIT
32393338
void IntrinsicLibrary::genExit(llvm::ArrayRef<fir::ExtendedValue> args) {
32403339
assert(args.size() == 1);

flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,20 @@ void fir::runtime::genDateAndTime(fir::FirOpBuilder &builder,
106106
builder.create<fir::CallOp>(loc, callee, args);
107107
}
108108

109+
void fir::runtime::genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
110+
mlir::Value values, mlir::Value time) {
111+
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Etime)>(loc, builder);
112+
mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
113+
114+
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
115+
mlir::Value sourceLine =
116+
fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(3));
117+
118+
llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
119+
builder, loc, runtimeFuncTy, values, time, sourceFile, sourceLine);
120+
builder.create<fir::CallOp>(loc, runtimeFunc, args);
121+
}
122+
109123
void fir::runtime::genRandomInit(fir::FirOpBuilder &builder, mlir::Location loc,
110124
mlir::Value repeatable,
111125
mlir::Value imageDistinct) {

0 commit comments

Comments
 (0)