llvm-project
197 строк · 5.5 Кб
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//===----------------------------------------------------------------------===//
8
9// Implements the intrinsic subroutines RANDOM_INIT, RANDOM_NUMBER, and
10// RANDOM_SEED.
11
12#include "flang/Runtime/random.h"
13#include "lock.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"
21#include <cmath>
22#include <cstdint>
23#include <limits>
24#include <memory>
25#include <time.h>
26
27namespace Fortran::runtime::random {
28
29Lock lock;
30Generator generator;
31Fortran::common::optional<GeneratedWord> nextValue;
32
33extern "C" {
34
35void RTNAME(RandomInit)(bool repeatable, bool /*image_distinct*/) {
36// TODO: multiple images and image_distinct: add image number
37{
38CriticalSection critical{lock};
39if (repeatable) {
40generator.seed(0);
41} else {
42#ifdef CLOCK_REALTIME
43timespec ts;
44clock_gettime(CLOCK_REALTIME, &ts);
45generator.seed(ts.tv_sec & ts.tv_nsec);
46#else
47generator.seed(time(nullptr));
48#endif
49}
50}
51}
52
53void RTNAME(RandomNumber)(
54const Descriptor &harvest, const char *source, int line) {
55Terminator terminator{source, line};
56auto typeCode{harvest.type().GetCategoryAndKind()};
57RUNTIME_CHECK(terminator, typeCode && typeCode->first == TypeCategory::Real);
58int kind{typeCode->second};
59switch (kind) {
60// TODO: REAL (2 & 3)
61case 4:
62Generate<CppTypeFor<TypeCategory::Real, 4>, 24>(harvest);
63return;
64case 8:
65Generate<CppTypeFor<TypeCategory::Real, 8>, 53>(harvest);
66return;
67case 10:
68if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
69#if LDBL_MANT_DIG == 64
70Generate<CppTypeFor<TypeCategory::Real, 10>, 64>(harvest);
71return;
72#endif
73}
74break;
75}
76terminator.Crash(
77"not yet implemented: intrinsic: REAL(KIND=%d) in RANDOM_NUMBER", kind);
78}
79
80void RTNAME(RandomSeedSize)(
81const Descriptor *size, const char *source, int line) {
82if (!size || !size->raw().base_addr) {
83RTNAME(RandomSeedDefaultPut)();
84return;
85}
86Terminator terminator{source, line};
87auto typeCode{size->type().GetCategoryAndKind()};
88RUNTIME_CHECK(terminator,
89size->rank() == 0 && typeCode &&
90typeCode->first == TypeCategory::Integer);
91int sizeArg{typeCode->second};
92switch (sizeArg) {
93case 4:
94*size->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = 1;
95break;
96case 8:
97*size->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = 1;
98break;
99default:
100terminator.Crash(
101"not yet implemented: intrinsic: RANDOM_SEED(SIZE=): size %d\n",
102sizeArg);
103}
104}
105
106void RTNAME(RandomSeedPut)(
107const Descriptor *put, const char *source, int line) {
108if (!put || !put->raw().base_addr) {
109RTNAME(RandomSeedDefaultPut)();
110return;
111}
112Terminator terminator{source, line};
113auto typeCode{put->type().GetCategoryAndKind()};
114RUNTIME_CHECK(terminator,
115put->rank() == 1 && typeCode &&
116typeCode->first == TypeCategory::Integer &&
117put->GetDimension(0).Extent() >= 1);
118int putArg{typeCode->second};
119GeneratedWord seed;
120switch (putArg) {
121case 4:
122seed = *put->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>();
123break;
124case 8:
125seed = *put->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>();
126break;
127default:
128terminator.Crash(
129"not yet implemented: intrinsic: RANDOM_SEED(PUT=): put %d\n", putArg);
130}
131{
132CriticalSection critical{lock};
133generator.seed(seed);
134nextValue = seed;
135}
136}
137
138void RTNAME(RandomSeedDefaultPut)() {
139// TODO: should this be time &/or image dependent?
140{
141CriticalSection critical{lock};
142generator.seed(0);
143}
144}
145
146void RTNAME(RandomSeedGet)(
147const Descriptor *get, const char *source, int line) {
148if (!get || !get->raw().base_addr) {
149RTNAME(RandomSeedDefaultPut)();
150return;
151}
152Terminator terminator{source, line};
153auto typeCode{get->type().GetCategoryAndKind()};
154RUNTIME_CHECK(terminator,
155get->rank() == 1 && typeCode &&
156typeCode->first == TypeCategory::Integer &&
157get->GetDimension(0).Extent() >= 1);
158int getArg{typeCode->second};
159GeneratedWord seed;
160{
161CriticalSection critical{lock};
162seed = GetNextValue();
163nextValue = seed;
164}
165switch (getArg) {
166case 4:
167*get->OffsetElement<CppTypeFor<TypeCategory::Integer, 4>>() = seed;
168break;
169case 8:
170*get->OffsetElement<CppTypeFor<TypeCategory::Integer, 8>>() = seed;
171break;
172default:
173terminator.Crash(
174"not yet implemented: intrinsic: RANDOM_SEED(GET=): get %d\n", getArg);
175}
176}
177
178void RTNAME(RandomSeed)(const Descriptor *size, const Descriptor *put,
179const Descriptor *get, const char *source, int line) {
180bool sizePresent = size && size->raw().base_addr;
181bool putPresent = put && put->raw().base_addr;
182bool getPresent = get && get->raw().base_addr;
183if (sizePresent + putPresent + getPresent > 1)
184Terminator{source, line}.Crash(
185"RANDOM_SEED must have either 1 or no arguments");
186if (sizePresent)
187RTNAME(RandomSeedSize)(size, source, line);
188else if (putPresent)
189RTNAME(RandomSeedPut)(put, source, line);
190else if (getPresent)
191RTNAME(RandomSeedGet)(get, source, line);
192else
193RTNAME(RandomSeedDefaultPut)();
194}
195
196} // extern "C"
197} // namespace Fortran::runtime::random
198