[AArch64,ELF] Restrict MOVZ/MOVK to non-PIC large code model (#70178)
[llvm-project.git] / flang / runtime / random.cpp
blob8b00cfd1cac195092720746053f85a283986eaa7
1 //===-- runtime/random.cpp ------------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
9 // Implements the intrinsic subroutines RANDOM_INIT, RANDOM_NUMBER, and
10 // RANDOM_SEED.
12 #include "flang/Runtime/random.h"
13 #include "lock.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"
20 #include <algorithm>
21 #include <cmath>
22 #include <cstdint>
23 #include <limits>
24 #include <memory>
25 #include <random>
26 #include <time.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
32 // permanence.
33 using Generator =
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};
43 static Lock lock;
44 static Generator generator;
45 static std::optional<GeneratedWord> nextValue;
47 // Call only with lock held
48 static GeneratedWord GetNextValue() {
49 GeneratedWord result;
50 if (nextValue.has_value()) {
51 result = *nextValue;
52 nextValue.reset();
53 } else {
54 result = generator();
56 return result;
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) {
72 while (true) {
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;
86 break;
89 harvest.IncrementSubscripts(at);
94 extern "C" {
96 void RTNAME(RandomInit)(bool repeatable, bool /*image_distinct*/) {
97 // TODO: multiple images and image_distinct: add image number
99 CriticalSection critical{lock};
100 if (repeatable) {
101 generator.seed(0);
102 } else {
103 #ifdef CLOCK_REALTIME
104 timespec ts;
105 clock_gettime(CLOCK_REALTIME, &ts);
106 generator.seed(ts.tv_sec & ts.tv_nsec);
107 #else
108 generator.seed(time(nullptr));
109 #endif
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};
120 switch (kind) {
121 // TODO: REAL (2 & 3)
122 case 4:
123 Generate<CppTypeFor<TypeCategory::Real, 4>, 24>(harvest);
124 return;
125 case 8:
126 Generate<CppTypeFor<TypeCategory::Real, 8>, 53>(harvest);
127 return;
128 case 10:
129 if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
130 #if LDBL_MANT_DIG == 64
131 Generate<CppTypeFor<TypeCategory::Real, 10>, 64>(harvest);
132 return;
133 #endif
135 break;
136 case 16:
137 if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) {
138 #if LDBL_MANT_DIG == 113
139 Generate<CppTypeFor<TypeCategory::Real, 16>, 113>(harvest);
140 return;
141 #endif
143 break;
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)();
152 return;
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};
160 switch (kind) {
161 case 4:
162 *size->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = 1;
163 break;
164 case 8:
165 *size->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = 1;
166 break;
167 default:
168 terminator.Crash(
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)();
177 return;
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};
186 GeneratedWord seed;
187 switch (kind) {
188 case 4:
189 seed = *put->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>();
190 break;
191 case 8:
192 seed = *put->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>();
193 break;
194 default:
195 terminator.Crash("not yet implemented: RANDOM_SEED(PUT=): kind %d\n", kind);
198 CriticalSection critical{lock};
199 generator.seed(seed);
200 nextValue = seed;
204 void RTNAME(RandomSeedDefaultPut)() {
205 // TODO: should this be time &/or image dependent?
207 CriticalSection critical{lock};
208 generator.seed(0);
212 void RTNAME(RandomSeedGet)(
213 const Descriptor *get, const char *source, int line) {
214 if (!get || !get->raw().base_addr) {
215 RTNAME(RandomSeedDefaultPut)();
216 return;
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};
225 GeneratedWord seed;
227 CriticalSection critical{lock};
228 seed = GetNextValue();
229 nextValue = seed;
231 switch (kind) {
232 case 4:
233 *get->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = seed;
234 break;
235 case 8:
236 *get->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = seed;
237 break;
238 default:
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");
251 if (sizePresent)
252 RTNAME(RandomSeedSize)(size, source, line);
253 else if (putPresent)
254 RTNAME(RandomSeedPut)(put, source, line);
255 else if (getPresent)
256 RTNAME(RandomSeedGet)(get, source, line);
257 else
258 RTNAME(RandomSeedDefaultPut)();
261 } // extern "C"
262 } // namespace Fortran::runtime