mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 16:56:06 +00:00

This is same as https://github.com/llvm/llvm-project/pull/90578 with an added fix. This PR updated tests of etime intrinsic due to Lowering changes for assigning dummy_scope to hlfir.declare. Referring to https://github.com/llvm/llvm-project/pull/92472 and https://github.com/llvm/llvm-project/pull/90989
539 lines
18 KiB
C++
539 lines
18 KiB
C++
//===-- runtime/tools.h -----------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef FORTRAN_RUNTIME_TOOLS_H_
|
|
#define FORTRAN_RUNTIME_TOOLS_H_
|
|
|
|
#include "stat.h"
|
|
#include "terminator.h"
|
|
#include "flang/Common/optional.h"
|
|
#include "flang/Runtime/cpp-type.h"
|
|
#include "flang/Runtime/descriptor.h"
|
|
#include "flang/Runtime/freestanding-tools.h"
|
|
#include "flang/Runtime/memory.h"
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <type_traits>
|
|
|
|
/// \macro RT_PRETTY_FUNCTION
|
|
/// Gets a user-friendly looking function signature for the current scope
|
|
/// using the best available method on each platform. The exact format of the
|
|
/// resulting string is implementation specific and non-portable, so this should
|
|
/// only be used, for example, for logging or diagnostics.
|
|
/// Copy of LLVM_PRETTY_FUNCTION
|
|
#if defined(_MSC_VER)
|
|
#define RT_PRETTY_FUNCTION __FUNCSIG__
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#define RT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
|
#else
|
|
#define RT_PRETTY_FUNCTION __func__
|
|
#endif
|
|
|
|
#if defined(RT_DEVICE_COMPILATION)
|
|
// Use the pseudo lock and pseudo file unit implementations
|
|
// for the device.
|
|
#define RT_USE_PSEUDO_LOCK 1
|
|
#define RT_USE_PSEUDO_FILE_UNIT 1
|
|
#endif
|
|
|
|
namespace Fortran::runtime {
|
|
|
|
class Terminator;
|
|
|
|
RT_API_ATTRS std::size_t TrimTrailingSpaces(const char *, std::size_t);
|
|
|
|
RT_API_ATTRS OwningPtr<char> SaveDefaultCharacter(
|
|
const char *, std::size_t, const Terminator &);
|
|
|
|
// For validating and recognizing default CHARACTER values in a
|
|
// case-insensitive manner. Returns the zero-based index into the
|
|
// null-terminated array of upper-case possibilities when the value is valid,
|
|
// or -1 when it has no match.
|
|
RT_API_ATTRS int IdentifyValue(
|
|
const char *value, std::size_t length, const char *possibilities[]);
|
|
|
|
// Truncates or pads as necessary
|
|
RT_API_ATTRS void ToFortranDefaultCharacter(
|
|
char *to, std::size_t toLength, const char *from);
|
|
|
|
// Utilities for dealing with elemental LOGICAL arguments
|
|
inline RT_API_ATTRS bool IsLogicalElementTrue(
|
|
const Descriptor &logical, const SubscriptValue at[]) {
|
|
// A LOGICAL value is false if and only if all of its bytes are zero.
|
|
const char *p{logical.Element<char>(at)};
|
|
for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) {
|
|
if (*p) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
inline RT_API_ATTRS bool IsLogicalScalarTrue(const Descriptor &logical) {
|
|
// A LOGICAL value is false if and only if all of its bytes are zero.
|
|
const char *p{logical.OffsetElement<char>()};
|
|
for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) {
|
|
if (*p) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check array conformability; a scalar 'x' conforms. Crashes on error.
|
|
RT_API_ATTRS void CheckConformability(const Descriptor &to, const Descriptor &x,
|
|
Terminator &, const char *funcName, const char *toName,
|
|
const char *fromName);
|
|
|
|
// Helper to store integer value in result[at].
|
|
template <int KIND> struct StoreIntegerAt {
|
|
RT_API_ATTRS void operator()(const Fortran::runtime::Descriptor &result,
|
|
std::size_t at, std::int64_t value) const {
|
|
*result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor<
|
|
Fortran::common::TypeCategory::Integer, KIND>>(at) = value;
|
|
}
|
|
};
|
|
|
|
// Helper to store floating value in result[at].
|
|
template <int KIND> struct StoreFloatingPointAt {
|
|
RT_API_ATTRS void operator()(const Fortran::runtime::Descriptor &result,
|
|
std::size_t at, std::double_t value) const {
|
|
*result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor<
|
|
Fortran::common::TypeCategory::Real, KIND>>(at) = value;
|
|
}
|
|
};
|
|
|
|
// Validate a KIND= argument
|
|
RT_API_ATTRS void CheckIntegerKind(
|
|
Terminator &, int kind, const char *intrinsic);
|
|
|
|
template <typename TO, typename FROM>
|
|
inline RT_API_ATTRS void PutContiguousConverted(
|
|
TO *to, FROM *from, std::size_t count) {
|
|
while (count-- > 0) {
|
|
*to++ = *from++;
|
|
}
|
|
}
|
|
|
|
static inline RT_API_ATTRS std::int64_t GetInt64(
|
|
const char *p, std::size_t bytes, Terminator &terminator) {
|
|
switch (bytes) {
|
|
case 1:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 1> *>(p);
|
|
case 2:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 2> *>(p);
|
|
case 4:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 4> *>(p);
|
|
case 8:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 8> *>(p);
|
|
default:
|
|
terminator.Crash("GetInt64: no case for %zd bytes", bytes);
|
|
}
|
|
}
|
|
|
|
static inline RT_API_ATTRS Fortran::common::optional<std::int64_t> GetInt64Safe(
|
|
const char *p, std::size_t bytes, Terminator &terminator) {
|
|
switch (bytes) {
|
|
case 1:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 1> *>(p);
|
|
case 2:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 2> *>(p);
|
|
case 4:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 4> *>(p);
|
|
case 8:
|
|
return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 8> *>(p);
|
|
case 16: {
|
|
using Int128 = CppTypeFor<TypeCategory::Integer, 16>;
|
|
auto n{*reinterpret_cast<const Int128 *>(p)};
|
|
std::int64_t result{static_cast<std::int64_t>(n)};
|
|
if (static_cast<Int128>(result) == n) {
|
|
return result;
|
|
}
|
|
return Fortran::common::nullopt;
|
|
}
|
|
default:
|
|
terminator.Crash("GetInt64Safe: no case for %zd bytes", bytes);
|
|
}
|
|
}
|
|
|
|
template <typename INT>
|
|
inline RT_API_ATTRS bool SetInteger(INT &x, int kind, std::int64_t value) {
|
|
switch (kind) {
|
|
case 1:
|
|
reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x) = value;
|
|
return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x);
|
|
case 2:
|
|
reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x) = value;
|
|
return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x);
|
|
case 4:
|
|
reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x) = value;
|
|
return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x);
|
|
case 8:
|
|
reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x) = value;
|
|
return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Maps intrinsic runtime type category and kind values to the appropriate
|
|
// instantiation of a function object template and calls it with the supplied
|
|
// arguments.
|
|
template <template <TypeCategory, int> class FUNC, typename RESULT,
|
|
typename... A>
|
|
inline RT_API_ATTRS RESULT ApplyType(
|
|
TypeCategory cat, int kind, Terminator &terminator, A &&...x) {
|
|
switch (cat) {
|
|
case TypeCategory::Integer:
|
|
switch (kind) {
|
|
case 1:
|
|
return FUNC<TypeCategory::Integer, 1>{}(std::forward<A>(x)...);
|
|
case 2:
|
|
return FUNC<TypeCategory::Integer, 2>{}(std::forward<A>(x)...);
|
|
case 4:
|
|
return FUNC<TypeCategory::Integer, 4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<TypeCategory::Integer, 8>{}(std::forward<A>(x)...);
|
|
#if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T
|
|
case 16:
|
|
return FUNC<TypeCategory::Integer, 16>{}(std::forward<A>(x)...);
|
|
#endif
|
|
default:
|
|
terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind);
|
|
}
|
|
case TypeCategory::Real:
|
|
switch (kind) {
|
|
#if 0 // TODO: REAL(2 & 3)
|
|
case 2:
|
|
return FUNC<TypeCategory::Real, 2>{}(std::forward<A>(x)...);
|
|
case 3:
|
|
return FUNC<TypeCategory::Real, 3>{}(std::forward<A>(x)...);
|
|
#endif
|
|
case 4:
|
|
return FUNC<TypeCategory::Real, 4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<TypeCategory::Real, 8>{}(std::forward<A>(x)...);
|
|
case 10:
|
|
if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
|
|
return FUNC<TypeCategory::Real, 10>{}(std::forward<A>(x)...);
|
|
}
|
|
break;
|
|
case 16:
|
|
if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) {
|
|
return FUNC<TypeCategory::Real, 16>{}(std::forward<A>(x)...);
|
|
}
|
|
break;
|
|
}
|
|
terminator.Crash("not yet implemented: REAL(KIND=%d)", kind);
|
|
case TypeCategory::Complex:
|
|
switch (kind) {
|
|
#if 0 // TODO: COMPLEX(2 & 3)
|
|
case 2:
|
|
return FUNC<TypeCategory::Complex, 2>{}(std::forward<A>(x)...);
|
|
case 3:
|
|
return FUNC<TypeCategory::Complex, 3>{}(std::forward<A>(x)...);
|
|
#endif
|
|
case 4:
|
|
return FUNC<TypeCategory::Complex, 4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<TypeCategory::Complex, 8>{}(std::forward<A>(x)...);
|
|
case 10:
|
|
if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
|
|
return FUNC<TypeCategory::Complex, 10>{}(std::forward<A>(x)...);
|
|
}
|
|
break;
|
|
case 16:
|
|
if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) {
|
|
return FUNC<TypeCategory::Complex, 16>{}(std::forward<A>(x)...);
|
|
}
|
|
break;
|
|
}
|
|
terminator.Crash("not yet implemented: COMPLEX(KIND=%d)", kind);
|
|
case TypeCategory::Character:
|
|
switch (kind) {
|
|
case 1:
|
|
return FUNC<TypeCategory::Character, 1>{}(std::forward<A>(x)...);
|
|
case 2:
|
|
return FUNC<TypeCategory::Character, 2>{}(std::forward<A>(x)...);
|
|
case 4:
|
|
return FUNC<TypeCategory::Character, 4>{}(std::forward<A>(x)...);
|
|
default:
|
|
terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind);
|
|
}
|
|
case TypeCategory::Logical:
|
|
switch (kind) {
|
|
case 1:
|
|
return FUNC<TypeCategory::Logical, 1>{}(std::forward<A>(x)...);
|
|
case 2:
|
|
return FUNC<TypeCategory::Logical, 2>{}(std::forward<A>(x)...);
|
|
case 4:
|
|
return FUNC<TypeCategory::Logical, 4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<TypeCategory::Logical, 8>{}(std::forward<A>(x)...);
|
|
default:
|
|
terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind);
|
|
}
|
|
default:
|
|
terminator.Crash(
|
|
"not yet implemented: type category(%d)", static_cast<int>(cat));
|
|
}
|
|
}
|
|
|
|
// Maps a runtime INTEGER kind value to the appropriate instantiation of
|
|
// a function object template and calls it with the supplied arguments.
|
|
template <template <int KIND> class FUNC, typename RESULT, typename... A>
|
|
inline RT_API_ATTRS RESULT ApplyIntegerKind(
|
|
int kind, Terminator &terminator, A &&...x) {
|
|
switch (kind) {
|
|
case 1:
|
|
return FUNC<1>{}(std::forward<A>(x)...);
|
|
case 2:
|
|
return FUNC<2>{}(std::forward<A>(x)...);
|
|
case 4:
|
|
return FUNC<4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<8>{}(std::forward<A>(x)...);
|
|
#if defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T
|
|
case 16:
|
|
return FUNC<16>{}(std::forward<A>(x)...);
|
|
#endif
|
|
default:
|
|
terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind);
|
|
}
|
|
}
|
|
|
|
template <template <int KIND> class FUNC, typename RESULT,
|
|
bool NEEDSMATH = false, typename... A>
|
|
inline RT_API_ATTRS RESULT ApplyFloatingPointKind(
|
|
int kind, Terminator &terminator, A &&...x) {
|
|
switch (kind) {
|
|
#if 0 // TODO: REAL/COMPLEX (2 & 3)
|
|
case 2:
|
|
return FUNC<2>{}(std::forward<A>(x)...);
|
|
case 3:
|
|
return FUNC<3>{}(std::forward<A>(x)...);
|
|
#endif
|
|
case 4:
|
|
return FUNC<4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<8>{}(std::forward<A>(x)...);
|
|
case 10:
|
|
if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) {
|
|
return FUNC<10>{}(std::forward<A>(x)...);
|
|
}
|
|
break;
|
|
case 16:
|
|
if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) {
|
|
// If FUNC implemenation relies on FP math functions,
|
|
// then we should not be here. The compiler should have
|
|
// generated a call to an entry in FortranFloat128Math
|
|
// library.
|
|
if constexpr (!NEEDSMATH) {
|
|
return FUNC<16>{}(std::forward<A>(x)...);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
terminator.Crash("not yet implemented: REAL/COMPLEX(KIND=%d)", kind);
|
|
}
|
|
|
|
template <template <int KIND> class FUNC, typename RESULT, typename... A>
|
|
inline RT_API_ATTRS RESULT ApplyCharacterKind(
|
|
int kind, Terminator &terminator, A &&...x) {
|
|
switch (kind) {
|
|
case 1:
|
|
return FUNC<1>{}(std::forward<A>(x)...);
|
|
case 2:
|
|
return FUNC<2>{}(std::forward<A>(x)...);
|
|
case 4:
|
|
return FUNC<4>{}(std::forward<A>(x)...);
|
|
default:
|
|
terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind);
|
|
}
|
|
}
|
|
|
|
template <template <int KIND> class FUNC, typename RESULT, typename... A>
|
|
inline RT_API_ATTRS RESULT ApplyLogicalKind(
|
|
int kind, Terminator &terminator, A &&...x) {
|
|
switch (kind) {
|
|
case 1:
|
|
return FUNC<1>{}(std::forward<A>(x)...);
|
|
case 2:
|
|
return FUNC<2>{}(std::forward<A>(x)...);
|
|
case 4:
|
|
return FUNC<4>{}(std::forward<A>(x)...);
|
|
case 8:
|
|
return FUNC<8>{}(std::forward<A>(x)...);
|
|
default:
|
|
terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind);
|
|
}
|
|
}
|
|
|
|
// Calculate result type of (X op Y) for *, //, DOT_PRODUCT, &c.
|
|
Fortran::common::optional<
|
|
std::pair<TypeCategory, int>> inline constexpr RT_API_ATTRS
|
|
GetResultType(TypeCategory xCat, int xKind, TypeCategory yCat, int yKind) {
|
|
int maxKind{std::max(xKind, yKind)};
|
|
switch (xCat) {
|
|
case TypeCategory::Integer:
|
|
switch (yCat) {
|
|
case TypeCategory::Integer:
|
|
return std::make_pair(TypeCategory::Integer, maxKind);
|
|
case TypeCategory::Real:
|
|
case TypeCategory::Complex:
|
|
#if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
|
|
if (xKind == 16) {
|
|
break;
|
|
}
|
|
#endif
|
|
return std::make_pair(yCat, yKind);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case TypeCategory::Real:
|
|
switch (yCat) {
|
|
case TypeCategory::Integer:
|
|
#if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
|
|
if (yKind == 16) {
|
|
break;
|
|
}
|
|
#endif
|
|
return std::make_pair(TypeCategory::Real, xKind);
|
|
case TypeCategory::Real:
|
|
case TypeCategory::Complex:
|
|
return std::make_pair(yCat, maxKind);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case TypeCategory::Complex:
|
|
switch (yCat) {
|
|
case TypeCategory::Integer:
|
|
#if !(defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128_T)
|
|
if (yKind == 16) {
|
|
break;
|
|
}
|
|
#endif
|
|
return std::make_pair(TypeCategory::Complex, xKind);
|
|
case TypeCategory::Real:
|
|
case TypeCategory::Complex:
|
|
return std::make_pair(TypeCategory::Complex, maxKind);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case TypeCategory::Character:
|
|
if (yCat == TypeCategory::Character) {
|
|
return std::make_pair(TypeCategory::Character, maxKind);
|
|
} else {
|
|
return Fortran::common::nullopt;
|
|
}
|
|
case TypeCategory::Logical:
|
|
if (yCat == TypeCategory::Logical) {
|
|
return std::make_pair(TypeCategory::Logical, maxKind);
|
|
} else {
|
|
return Fortran::common::nullopt;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return Fortran::common::nullopt;
|
|
}
|
|
|
|
// Accumulate floating-point results in (at least) double precision
|
|
template <TypeCategory CAT, int KIND>
|
|
using AccumulationType = CppTypeFor<CAT,
|
|
CAT == TypeCategory::Real || CAT == TypeCategory::Complex
|
|
? std::max(KIND, static_cast<int>(sizeof(double)))
|
|
: KIND>;
|
|
|
|
// memchr() for any character type
|
|
template <typename CHAR>
|
|
static inline RT_API_ATTRS const CHAR *FindCharacter(
|
|
const CHAR *data, CHAR ch, std::size_t chars) {
|
|
const CHAR *end{data + chars};
|
|
for (const CHAR *p{data}; p < end; ++p) {
|
|
if (*p == ch) {
|
|
return p;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
template <>
|
|
inline RT_API_ATTRS const char *FindCharacter(
|
|
const char *data, char ch, std::size_t chars) {
|
|
return reinterpret_cast<const char *>(
|
|
runtime::memchr(data, static_cast<int>(ch), chars));
|
|
}
|
|
|
|
// Copy payload data from one allocated descriptor to another.
|
|
// Assumes element counts and element sizes match, and that both
|
|
// descriptors are allocated.
|
|
RT_API_ATTRS void ShallowCopyDiscontiguousToDiscontiguous(
|
|
const Descriptor &to, const Descriptor &from);
|
|
RT_API_ATTRS void ShallowCopyDiscontiguousToContiguous(
|
|
const Descriptor &to, const Descriptor &from);
|
|
RT_API_ATTRS void ShallowCopyContiguousToDiscontiguous(
|
|
const Descriptor &to, const Descriptor &from);
|
|
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
|
|
bool toIsContiguous, bool fromIsContiguous);
|
|
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);
|
|
|
|
// Ensures that a character string is null-terminated, allocating a /p length +1
|
|
// size memory for null-terminator if necessary. Returns the original or a newly
|
|
// allocated null-terminated string (responsibility for deallocation is on the
|
|
// caller).
|
|
RT_API_ATTRS char *EnsureNullTerminated(
|
|
char *str, std::size_t length, Terminator &terminator);
|
|
|
|
RT_API_ATTRS bool IsValidCharDescriptor(const Descriptor *value);
|
|
|
|
RT_API_ATTRS bool IsValidIntDescriptor(const Descriptor *intVal);
|
|
|
|
// Copy a null-terminated character array \p rawValue to descriptor \p value.
|
|
// The copy starts at the given \p offset, if not present then start at 0.
|
|
// If descriptor `errmsg` is provided, error messages will be stored to it.
|
|
// Returns stats specified in standard.
|
|
RT_API_ATTRS std::int32_t CopyCharsToDescriptor(const Descriptor &value,
|
|
const char *rawValue, std::size_t rawValueLength,
|
|
const Descriptor *errmsg = nullptr, std::size_t offset = 0);
|
|
|
|
RT_API_ATTRS void StoreIntToDescriptor(
|
|
const Descriptor *length, std::int64_t value, Terminator &terminator);
|
|
|
|
// Defines a utility function for copying and padding characters
|
|
template <typename TO, typename FROM>
|
|
RT_API_ATTRS void CopyAndPad(
|
|
TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
|
|
if constexpr (sizeof(TO) != sizeof(FROM)) {
|
|
std::size_t copyChars{std::min(toChars, fromChars)};
|
|
for (std::size_t j{0}; j < copyChars; ++j) {
|
|
to[j] = from[j];
|
|
}
|
|
for (std::size_t j{copyChars}; j < toChars; ++j) {
|
|
to[j] = static_cast<TO>(' ');
|
|
}
|
|
} else if (toChars <= fromChars) {
|
|
std::memcpy(to, from, toChars * sizeof(TO));
|
|
} else {
|
|
std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
|
|
for (std::size_t j{fromChars}; j < toChars; ++j) {
|
|
to[j] = static_cast<TO>(' ');
|
|
}
|
|
}
|
|
}
|
|
|
|
RT_API_ATTRS void CreatePartialReductionResult(Descriptor &result,
|
|
const Descriptor &x, std::size_t resultElementSize, int dim, Terminator &,
|
|
const char *intrinsic, TypeCode);
|
|
|
|
} // namespace Fortran::runtime
|
|
#endif // FORTRAN_RUNTIME_TOOLS_H_
|