xref: /llvm-project/flang/runtime/tools.cpp (revision 3ada883f7c96e099e1a665c091751bff5f16690e)
1651f58bfSDiana Picus //===-- runtime/tools.cpp -------------------------------------------------===//
2352d347aSAlexis Perry //
3352d347aSAlexis Perry // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4352d347aSAlexis Perry // See https://llvm.org/LICENSE.txt for license information.
5352d347aSAlexis Perry // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6352d347aSAlexis Perry //
7352d347aSAlexis Perry //===----------------------------------------------------------------------===//
8352d347aSAlexis Perry 
9352d347aSAlexis Perry #include "tools.h"
10e372e0f9Speter klausler #include "terminator.h"
11231fae90SIsuru Fernando #include <algorithm>
12e372e0f9Speter klausler #include <cstdint>
13824bf908SDiana Picus #include <cstdlib>
14352d347aSAlexis Perry #include <cstring>
15352d347aSAlexis Perry 
16352d347aSAlexis Perry namespace Fortran::runtime {
17352d347aSAlexis Perry 
188b953fddSSlava Zakharin RT_OFFLOAD_API_GROUP_BEGIN
198b953fddSSlava Zakharin 
TrimTrailingSpaces(const char * s,std::size_t n)208b953fddSSlava Zakharin RT_API_ATTRS std::size_t TrimTrailingSpaces(const char *s, std::size_t n) {
21675ad1bcSpeter klausler   while (n > 0 && s[n - 1] == ' ') {
22675ad1bcSpeter klausler     --n;
23675ad1bcSpeter klausler   }
24675ad1bcSpeter klausler   return n;
25675ad1bcSpeter klausler }
26675ad1bcSpeter klausler 
SaveDefaultCharacter(const char * s,std::size_t length,const Terminator & terminator)278b953fddSSlava Zakharin RT_API_ATTRS OwningPtr<char> SaveDefaultCharacter(
2895696d56Speter klausler     const char *s, std::size_t length, const Terminator &terminator) {
29352d347aSAlexis Perry   if (s) {
30352d347aSAlexis Perry     auto *p{static_cast<char *>(AllocateMemoryOrCrash(terminator, length + 1))};
31352d347aSAlexis Perry     std::memcpy(p, s, length);
32352d347aSAlexis Perry     p[length] = '\0';
33352d347aSAlexis Perry     return OwningPtr<char>{p};
34352d347aSAlexis Perry   } else {
35352d347aSAlexis Perry     return OwningPtr<char>{};
36352d347aSAlexis Perry   }
37352d347aSAlexis Perry }
38352d347aSAlexis Perry 
CaseInsensitiveMatch(const char * value,std::size_t length,const char * possibility)398b953fddSSlava Zakharin static RT_API_ATTRS bool CaseInsensitiveMatch(
40352d347aSAlexis Perry     const char *value, std::size_t length, const char *possibility) {
413b635714Speter klausler   for (; length-- > 0; ++possibility) {
423b635714Speter klausler     char ch{*value++};
43352d347aSAlexis Perry     if (ch >= 'a' && ch <= 'z') {
44352d347aSAlexis Perry       ch += 'A' - 'a';
45352d347aSAlexis Perry     }
463b635714Speter klausler     if (*possibility != ch) {
473b635714Speter klausler       if (*possibility != '\0' || ch != ' ') {
48352d347aSAlexis Perry         return false;
49352d347aSAlexis Perry       }
503b635714Speter klausler       // Ignore trailing blanks (12.5.6.2 p1)
513b635714Speter klausler       while (length-- > 0) {
523b635714Speter klausler         if (*value++ != ' ') {
533b635714Speter klausler           return false;
543b635714Speter klausler         }
553b635714Speter klausler       }
563b635714Speter klausler       return true;
573b635714Speter klausler     }
58352d347aSAlexis Perry   }
59352d347aSAlexis Perry   return *possibility == '\0';
60352d347aSAlexis Perry }
61352d347aSAlexis Perry 
IdentifyValue(const char * value,std::size_t length,const char * possibilities[])628b953fddSSlava Zakharin RT_API_ATTRS int IdentifyValue(
63352d347aSAlexis Perry     const char *value, std::size_t length, const char *possibilities[]) {
64352d347aSAlexis Perry   if (value) {
65352d347aSAlexis Perry     for (int j{0}; possibilities[j]; ++j) {
66352d347aSAlexis Perry       if (CaseInsensitiveMatch(value, length, possibilities[j])) {
67352d347aSAlexis Perry         return j;
68352d347aSAlexis Perry       }
69352d347aSAlexis Perry     }
70352d347aSAlexis Perry   }
71352d347aSAlexis Perry   return -1;
72352d347aSAlexis Perry }
733b635714Speter klausler 
ToFortranDefaultCharacter(char * to,std::size_t toLength,const char * from)748b953fddSSlava Zakharin RT_API_ATTRS void ToFortranDefaultCharacter(
753b635714Speter klausler     char *to, std::size_t toLength, const char *from) {
768b953fddSSlava Zakharin   std::size_t len{Fortran::runtime::strlen(from)};
773b635714Speter klausler   if (len < toLength) {
7843fadefbSpeter klausler     std::memcpy(to, from, len);
793b635714Speter klausler     std::memset(to + len, ' ', toLength - len);
8043fadefbSpeter klausler   } else {
8143fadefbSpeter klausler     std::memcpy(to, from, toLength);
823b635714Speter klausler   }
833b635714Speter klausler }
843b635714Speter klausler 
CheckConformability(const Descriptor & to,const Descriptor & x,Terminator & terminator,const char * funcName,const char * toName,const char * xName)858b953fddSSlava Zakharin RT_API_ATTRS void CheckConformability(const Descriptor &to, const Descriptor &x,
86e372e0f9Speter klausler     Terminator &terminator, const char *funcName, const char *toName,
87e372e0f9Speter klausler     const char *xName) {
88e372e0f9Speter klausler   if (x.rank() == 0) {
89e372e0f9Speter klausler     return; // scalar conforms with anything
90e372e0f9Speter klausler   }
91e372e0f9Speter klausler   int rank{to.rank()};
92e372e0f9Speter klausler   if (x.rank() != rank) {
93e372e0f9Speter klausler     terminator.Crash(
94e372e0f9Speter klausler         "Incompatible array arguments to %s: %s has rank %d but %s has rank %d",
95e372e0f9Speter klausler         funcName, toName, rank, xName, x.rank());
96e372e0f9Speter klausler   } else {
97e372e0f9Speter klausler     for (int j{0}; j < rank; ++j) {
98e372e0f9Speter klausler       auto toExtent{static_cast<std::int64_t>(to.GetDimension(j).Extent())};
99e372e0f9Speter klausler       auto xExtent{static_cast<std::int64_t>(x.GetDimension(j).Extent())};
100e372e0f9Speter klausler       if (xExtent != toExtent) {
101e372e0f9Speter klausler         terminator.Crash("Incompatible array arguments to %s: dimension %d of "
102e372e0f9Speter klausler                          "%s has extent %" PRId64 " but %s has extent %" PRId64,
103b910cf98SJean Perier             funcName, j + 1, toName, toExtent, xName, xExtent);
104e372e0f9Speter klausler       }
105e372e0f9Speter klausler     }
106e372e0f9Speter klausler   }
107e372e0f9Speter klausler }
108e372e0f9Speter klausler 
CheckIntegerKind(Terminator & terminator,int kind,const char * intrinsic)1098b953fddSSlava Zakharin RT_API_ATTRS void CheckIntegerKind(
1108b953fddSSlava Zakharin     Terminator &terminator, int kind, const char *intrinsic) {
111e372e0f9Speter klausler   if (kind < 1 || kind > 16 || (kind & (kind - 1)) != 0) {
11204b18530SPete Steinfeld     terminator.Crash("not yet implemented: INTEGER(KIND=%d) in %s intrinsic",
11304b18530SPete Steinfeld         intrinsic, kind);
114e372e0f9Speter klausler   }
115e372e0f9Speter klausler }
116b21c24c3SPeter Klausler 
ShallowCopyDiscontiguousToDiscontiguous(const Descriptor & to,const Descriptor & from)1178b953fddSSlava Zakharin RT_API_ATTRS void ShallowCopyDiscontiguousToDiscontiguous(
118b21c24c3SPeter Klausler     const Descriptor &to, const Descriptor &from) {
119b21c24c3SPeter Klausler   SubscriptValue toAt[maxRank], fromAt[maxRank];
120b21c24c3SPeter Klausler   to.GetLowerBounds(toAt);
121b21c24c3SPeter Klausler   from.GetLowerBounds(fromAt);
122b21c24c3SPeter Klausler   std::size_t elementBytes{to.ElementBytes()};
123b21c24c3SPeter Klausler   for (std::size_t n{to.Elements()}; n-- > 0;
124b21c24c3SPeter Klausler        to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
125b21c24c3SPeter Klausler     std::memcpy(
126b21c24c3SPeter Klausler         to.Element<char>(toAt), from.Element<char>(fromAt), elementBytes);
127b21c24c3SPeter Klausler   }
128b21c24c3SPeter Klausler }
129b21c24c3SPeter Klausler 
ShallowCopyDiscontiguousToContiguous(const Descriptor & to,const Descriptor & from)1308b953fddSSlava Zakharin RT_API_ATTRS void ShallowCopyDiscontiguousToContiguous(
131b21c24c3SPeter Klausler     const Descriptor &to, const Descriptor &from) {
132b21c24c3SPeter Klausler   char *toAt{to.OffsetElement()};
133b21c24c3SPeter Klausler   SubscriptValue fromAt[maxRank];
134b21c24c3SPeter Klausler   from.GetLowerBounds(fromAt);
135b21c24c3SPeter Klausler   std::size_t elementBytes{to.ElementBytes()};
136b21c24c3SPeter Klausler   for (std::size_t n{to.Elements()}; n-- > 0;
137b21c24c3SPeter Klausler        toAt += elementBytes, from.IncrementSubscripts(fromAt)) {
138b21c24c3SPeter Klausler     std::memcpy(toAt, from.Element<char>(fromAt), elementBytes);
139b21c24c3SPeter Klausler   }
140b21c24c3SPeter Klausler }
141b21c24c3SPeter Klausler 
ShallowCopyContiguousToDiscontiguous(const Descriptor & to,const Descriptor & from)1428b953fddSSlava Zakharin RT_API_ATTRS void ShallowCopyContiguousToDiscontiguous(
143b21c24c3SPeter Klausler     const Descriptor &to, const Descriptor &from) {
144b21c24c3SPeter Klausler   SubscriptValue toAt[maxRank];
145b21c24c3SPeter Klausler   to.GetLowerBounds(toAt);
146b21c24c3SPeter Klausler   char *fromAt{from.OffsetElement()};
147b21c24c3SPeter Klausler   std::size_t elementBytes{to.ElementBytes()};
148b21c24c3SPeter Klausler   for (std::size_t n{to.Elements()}; n-- > 0;
149b21c24c3SPeter Klausler        to.IncrementSubscripts(toAt), fromAt += elementBytes) {
150b21c24c3SPeter Klausler     std::memcpy(to.Element<char>(toAt), fromAt, elementBytes);
151b21c24c3SPeter Klausler   }
152b21c24c3SPeter Klausler }
153b21c24c3SPeter Klausler 
ShallowCopy(const Descriptor & to,const Descriptor & from,bool toIsContiguous,bool fromIsContiguous)1548b953fddSSlava Zakharin RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
155b21c24c3SPeter Klausler     bool toIsContiguous, bool fromIsContiguous) {
156b21c24c3SPeter Klausler   if (toIsContiguous) {
157b21c24c3SPeter Klausler     if (fromIsContiguous) {
158b21c24c3SPeter Klausler       std::memcpy(to.OffsetElement(), from.OffsetElement(),
159b21c24c3SPeter Klausler           to.Elements() * to.ElementBytes());
160b21c24c3SPeter Klausler     } else {
161b21c24c3SPeter Klausler       ShallowCopyDiscontiguousToContiguous(to, from);
162b21c24c3SPeter Klausler     }
163b21c24c3SPeter Klausler   } else {
164b21c24c3SPeter Klausler     if (fromIsContiguous) {
165b21c24c3SPeter Klausler       ShallowCopyContiguousToDiscontiguous(to, from);
166b21c24c3SPeter Klausler     } else {
167b21c24c3SPeter Klausler       ShallowCopyDiscontiguousToDiscontiguous(to, from);
168b21c24c3SPeter Klausler     }
169b21c24c3SPeter Klausler   }
170b21c24c3SPeter Klausler }
171b21c24c3SPeter Klausler 
ShallowCopy(const Descriptor & to,const Descriptor & from)1728b953fddSSlava Zakharin RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from) {
173b21c24c3SPeter Klausler   ShallowCopy(to, from, to.IsContiguous(), from.IsContiguous());
174b21c24c3SPeter Klausler }
1758b953fddSSlava Zakharin 
EnsureNullTerminated(char * str,std::size_t length,Terminator & terminator)1767dd4d28eSYi Wu RT_API_ATTRS char *EnsureNullTerminated(
1777dd4d28eSYi Wu     char *str, std::size_t length, Terminator &terminator) {
178f4e90e3fSSlava Zakharin   if (runtime::memchr(str, '\0', length) == nullptr) {
179e2b896aaSYi Wu     char *newCmd{(char *)AllocateMemoryOrCrash(terminator, length + 1)};
180e2b896aaSYi Wu     std::memcpy(newCmd, str, length);
181e2b896aaSYi Wu     newCmd[length] = '\0';
182e2b896aaSYi Wu     return newCmd;
183e2b896aaSYi Wu   } else {
184e2b896aaSYi Wu     return str;
185e2b896aaSYi Wu   }
186e2b896aaSYi Wu }
187e2b896aaSYi Wu 
IsValidCharDescriptor(const Descriptor * value)188e2b896aaSYi Wu RT_API_ATTRS bool IsValidCharDescriptor(const Descriptor *value) {
189e2b896aaSYi Wu   return value && value->IsAllocated() &&
190e2b896aaSYi Wu       value->type() == TypeCode(TypeCategory::Character, 1) &&
191e2b896aaSYi Wu       value->rank() == 0;
192e2b896aaSYi Wu }
193e2b896aaSYi Wu 
IsValidIntDescriptor(const Descriptor * intVal)194e2b896aaSYi Wu RT_API_ATTRS bool IsValidIntDescriptor(const Descriptor *intVal) {
195e2b896aaSYi Wu   // Check that our descriptor is allocated and is a scalar integer with
196e2b896aaSYi Wu   // kind != 1 (i.e. with a large enough decimal exponent range).
197e2b896aaSYi Wu   return intVal && intVal->IsAllocated() && intVal->rank() == 0 &&
198e2b896aaSYi Wu       intVal->type().IsInteger() && intVal->type().GetCategoryAndKind() &&
199e2b896aaSYi Wu       intVal->type().GetCategoryAndKind()->second != 1;
200e2b896aaSYi Wu }
201e2b896aaSYi Wu 
CopyCharsToDescriptor(const Descriptor & value,const char * rawValue,std::size_t rawValueLength,const Descriptor * errmsg,std::size_t offset)202e2b896aaSYi Wu RT_API_ATTRS std::int32_t CopyCharsToDescriptor(const Descriptor &value,
203e2b896aaSYi Wu     const char *rawValue, std::size_t rawValueLength, const Descriptor *errmsg,
204e2b896aaSYi Wu     std::size_t offset) {
205e2b896aaSYi Wu 
206e2b896aaSYi Wu   const std::int64_t toCopy{std::min(static_cast<std::int64_t>(rawValueLength),
207e2b896aaSYi Wu       static_cast<std::int64_t>(value.ElementBytes() - offset))};
208e2b896aaSYi Wu   if (toCopy < 0) {
209e2b896aaSYi Wu     return ToErrmsg(errmsg, StatValueTooShort);
210e2b896aaSYi Wu   }
211e2b896aaSYi Wu 
212e2b896aaSYi Wu   std::memcpy(value.OffsetElement(offset), rawValue, toCopy);
213e2b896aaSYi Wu 
214e2b896aaSYi Wu   if (static_cast<std::int64_t>(rawValueLength) > toCopy) {
215e2b896aaSYi Wu     return ToErrmsg(errmsg, StatValueTooShort);
216e2b896aaSYi Wu   }
217e2b896aaSYi Wu 
218e2b896aaSYi Wu   return StatOk;
219e2b896aaSYi Wu }
220e2b896aaSYi Wu 
StoreIntToDescriptor(const Descriptor * length,std::int64_t value,Terminator & terminator)221e2b896aaSYi Wu RT_API_ATTRS void StoreIntToDescriptor(
222e2b896aaSYi Wu     const Descriptor *length, std::int64_t value, Terminator &terminator) {
223e2b896aaSYi Wu   auto typeCode{length->type().GetCategoryAndKind()};
224e2b896aaSYi Wu   int kind{typeCode->second};
225e2b896aaSYi Wu   ApplyIntegerKind<StoreIntegerAt, void>(
226e2b896aaSYi Wu       kind, terminator, *length, /* atIndex = */ 0, value);
227e2b896aaSYi Wu }
228e2b896aaSYi Wu 
229e2b896aaSYi Wu template <int KIND> struct FitsInIntegerKind {
operator ()Fortran::runtime::FitsInIntegerKind230e2b896aaSYi Wu   RT_API_ATTRS bool operator()([[maybe_unused]] std::int64_t value) {
231e2b896aaSYi Wu     if constexpr (KIND >= 8) {
232e2b896aaSYi Wu       return true;
233e2b896aaSYi Wu     } else {
234e2b896aaSYi Wu       return value <=
235e2b896aaSYi Wu           std::numeric_limits<
236e2b896aaSYi Wu               CppTypeFor<Fortran::common::TypeCategory::Integer, KIND>>::max();
237e2b896aaSYi Wu     }
238e2b896aaSYi Wu   }
239e2b896aaSYi Wu };
240e2b896aaSYi Wu 
241*3ada883fSPeter Klausler // Utility: establishes & allocates the result array for a partial
242*3ada883fSPeter Klausler // reduction (i.e., one with DIM=).
CreatePartialReductionResult(Descriptor & result,const Descriptor & x,std::size_t resultElementSize,int dim,Terminator & terminator,const char * intrinsic,TypeCode typeCode)243*3ada883fSPeter Klausler RT_API_ATTRS void CreatePartialReductionResult(Descriptor &result,
244*3ada883fSPeter Klausler     const Descriptor &x, std::size_t resultElementSize, int dim,
245*3ada883fSPeter Klausler     Terminator &terminator, const char *intrinsic, TypeCode typeCode) {
246*3ada883fSPeter Klausler   int xRank{x.rank()};
247*3ada883fSPeter Klausler   if (dim < 1 || dim > xRank) {
248*3ada883fSPeter Klausler     terminator.Crash(
249*3ada883fSPeter Klausler         "%s: bad DIM=%d for ARRAY with rank %d", intrinsic, dim, xRank);
250*3ada883fSPeter Klausler   }
251*3ada883fSPeter Klausler   int zeroBasedDim{dim - 1};
252*3ada883fSPeter Klausler   SubscriptValue resultExtent[maxRank];
253*3ada883fSPeter Klausler   for (int j{0}; j < zeroBasedDim; ++j) {
254*3ada883fSPeter Klausler     resultExtent[j] = x.GetDimension(j).Extent();
255*3ada883fSPeter Klausler   }
256*3ada883fSPeter Klausler   for (int j{zeroBasedDim + 1}; j < xRank; ++j) {
257*3ada883fSPeter Klausler     resultExtent[j - 1] = x.GetDimension(j).Extent();
258*3ada883fSPeter Klausler   }
259*3ada883fSPeter Klausler   result.Establish(typeCode, resultElementSize, nullptr, xRank - 1,
260*3ada883fSPeter Klausler       resultExtent, CFI_attribute_allocatable);
261*3ada883fSPeter Klausler   for (int j{0}; j + 1 < xRank; ++j) {
262*3ada883fSPeter Klausler     result.GetDimension(j).SetBounds(1, resultExtent[j]);
263*3ada883fSPeter Klausler   }
264*3ada883fSPeter Klausler   if (int stat{result.Allocate()}) {
265*3ada883fSPeter Klausler     terminator.Crash(
266*3ada883fSPeter Klausler         "%s: could not allocate memory for result; STAT=%d", intrinsic, stat);
267*3ada883fSPeter Klausler   }
268*3ada883fSPeter Klausler }
269*3ada883fSPeter Klausler 
2708b953fddSSlava Zakharin RT_OFFLOAD_API_GROUP_END
2711f879005STim Keith } // namespace Fortran::runtime
272