peter klausler | fae12a0 | 2020-01-24 00:10:00 | [diff] [blame] | 1 | //===-- 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 klausler | f7be251 | 2020-01-24 00:59:27 | [diff] [blame] | 11 | |
Yi Wu | e2b896a | 2024-01-10 10:02:48 | [diff] [blame] | 12 | #include "stat.h" |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 13 | #include "terminator.h" |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 14 | #include "flang/Common/optional.h" |
Peter Klausler | 830c0b9 | 2021-09-01 23:00:53 | [diff] [blame] | 15 | #include "flang/Runtime/cpp-type.h" |
| 16 | #include "flang/Runtime/descriptor.h" |
Slava Zakharin | 3b33724 | 2024-04-05 22:10:04 | [diff] [blame] | 17 | #include "flang/Runtime/freestanding-tools.h" |
Peter Klausler | 830c0b9 | 2021-09-01 23:00:53 | [diff] [blame] | 18 | #include "flang/Runtime/memory.h" |
Peter Klausler | 52a0b02 | 2023-04-12 19:32:37 | [diff] [blame] | 19 | #include <cstring> |
peter klausler | f7be251 | 2020-01-24 00:59:27 | [diff] [blame] | 20 | #include <functional> |
| 21 | #include <map> |
| 22 | #include <type_traits> |
| 23 | |
Slava Zakharin | 00f3454 | 2024-03-21 22:12:31 | [diff] [blame] | 24 | /// \macro RT_PRETTY_FUNCTION |
| 25 | /// Gets a user-friendly looking function signature for the current scope |
| 26 | /// using the best available method on each platform. The exact format of the |
| 27 | /// resulting string is implementation specific and non-portable, so this should |
| 28 | /// only be used, for example, for logging or diagnostics. |
| 29 | /// Copy of LLVM_PRETTY_FUNCTION |
| 30 | #if defined(_MSC_VER) |
| 31 | #define RT_PRETTY_FUNCTION __FUNCSIG__ |
| 32 | #elif defined(__GNUC__) || defined(__clang__) |
| 33 | #define RT_PRETTY_FUNCTION __PRETTY_FUNCTION__ |
| 34 | #else |
| 35 | #define RT_PRETTY_FUNCTION __func__ |
| 36 | #endif |
| 37 | |
| 38 | #if defined(RT_DEVICE_COMPILATION) |
| 39 | // Use the pseudo lock and pseudo file unit implementations |
| 40 | // for the device. |
| 41 | #define RT_USE_PSEUDO_LOCK 1 |
| 42 | #define RT_USE_PSEUDO_FILE_UNIT 1 |
| 43 | #endif |
| 44 | |
peter klausler | fae12a0 | 2020-01-24 00:10:00 | [diff] [blame] | 45 | namespace Fortran::runtime { |
| 46 | |
| 47 | class Terminator; |
| 48 | |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 49 | RT_API_ATTRS std::size_t TrimTrailingSpaces(const char *, std::size_t); |
peter klausler | 675ad1b | 2020-08-03 18:35:29 | [diff] [blame] | 50 | |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 51 | RT_API_ATTRS OwningPtr<char> SaveDefaultCharacter( |
peter klausler | 95696d5 | 2020-02-05 00:55:45 | [diff] [blame] | 52 | const char *, std::size_t, const Terminator &); |
peter klausler | fae12a0 | 2020-01-24 00:10:00 | [diff] [blame] | 53 | |
| 54 | // For validating and recognizing default CHARACTER values in a |
| 55 | // case-insensitive manner. Returns the zero-based index into the |
| 56 | // null-terminated array of upper-case possibilities when the value is valid, |
| 57 | // or -1 when it has no match. |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 58 | RT_API_ATTRS int IdentifyValue( |
peter klausler | fae12a0 | 2020-01-24 00:10:00 | [diff] [blame] | 59 | const char *value, std::size_t length, const char *possibilities[]); |
peter klausler | f7be251 | 2020-01-24 00:59:27 | [diff] [blame] | 60 | |
peter klausler | 3b63571 | 2020-02-13 22:41:56 | [diff] [blame] | 61 | // Truncates or pads as necessary |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 62 | RT_API_ATTRS void ToFortranDefaultCharacter( |
peter klausler | 3b63571 | 2020-02-13 22:41:56 | [diff] [blame] | 63 | char *to, std::size_t toLength, const char *from); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 64 | |
Peter Klausler | 3ada883 | 2024-03-26 16:21:16 | [diff] [blame] | 65 | // Utilities for dealing with elemental LOGICAL arguments |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 66 | inline RT_API_ATTRS bool IsLogicalElementTrue( |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 67 | const Descriptor &logical, const SubscriptValue at[]) { |
| 68 | // A LOGICAL value is false if and only if all of its bytes are zero. |
| 69 | const char *p{logical.Element<char>(at)}; |
| 70 | for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) { |
| 71 | if (*p) { |
| 72 | return true; |
| 73 | } |
| 74 | } |
| 75 | return false; |
| 76 | } |
Peter Klausler | 3ada883 | 2024-03-26 16:21:16 | [diff] [blame] | 77 | inline RT_API_ATTRS bool IsLogicalScalarTrue(const Descriptor &logical) { |
| 78 | // A LOGICAL value is false if and only if all of its bytes are zero. |
| 79 | const char *p{logical.OffsetElement<char>()}; |
| 80 | for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) { |
| 81 | if (*p) { |
| 82 | return true; |
| 83 | } |
| 84 | } |
| 85 | return false; |
| 86 | } |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 87 | |
| 88 | // Check array conformability; a scalar 'x' conforms. Crashes on error. |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 89 | RT_API_ATTRS void CheckConformability(const Descriptor &to, const Descriptor &x, |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 90 | Terminator &, const char *funcName, const char *toName, |
| 91 | const char *fromName); |
| 92 | |
Peter Steinfeld | 6cd417b | 2022-02-09 19:17:18 | [diff] [blame] | 93 | // Helper to store integer value in result[at]. |
| 94 | template <int KIND> struct StoreIntegerAt { |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 95 | RT_API_ATTRS void operator()(const Fortran::runtime::Descriptor &result, |
| 96 | std::size_t at, std::int64_t value) const { |
Peter Steinfeld | 6cd417b | 2022-02-09 19:17:18 | [diff] [blame] | 97 | *result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor< |
| 98 | Fortran::common::TypeCategory::Integer, KIND>>(at) = value; |
| 99 | } |
| 100 | }; |
| 101 | |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 102 | // Validate a KIND= argument |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 103 | RT_API_ATTRS void CheckIntegerKind( |
| 104 | Terminator &, int kind, const char *intrinsic); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 105 | |
| 106 | template <typename TO, typename FROM> |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 107 | inline RT_API_ATTRS void PutContiguousConverted( |
| 108 | TO *to, FROM *from, std::size_t count) { |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 109 | while (count-- > 0) { |
| 110 | *to++ = *from++; |
| 111 | } |
| 112 | } |
| 113 | |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 114 | static inline RT_API_ATTRS std::int64_t GetInt64( |
peter klausler | c1db35f | 2021-05-20 17:37:03 | [diff] [blame] | 115 | const char *p, std::size_t bytes, Terminator &terminator) { |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 116 | switch (bytes) { |
| 117 | case 1: |
| 118 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 1> *>(p); |
| 119 | case 2: |
| 120 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 2> *>(p); |
| 121 | case 4: |
| 122 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 4> *>(p); |
| 123 | case 8: |
| 124 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 8> *>(p); |
| 125 | default: |
peter klausler | c1db35f | 2021-05-20 17:37:03 | [diff] [blame] | 126 | terminator.Crash("GetInt64: no case for %zd bytes", bytes); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 130 | static inline RT_API_ATTRS Fortran::common::optional<std::int64_t> GetInt64Safe( |
Peter Klausler | 8fc045e | 2023-12-26 23:12:39 | [diff] [blame] | 131 | const char *p, std::size_t bytes, Terminator &terminator) { |
| 132 | switch (bytes) { |
| 133 | case 1: |
| 134 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 1> *>(p); |
| 135 | case 2: |
| 136 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 2> *>(p); |
| 137 | case 4: |
| 138 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 4> *>(p); |
| 139 | case 8: |
| 140 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 8> *>(p); |
| 141 | case 16: { |
| 142 | using Int128 = CppTypeFor<TypeCategory::Integer, 16>; |
| 143 | auto n{*reinterpret_cast<const Int128 *>(p)}; |
Slava Zakharin | 76facde | 2023-12-28 21:50:43 | [diff] [blame] | 144 | std::int64_t result{static_cast<std::int64_t>(n)}; |
| 145 | if (static_cast<Int128>(result) == n) { |
Peter Klausler | 8fc045e | 2023-12-26 23:12:39 | [diff] [blame] | 146 | return result; |
| 147 | } |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 148 | return Fortran::common::nullopt; |
Peter Klausler | 8fc045e | 2023-12-26 23:12:39 | [diff] [blame] | 149 | } |
| 150 | default: |
| 151 | terminator.Crash("GetInt64Safe: no case for %zd bytes", bytes); |
| 152 | } |
| 153 | } |
| 154 | |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 155 | template <typename INT> |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 156 | inline RT_API_ATTRS bool SetInteger(INT &x, int kind, std::int64_t value) { |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 157 | switch (kind) { |
| 158 | case 1: |
| 159 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x) = value; |
Peter Klausler | 73b193a | 2022-02-16 21:26:44 | [diff] [blame] | 160 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 161 | case 2: |
| 162 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x) = value; |
Peter Klausler | 73b193a | 2022-02-16 21:26:44 | [diff] [blame] | 163 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 164 | case 4: |
| 165 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x) = value; |
Peter Klausler | 73b193a | 2022-02-16 21:26:44 | [diff] [blame] | 166 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 167 | case 8: |
| 168 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x) = value; |
Peter Klausler | 73b193a | 2022-02-16 21:26:44 | [diff] [blame] | 169 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x); |
peter klausler | e372e0f | 2021-03-31 16:14:08 | [diff] [blame] | 170 | default: |
| 171 | return false; |
| 172 | } |
| 173 | } |
| 174 | |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 175 | // Maps intrinsic runtime type category and kind values to the appropriate |
| 176 | // instantiation of a function object template and calls it with the supplied |
| 177 | // arguments. |
| 178 | template <template <TypeCategory, int> class FUNC, typename RESULT, |
| 179 | typename... A> |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 180 | inline RT_API_ATTRS RESULT ApplyType( |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 181 | TypeCategory cat, int kind, Terminator &terminator, A &&...x) { |
| 182 | switch (cat) { |
| 183 | case TypeCategory::Integer: |
| 184 | switch (kind) { |
| 185 | case 1: |
| 186 | return FUNC<TypeCategory::Integer, 1>{}(std::forward<A>(x)...); |
| 187 | case 2: |
| 188 | return FUNC<TypeCategory::Integer, 2>{}(std::forward<A>(x)...); |
| 189 | case 4: |
| 190 | return FUNC<TypeCategory::Integer, 4>{}(std::forward<A>(x)...); |
| 191 | case 8: |
| 192 | return FUNC<TypeCategory::Integer, 8>{}(std::forward<A>(x)...); |
Peter Klausler | 1c35c1a | 2023-07-06 22:03:05 | [diff] [blame] | 193 | #if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 194 | case 16: |
| 195 | return FUNC<TypeCategory::Integer, 16>{}(std::forward<A>(x)...); |
| 196 | #endif |
| 197 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 198 | terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 199 | } |
| 200 | case TypeCategory::Real: |
| 201 | switch (kind) { |
| 202 | #if 0 // TODO: REAL(2 & 3) |
| 203 | case 2: |
| 204 | return FUNC<TypeCategory::Real, 2>{}(std::forward<A>(x)...); |
| 205 | case 3: |
| 206 | return FUNC<TypeCategory::Real, 3>{}(std::forward<A>(x)...); |
| 207 | #endif |
| 208 | case 4: |
| 209 | return FUNC<TypeCategory::Real, 4>{}(std::forward<A>(x)...); |
| 210 | case 8: |
| 211 | return FUNC<TypeCategory::Real, 8>{}(std::forward<A>(x)...); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 212 | case 10: |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 213 | if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) { |
| 214 | return FUNC<TypeCategory::Real, 10>{}(std::forward<A>(x)...); |
| 215 | } |
| 216 | break; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 217 | case 16: |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 218 | if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) { |
| 219 | return FUNC<TypeCategory::Real, 16>{}(std::forward<A>(x)...); |
| 220 | } |
| 221 | break; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 222 | } |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 223 | terminator.Crash("not yet implemented: REAL(KIND=%d)", kind); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 224 | case TypeCategory::Complex: |
| 225 | switch (kind) { |
| 226 | #if 0 // TODO: COMPLEX(2 & 3) |
| 227 | case 2: |
| 228 | return FUNC<TypeCategory::Complex, 2>{}(std::forward<A>(x)...); |
| 229 | case 3: |
| 230 | return FUNC<TypeCategory::Complex, 3>{}(std::forward<A>(x)...); |
| 231 | #endif |
| 232 | case 4: |
| 233 | return FUNC<TypeCategory::Complex, 4>{}(std::forward<A>(x)...); |
| 234 | case 8: |
| 235 | return FUNC<TypeCategory::Complex, 8>{}(std::forward<A>(x)...); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 236 | case 10: |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 237 | if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) { |
| 238 | return FUNC<TypeCategory::Complex, 10>{}(std::forward<A>(x)...); |
| 239 | } |
| 240 | break; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 241 | case 16: |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 242 | if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) { |
| 243 | return FUNC<TypeCategory::Complex, 16>{}(std::forward<A>(x)...); |
| 244 | } |
| 245 | break; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 246 | } |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 247 | terminator.Crash("not yet implemented: COMPLEX(KIND=%d)", kind); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 248 | case TypeCategory::Character: |
| 249 | switch (kind) { |
| 250 | case 1: |
| 251 | return FUNC<TypeCategory::Character, 1>{}(std::forward<A>(x)...); |
| 252 | case 2: |
| 253 | return FUNC<TypeCategory::Character, 2>{}(std::forward<A>(x)...); |
| 254 | case 4: |
| 255 | return FUNC<TypeCategory::Character, 4>{}(std::forward<A>(x)...); |
| 256 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 257 | terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 258 | } |
| 259 | case TypeCategory::Logical: |
| 260 | switch (kind) { |
| 261 | case 1: |
| 262 | return FUNC<TypeCategory::Logical, 1>{}(std::forward<A>(x)...); |
| 263 | case 2: |
| 264 | return FUNC<TypeCategory::Logical, 2>{}(std::forward<A>(x)...); |
| 265 | case 4: |
| 266 | return FUNC<TypeCategory::Logical, 4>{}(std::forward<A>(x)...); |
| 267 | case 8: |
| 268 | return FUNC<TypeCategory::Logical, 8>{}(std::forward<A>(x)...); |
| 269 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 270 | terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 271 | } |
| 272 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 273 | terminator.Crash( |
| 274 | "not yet implemented: type category(%d)", static_cast<int>(cat)); |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 275 | } |
| 276 | } |
| 277 | |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 278 | // Maps a runtime INTEGER kind value to the appropriate instantiation of |
| 279 | // a function object template and calls it with the supplied arguments. |
| 280 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 281 | inline RT_API_ATTRS RESULT ApplyIntegerKind( |
| 282 | int kind, Terminator &terminator, A &&...x) { |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 283 | switch (kind) { |
| 284 | case 1: |
| 285 | return FUNC<1>{}(std::forward<A>(x)...); |
| 286 | case 2: |
| 287 | return FUNC<2>{}(std::forward<A>(x)...); |
| 288 | case 4: |
| 289 | return FUNC<4>{}(std::forward<A>(x)...); |
| 290 | case 8: |
| 291 | return FUNC<8>{}(std::forward<A>(x)...); |
Peter Klausler | 1c35c1a | 2023-07-06 22:03:05 | [diff] [blame] | 292 | #if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 293 | case 16: |
| 294 | return FUNC<16>{}(std::forward<A>(x)...); |
| 295 | #endif |
| 296 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 297 | terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind); |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 298 | } |
| 299 | } |
| 300 | |
Slava Zakharin | baf6725 | 2024-02-28 18:39:14 | [diff] [blame] | 301 | template <template <int KIND> class FUNC, typename RESULT, |
| 302 | bool NEEDSMATH = false, typename... A> |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 303 | inline RT_API_ATTRS RESULT ApplyFloatingPointKind( |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 304 | int kind, Terminator &terminator, A &&...x) { |
| 305 | switch (kind) { |
| 306 | #if 0 // TODO: REAL/COMPLEX (2 & 3) |
| 307 | case 2: |
| 308 | return FUNC<2>{}(std::forward<A>(x)...); |
| 309 | case 3: |
| 310 | return FUNC<3>{}(std::forward<A>(x)...); |
| 311 | #endif |
| 312 | case 4: |
| 313 | return FUNC<4>{}(std::forward<A>(x)...); |
| 314 | case 8: |
| 315 | return FUNC<8>{}(std::forward<A>(x)...); |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 316 | case 10: |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 317 | if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) { |
| 318 | return FUNC<10>{}(std::forward<A>(x)...); |
| 319 | } |
| 320 | break; |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 321 | case 16: |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 322 | if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) { |
Slava Zakharin | baf6725 | 2024-02-28 18:39:14 | [diff] [blame] | 323 | // If FUNC implemenation relies on FP math functions, |
| 324 | // then we should not be here. The compiler should have |
| 325 | // generated a call to an entry in FortranFloat128Math |
| 326 | // library. |
| 327 | if constexpr (!NEEDSMATH) { |
| 328 | return FUNC<16>{}(std::forward<A>(x)...); |
| 329 | } |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 330 | } |
| 331 | break; |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 332 | } |
Peter Klausler | 4daa33f | 2022-05-31 21:06:11 | [diff] [blame] | 333 | terminator.Crash("not yet implemented: REAL/COMPLEX(KIND=%d)", kind); |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 334 | } |
| 335 | |
| 336 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 337 | inline RT_API_ATTRS RESULT ApplyCharacterKind( |
| 338 | int kind, Terminator &terminator, A &&...x) { |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 339 | switch (kind) { |
| 340 | case 1: |
| 341 | return FUNC<1>{}(std::forward<A>(x)...); |
| 342 | case 2: |
| 343 | return FUNC<2>{}(std::forward<A>(x)...); |
| 344 | case 4: |
| 345 | return FUNC<4>{}(std::forward<A>(x)...); |
| 346 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 347 | terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind); |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 348 | } |
| 349 | } |
| 350 | |
| 351 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
Slava Zakharin | 3212051 | 2023-05-22 21:05:18 | [diff] [blame] | 352 | inline RT_API_ATTRS RESULT ApplyLogicalKind( |
| 353 | int kind, Terminator &terminator, A &&...x) { |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 354 | switch (kind) { |
| 355 | case 1: |
| 356 | return FUNC<1>{}(std::forward<A>(x)...); |
| 357 | case 2: |
| 358 | return FUNC<2>{}(std::forward<A>(x)...); |
| 359 | case 4: |
| 360 | return FUNC<4>{}(std::forward<A>(x)...); |
| 361 | case 8: |
| 362 | return FUNC<8>{}(std::forward<A>(x)...); |
| 363 | default: |
Peter Steinfeld | ebe24a2 | 2022-05-05 21:54:57 | [diff] [blame] | 364 | terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind); |
peter klausler | 8d672c0b | 2021-04-20 16:19:21 | [diff] [blame] | 365 | } |
| 366 | } |
| 367 | |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 368 | // Calculate result type of (X op Y) for *, //, DOT_PRODUCT, &c. |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 369 | Fortran::common::optional< |
| 370 | std::pair<TypeCategory, int>> inline constexpr RT_API_ATTRS |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 371 | GetResultType(TypeCategory xCat, int xKind, TypeCategory yCat, int yKind) { |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 372 | int maxKind{std::max(xKind, yKind)}; |
| 373 | switch (xCat) { |
| 374 | case TypeCategory::Integer: |
| 375 | switch (yCat) { |
| 376 | case TypeCategory::Integer: |
| 377 | return std::make_pair(TypeCategory::Integer, maxKind); |
| 378 | case TypeCategory::Real: |
| 379 | case TypeCategory::Complex: |
Peter Klausler | 1c35c1a | 2023-07-06 22:03:05 | [diff] [blame] | 380 | #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T) |
| 381 | if (xKind == 16) { |
| 382 | break; |
| 383 | } |
| 384 | #endif |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 385 | return std::make_pair(yCat, yKind); |
| 386 | default: |
| 387 | break; |
| 388 | } |
| 389 | break; |
| 390 | case TypeCategory::Real: |
| 391 | switch (yCat) { |
| 392 | case TypeCategory::Integer: |
Peter Klausler | 1c35c1a | 2023-07-06 22:03:05 | [diff] [blame] | 393 | #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T) |
| 394 | if (yKind == 16) { |
| 395 | break; |
| 396 | } |
| 397 | #endif |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 398 | return std::make_pair(TypeCategory::Real, xKind); |
| 399 | case TypeCategory::Real: |
| 400 | case TypeCategory::Complex: |
| 401 | return std::make_pair(yCat, maxKind); |
| 402 | default: |
| 403 | break; |
| 404 | } |
| 405 | break; |
| 406 | case TypeCategory::Complex: |
| 407 | switch (yCat) { |
| 408 | case TypeCategory::Integer: |
Peter Klausler | 1c35c1a | 2023-07-06 22:03:05 | [diff] [blame] | 409 | #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T) |
| 410 | if (yKind == 16) { |
| 411 | break; |
| 412 | } |
| 413 | #endif |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 414 | return std::make_pair(TypeCategory::Complex, xKind); |
| 415 | case TypeCategory::Real: |
| 416 | case TypeCategory::Complex: |
| 417 | return std::make_pair(TypeCategory::Complex, maxKind); |
| 418 | default: |
| 419 | break; |
| 420 | } |
| 421 | break; |
| 422 | case TypeCategory::Character: |
| 423 | if (yCat == TypeCategory::Character) { |
| 424 | return std::make_pair(TypeCategory::Character, maxKind); |
| 425 | } else { |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 426 | return Fortran::common::nullopt; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 427 | } |
| 428 | case TypeCategory::Logical: |
| 429 | if (yCat == TypeCategory::Logical) { |
| 430 | return std::make_pair(TypeCategory::Logical, maxKind); |
| 431 | } else { |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 432 | return Fortran::common::nullopt; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 433 | } |
| 434 | default: |
| 435 | break; |
| 436 | } |
Slava Zakharin | 71e0261 | 2024-03-15 21:25:47 | [diff] [blame] | 437 | return Fortran::common::nullopt; |
peter klausler | 50e0b29 | 2021-05-12 19:07:51 | [diff] [blame] | 438 | } |
peter klausler | 79caf69 | 2021-06-17 20:13:19 | [diff] [blame] | 439 | |
Peter Klausler | a5a493e | 2021-10-19 18:30:45 | [diff] [blame] | 440 | // Accumulate floating-point results in (at least) double precision |
| 441 | template <TypeCategory CAT, int KIND> |
| 442 | using AccumulationType = CppTypeFor<CAT, |
| 443 | CAT == TypeCategory::Real || CAT == TypeCategory::Complex |
| 444 | ? std::max(KIND, static_cast<int>(sizeof(double))) |
| 445 | : KIND>; |
| 446 | |
Peter Klausler | 52a0b02 | 2023-04-12 19:32:37 | [diff] [blame] | 447 | // memchr() for any character type |
| 448 | template <typename CHAR> |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 449 | static inline RT_API_ATTRS const CHAR *FindCharacter( |
Peter Klausler | 52a0b02 | 2023-04-12 19:32:37 | [diff] [blame] | 450 | const CHAR *data, CHAR ch, std::size_t chars) { |
| 451 | const CHAR *end{data + chars}; |
| 452 | for (const CHAR *p{data}; p < end; ++p) { |
| 453 | if (*p == ch) { |
| 454 | return p; |
| 455 | } |
| 456 | } |
| 457 | return nullptr; |
| 458 | } |
| 459 | |
| 460 | template <> |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 461 | inline RT_API_ATTRS const char *FindCharacter( |
| 462 | const char *data, char ch, std::size_t chars) { |
Peter Klausler | 52a0b02 | 2023-04-12 19:32:37 | [diff] [blame] | 463 | return reinterpret_cast<const char *>( |
Slava Zakharin | f4e90e3 | 2024-03-18 23:29:58 | [diff] [blame] | 464 | runtime::memchr(data, static_cast<int>(ch), chars)); |
Peter Klausler | 52a0b02 | 2023-04-12 19:32:37 | [diff] [blame] | 465 | } |
| 466 | |
Peter Klausler | b21c24c | 2023-07-29 15:34:14 | [diff] [blame] | 467 | // Copy payload data from one allocated descriptor to another. |
| 468 | // Assumes element counts and element sizes match, and that both |
| 469 | // descriptors are allocated. |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 470 | RT_API_ATTRS void ShallowCopyDiscontiguousToDiscontiguous( |
Peter Klausler | b21c24c | 2023-07-29 15:34:14 | [diff] [blame] | 471 | const Descriptor &to, const Descriptor &from); |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 472 | RT_API_ATTRS void ShallowCopyDiscontiguousToContiguous( |
Peter Klausler | b21c24c | 2023-07-29 15:34:14 | [diff] [blame] | 473 | const Descriptor &to, const Descriptor &from); |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 474 | RT_API_ATTRS void ShallowCopyContiguousToDiscontiguous( |
Peter Klausler | b21c24c | 2023-07-29 15:34:14 | [diff] [blame] | 475 | const Descriptor &to, const Descriptor &from); |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 476 | RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from, |
Peter Klausler | b21c24c | 2023-07-29 15:34:14 | [diff] [blame] | 477 | bool toIsContiguous, bool fromIsContiguous); |
Slava Zakharin | 8b953fd | 2023-10-04 15:21:46 | [diff] [blame] | 478 | RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from); |
Peter Klausler | b21c24c | 2023-07-29 15:34:14 | [diff] [blame] | 479 | |
Yi Wu | e2b896a | 2024-01-10 10:02:48 | [diff] [blame] | 480 | // Ensures that a character string is null-terminated, allocating a /p length +1 |
| 481 | // size memory for null-terminator if necessary. Returns the original or a newly |
| 482 | // allocated null-terminated string (responsibility for deallocation is on the |
| 483 | // caller). |
Yi Wu | 7dd4d28 | 2024-01-13 01:22:40 | [diff] [blame] | 484 | RT_API_ATTRS char *EnsureNullTerminated( |
| 485 | char *str, std::size_t length, Terminator &terminator); |
Yi Wu | e2b896a | 2024-01-10 10:02:48 | [diff] [blame] | 486 | |
| 487 | RT_API_ATTRS bool IsValidCharDescriptor(const Descriptor *value); |
| 488 | |
| 489 | RT_API_ATTRS bool IsValidIntDescriptor(const Descriptor *intVal); |
| 490 | |
| 491 | // Copy a null-terminated character array \p rawValue to descriptor \p value. |
| 492 | // The copy starts at the given \p offset, if not present then start at 0. |
| 493 | // If descriptor `errmsg` is provided, error messages will be stored to it. |
| 494 | // Returns stats specified in standard. |
| 495 | RT_API_ATTRS std::int32_t CopyCharsToDescriptor(const Descriptor &value, |
| 496 | const char *rawValue, std::size_t rawValueLength, |
| 497 | const Descriptor *errmsg = nullptr, std::size_t offset = 0); |
| 498 | |
| 499 | RT_API_ATTRS void StoreIntToDescriptor( |
| 500 | const Descriptor *length, std::int64_t value, Terminator &terminator); |
| 501 | |
Yi Wu | 18af032 | 2023-12-21 10:35:28 | [diff] [blame] | 502 | // Defines a utility function for copying and padding characters |
| 503 | template <typename TO, typename FROM> |
| 504 | RT_API_ATTRS void CopyAndPad( |
| 505 | TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) { |
| 506 | if constexpr (sizeof(TO) != sizeof(FROM)) { |
| 507 | std::size_t copyChars{std::min(toChars, fromChars)}; |
| 508 | for (std::size_t j{0}; j < copyChars; ++j) { |
| 509 | to[j] = from[j]; |
| 510 | } |
| 511 | for (std::size_t j{copyChars}; j < toChars; ++j) { |
| 512 | to[j] = static_cast<TO>(' '); |
| 513 | } |
| 514 | } else if (toChars <= fromChars) { |
| 515 | std::memcpy(to, from, toChars * sizeof(TO)); |
| 516 | } else { |
| 517 | std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO)); |
| 518 | for (std::size_t j{fromChars}; j < toChars; ++j) { |
| 519 | to[j] = static_cast<TO>(' '); |
| 520 | } |
| 521 | } |
| 522 | } |
| 523 | |
Peter Klausler | 3ada883 | 2024-03-26 16:21:16 | [diff] [blame] | 524 | RT_API_ATTRS void CreatePartialReductionResult(Descriptor &result, |
| 525 | const Descriptor &x, std::size_t resultElementSize, int dim, Terminator &, |
| 526 | const char *intrinsic, TypeCode); |
| 527 | |
Tim Keith | 1f87900 | 2020-03-29 04:00:16 | [diff] [blame] | 528 | } // namespace Fortran::runtime |
| 529 | #endif // FORTRAN_RUNTIME_TOOLS_H_ |