1 //===-- runtime/random.cpp ------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Implements the intrinsic subroutines RANDOM_INIT, RANDOM_NUMBER, and
12 #include "flang/Runtime/random.h"
14 #include "random-templates.h"
15 #include "terminator.h"
16 #include "flang/Common/float128.h"
17 #include "flang/Common/leading-zero-bit-count.h"
18 #include "flang/Common/uint128.h"
19 #include "flang/Runtime/cpp-type.h"
20 #include "flang/Runtime/descriptor.h"
27 namespace Fortran::runtime::random
{
31 Fortran::common::optional
<GeneratedWord
> nextValue
;
35 void RTNAME(RandomInit
)(bool repeatable
, bool /*image_distinct*/) {
36 // TODO: multiple images and image_distinct: add image number
38 CriticalSection critical
{lock
};
44 clock_gettime(CLOCK_REALTIME
, &ts
);
45 generator
.seed(ts
.tv_sec
^ ts
.tv_nsec
);
47 generator
.seed(time(nullptr));
53 void RTNAME(RandomNumber
)(
54 const Descriptor
&harvest
, const char *source
, int line
) {
55 Terminator terminator
{source
, line
};
56 auto typeCode
{harvest
.type().GetCategoryAndKind()};
57 RUNTIME_CHECK(terminator
, typeCode
&& typeCode
->first
== TypeCategory::Real
);
58 int kind
{typeCode
->second
};
62 Generate
<CppTypeFor
<TypeCategory::Real
, 4>, 24>(harvest
);
65 Generate
<CppTypeFor
<TypeCategory::Real
, 8>, 53>(harvest
);
68 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 10>) {
70 Generate
<CppTypeFor
<TypeCategory::Real
, 10>, 64>(harvest
);
77 "not yet implemented: intrinsic: REAL(KIND=%d) in RANDOM_NUMBER", kind
);
80 void RTNAME(RandomSeedSize
)(
81 const Descriptor
*size
, const char *source
, int line
) {
82 if (!size
|| !size
->raw().base_addr
) {
83 RTNAME(RandomSeedDefaultPut
)();
86 Terminator terminator
{source
, line
};
87 auto typeCode
{size
->type().GetCategoryAndKind()};
88 RUNTIME_CHECK(terminator
,
89 size
->rank() == 0 && typeCode
&&
90 typeCode
->first
== TypeCategory::Integer
);
91 int sizeArg
{typeCode
->second
};
94 *size
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 4>>() = 1;
97 *size
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 8>>() = 1;
101 "not yet implemented: intrinsic: RANDOM_SEED(SIZE=): size %d\n",
106 void RTNAME(RandomSeedPut
)(
107 const Descriptor
*put
, const char *source
, int line
) {
108 if (!put
|| !put
->raw().base_addr
) {
109 RTNAME(RandomSeedDefaultPut
)();
112 Terminator terminator
{source
, line
};
113 auto typeCode
{put
->type().GetCategoryAndKind()};
114 RUNTIME_CHECK(terminator
,
115 put
->rank() == 1 && typeCode
&&
116 typeCode
->first
== TypeCategory::Integer
&&
117 put
->GetDimension(0).Extent() >= 1);
118 int putArg
{typeCode
->second
};
122 seed
= *put
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 4>>();
125 seed
= *put
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 8>>();
129 "not yet implemented: intrinsic: RANDOM_SEED(PUT=): put %d\n", putArg
);
132 CriticalSection critical
{lock
};
133 generator
.seed(seed
);
138 void RTNAME(RandomSeedDefaultPut
)() {
139 // TODO: should this be time &/or image dependent?
141 CriticalSection critical
{lock
};
146 void RTNAME(RandomSeedGet
)(
147 const Descriptor
*get
, const char *source
, int line
) {
148 if (!get
|| !get
->raw().base_addr
) {
149 RTNAME(RandomSeedDefaultPut
)();
152 Terminator terminator
{source
, line
};
153 auto typeCode
{get
->type().GetCategoryAndKind()};
154 RUNTIME_CHECK(terminator
,
155 get
->rank() == 1 && typeCode
&&
156 typeCode
->first
== TypeCategory::Integer
&&
157 get
->GetDimension(0).Extent() >= 1);
158 int getArg
{typeCode
->second
};
161 CriticalSection critical
{lock
};
162 seed
= GetNextValue();
167 *get
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 4>>() = seed
;
170 *get
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 8>>() = seed
;
174 "not yet implemented: intrinsic: RANDOM_SEED(GET=): get %d\n", getArg
);
178 void RTNAME(RandomSeed
)(const Descriptor
*size
, const Descriptor
*put
,
179 const Descriptor
*get
, const char *source
, int line
) {
180 bool sizePresent
= size
&& size
->raw().base_addr
;
181 bool putPresent
= put
&& put
->raw().base_addr
;
182 bool getPresent
= get
&& get
->raw().base_addr
;
183 if (sizePresent
+ putPresent
+ getPresent
> 1)
184 Terminator
{source
, line
}.Crash(
185 "RANDOM_SEED must have either 1 or no arguments");
187 RTNAME(RandomSeedSize
)(size
, source
, line
);
189 RTNAME(RandomSeedPut
)(put
, source
, line
);
191 RTNAME(RandomSeedGet
)(get
, source
, line
);
193 RTNAME(RandomSeedDefaultPut
)();
197 } // namespace Fortran::runtime::random