1 //===-- runtime/tools.h -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef FORTRAN_RUNTIME_TOOLS_H_
10 #define FORTRAN_RUNTIME_TOOLS_H_
13 #include "terminator.h"
14 #include "flang/Common/optional.h"
15 #include "flang/Runtime/cpp-type.h"
16 #include "flang/Runtime/descriptor.h"
17 #include "flang/Runtime/freestanding-tools.h"
18 #include "flang/Runtime/memory.h"
22 #include <type_traits>
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
31 #define RT_PRETTY_FUNCTION __FUNCSIG__
32 #elif defined(__GNUC__) || defined(__clang__)
33 #define RT_PRETTY_FUNCTION __PRETTY_FUNCTION__
35 #define RT_PRETTY_FUNCTION __func__
38 #if defined(RT_DEVICE_COMPILATION)
39 // Use the pseudo lock and pseudo file unit implementations
41 #define RT_USE_PSEUDO_LOCK 1
42 #define RT_USE_PSEUDO_FILE_UNIT 1
45 namespace Fortran::runtime
{
49 RT_API_ATTRS
std::size_t TrimTrailingSpaces(const char *, std::size_t);
51 RT_API_ATTRS OwningPtr
<char> SaveDefaultCharacter(
52 const char *, std::size_t, const Terminator
&);
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.
58 RT_API_ATTRS
int IdentifyValue(
59 const char *value
, std::size_t length
, const char *possibilities
[]);
61 // Truncates or pads as necessary
62 RT_API_ATTRS
void ToFortranDefaultCharacter(
63 char *to
, std::size_t toLength
, const char *from
);
65 // Utilities for dealing with elemental LOGICAL arguments
66 inline RT_API_ATTRS
bool IsLogicalElementTrue(
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
) {
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
) {
88 // Check array conformability; a scalar 'x' conforms. Crashes on error.
89 RT_API_ATTRS
void CheckConformability(const Descriptor
&to
, const Descriptor
&x
,
90 Terminator
&, const char *funcName
, const char *toName
,
91 const char *fromName
);
93 // Helper to store integer value in result[at].
94 template <int KIND
> struct StoreIntegerAt
{
95 RT_API_ATTRS
void operator()(const Fortran::runtime::Descriptor
&result
,
96 std::size_t at
, std::int64_t value
) const {
97 *result
.ZeroBasedIndexedElement
<Fortran::runtime::CppTypeFor
<
98 Fortran::common::TypeCategory::Integer
, KIND
>>(at
) = value
;
102 // Helper to store floating value in result[at].
103 template <int KIND
> struct StoreFloatingPointAt
{
104 RT_API_ATTRS
void operator()(const Fortran::runtime::Descriptor
&result
,
105 std::size_t at
, std::double_t value
) const {
106 *result
.ZeroBasedIndexedElement
<Fortran::runtime::CppTypeFor
<
107 Fortran::common::TypeCategory::Real
, KIND
>>(at
) = value
;
111 // Validate a KIND= argument
112 RT_API_ATTRS
void CheckIntegerKind(
113 Terminator
&, int kind
, const char *intrinsic
);
115 template <typename TO
, typename FROM
>
116 inline RT_API_ATTRS
void PutContiguousConverted(
117 TO
*to
, FROM
*from
, std::size_t count
) {
118 while (count
-- > 0) {
123 static inline RT_API_ATTRS
std::int64_t GetInt64(
124 const char *p
, std::size_t bytes
, Terminator
&terminator
) {
127 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 1> *>(p
);
129 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 2> *>(p
);
131 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 4> *>(p
);
133 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 8> *>(p
);
135 terminator
.Crash("GetInt64: no case for %zd bytes", bytes
);
139 static inline RT_API_ATTRS
Fortran::common::optional
<std::int64_t> GetInt64Safe(
140 const char *p
, std::size_t bytes
, Terminator
&terminator
) {
143 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 1> *>(p
);
145 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 2> *>(p
);
147 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 4> *>(p
);
149 return *reinterpret_cast<const CppTypeFor
<TypeCategory::Integer
, 8> *>(p
);
151 using Int128
= CppTypeFor
<TypeCategory::Integer
, 16>;
152 auto n
{*reinterpret_cast<const Int128
*>(p
)};
153 std::int64_t result
{static_cast<std::int64_t>(n
)};
154 if (static_cast<Int128
>(result
) == n
) {
157 return Fortran::common::nullopt
;
160 terminator
.Crash("GetInt64Safe: no case for %zd bytes", bytes
);
164 template <typename INT
>
165 inline RT_API_ATTRS
bool SetInteger(INT
&x
, int kind
, std::int64_t value
) {
168 reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 1> &>(x
) = value
;
169 return value
== reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 1> &>(x
);
171 reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 2> &>(x
) = value
;
172 return value
== reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 2> &>(x
);
174 reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 4> &>(x
) = value
;
175 return value
== reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 4> &>(x
);
177 reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 8> &>(x
) = value
;
178 return value
== reinterpret_cast<CppTypeFor
<TypeCategory::Integer
, 8> &>(x
);
184 // Maps intrinsic runtime type category and kind values to the appropriate
185 // instantiation of a function object template and calls it with the supplied
187 template <template <TypeCategory
, int> class FUNC
, typename RESULT
,
189 inline RT_API_ATTRS RESULT
ApplyType(
190 TypeCategory cat
, int kind
, Terminator
&terminator
, A
&&...x
) {
192 case TypeCategory::Integer
:
195 return FUNC
<TypeCategory::Integer
, 1>{}(std::forward
<A
>(x
)...);
197 return FUNC
<TypeCategory::Integer
, 2>{}(std::forward
<A
>(x
)...);
199 return FUNC
<TypeCategory::Integer
, 4>{}(std::forward
<A
>(x
)...);
201 return FUNC
<TypeCategory::Integer
, 8>{}(std::forward
<A
>(x
)...);
202 #if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T
204 return FUNC
<TypeCategory::Integer
, 16>{}(std::forward
<A
>(x
)...);
207 terminator
.Crash("not yet implemented: INTEGER(KIND=%d)", kind
);
209 case TypeCategory::Unsigned
:
212 return FUNC
<TypeCategory::Unsigned
, 1>{}(std::forward
<A
>(x
)...);
214 return FUNC
<TypeCategory::Unsigned
, 2>{}(std::forward
<A
>(x
)...);
216 return FUNC
<TypeCategory::Unsigned
, 4>{}(std::forward
<A
>(x
)...);
218 return FUNC
<TypeCategory::Unsigned
, 8>{}(std::forward
<A
>(x
)...);
219 #if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T
221 return FUNC
<TypeCategory::Unsigned
, 16>{}(std::forward
<A
>(x
)...);
224 terminator
.Crash("not yet implemented: UNSIGNED(KIND=%d)", kind
);
226 case TypeCategory::Real
:
228 #if 0 // TODO: REAL(2 & 3)
230 return FUNC
<TypeCategory::Real
, 2>{}(std::forward
<A
>(x
)...);
232 return FUNC
<TypeCategory::Real
, 3>{}(std::forward
<A
>(x
)...);
235 return FUNC
<TypeCategory::Real
, 4>{}(std::forward
<A
>(x
)...);
237 return FUNC
<TypeCategory::Real
, 8>{}(std::forward
<A
>(x
)...);
239 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 10>) {
240 return FUNC
<TypeCategory::Real
, 10>{}(std::forward
<A
>(x
)...);
244 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 16>) {
245 return FUNC
<TypeCategory::Real
, 16>{}(std::forward
<A
>(x
)...);
249 terminator
.Crash("not yet implemented: REAL(KIND=%d)", kind
);
250 case TypeCategory::Complex
:
252 #if 0 // TODO: COMPLEX(2 & 3)
254 return FUNC
<TypeCategory::Complex
, 2>{}(std::forward
<A
>(x
)...);
256 return FUNC
<TypeCategory::Complex
, 3>{}(std::forward
<A
>(x
)...);
259 return FUNC
<TypeCategory::Complex
, 4>{}(std::forward
<A
>(x
)...);
261 return FUNC
<TypeCategory::Complex
, 8>{}(std::forward
<A
>(x
)...);
263 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 10>) {
264 return FUNC
<TypeCategory::Complex
, 10>{}(std::forward
<A
>(x
)...);
268 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 16>) {
269 return FUNC
<TypeCategory::Complex
, 16>{}(std::forward
<A
>(x
)...);
273 terminator
.Crash("not yet implemented: COMPLEX(KIND=%d)", kind
);
274 case TypeCategory::Character
:
277 return FUNC
<TypeCategory::Character
, 1>{}(std::forward
<A
>(x
)...);
279 return FUNC
<TypeCategory::Character
, 2>{}(std::forward
<A
>(x
)...);
281 return FUNC
<TypeCategory::Character
, 4>{}(std::forward
<A
>(x
)...);
283 terminator
.Crash("not yet implemented: CHARACTER(KIND=%d)", kind
);
285 case TypeCategory::Logical
:
288 return FUNC
<TypeCategory::Logical
, 1>{}(std::forward
<A
>(x
)...);
290 return FUNC
<TypeCategory::Logical
, 2>{}(std::forward
<A
>(x
)...);
292 return FUNC
<TypeCategory::Logical
, 4>{}(std::forward
<A
>(x
)...);
294 return FUNC
<TypeCategory::Logical
, 8>{}(std::forward
<A
>(x
)...);
296 terminator
.Crash("not yet implemented: LOGICAL(KIND=%d)", kind
);
300 "not yet implemented: type category(%d)", static_cast<int>(cat
));
304 // Maps a runtime INTEGER kind value to the appropriate instantiation of
305 // a function object template and calls it with the supplied arguments.
306 template <template <int KIND
> class FUNC
, typename RESULT
, typename
... A
>
307 inline RT_API_ATTRS RESULT
ApplyIntegerKind(
308 int kind
, Terminator
&terminator
, A
&&...x
) {
311 return FUNC
<1>{}(std::forward
<A
>(x
)...);
313 return FUNC
<2>{}(std::forward
<A
>(x
)...);
315 return FUNC
<4>{}(std::forward
<A
>(x
)...);
317 return FUNC
<8>{}(std::forward
<A
>(x
)...);
318 #if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T
320 return FUNC
<16>{}(std::forward
<A
>(x
)...);
323 terminator
.Crash("not yet implemented: INTEGER/UNSIGNED(KIND=%d)", kind
);
327 template <template <int KIND
> class FUNC
, typename RESULT
,
328 bool NEEDSMATH
= false, typename
... A
>
329 inline RT_API_ATTRS RESULT
ApplyFloatingPointKind(
330 int kind
, Terminator
&terminator
, A
&&...x
) {
332 #if 0 // TODO: REAL/COMPLEX (2 & 3)
334 return FUNC
<2>{}(std::forward
<A
>(x
)...);
336 return FUNC
<3>{}(std::forward
<A
>(x
)...);
339 return FUNC
<4>{}(std::forward
<A
>(x
)...);
341 return FUNC
<8>{}(std::forward
<A
>(x
)...);
343 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 10>) {
344 return FUNC
<10>{}(std::forward
<A
>(x
)...);
348 if constexpr (HasCppTypeFor
<TypeCategory::Real
, 16>) {
349 // If FUNC implemenation relies on FP math functions,
350 // then we should not be here. The compiler should have
351 // generated a call to an entry in FortranFloat128Math
353 if constexpr (!NEEDSMATH
) {
354 return FUNC
<16>{}(std::forward
<A
>(x
)...);
359 terminator
.Crash("not yet implemented: REAL/COMPLEX(KIND=%d)", kind
);
362 template <template <int KIND
> class FUNC
, typename RESULT
, typename
... A
>
363 inline RT_API_ATTRS RESULT
ApplyCharacterKind(
364 int kind
, Terminator
&terminator
, A
&&...x
) {
367 return FUNC
<1>{}(std::forward
<A
>(x
)...);
369 return FUNC
<2>{}(std::forward
<A
>(x
)...);
371 return FUNC
<4>{}(std::forward
<A
>(x
)...);
373 terminator
.Crash("not yet implemented: CHARACTER(KIND=%d)", kind
);
377 template <template <int KIND
> class FUNC
, typename RESULT
, typename
... A
>
378 inline RT_API_ATTRS RESULT
ApplyLogicalKind(
379 int kind
, Terminator
&terminator
, A
&&...x
) {
382 return FUNC
<1>{}(std::forward
<A
>(x
)...);
384 return FUNC
<2>{}(std::forward
<A
>(x
)...);
386 return FUNC
<4>{}(std::forward
<A
>(x
)...);
388 return FUNC
<8>{}(std::forward
<A
>(x
)...);
390 terminator
.Crash("not yet implemented: LOGICAL(KIND=%d)", kind
);
394 // Calculate result type of (X op Y) for *, //, DOT_PRODUCT, &c.
395 Fortran::common::optional
<
396 std::pair
<TypeCategory
, int>> inline constexpr RT_API_ATTRS
397 GetResultType(TypeCategory xCat
, int xKind
, TypeCategory yCat
, int yKind
) {
398 int maxKind
{std::max(xKind
, yKind
)};
400 case TypeCategory::Integer
:
402 case TypeCategory::Integer
:
403 return std::make_pair(TypeCategory::Integer
, maxKind
);
404 case TypeCategory::Real
:
405 case TypeCategory::Complex
:
406 #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
411 return std::make_pair(yCat
, yKind
);
416 case TypeCategory::Unsigned
:
418 case TypeCategory::Unsigned
:
419 return std::make_pair(TypeCategory::Unsigned
, maxKind
);
420 case TypeCategory::Real
:
421 case TypeCategory::Complex
:
422 #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
427 return std::make_pair(yCat
, yKind
);
432 case TypeCategory::Real
:
434 case TypeCategory::Integer
:
435 case TypeCategory::Unsigned
:
436 #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
441 return std::make_pair(TypeCategory::Real
, xKind
);
442 case TypeCategory::Real
:
443 case TypeCategory::Complex
:
444 return std::make_pair(yCat
, maxKind
);
449 case TypeCategory::Complex
:
451 case TypeCategory::Integer
:
452 case TypeCategory::Unsigned
:
453 #if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
458 return std::make_pair(TypeCategory::Complex
, xKind
);
459 case TypeCategory::Real
:
460 case TypeCategory::Complex
:
461 return std::make_pair(TypeCategory::Complex
, maxKind
);
466 case TypeCategory::Character
:
467 if (yCat
== TypeCategory::Character
) {
468 return std::make_pair(TypeCategory::Character
, maxKind
);
470 return Fortran::common::nullopt
;
472 case TypeCategory::Logical
:
473 if (yCat
== TypeCategory::Logical
) {
474 return std::make_pair(TypeCategory::Logical
, maxKind
);
476 return Fortran::common::nullopt
;
481 return Fortran::common::nullopt
;
484 // Accumulate floating-point results in (at least) double precision
485 template <TypeCategory CAT
, int KIND
>
486 using AccumulationType
= CppTypeFor
<CAT
,
487 CAT
== TypeCategory::Real
|| CAT
== TypeCategory::Complex
488 ? std::max(KIND
, static_cast<int>(sizeof(double)))
491 // memchr() for any character type
492 template <typename CHAR
>
493 static inline RT_API_ATTRS
const CHAR
*FindCharacter(
494 const CHAR
*data
, CHAR ch
, std::size_t chars
) {
495 const CHAR
*end
{data
+ chars
};
496 for (const CHAR
*p
{data
}; p
< end
; ++p
) {
505 inline RT_API_ATTRS
const char *FindCharacter(
506 const char *data
, char ch
, std::size_t chars
) {
507 return reinterpret_cast<const char *>(
508 runtime::memchr(data
, static_cast<int>(ch
), chars
));
511 // Copy payload data from one allocated descriptor to another.
512 // Assumes element counts and element sizes match, and that both
513 // descriptors are allocated.
514 RT_API_ATTRS
void ShallowCopyDiscontiguousToDiscontiguous(
515 const Descriptor
&to
, const Descriptor
&from
);
516 RT_API_ATTRS
void ShallowCopyDiscontiguousToContiguous(
517 const Descriptor
&to
, const Descriptor
&from
);
518 RT_API_ATTRS
void ShallowCopyContiguousToDiscontiguous(
519 const Descriptor
&to
, const Descriptor
&from
);
520 RT_API_ATTRS
void ShallowCopy(const Descriptor
&to
, const Descriptor
&from
,
521 bool toIsContiguous
, bool fromIsContiguous
);
522 RT_API_ATTRS
void ShallowCopy(const Descriptor
&to
, const Descriptor
&from
);
524 // Ensures that a character string is null-terminated, allocating a /p length +1
525 // size memory for null-terminator if necessary. Returns the original or a newly
526 // allocated null-terminated string (responsibility for deallocation is on the
528 RT_API_ATTRS
char *EnsureNullTerminated(
529 char *str
, std::size_t length
, Terminator
&terminator
);
531 RT_API_ATTRS
bool IsValidCharDescriptor(const Descriptor
*value
);
533 RT_API_ATTRS
bool IsValidIntDescriptor(const Descriptor
*intVal
);
535 // Copy a null-terminated character array \p rawValue to descriptor \p value.
536 // The copy starts at the given \p offset, if not present then start at 0.
537 // If descriptor `errmsg` is provided, error messages will be stored to it.
538 // Returns stats specified in standard.
539 RT_API_ATTRS
std::int32_t CopyCharsToDescriptor(const Descriptor
&value
,
540 const char *rawValue
, std::size_t rawValueLength
,
541 const Descriptor
*errmsg
= nullptr, std::size_t offset
= 0);
543 RT_API_ATTRS
void StoreIntToDescriptor(
544 const Descriptor
*length
, std::int64_t value
, Terminator
&terminator
);
546 // Defines a utility function for copying and padding characters
547 template <typename TO
, typename FROM
>
548 RT_API_ATTRS
void CopyAndPad(
549 TO
*to
, const FROM
*from
, std::size_t toChars
, std::size_t fromChars
) {
550 if constexpr (sizeof(TO
) != sizeof(FROM
)) {
551 std::size_t copyChars
{std::min(toChars
, fromChars
)};
552 for (std::size_t j
{0}; j
< copyChars
; ++j
) {
555 for (std::size_t j
{copyChars
}; j
< toChars
; ++j
) {
556 to
[j
] = static_cast<TO
>(' ');
558 } else if (toChars
<= fromChars
) {
559 std::memcpy(to
, from
, toChars
* sizeof(TO
));
561 std::memcpy(to
, from
, std::min(toChars
, fromChars
) * sizeof(TO
));
562 for (std::size_t j
{fromChars
}; j
< toChars
; ++j
) {
563 to
[j
] = static_cast<TO
>(' ');
568 RT_API_ATTRS
void CreatePartialReductionResult(Descriptor
&result
,
569 const Descriptor
&x
, std::size_t resultElementSize
, int dim
, Terminator
&,
570 const char *intrinsic
, TypeCode
);
572 } // namespace Fortran::runtime
573 #endif // FORTRAN_RUNTIME_TOOLS_H_