Skip to content

Commit b17e644

Browse files
authored
[flang/flang-rt] Adding support of RAND, IRAND and SRAND intrinsics (llvm#166780)
This PR adds support of [RAND](https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gfortran/RAND.html), [IRAND](https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gfortran/IRAND.html) and [SRAND](https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gfortran/SRAND.html) intrinsics in Flang, which are part of the GNU extension. These intrinsics are used in the following benchmark: [floatingspeed](https://github.com/ahbarnett/floatingspeed/)
1 parent fa2ddf2 commit b17e644

File tree

9 files changed

+227
-0
lines changed

9 files changed

+227
-0
lines changed

flang-rt/lib/runtime/extensions.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "flang/Runtime/extensions.h"
1313
#include "unit.h"
1414
#include "flang-rt/runtime/descriptor.h"
15+
#include "flang-rt/runtime/lock.h"
1516
#include "flang-rt/runtime/terminator.h"
1617
#include "flang-rt/runtime/tools.h"
1718
#include "flang/Runtime/command.h"
@@ -23,6 +24,7 @@
2324
#include <cstdio>
2425
#include <cstring>
2526
#include <ctime>
27+
#include <limits>
2628
#include <signal.h>
2729
#include <stdlib.h>
2830
#include <thread>
@@ -60,6 +62,11 @@ inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
6062

6163
namespace Fortran::runtime {
6264

65+
#define GFC_RAND_A 16807
66+
#define GFC_RAND_M 2147483647
67+
static unsigned rand_seed = 1;
68+
static Lock rand_seed_lock;
69+
6370
// Common implementation that could be used for either SECNDS() or DSECNDS(),
6471
// which are defined for float or double.
6572
template <typename T> T SecndsImpl(T *refTime) {
@@ -417,6 +424,57 @@ void RTNAME(ShowDescriptor)(const Fortran::runtime::Descriptor *descr) {
417424
}
418425
}
419426

427+
static void _internal_srand(int seed) { rand_seed = seed ? seed : 123459876; }
428+
429+
// IRAND(I)
430+
int RTNAME(Irand)(int *i) {
431+
int j;
432+
if (i)
433+
j = *i;
434+
else
435+
j = 0;
436+
437+
rand_seed_lock.Take();
438+
switch (j) {
439+
case 0:
440+
break;
441+
case 1:
442+
_internal_srand(0);
443+
break;
444+
default:
445+
_internal_srand(j);
446+
break;
447+
}
448+
449+
rand_seed = GFC_RAND_A * rand_seed % GFC_RAND_M;
450+
j = (int)rand_seed;
451+
rand_seed_lock.Drop();
452+
return j;
453+
}
454+
455+
// RAND(I)
456+
float RTNAME(Rand)(int *i, const char *sourceFile, int line) {
457+
unsigned mask = 0;
458+
constexpr int radix = std::numeric_limits<float>::radix;
459+
constexpr int digits = std::numeric_limits<float>::digits;
460+
if (radix == 2) {
461+
mask = ~(unsigned)0u << (32 - digits + 1);
462+
} else if (radix == 16) {
463+
mask = ~(unsigned)0u << ((8 - digits) * 4 + 1);
464+
} else {
465+
Terminator terminator{sourceFile, line};
466+
terminator.Crash("Radix unknown value.");
467+
}
468+
return ((unsigned)(RTNAME(Irand)(i) - 1) & mask) * (float)0x1.p-31f;
469+
}
470+
471+
// SRAND(SEED)
472+
void FORTRAN_PROCEDURE_NAME(srand)(int *seed) {
473+
rand_seed_lock.Take();
474+
_internal_srand(*seed);
475+
rand_seed_lock.Drop();
476+
}
477+
420478
// Extension procedures related to I/O
421479

422480
namespace io {

flang/docs/Intrinsics.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,3 +1455,45 @@ subroutine test
14551455
call show_descriptor(a)
14561456
end subroutine test
14571457
```
1458+
1459+
### Non-Standard Intrinsics: SRAND
1460+
1461+
#### Description
1462+
`SRAND` reinitializes the pseudo-random number generator called by `RAND` and `IRAND`.
1463+
The new seed used by the generator is specified by the required argument `SEED`.
1464+
1465+
#### Usage and Info
1466+
1467+
- **Standard:** GNU extension
1468+
- **Class:** Subroutine
1469+
- **Syntax:** `CALL SRAND(SEED)`
1470+
1471+
### Non-Standard Intrinsics: IRAND
1472+
1473+
#### Description
1474+
`IRAND(FLAG)` returns a pseudo-random number from a uniform distribution between 0 and a system-dependent limit.
1475+
If `FLAG` is 0, the next number in the current sequence is returned;
1476+
If `FLAG` is 1, the generator is restarted by `CALL SRAND(0)`;
1477+
If `FLAG` has any other value, it is used as a new seed with `SRAND`.
1478+
The return value is of `INTEGER` type of kind 4.
1479+
1480+
#### Usage and Info
1481+
1482+
- **Standard:** GNU extension
1483+
- **Class:** function
1484+
- **Syntax:** `RESULT = IRAND(I)`
1485+
1486+
### Non-Standard Intrinsics: RAND
1487+
1488+
#### Description
1489+
`RAND(FLAG)` returns a pseudo-random number from a uniform distribution between 0 and 1.
1490+
If `FLAG` is 0, the next number in the current sequence is returned;
1491+
If `FLAG` is 1, the generator is restarted by `CALL SRAND(0)`;
1492+
If `FLAG` has any other value, it is used as a new seed with `SRAND`.
1493+
The return value is of `REAL` type with the default kind.
1494+
1495+
#### Usage and Info
1496+
1497+
- **Standard:** GNU extension
1498+
- **Class:** function
1499+
- **Syntax:** `RESULT = RAND(I)`

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ struct IntrinsicLibrary {
330330
fir::ExtendedValue genIndex(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
331331
mlir::Value genIor(mlir::Type, llvm::ArrayRef<mlir::Value>);
332332
fir::ExtendedValue genIparity(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
333+
fir::ExtendedValue genIrand(mlir::Type resultType,
334+
llvm::ArrayRef<fir::ExtendedValue>);
333335
fir::ExtendedValue genIsContiguous(mlir::Type,
334336
llvm::ArrayRef<fir::ExtendedValue>);
335337
template <Fortran::runtime::io::Iostat value>
@@ -377,6 +379,8 @@ struct IntrinsicLibrary {
377379
fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
378380
fir::ExtendedValue genPutenv(std::optional<mlir::Type>,
379381
llvm::ArrayRef<fir::ExtendedValue>);
382+
fir::ExtendedValue genRand(mlir::Type resultType,
383+
llvm::ArrayRef<fir::ExtendedValue>);
380384
void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>);
381385
void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>);
382386
void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ mlir::Value genChdir(fir::FirOpBuilder &builder, mlir::Location loc,
114114
/// generate dump of a descriptor
115115
void genShowDescriptor(fir::FirOpBuilder &builder, mlir::Location loc,
116116
mlir::Value descriptor);
117+
118+
mlir::Value genIrand(fir::FirOpBuilder &builder, mlir::Location loc,
119+
mlir::Value i);
120+
mlir::Value genRand(fir::FirOpBuilder &builder, mlir::Location loc,
121+
mlir::Value i);
122+
117123
} // namespace runtime
118124
} // namespace fir
119125

flang/include/flang/Runtime/extensions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,14 @@ float RTNAME(Secnds)(float *refTime, const char *sourceFile, int line);
110110
// Extension subroutine SHOW_DESCRIPTOR(D)
111111
void RTNAME(ShowDescriptor)(const Fortran::runtime::Descriptor *descr);
112112

113+
// GNU extension function IRAND(I)
114+
int RTNAME(Irand)(int *i);
115+
116+
// GNU extension function RAND(I)
117+
float RTNAME(Rand)(int *i, const char *sourceFile, int line);
118+
119+
// GNU extension subroutine SRAND(SEED)
120+
void FORTRAN_PROCEDURE_NAME(srand)(int *seed);
121+
113122
} // extern "C"
114123
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
654654
{{"i", OperandUnsigned}, {"j", OperandUnsigned, Rank::elementalOrBOZ}},
655655
OperandUnsigned},
656656
{"ior", {{"i", BOZ}, {"j", SameIntOrUnsigned}}, SameIntOrUnsigned},
657+
{"irand",
658+
{{"i", TypePattern{IntType, KindCode::exactKind, 4}, Rank::scalar,
659+
Optionality::optional}},
660+
TypePattern{IntType, KindCode::exactKind, 4}, Rank::scalar},
657661
{"ishft", {{"i", SameIntOrUnsigned}, {"shift", AnyInt}}, SameIntOrUnsigned},
658662
{"ishftc",
659663
{{"i", SameIntOrUnsigned}, {"shift", AnyInt},
@@ -872,6 +876,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
872876
common::Intent::In,
873877
{ArgFlag::canBeMoldNull, ArgFlag::onlyConstantInquiry}}},
874878
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
879+
{"rand",
880+
{{"i", TypePattern{IntType, KindCode::exactKind, 4}, Rank::scalar,
881+
Optionality::optional}},
882+
TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar},
875883
{"range",
876884
{{"x", AnyNumeric, Rank::anyOrAssumedRank, Optionality::required,
877885
common::Intent::In,

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,10 @@ static constexpr IntrinsicHandler handlers[]{
499499
{"dim", asValue},
500500
{"mask", asBox, handleDynamicOptional}}},
501501
/*isElemental=*/false},
502+
{"irand",
503+
&I::genIrand,
504+
{{{"i", asAddr, handleDynamicOptional}}},
505+
/*isElemental=*/false},
502506
{"is_contiguous",
503507
&I::genIsContiguous,
504508
{{{"array", asBox}}},
@@ -625,6 +629,10 @@ static constexpr IntrinsicHandler handlers[]{
625629
&I::genPutenv,
626630
{{{"str", asAddr}, {"status", asAddr, handleDynamicOptional}}},
627631
/*isElemental=*/false},
632+
{"rand",
633+
&I::genRand,
634+
{{{"i", asAddr, handleDynamicOptional}}},
635+
/*isElemental=*/false},
628636
{"random_init",
629637
&I::genRandomInit,
630638
{{{"repeatable", asValue}, {"image_distinct", asValue}}},
@@ -6162,6 +6170,20 @@ IntrinsicLibrary::genIparity(mlir::Type resultType,
61626170
"IPARITY", resultType, args);
61636171
}
61646172

6173+
// IRAND
6174+
fir::ExtendedValue
6175+
IntrinsicLibrary::genIrand(mlir::Type resultType,
6176+
llvm::ArrayRef<fir::ExtendedValue> args) {
6177+
assert(args.size() == 1);
6178+
mlir::Value i =
6179+
isStaticallyPresent(args[0])
6180+
? fir::getBase(args[0])
6181+
: fir::AbsentOp::create(builder, loc,
6182+
builder.getRefType(builder.getI32Type()))
6183+
.getResult();
6184+
return fir::runtime::genIrand(builder, loc, i);
6185+
}
6186+
61656187
// IS_CONTIGUOUS
61666188
fir::ExtendedValue
61676189
IntrinsicLibrary::genIsContiguous(mlir::Type resultType,
@@ -7188,6 +7210,19 @@ IntrinsicLibrary::genPutenv(std::optional<mlir::Type> resultType,
71887210
return {};
71897211
}
71907212

7213+
// RAND
7214+
fir::ExtendedValue
7215+
IntrinsicLibrary::genRand(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) {
7216+
assert(args.size() == 1);
7217+
mlir::Value i =
7218+
isStaticallyPresent(args[0])
7219+
? fir::getBase(args[0])
7220+
: fir::AbsentOp::create(builder, loc,
7221+
builder.getRefType(builder.getI32Type()))
7222+
.getResult();
7223+
return fir::runtime::genRand(builder, loc, i);
7224+
}
7225+
71917226
// RANDOM_INIT
71927227
void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) {
71937228
assert(args.size() == 2);

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,27 @@ void fir::runtime::genShowDescriptor(fir::FirOpBuilder &builder,
477477
fir::runtime::getRuntimeFunc<mkRTKey(ShowDescriptor)>(loc, builder)};
478478
fir::CallOp::create(builder, loc, func, descAddr);
479479
}
480+
481+
mlir::Value fir::runtime::genIrand(fir::FirOpBuilder &builder,
482+
mlir::Location loc, mlir::Value i) {
483+
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Irand)>(loc, builder);
484+
mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
485+
486+
llvm::SmallVector<mlir::Value> args =
487+
fir::runtime::createArguments(builder, loc, runtimeFuncTy, i);
488+
return fir::CallOp::create(builder, loc, runtimeFunc, args).getResult(0);
489+
}
490+
491+
mlir::Value fir::runtime::genRand(fir::FirOpBuilder &builder,
492+
mlir::Location loc, mlir::Value i) {
493+
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Rand)>(loc, builder);
494+
mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
495+
496+
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
497+
mlir::Value sourceLine =
498+
fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(2));
499+
500+
llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
501+
builder, loc, runtimeFuncTy, i, sourceFile, sourceLine);
502+
return fir::CallOp::create(builder, loc, runtimeFunc, args).getResult(0);
503+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
! RUN: bbc -emit-hlfir %s -o - | FileCheck --check-prefixes=CHECK %s
2+
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck --check-prefixes=CHECK %s
3+
4+
! CHECK-LABEL: func @_QPtest_srand(
5+
subroutine test_srand()
6+
integer :: seed = 0
7+
call srand(seed)
8+
! CHECK: %[[VAL_0:.*]] = fir.address_of(@_QFtest_srandEseed) : !fir.ref<i32>
9+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_srandEseed"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
10+
! CHECK: fir.call @_QPsrand(%[[VAL_1]]#0) fastmath<contract> : (!fir.ref<i32>) -> ()
11+
! CHECK: return
12+
end subroutine test_srand
13+
14+
! CHECK-LABEL: func @_QPtest_irand(
15+
subroutine test_irand()
16+
integer :: seed = 0
17+
integer :: result
18+
result = irand(seed)
19+
! CHECK: %[[VAL_0:.*]] = fir.alloca i32 {bindc_name = "result", uniq_name = "_QFtest_irandEresult"}
20+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_irandEresult"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
21+
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QFtest_irandEseed) : !fir.ref<i32>
22+
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {uniq_name = "_QFtest_irandEseed"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
23+
! CHECK: %[[VAL_4:.*]] = fir.call @_FortranAIrand(%[[VAL_3]]#0) fastmath<contract> : (!fir.ref<i32>) -> i32
24+
! CHECK: hlfir.assign %[[VAL_4]] to %[[VAL_1]]#0 : i32, !fir.ref<i32>
25+
! CHECK: return
26+
end subroutine test_irand
27+
28+
! CHECK-LABEL: func @_QPtest_rand(
29+
subroutine test_rand()
30+
integer :: seed = 0
31+
real :: result
32+
result = rand(seed)
33+
! CHECK: %[[VAL_0:.*]] = fir.alloca f32 {bindc_name = "result", uniq_name = "_QFtest_randEresult"}
34+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_randEresult"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
35+
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QFtest_randEseed) : !fir.ref<i32>
36+
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] {uniq_name = "_QFtest_randEseed"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
37+
! CHECK: %[[VAL_4:.*]] = fir.call @_FortranARand(%[[VAL_3]]#0, %[[SOURCE:.*]], %[[LINE:.*]]) fastmath<contract> : (!fir.ref<i32>, !fir.ref<i8>, i32) -> f32
38+
! CHECK: hlfir.assign %[[VAL_4]] to %[[VAL_1]]#0 : f32, !fir.ref<f32>
39+
! CHECK: return
40+
end subroutine test_rand
41+

0 commit comments

Comments
 (0)