blob: dd35917b58e9ba542b85611ecbb49ebc1884c7c4 [file] [log] [blame]
peter klauslerfae12a02020-01-24 00:10:001//===-- runtime/tools.h -----------------------------------------*- C++ -*-===//
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#ifndef FORTRAN_RUNTIME_TOOLS_H_
10#define FORTRAN_RUNTIME_TOOLS_H_
peter klauslerf7be2512020-01-24 00:59:2711
peter klauslere372e0f2021-03-31 16:14:0812#include "terminator.h"
peter klausler8d672c0b2021-04-20 16:19:2113#include "flang/Common/long-double.h"
Peter Klausler830c0b92021-09-01 23:00:5314#include "flang/Runtime/cpp-type.h"
15#include "flang/Runtime/descriptor.h"
16#include "flang/Runtime/memory.h"
peter klauslerf7be2512020-01-24 00:59:2717#include <functional>
18#include <map>
19#include <type_traits>
20
peter klauslerfae12a02020-01-24 00:10:0021namespace Fortran::runtime {
22
23class Terminator;
24
peter klausler675ad1b2020-08-03 18:35:2925std::size_t TrimTrailingSpaces(const char *, std::size_t);
26
peter klausler95696d52020-02-05 00:55:4527OwningPtr<char> SaveDefaultCharacter(
28 const char *, std::size_t, const Terminator &);
peter klauslerfae12a02020-01-24 00:10:0029
30// For validating and recognizing default CHARACTER values in a
31// case-insensitive manner. Returns the zero-based index into the
32// null-terminated array of upper-case possibilities when the value is valid,
33// or -1 when it has no match.
34int IdentifyValue(
35 const char *value, std::size_t length, const char *possibilities[]);
peter klauslerf7be2512020-01-24 00:59:2736
peter klausler3b635712020-02-13 22:41:5637// Truncates or pads as necessary
38void ToFortranDefaultCharacter(
39 char *to, std::size_t toLength, const char *from);
peter klauslere372e0f2021-03-31 16:14:0840
41// Utility for dealing with elemental LOGICAL arguments
42inline bool IsLogicalElementTrue(
43 const Descriptor &logical, const SubscriptValue at[]) {
44 // A LOGICAL value is false if and only if all of its bytes are zero.
45 const char *p{logical.Element<char>(at)};
46 for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) {
47 if (*p) {
48 return true;
49 }
50 }
51 return false;
52}
53
54// Check array conformability; a scalar 'x' conforms. Crashes on error.
55void CheckConformability(const Descriptor &to, const Descriptor &x,
56 Terminator &, const char *funcName, const char *toName,
57 const char *fromName);
58
Peter Steinfeld6cd417b2022-02-09 19:17:1859// Helper to store integer value in result[at].
60template <int KIND> struct StoreIntegerAt {
61 void operator()(const Fortran::runtime::Descriptor &result, std::size_t at,
62 std::int64_t value) const {
63 *result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor<
64 Fortran::common::TypeCategory::Integer, KIND>>(at) = value;
65 }
66};
67
peter klauslere372e0f2021-03-31 16:14:0868// Validate a KIND= argument
69void CheckIntegerKind(Terminator &, int kind, const char *intrinsic);
70
71template <typename TO, typename FROM>
72inline void PutContiguousConverted(TO *to, FROM *from, std::size_t count) {
73 while (count-- > 0) {
74 *to++ = *from++;
75 }
76}
77
peter klauslerc1db35f2021-05-20 17:37:0378static inline std::int64_t GetInt64(
79 const char *p, std::size_t bytes, Terminator &terminator) {
peter klauslere372e0f2021-03-31 16:14:0880 switch (bytes) {
81 case 1:
82 return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 1> *>(p);
83 case 2:
84 return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 2> *>(p);
85 case 4:
86 return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 4> *>(p);
87 case 8:
88 return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 8> *>(p);
89 default:
peter klauslerc1db35f2021-05-20 17:37:0390 terminator.Crash("GetInt64: no case for %zd bytes", bytes);
peter klauslere372e0f2021-03-31 16:14:0891 }
92}
93
94template <typename INT>
95inline bool SetInteger(INT &x, int kind, std::int64_t value) {
96 switch (kind) {
97 case 1:
98 reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x) = value;
Peter Klausler73b193a2022-02-16 21:26:4499 return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x);
peter klauslere372e0f2021-03-31 16:14:08100 case 2:
101 reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x) = value;
Peter Klausler73b193a2022-02-16 21:26:44102 return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x);
peter klauslere372e0f2021-03-31 16:14:08103 case 4:
104 reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x) = value;
Peter Klausler73b193a2022-02-16 21:26:44105 return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x);
peter klauslere372e0f2021-03-31 16:14:08106 case 8:
107 reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x) = value;
Peter Klausler73b193a2022-02-16 21:26:44108 return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x);
peter klauslere372e0f2021-03-31 16:14:08109 default:
110 return false;
111 }
112}
113
peter klausler50e0b292021-05-12 19:07:51114// Maps intrinsic runtime type category and kind values to the appropriate
115// instantiation of a function object template and calls it with the supplied
116// arguments.
117template <template <TypeCategory, int> class FUNC, typename RESULT,
118 typename... A>
119inline RESULT ApplyType(
120 TypeCategory cat, int kind, Terminator &terminator, A &&...x) {
121 switch (cat) {
122 case TypeCategory::Integer:
123 switch (kind) {
124 case 1:
125 return FUNC<TypeCategory::Integer, 1>{}(std::forward<A>(x)...);
126 case 2:
127 return FUNC<TypeCategory::Integer, 2>{}(std::forward<A>(x)...);
128 case 4:
129 return FUNC<TypeCategory::Integer, 4>{}(std::forward<A>(x)...);
130 case 8:
131 return FUNC<TypeCategory::Integer, 8>{}(std::forward<A>(x)...);
132#ifdef __SIZEOF_INT128__
133 case 16:
134 return FUNC<TypeCategory::Integer, 16>{}(std::forward<A>(x)...);
135#endif
136 default:
Peter Steinfeldebe24a22022-05-05 21:54:57137 terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind);
peter klausler50e0b292021-05-12 19:07:51138 }
139 case TypeCategory::Real:
140 switch (kind) {
141#if 0 // TODO: REAL(2 & 3)
142 case 2:
143 return FUNC<TypeCategory::Real, 2>{}(std::forward<A>(x)...);
144 case 3:
145 return FUNC<TypeCategory::Real, 3>{}(std::forward<A>(x)...);
146#endif
147 case 4:
148 return FUNC<TypeCategory::Real, 4>{}(std::forward<A>(x)...);
149 case 8:
150 return FUNC<TypeCategory::Real, 8>{}(std::forward<A>(x)...);
151#if LONG_DOUBLE == 80
152 case 10:
153 return FUNC<TypeCategory::Real, 10>{}(std::forward<A>(x)...);
154#elif LONG_DOUBLE == 128
155 case 16:
156 return FUNC<TypeCategory::Real, 16>{}(std::forward<A>(x)...);
157#endif
158 default:
Peter Steinfeldebe24a22022-05-05 21:54:57159 terminator.Crash("not yet implemented: REAL(KIND=%d)", kind);
peter klausler50e0b292021-05-12 19:07:51160 }
161 case TypeCategory::Complex:
162 switch (kind) {
163#if 0 // TODO: COMPLEX(2 & 3)
164 case 2:
165 return FUNC<TypeCategory::Complex, 2>{}(std::forward<A>(x)...);
166 case 3:
167 return FUNC<TypeCategory::Complex, 3>{}(std::forward<A>(x)...);
168#endif
169 case 4:
170 return FUNC<TypeCategory::Complex, 4>{}(std::forward<A>(x)...);
171 case 8:
172 return FUNC<TypeCategory::Complex, 8>{}(std::forward<A>(x)...);
173#if LONG_DOUBLE == 80
174 case 10:
175 return FUNC<TypeCategory::Complex, 10>{}(std::forward<A>(x)...);
176#elif LONG_DOUBLE == 128
177 case 16:
178 return FUNC<TypeCategory::Complex, 16>{}(std::forward<A>(x)...);
179#endif
180 default:
Peter Steinfeldebe24a22022-05-05 21:54:57181 terminator.Crash("not yet implemented: COMPLEX(KIND=%d)", kind);
peter klausler50e0b292021-05-12 19:07:51182 }
183 case TypeCategory::Character:
184 switch (kind) {
185 case 1:
186 return FUNC<TypeCategory::Character, 1>{}(std::forward<A>(x)...);
187 case 2:
188 return FUNC<TypeCategory::Character, 2>{}(std::forward<A>(x)...);
189 case 4:
190 return FUNC<TypeCategory::Character, 4>{}(std::forward<A>(x)...);
191 default:
Peter Steinfeldebe24a22022-05-05 21:54:57192 terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind);
peter klausler50e0b292021-05-12 19:07:51193 }
194 case TypeCategory::Logical:
195 switch (kind) {
196 case 1:
197 return FUNC<TypeCategory::Logical, 1>{}(std::forward<A>(x)...);
198 case 2:
199 return FUNC<TypeCategory::Logical, 2>{}(std::forward<A>(x)...);
200 case 4:
201 return FUNC<TypeCategory::Logical, 4>{}(std::forward<A>(x)...);
202 case 8:
203 return FUNC<TypeCategory::Logical, 8>{}(std::forward<A>(x)...);
204 default:
Peter Steinfeldebe24a22022-05-05 21:54:57205 terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind);
peter klausler50e0b292021-05-12 19:07:51206 }
207 default:
Peter Steinfeldebe24a22022-05-05 21:54:57208 terminator.Crash(
209 "not yet implemented: type category(%d)", static_cast<int>(cat));
peter klausler50e0b292021-05-12 19:07:51210 }
211}
212
peter klausler8d672c0b2021-04-20 16:19:21213// Maps a runtime INTEGER kind value to the appropriate instantiation of
214// a function object template and calls it with the supplied arguments.
215template <template <int KIND> class FUNC, typename RESULT, typename... A>
216inline RESULT ApplyIntegerKind(int kind, Terminator &terminator, A &&...x) {
217 switch (kind) {
218 case 1:
219 return FUNC<1>{}(std::forward<A>(x)...);
220 case 2:
221 return FUNC<2>{}(std::forward<A>(x)...);
222 case 4:
223 return FUNC<4>{}(std::forward<A>(x)...);
224 case 8:
225 return FUNC<8>{}(std::forward<A>(x)...);
226#ifdef __SIZEOF_INT128__
227 case 16:
228 return FUNC<16>{}(std::forward<A>(x)...);
229#endif
230 default:
Peter Steinfeldebe24a22022-05-05 21:54:57231 terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind);
peter klausler8d672c0b2021-04-20 16:19:21232 }
233}
234
235template <template <int KIND> class FUNC, typename RESULT, typename... A>
236inline RESULT ApplyFloatingPointKind(
237 int kind, Terminator &terminator, A &&...x) {
238 switch (kind) {
239#if 0 // TODO: REAL/COMPLEX (2 & 3)
240 case 2:
241 return FUNC<2>{}(std::forward<A>(x)...);
242 case 3:
243 return FUNC<3>{}(std::forward<A>(x)...);
244#endif
245 case 4:
246 return FUNC<4>{}(std::forward<A>(x)...);
247 case 8:
248 return FUNC<8>{}(std::forward<A>(x)...);
249#if LONG_DOUBLE == 80
250 case 10:
251 return FUNC<10>{}(std::forward<A>(x)...);
252#elif LONG_DOUBLE == 128
253 case 16:
254 return FUNC<16>{}(std::forward<A>(x)...);
255#endif
256 default:
Peter Steinfeldebe24a22022-05-05 21:54:57257 terminator.Crash("not yet implemented: REAL/COMPLEX(KIND=%d)", kind);
peter klausler8d672c0b2021-04-20 16:19:21258 }
259}
260
261template <template <int KIND> class FUNC, typename RESULT, typename... A>
262inline RESULT ApplyCharacterKind(int kind, Terminator &terminator, A &&...x) {
263 switch (kind) {
264 case 1:
265 return FUNC<1>{}(std::forward<A>(x)...);
266 case 2:
267 return FUNC<2>{}(std::forward<A>(x)...);
268 case 4:
269 return FUNC<4>{}(std::forward<A>(x)...);
270 default:
Peter Steinfeldebe24a22022-05-05 21:54:57271 terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind);
peter klausler8d672c0b2021-04-20 16:19:21272 }
273}
274
275template <template <int KIND> class FUNC, typename RESULT, typename... A>
276inline RESULT ApplyLogicalKind(int kind, Terminator &terminator, A &&...x) {
277 switch (kind) {
278 case 1:
279 return FUNC<1>{}(std::forward<A>(x)...);
280 case 2:
281 return FUNC<2>{}(std::forward<A>(x)...);
282 case 4:
283 return FUNC<4>{}(std::forward<A>(x)...);
284 case 8:
285 return FUNC<8>{}(std::forward<A>(x)...);
286 default:
Peter Steinfeldebe24a22022-05-05 21:54:57287 terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind);
peter klausler8d672c0b2021-04-20 16:19:21288 }
289}
290
peter klausler50e0b292021-05-12 19:07:51291// Calculate result type of (X op Y) for *, //, DOT_PRODUCT, &c.
292std::optional<std::pair<TypeCategory, int>> inline constexpr GetResultType(
293 TypeCategory xCat, int xKind, TypeCategory yCat, int yKind) {
294 int maxKind{std::max(xKind, yKind)};
295 switch (xCat) {
296 case TypeCategory::Integer:
297 switch (yCat) {
298 case TypeCategory::Integer:
299 return std::make_pair(TypeCategory::Integer, maxKind);
300 case TypeCategory::Real:
301 case TypeCategory::Complex:
302 return std::make_pair(yCat, yKind);
303 default:
304 break;
305 }
306 break;
307 case TypeCategory::Real:
308 switch (yCat) {
309 case TypeCategory::Integer:
310 return std::make_pair(TypeCategory::Real, xKind);
311 case TypeCategory::Real:
312 case TypeCategory::Complex:
313 return std::make_pair(yCat, maxKind);
314 default:
315 break;
316 }
317 break;
318 case TypeCategory::Complex:
319 switch (yCat) {
320 case TypeCategory::Integer:
321 return std::make_pair(TypeCategory::Complex, xKind);
322 case TypeCategory::Real:
323 case TypeCategory::Complex:
324 return std::make_pair(TypeCategory::Complex, maxKind);
325 default:
326 break;
327 }
328 break;
329 case TypeCategory::Character:
330 if (yCat == TypeCategory::Character) {
331 return std::make_pair(TypeCategory::Character, maxKind);
332 } else {
333 return std::nullopt;
334 }
335 case TypeCategory::Logical:
336 if (yCat == TypeCategory::Logical) {
337 return std::make_pair(TypeCategory::Logical, maxKind);
338 } else {
339 return std::nullopt;
340 }
341 default:
342 break;
343 }
344 return std::nullopt;
345}
peter klausler79caf692021-06-17 20:13:19346
Peter Klauslera5a493e2021-10-19 18:30:45347// Accumulate floating-point results in (at least) double precision
348template <TypeCategory CAT, int KIND>
349using AccumulationType = CppTypeFor<CAT,
350 CAT == TypeCategory::Real || CAT == TypeCategory::Complex
351 ? std::max(KIND, static_cast<int>(sizeof(double)))
352 : KIND>;
353
Tim Keith1f879002020-03-29 04:00:16354} // namespace Fortran::runtime
355#endif // FORTRAN_RUNTIME_TOOLS_H_