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 "terminator.h"
15 #include "flang/Common/float128.h"
16 #include "flang/Common/leading-zero-bit-count.h"
17 #include "flang/Common/uint128.h"
18 #include "flang/Runtime/cpp-type.h"
19 #include "flang/Runtime/descriptor.h"
28 namespace Fortran::runtime
{
30 // Newer "Minimum standard", recommended by Park, Miller, and Stockmeyer in
31 // 1993. Same as C++17 std::minstd_rand, but explicitly instantiated for
34 std::linear_congruential_engine
<std::uint_fast32_t, 48271, 0, 2147483647>;
36 using GeneratedWord
= typename
Generator::result_type
;
37 static constexpr std::uint64_t range
{
38 static_cast<std::uint64_t>(Generator::max() - Generator::min() + 1)};
39 static constexpr bool rangeIsPowerOfTwo
{(range
& (range
- 1)) == 0};
40 static constexpr int rangeBits
{
41 64 - common::LeadingZeroBitCount(range
) - !rangeIsPowerOfTwo
};
44 static Generator generator
;
45 static std::optional
<GeneratedWord
> nextValue
;
47 // Call only with lock held
48 static GeneratedWord
GetNextValue() {
50 if (nextValue
.has_value()) {
59 template <typename REAL
, int PREC
>
60 inline void Generate(const Descriptor
&harvest
) {
61 static constexpr std::size_t minBits
{
62 std::max
<std::size_t>(PREC
, 8 * sizeof(GeneratedWord
))};
63 using Int
= common::HostUnsignedIntType
<minBits
>;
64 static constexpr std::size_t words
{
65 static_cast<std::size_t>(PREC
+ rangeBits
- 1) / rangeBits
};
66 std::size_t elements
{harvest
.Elements()};
67 SubscriptValue at
[maxRank
];
68 harvest
.GetLowerBounds(at
);
70 CriticalSection critical
{lock
};
71 for (std::size_t j
{0}; j
< elements
; ++j
) {
73 Int fraction
{GetNextValue()};
74 if constexpr (words
> 1) {
75 for (std::size_t k
{1}; k
< words
; ++k
) {
76 static constexpr auto rangeMask
{
77 (GeneratedWord
{1} << rangeBits
) - 1};
78 GeneratedWord word
{(GetNextValue() - generator
.min()) & rangeMask
};
79 fraction
= (fraction
<< rangeBits
) | word
;
82 fraction
>>= words
* rangeBits
- PREC
;
83 REAL next
{std::ldexp(static_cast<REAL
>(fraction
), -(PREC
+ 1))};
84 if (next
>= 0.0 && next
< 1.0) {
85 *harvest
.Element
<REAL
>(at
) = next
;
89 harvest
.IncrementSubscripts(at
);
96 void RTNAME(RandomInit
)(bool repeatable
, bool /*image_distinct*/) {
97 // TODO: multiple images and image_distinct: add image number
99 CriticalSection critical
{lock
};
103 #ifdef CLOCK_REALTIME
105 clock_gettime(CLOCK_REALTIME
, &ts
);
106 generator
.seed(ts
.tv_sec
& ts
.tv_nsec
);
108 generator
.seed(time(nullptr));
114 void RTNAME(RandomNumber
)(
115 const Descriptor
&harvest
, const char *source
, int line
) {
116 Terminator terminator
{source
, line
};
117 auto typeCode
{harvest
.type().GetCategoryAndKind()};
118 RUNTIME_CHECK(terminator
, typeCode
&& typeCode
->first
== TypeCategory::Real
);
119 int kind
{typeCode
->second
};
121 // TODO: REAL (2 & 3)
123 Generate
<CppTypeFor
<TypeCategory::Real
, 4>, 24>(harvest
);
126 Generate
<CppTypeFor
<TypeCategory::Real
, 8>, 53>(harvest
);
129 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 10>) {
130 #if LDBL_MANT_DIG == 64
131 Generate
<CppTypeFor
<TypeCategory::Real
, 10>, 64>(harvest
);
137 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 16>) {
138 #if LDBL_MANT_DIG == 113
139 Generate
<CppTypeFor
<TypeCategory::Real
, 16>, 113>(harvest
);
145 terminator
.Crash("not yet implemented: RANDOM_NUMBER(): REAL kind %d", kind
);
148 void RTNAME(RandomSeedSize
)(
149 const Descriptor
*size
, const char *source
, int line
) {
150 if (!size
|| !size
->raw().base_addr
) {
151 RTNAME(RandomSeedDefaultPut
)();
154 Terminator terminator
{source
, line
};
155 auto typeCode
{size
->type().GetCategoryAndKind()};
156 RUNTIME_CHECK(terminator
,
157 size
->rank() == 0 && typeCode
&&
158 typeCode
->first
== TypeCategory::Integer
);
159 int kind
{typeCode
->second
};
162 *size
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 4>>() = 1;
165 *size
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 8>>() = 1;
169 "not yet implemented: RANDOM_SEED(SIZE=): kind %d\n", kind
);
173 void RTNAME(RandomSeedPut
)(
174 const Descriptor
*put
, const char *source
, int line
) {
175 if (!put
|| !put
->raw().base_addr
) {
176 RTNAME(RandomSeedDefaultPut
)();
179 Terminator terminator
{source
, line
};
180 auto typeCode
{put
->type().GetCategoryAndKind()};
181 RUNTIME_CHECK(terminator
,
182 put
->rank() == 1 && typeCode
&&
183 typeCode
->first
== TypeCategory::Integer
&&
184 put
->GetDimension(0).Extent() >= 1);
185 int kind
{typeCode
->second
};
189 seed
= *put
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 4>>();
192 seed
= *put
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 8>>();
195 terminator
.Crash("not yet implemented: RANDOM_SEED(PUT=): kind %d\n", kind
);
198 CriticalSection critical
{lock
};
199 generator
.seed(seed
);
204 void RTNAME(RandomSeedDefaultPut
)() {
205 // TODO: should this be time &/or image dependent?
207 CriticalSection critical
{lock
};
212 void RTNAME(RandomSeedGet
)(
213 const Descriptor
*get
, const char *source
, int line
) {
214 if (!get
|| !get
->raw().base_addr
) {
215 RTNAME(RandomSeedDefaultPut
)();
218 Terminator terminator
{source
, line
};
219 auto typeCode
{get
->type().GetCategoryAndKind()};
220 RUNTIME_CHECK(terminator
,
221 get
->rank() == 1 && typeCode
&&
222 typeCode
->first
== TypeCategory::Integer
&&
223 get
->GetDimension(0).Extent() >= 1);
224 int kind
{typeCode
->second
};
227 CriticalSection critical
{lock
};
228 seed
= GetNextValue();
233 *get
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 4>>() = seed
;
236 *get
->OffsetElement
<CppTypeFor
<TypeCategory::Integer
, 8>>() = seed
;
239 terminator
.Crash("not yet implemented: RANDOM_SEED(GET=): kind %d\n", kind
);
243 void RTNAME(RandomSeed
)(const Descriptor
*size
, const Descriptor
*put
,
244 const Descriptor
*get
, const char *source
, int line
) {
245 bool sizePresent
= size
&& size
->raw().base_addr
;
246 bool putPresent
= put
&& put
->raw().base_addr
;
247 bool getPresent
= get
&& get
->raw().base_addr
;
248 if (sizePresent
+ putPresent
+ getPresent
> 1)
249 Terminator
{source
, line
}.Crash(
250 "RANDOM_SEED must have either 1 or no arguments");
252 RTNAME(RandomSeedSize
)(size
, source
, line
);
254 RTNAME(RandomSeedPut
)(put
, source
, line
);
256 RTNAME(RandomSeedGet
)(get
, source
, line
);
258 RTNAME(RandomSeedDefaultPut
)();
262 } // namespace Fortran::runtime