xref: /llvm-project/flang/runtime/random.cpp (revision fc97d2e68b03bc2979395e84b645e5b3ba35aecd)
1e1114045Speter klausler //===-- runtime/random.cpp ------------------------------------------------===//
2e1114045Speter klausler //
3e1114045Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e1114045Speter klausler // See https://llvm.org/LICENSE.txt for license information.
5e1114045Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e1114045Speter klausler //
7e1114045Speter klausler //===----------------------------------------------------------------------===//
8e1114045Speter klausler 
9e1114045Speter klausler // Implements the intrinsic subroutines RANDOM_INIT, RANDOM_NUMBER, and
10e1114045Speter klausler // RANDOM_SEED.
11e1114045Speter klausler 
12830c0b90SPeter Klausler #include "flang/Runtime/random.h"
13e1114045Speter klausler #include "lock.h"
14e0738cc6SSlava Zakharin #include "random-templates.h"
15cc71e694SV Donaldson #include "terminator.h"
16478e0b58SPeter Steinfeld #include "flang/Common/float128.h"
17e1114045Speter klausler #include "flang/Common/leading-zero-bit-count.h"
18e1114045Speter klausler #include "flang/Common/uint128.h"
19830c0b90SPeter Klausler #include "flang/Runtime/cpp-type.h"
20830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h"
21e1114045Speter klausler #include <cmath>
22e1114045Speter klausler #include <cstdint>
23e1114045Speter klausler #include <limits>
24e1114045Speter klausler #include <memory>
25301a0dbaSPeter Klausler #include <time.h>
26e1114045Speter klausler 
27e0738cc6SSlava Zakharin namespace Fortran::runtime::random {
28e1114045Speter klausler 
29e0738cc6SSlava Zakharin Lock lock;
30e0738cc6SSlava Zakharin Generator generator;
3171e0261fSSlava Zakharin Fortran::common::optional<GeneratedWord> nextValue;
32e1114045Speter klausler 
33e1114045Speter klausler extern "C" {
34e1114045Speter klausler 
35e1114045Speter klausler void RTNAME(RandomInit)(bool repeatable, bool /*image_distinct*/) {
36e1114045Speter klausler   // TODO: multiple images and image_distinct: add image number
37e1114045Speter klausler   {
38e1114045Speter klausler     CriticalSection critical{lock};
39e1114045Speter klausler     if (repeatable) {
40e1114045Speter klausler       generator.seed(0);
41e1114045Speter klausler     } else {
42301a0dbaSPeter Klausler #ifdef CLOCK_REALTIME
43301a0dbaSPeter Klausler       timespec ts;
44301a0dbaSPeter Klausler       clock_gettime(CLOCK_REALTIME, &ts);
456facf698SPeter Klausler       generator.seed(ts.tv_sec ^ ts.tv_nsec);
46301a0dbaSPeter Klausler #else
47301a0dbaSPeter Klausler       generator.seed(time(nullptr));
48301a0dbaSPeter Klausler #endif
49e1114045Speter klausler     }
50e1114045Speter klausler   }
51e1114045Speter klausler }
52e1114045Speter klausler 
53e1114045Speter klausler void RTNAME(RandomNumber)(
54e1114045Speter klausler     const Descriptor &harvest, const char *source, int line) {
55e1114045Speter klausler   Terminator terminator{source, line};
56e1114045Speter klausler   auto typeCode{harvest.type().GetCategoryAndKind()};
57*fc97d2e6SPeter Klausler   RUNTIME_CHECK(terminator,
58*fc97d2e6SPeter Klausler       typeCode &&
59*fc97d2e6SPeter Klausler           (typeCode->first == TypeCategory::Real ||
60*fc97d2e6SPeter Klausler               typeCode->first == TypeCategory::Unsigned));
61e1114045Speter klausler   int kind{typeCode->second};
62*fc97d2e6SPeter Klausler   if (typeCode->first == TypeCategory::Real) {
63e1114045Speter klausler     switch (kind) {
64e1114045Speter klausler     // TODO: REAL (2 & 3)
65e1114045Speter klausler     case 4:
66*fc97d2e6SPeter Klausler       GenerateReal<CppTypeFor<TypeCategory::Real, 4>, 24>(harvest);
674daa33f6SPeter Klausler       return;
68e1114045Speter klausler     case 8:
69*fc97d2e6SPeter Klausler       GenerateReal<CppTypeFor<TypeCategory::Real, 8>, 53>(harvest);
704daa33f6SPeter Klausler       return;
71e1114045Speter klausler     case 10:
724daa33f6SPeter Klausler       if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
73104f3c18SSlava Zakharin #if HAS_FLOAT80
74*fc97d2e6SPeter Klausler         GenerateReal<CppTypeFor<TypeCategory::Real, 10>, 64>(harvest);
754daa33f6SPeter Klausler         return;
76e1114045Speter klausler #endif
77e1114045Speter klausler       }
784daa33f6SPeter Klausler       break;
794daa33f6SPeter Klausler     }
8004b18530SPete Steinfeld     terminator.Crash(
8104b18530SPete Steinfeld         "not yet implemented: intrinsic: REAL(KIND=%d) in RANDOM_NUMBER", kind);
82*fc97d2e6SPeter Klausler   } else if (typeCode->first == TypeCategory::Unsigned) {
83*fc97d2e6SPeter Klausler     switch (kind) {
84*fc97d2e6SPeter Klausler     case 1:
85*fc97d2e6SPeter Klausler       GenerateUnsigned<CppTypeFor<TypeCategory::Unsigned, 1>>(harvest);
86*fc97d2e6SPeter Klausler       return;
87*fc97d2e6SPeter Klausler     case 2:
88*fc97d2e6SPeter Klausler       GenerateUnsigned<CppTypeFor<TypeCategory::Unsigned, 2>>(harvest);
89*fc97d2e6SPeter Klausler       return;
90*fc97d2e6SPeter Klausler     case 4:
91*fc97d2e6SPeter Klausler       GenerateUnsigned<CppTypeFor<TypeCategory::Unsigned, 4>>(harvest);
92*fc97d2e6SPeter Klausler       return;
93*fc97d2e6SPeter Klausler     case 8:
94*fc97d2e6SPeter Klausler       GenerateUnsigned<CppTypeFor<TypeCategory::Unsigned, 8>>(harvest);
95*fc97d2e6SPeter Klausler       return;
96*fc97d2e6SPeter Klausler #ifdef __SIZEOF_INT128__
97*fc97d2e6SPeter Klausler     case 16:
98*fc97d2e6SPeter Klausler       if constexpr (HasCppTypeFor<TypeCategory::Unsigned, 16>) {
99*fc97d2e6SPeter Klausler         GenerateUnsigned<CppTypeFor<TypeCategory::Unsigned, 16>>(harvest);
100*fc97d2e6SPeter Klausler         return;
101*fc97d2e6SPeter Klausler       }
102*fc97d2e6SPeter Klausler       break;
103*fc97d2e6SPeter Klausler #endif
104*fc97d2e6SPeter Klausler     }
105*fc97d2e6SPeter Klausler     terminator.Crash(
106*fc97d2e6SPeter Klausler         "not yet implemented: intrinsic: UNSIGNED(KIND=%d) in RANDOM_NUMBER",
107*fc97d2e6SPeter Klausler         kind);
108*fc97d2e6SPeter Klausler   }
109e1114045Speter klausler }
110e1114045Speter klausler 
111e1114045Speter klausler void RTNAME(RandomSeedSize)(
112cc71e694SV Donaldson     const Descriptor *size, const char *source, int line) {
113cc71e694SV Donaldson   if (!size || !size->raw().base_addr) {
114cc71e694SV Donaldson     RTNAME(RandomSeedDefaultPut)();
115cc71e694SV Donaldson     return;
116cc71e694SV Donaldson   }
117e1114045Speter klausler   Terminator terminator{source, line};
118cc71e694SV Donaldson   auto typeCode{size->type().GetCategoryAndKind()};
119e1114045Speter klausler   RUNTIME_CHECK(terminator,
120cc71e694SV Donaldson       size->rank() == 0 && typeCode &&
121cc71e694SV Donaldson           typeCode->first == TypeCategory::Integer);
12204b18530SPete Steinfeld   int sizeArg{typeCode->second};
12304b18530SPete Steinfeld   switch (sizeArg) {
124e1114045Speter klausler   case 4:
125cc71e694SV Donaldson     *size->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = 1;
126e1114045Speter klausler     break;
127e1114045Speter klausler   case 8:
128cc71e694SV Donaldson     *size->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = 1;
129e1114045Speter klausler     break;
130e1114045Speter klausler   default:
131d4609ae4SPeter Steinfeld     terminator.Crash(
13204b18530SPete Steinfeld         "not yet implemented: intrinsic: RANDOM_SEED(SIZE=): size %d\n",
13304b18530SPete Steinfeld         sizeArg);
134e1114045Speter klausler   }
135e1114045Speter klausler }
136e1114045Speter klausler 
137e1114045Speter klausler void RTNAME(RandomSeedPut)(
138cc71e694SV Donaldson     const Descriptor *put, const char *source, int line) {
139cc71e694SV Donaldson   if (!put || !put->raw().base_addr) {
140cc71e694SV Donaldson     RTNAME(RandomSeedDefaultPut)();
141cc71e694SV Donaldson     return;
142cc71e694SV Donaldson   }
143e1114045Speter klausler   Terminator terminator{source, line};
144cc71e694SV Donaldson   auto typeCode{put->type().GetCategoryAndKind()};
145e1114045Speter klausler   RUNTIME_CHECK(terminator,
146cc71e694SV Donaldson       put->rank() == 1 && typeCode &&
147cc71e694SV Donaldson           typeCode->first == TypeCategory::Integer &&
148cc71e694SV Donaldson           put->GetDimension(0).Extent() >= 1);
14904b18530SPete Steinfeld   int putArg{typeCode->second};
150e1114045Speter klausler   GeneratedWord seed;
15104b18530SPete Steinfeld   switch (putArg) {
152e1114045Speter klausler   case 4:
153cc71e694SV Donaldson     seed = *put->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>();
154e1114045Speter klausler     break;
155e1114045Speter klausler   case 8:
156cc71e694SV Donaldson     seed = *put->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>();
157e1114045Speter klausler     break;
158e1114045Speter klausler   default:
15904b18530SPete Steinfeld     terminator.Crash(
16004b18530SPete Steinfeld         "not yet implemented: intrinsic: RANDOM_SEED(PUT=): put %d\n", putArg);
161e1114045Speter klausler   }
162e1114045Speter klausler   {
163e1114045Speter klausler     CriticalSection critical{lock};
164e1114045Speter klausler     generator.seed(seed);
165ea1a69d6SPeter Klausler     nextValue = seed;
166e1114045Speter klausler   }
167e1114045Speter klausler }
168e1114045Speter klausler 
169e1114045Speter klausler void RTNAME(RandomSeedDefaultPut)() {
170e1114045Speter klausler   // TODO: should this be time &/or image dependent?
171e1114045Speter klausler   {
172e1114045Speter klausler     CriticalSection critical{lock};
173e1114045Speter klausler     generator.seed(0);
174e1114045Speter klausler   }
175e1114045Speter klausler }
176e1114045Speter klausler 
177e1114045Speter klausler void RTNAME(RandomSeedGet)(
178cc71e694SV Donaldson     const Descriptor *get, const char *source, int line) {
179cc71e694SV Donaldson   if (!get || !get->raw().base_addr) {
180cc71e694SV Donaldson     RTNAME(RandomSeedDefaultPut)();
181cc71e694SV Donaldson     return;
182cc71e694SV Donaldson   }
183e1114045Speter klausler   Terminator terminator{source, line};
184cc71e694SV Donaldson   auto typeCode{get->type().GetCategoryAndKind()};
185e1114045Speter klausler   RUNTIME_CHECK(terminator,
186cc71e694SV Donaldson       get->rank() == 1 && typeCode &&
187cc71e694SV Donaldson           typeCode->first == TypeCategory::Integer &&
188cc71e694SV Donaldson           get->GetDimension(0).Extent() >= 1);
18904b18530SPete Steinfeld   int getArg{typeCode->second};
190e1114045Speter klausler   GeneratedWord seed;
191e1114045Speter klausler   {
192e1114045Speter klausler     CriticalSection critical{lock};
193ea1a69d6SPeter Klausler     seed = GetNextValue();
194ea1a69d6SPeter Klausler     nextValue = seed;
195e1114045Speter klausler   }
19604b18530SPete Steinfeld   switch (getArg) {
197e1114045Speter klausler   case 4:
198cc71e694SV Donaldson     *get->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = seed;
199e1114045Speter klausler     break;
200e1114045Speter klausler   case 8:
201cc71e694SV Donaldson     *get->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = seed;
202e1114045Speter klausler     break;
203e1114045Speter klausler   default:
20404b18530SPete Steinfeld     terminator.Crash(
20504b18530SPete Steinfeld         "not yet implemented: intrinsic: RANDOM_SEED(GET=): get %d\n", getArg);
206e1114045Speter klausler   }
207e1114045Speter klausler }
208cc71e694SV Donaldson 
209cc71e694SV Donaldson void RTNAME(RandomSeed)(const Descriptor *size, const Descriptor *put,
210cc71e694SV Donaldson     const Descriptor *get, const char *source, int line) {
211cc71e694SV Donaldson   bool sizePresent = size && size->raw().base_addr;
212cc71e694SV Donaldson   bool putPresent = put && put->raw().base_addr;
213cc71e694SV Donaldson   bool getPresent = get && get->raw().base_addr;
214cc71e694SV Donaldson   if (sizePresent + putPresent + getPresent > 1)
215cc71e694SV Donaldson     Terminator{source, line}.Crash(
216cc71e694SV Donaldson         "RANDOM_SEED must have either 1 or no arguments");
217cc71e694SV Donaldson   if (sizePresent)
218cc71e694SV Donaldson     RTNAME(RandomSeedSize)(size, source, line);
219cc71e694SV Donaldson   else if (putPresent)
220cc71e694SV Donaldson     RTNAME(RandomSeedPut)(put, source, line);
221cc71e694SV Donaldson   else if (getPresent)
222cc71e694SV Donaldson     RTNAME(RandomSeedGet)(get, source, line);
223cc71e694SV Donaldson   else
224cc71e694SV Donaldson     RTNAME(RandomSeedDefaultPut)();
225cc71e694SV Donaldson }
226cc71e694SV Donaldson 
227e1114045Speter klausler } // extern "C"
228e0738cc6SSlava Zakharin } // namespace Fortran::runtime::random
229