1 //===-- runtime/tools.cpp -------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
10 #include "terminator.h"
16 namespace Fortran::runtime
{
18 RT_OFFLOAD_API_GROUP_BEGIN
20 RT_API_ATTRS
std::size_t TrimTrailingSpaces(const char *s
, std::size_t n
) {
21 while (n
> 0 && s
[n
- 1] == ' ') {
27 RT_API_ATTRS OwningPtr
<char> SaveDefaultCharacter(
28 const char *s
, std::size_t length
, const Terminator
&terminator
) {
30 auto *p
{static_cast<char *>(AllocateMemoryOrCrash(terminator
, length
+ 1))};
31 std::memcpy(p
, s
, length
);
33 return OwningPtr
<char>{p
};
35 return OwningPtr
<char>{};
39 static RT_API_ATTRS
bool CaseInsensitiveMatch(
40 const char *value
, std::size_t length
, const char *possibility
) {
41 for (; length
-- > 0; ++possibility
) {
43 if (ch
>= 'a' && ch
<= 'z') {
46 if (*possibility
!= ch
) {
47 if (*possibility
!= '\0' || ch
!= ' ') {
50 // Ignore trailing blanks (12.5.6.2 p1)
51 while (length
-- > 0) {
52 if (*value
++ != ' ') {
59 return *possibility
== '\0';
62 RT_API_ATTRS
int IdentifyValue(
63 const char *value
, std::size_t length
, const char *possibilities
[]) {
65 for (int j
{0}; possibilities
[j
]; ++j
) {
66 if (CaseInsensitiveMatch(value
, length
, possibilities
[j
])) {
74 RT_API_ATTRS
void ToFortranDefaultCharacter(
75 char *to
, std::size_t toLength
, const char *from
) {
76 std::size_t len
{Fortran::runtime::strlen(from
)};
78 std::memcpy(to
, from
, len
);
79 std::memset(to
+ len
, ' ', toLength
- len
);
81 std::memcpy(to
, from
, toLength
);
85 RT_API_ATTRS
void CheckConformability(const Descriptor
&to
, const Descriptor
&x
,
86 Terminator
&terminator
, const char *funcName
, const char *toName
,
89 return; // scalar conforms with anything
92 if (x
.rank() != rank
) {
94 "Incompatible array arguments to %s: %s has rank %d but %s has rank %d",
95 funcName
, toName
, rank
, xName
, x
.rank());
97 for (int j
{0}; j
< rank
; ++j
) {
98 auto toExtent
{static_cast<std::int64_t>(to
.GetDimension(j
).Extent())};
99 auto xExtent
{static_cast<std::int64_t>(x
.GetDimension(j
).Extent())};
100 if (xExtent
!= toExtent
) {
101 terminator
.Crash("Incompatible array arguments to %s: dimension %d of "
102 "%s has extent %" PRId64
" but %s has extent %" PRId64
,
103 funcName
, j
+ 1, toName
, toExtent
, xName
, xExtent
);
109 RT_API_ATTRS
void CheckIntegerKind(
110 Terminator
&terminator
, int kind
, const char *intrinsic
) {
111 if (kind
< 1 || kind
> 16 || (kind
& (kind
- 1)) != 0) {
112 terminator
.Crash("not yet implemented: INTEGER(KIND=%d) in %s intrinsic",
117 RT_API_ATTRS
void ShallowCopyDiscontiguousToDiscontiguous(
118 const Descriptor
&to
, const Descriptor
&from
) {
119 SubscriptValue toAt
[maxRank
], fromAt
[maxRank
];
120 to
.GetLowerBounds(toAt
);
121 from
.GetLowerBounds(fromAt
);
122 std::size_t elementBytes
{to
.ElementBytes()};
123 for (std::size_t n
{to
.Elements()}; n
-- > 0;
124 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
126 to
.Element
<char>(toAt
), from
.Element
<char>(fromAt
), elementBytes
);
130 RT_API_ATTRS
void ShallowCopyDiscontiguousToContiguous(
131 const Descriptor
&to
, const Descriptor
&from
) {
132 char *toAt
{to
.OffsetElement()};
133 SubscriptValue fromAt
[maxRank
];
134 from
.GetLowerBounds(fromAt
);
135 std::size_t elementBytes
{to
.ElementBytes()};
136 for (std::size_t n
{to
.Elements()}; n
-- > 0;
137 toAt
+= elementBytes
, from
.IncrementSubscripts(fromAt
)) {
138 std::memcpy(toAt
, from
.Element
<char>(fromAt
), elementBytes
);
142 RT_API_ATTRS
void ShallowCopyContiguousToDiscontiguous(
143 const Descriptor
&to
, const Descriptor
&from
) {
144 SubscriptValue toAt
[maxRank
];
145 to
.GetLowerBounds(toAt
);
146 char *fromAt
{from
.OffsetElement()};
147 std::size_t elementBytes
{to
.ElementBytes()};
148 for (std::size_t n
{to
.Elements()}; n
-- > 0;
149 to
.IncrementSubscripts(toAt
), fromAt
+= elementBytes
) {
150 std::memcpy(to
.Element
<char>(toAt
), fromAt
, elementBytes
);
154 RT_API_ATTRS
void ShallowCopy(const Descriptor
&to
, const Descriptor
&from
,
155 bool toIsContiguous
, bool fromIsContiguous
) {
156 if (toIsContiguous
) {
157 if (fromIsContiguous
) {
158 std::memcpy(to
.OffsetElement(), from
.OffsetElement(),
159 to
.Elements() * to
.ElementBytes());
161 ShallowCopyDiscontiguousToContiguous(to
, from
);
164 if (fromIsContiguous
) {
165 ShallowCopyContiguousToDiscontiguous(to
, from
);
167 ShallowCopyDiscontiguousToDiscontiguous(to
, from
);
172 RT_API_ATTRS
void ShallowCopy(const Descriptor
&to
, const Descriptor
&from
) {
173 ShallowCopy(to
, from
, to
.IsContiguous(), from
.IsContiguous());
176 RT_API_ATTRS
char *EnsureNullTerminated(
177 char *str
, std::size_t length
, Terminator
&terminator
) {
178 if (runtime::memchr(str
, '\0', length
) == nullptr) {
179 char *newCmd
{(char *)AllocateMemoryOrCrash(terminator
, length
+ 1)};
180 std::memcpy(newCmd
, str
, length
);
181 newCmd
[length
] = '\0';
188 RT_API_ATTRS
bool IsValidCharDescriptor(const Descriptor
*value
) {
189 return value
&& value
->IsAllocated() &&
190 value
->type() == TypeCode(TypeCategory::Character
, 1) &&
194 RT_API_ATTRS
bool IsValidIntDescriptor(const Descriptor
*intVal
) {
195 // Check that our descriptor is allocated and is a scalar integer with
196 // kind != 1 (i.e. with a large enough decimal exponent range).
197 return intVal
&& intVal
->IsAllocated() && intVal
->rank() == 0 &&
198 intVal
->type().IsInteger() && intVal
->type().GetCategoryAndKind() &&
199 intVal
->type().GetCategoryAndKind()->second
!= 1;
202 RT_API_ATTRS
std::int32_t CopyCharsToDescriptor(const Descriptor
&value
,
203 const char *rawValue
, std::size_t rawValueLength
, const Descriptor
*errmsg
,
204 std::size_t offset
) {
206 const std::int64_t toCopy
{std::min(static_cast<std::int64_t>(rawValueLength
),
207 static_cast<std::int64_t>(value
.ElementBytes() - offset
))};
209 return ToErrmsg(errmsg
, StatValueTooShort
);
212 std::memcpy(value
.OffsetElement(offset
), rawValue
, toCopy
);
214 if (static_cast<std::int64_t>(rawValueLength
) > toCopy
) {
215 return ToErrmsg(errmsg
, StatValueTooShort
);
221 RT_API_ATTRS
void StoreIntToDescriptor(
222 const Descriptor
*length
, std::int64_t value
, Terminator
&terminator
) {
223 auto typeCode
{length
->type().GetCategoryAndKind()};
224 int kind
{typeCode
->second
};
225 ApplyIntegerKind
<StoreIntegerAt
, void>(
226 kind
, terminator
, *length
, /* atIndex = */ 0, value
);
229 template <int KIND
> struct FitsInIntegerKind
{
230 RT_API_ATTRS
bool operator()([[maybe_unused
]] std::int64_t value
) {
231 if constexpr (KIND
>= 8) {
236 CppTypeFor
<Fortran::common::TypeCategory::Integer
, KIND
>>::max();
241 // Utility: establishes & allocates the result array for a partial
242 // reduction (i.e., one with DIM=).
243 RT_API_ATTRS
void CreatePartialReductionResult(Descriptor
&result
,
244 const Descriptor
&x
, std::size_t resultElementSize
, int dim
,
245 Terminator
&terminator
, const char *intrinsic
, TypeCode typeCode
) {
247 if (dim
< 1 || dim
> xRank
) {
249 "%s: bad DIM=%d for ARRAY with rank %d", intrinsic
, dim
, xRank
);
251 int zeroBasedDim
{dim
- 1};
252 SubscriptValue resultExtent
[maxRank
];
253 for (int j
{0}; j
< zeroBasedDim
; ++j
) {
254 resultExtent
[j
] = x
.GetDimension(j
).Extent();
256 for (int j
{zeroBasedDim
+ 1}; j
< xRank
; ++j
) {
257 resultExtent
[j
- 1] = x
.GetDimension(j
).Extent();
259 result
.Establish(typeCode
, resultElementSize
, nullptr, xRank
- 1,
260 resultExtent
, CFI_attribute_allocatable
);
261 for (int j
{0}; j
+ 1 < xRank
; ++j
) {
262 result
.GetDimension(j
).SetBounds(1, resultExtent
[j
]);
264 if (int stat
{result
.Allocate()}) {
266 "%s: could not allocate memory for result; STAT=%d", intrinsic
, stat
);
270 RT_OFFLOAD_API_GROUP_END
271 } // namespace Fortran::runtime