1 //===-- runtime/character.cpp ---------------------------------------------===//
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 #include "flang/Runtime/character.h"
10 #include "terminator.h"
11 #include "tools.h"
12 #include "flang/Common/bit-population-count.h"
13 #include "flang/Common/uint128.h"
14 #include "flang/Runtime/cpp-type.h"
15 #include "flang/Runtime/descriptor.h"
16 #include <algorithm>
17 #include <cstring>
18
19 namespace Fortran::runtime {
20
21 template <typename CHAR>
CompareToBlankPadding(const CHAR * x,std::size_t chars)22 inline int CompareToBlankPadding(const CHAR *x, std::size_t chars) {
23 for (; chars-- > 0; ++x) {
24 if (*x < ' ') {
25 return -1;
26 }
27 if (*x > ' ') {
28 return 1;
29 }
30 }
31 return 0;
32 }
33
34 template <typename CHAR>
CharacterScalarCompare(const CHAR * x,const CHAR * y,std::size_t xChars,std::size_t yChars)35 int CharacterScalarCompare(
36 const CHAR *x, const CHAR *y, std::size_t xChars, std::size_t yChars) {
37 auto minChars{std::min(xChars, yChars)};
38 if constexpr (sizeof(CHAR) == 1) {
39 // don't use for kind=2 or =4, that would fail on little-endian machines
40 int cmp{std::memcmp(x, y, minChars)};
41 if (cmp < 0) {
42 return -1;
43 }
44 if (cmp > 0) {
45 return 1;
46 }
47 if (xChars == yChars) {
48 return 0;
49 }
50 x += minChars;
51 y += minChars;
52 } else {
53 for (std::size_t n{minChars}; n-- > 0; ++x, ++y) {
54 if (*x < *y) {
55 return -1;
56 }
57 if (*x > *y) {
58 return 1;
59 }
60 }
61 }
62 if (int cmp{CompareToBlankPadding(x, xChars - minChars)}) {
63 return cmp;
64 }
65 return -CompareToBlankPadding(y, yChars - minChars);
66 }
67
68 template int CharacterScalarCompare<char>(
69 const char *x, const char *y, std::size_t xChars, std::size_t yChars);
70 template int CharacterScalarCompare<char16_t>(const char16_t *x,
71 const char16_t *y, std::size_t xChars, std::size_t yChars);
72 template int CharacterScalarCompare<char32_t>(const char32_t *x,
73 const char32_t *y, std::size_t xChars, std::size_t yChars);
74
75 // Shift count to use when converting between character lengths
76 // and byte counts.
77 template <typename CHAR>
78 constexpr int shift{common::TrailingZeroBitCount(sizeof(CHAR))};
79
80 template <typename CHAR>
Compare(Descriptor & result,const Descriptor & x,const Descriptor & y,const Terminator & terminator)81 static void Compare(Descriptor &result, const Descriptor &x,
82 const Descriptor &y, const Terminator &terminator) {
83 RUNTIME_CHECK(
84 terminator, x.rank() == y.rank() || x.rank() == 0 || y.rank() == 0);
85 int rank{std::max(x.rank(), y.rank())};
86 SubscriptValue ub[maxRank], xAt[maxRank], yAt[maxRank];
87 SubscriptValue elements{1};
88 for (int j{0}; j < rank; ++j) {
89 if (x.rank() > 0 && y.rank() > 0) {
90 SubscriptValue xUB{x.GetDimension(j).Extent()};
91 SubscriptValue yUB{y.GetDimension(j).Extent()};
92 if (xUB != yUB) {
93 terminator.Crash("Character array comparison: operands are not "
94 "conforming on dimension %d (%jd != %jd)",
95 j + 1, static_cast<std::intmax_t>(xUB),
96 static_cast<std::intmax_t>(yUB));
97 }
98 ub[j] = xUB;
99 } else {
100 ub[j] = (x.rank() ? x : y).GetDimension(j).Extent();
101 }
102 elements *= ub[j];
103 }
104 x.GetLowerBounds(xAt);
105 y.GetLowerBounds(yAt);
106 result.Establish(
107 TypeCategory::Logical, 1, nullptr, rank, ub, CFI_attribute_allocatable);
108 for (int j{0}; j < rank; ++j) {
109 result.GetDimension(j).SetBounds(1, ub[j]);
110 }
111 if (result.Allocate() != CFI_SUCCESS) {
112 terminator.Crash("Compare: could not allocate storage for result");
113 }
114 std::size_t xChars{x.ElementBytes() >> shift<CHAR>};
115 std::size_t yChars{y.ElementBytes() >> shift<char>};
116 for (SubscriptValue resultAt{0}; elements-- > 0;
117 ++resultAt, x.IncrementSubscripts(xAt), y.IncrementSubscripts(yAt)) {
118 *result.OffsetElement<char>(resultAt) = CharacterScalarCompare<CHAR>(
119 x.Element<CHAR>(xAt), y.Element<CHAR>(yAt), xChars, yChars);
120 }
121 }
122
123 template <typename CHAR, bool ADJUSTR>
Adjust(CHAR * to,const CHAR * from,std::size_t chars)124 static void Adjust(CHAR *to, const CHAR *from, std::size_t chars) {
125 if constexpr (ADJUSTR) {
126 std::size_t j{chars}, k{chars};
127 for (; k > 0 && from[k - 1] == ' '; --k) {
128 }
129 while (k > 0) {
130 to[--j] = from[--k];
131 }
132 while (j > 0) {
133 to[--j] = ' ';
134 }
135 } else { // ADJUSTL
136 std::size_t j{0}, k{0};
137 for (; k < chars && from[k] == ' '; ++k) {
138 }
139 while (k < chars) {
140 to[j++] = from[k++];
141 }
142 while (j < chars) {
143 to[j++] = ' ';
144 }
145 }
146 }
147
148 template <typename CHAR, bool ADJUSTR>
AdjustLRHelper(Descriptor & result,const Descriptor & string,const Terminator & terminator)149 static void AdjustLRHelper(Descriptor &result, const Descriptor &string,
150 const Terminator &terminator) {
151 int rank{string.rank()};
152 SubscriptValue ub[maxRank], stringAt[maxRank];
153 SubscriptValue elements{1};
154 for (int j{0}; j < rank; ++j) {
155 ub[j] = string.GetDimension(j).Extent();
156 elements *= ub[j];
157 stringAt[j] = 1;
158 }
159 string.GetLowerBounds(stringAt);
160 std::size_t elementBytes{string.ElementBytes()};
161 result.Establish(string.type(), elementBytes, nullptr, rank, ub,
162 CFI_attribute_allocatable);
163 for (int j{0}; j < rank; ++j) {
164 result.GetDimension(j).SetBounds(1, ub[j]);
165 }
166 if (result.Allocate() != CFI_SUCCESS) {
167 terminator.Crash("ADJUSTL/R: could not allocate storage for result");
168 }
169 for (SubscriptValue resultAt{0}; elements-- > 0;
170 resultAt += elementBytes, string.IncrementSubscripts(stringAt)) {
171 Adjust<CHAR, ADJUSTR>(result.OffsetElement<CHAR>(resultAt),
172 string.Element<const CHAR>(stringAt), elementBytes >> shift<CHAR>);
173 }
174 }
175
176 template <bool ADJUSTR>
AdjustLR(Descriptor & result,const Descriptor & string,const char * sourceFile,int sourceLine)177 void AdjustLR(Descriptor &result, const Descriptor &string,
178 const char *sourceFile, int sourceLine) {
179 Terminator terminator{sourceFile, sourceLine};
180 switch (string.raw().type) {
181 case CFI_type_char:
182 AdjustLRHelper<char, ADJUSTR>(result, string, terminator);
183 break;
184 case CFI_type_char16_t:
185 AdjustLRHelper<char16_t, ADJUSTR>(result, string, terminator);
186 break;
187 case CFI_type_char32_t:
188 AdjustLRHelper<char32_t, ADJUSTR>(result, string, terminator);
189 break;
190 default:
191 terminator.Crash("ADJUSTL/R: bad string type code %d",
192 static_cast<int>(string.raw().type));
193 }
194 }
195
196 template <typename CHAR>
LenTrim(const CHAR * x,std::size_t chars)197 inline std::size_t LenTrim(const CHAR *x, std::size_t chars) {
198 while (chars > 0 && x[chars - 1] == ' ') {
199 --chars;
200 }
201 return chars;
202 }
203
204 template <typename INT, typename CHAR>
LenTrim(Descriptor & result,const Descriptor & string,const Terminator & terminator)205 static void LenTrim(Descriptor &result, const Descriptor &string,
206 const Terminator &terminator) {
207 int rank{string.rank()};
208 SubscriptValue ub[maxRank], stringAt[maxRank];
209 SubscriptValue elements{1};
210 for (int j{0}; j < rank; ++j) {
211 ub[j] = string.GetDimension(j).Extent();
212 elements *= ub[j];
213 }
214 string.GetLowerBounds(stringAt);
215 result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub,
216 CFI_attribute_allocatable);
217 for (int j{0}; j < rank; ++j) {
218 result.GetDimension(j).SetBounds(1, ub[j]);
219 }
220 if (result.Allocate() != CFI_SUCCESS) {
221 terminator.Crash("LEN_TRIM: could not allocate storage for result");
222 }
223 std::size_t stringElementChars{string.ElementBytes() >> shift<CHAR>};
224 for (SubscriptValue resultAt{0}; elements-- > 0;
225 resultAt += sizeof(INT), string.IncrementSubscripts(stringAt)) {
226 *result.OffsetElement<INT>(resultAt) =
227 LenTrim(string.Element<CHAR>(stringAt), stringElementChars);
228 }
229 }
230
231 template <typename CHAR>
LenTrimKind(Descriptor & result,const Descriptor & string,int kind,const Terminator & terminator)232 static void LenTrimKind(Descriptor &result, const Descriptor &string, int kind,
233 const Terminator &terminator) {
234 switch (kind) {
235 case 1:
236 LenTrim<CppTypeFor<TypeCategory::Integer, 1>, CHAR>(
237 result, string, terminator);
238 break;
239 case 2:
240 LenTrim<CppTypeFor<TypeCategory::Integer, 2>, CHAR>(
241 result, string, terminator);
242 break;
243 case 4:
244 LenTrim<CppTypeFor<TypeCategory::Integer, 4>, CHAR>(
245 result, string, terminator);
246 break;
247 case 8:
248 LenTrim<CppTypeFor<TypeCategory::Integer, 8>, CHAR>(
249 result, string, terminator);
250 break;
251 case 16:
252 LenTrim<CppTypeFor<TypeCategory::Integer, 16>, CHAR>(
253 result, string, terminator);
254 break;
255 default:
256 terminator.Crash("not yet implemented: LEN_TRIM: KIND=%d", kind);
257 }
258 }
259
260 // INDEX implementation
261 template <typename CHAR>
Index(const CHAR * x,std::size_t xLen,const CHAR * want,std::size_t wantLen,bool back)262 inline std::size_t Index(const CHAR *x, std::size_t xLen, const CHAR *want,
263 std::size_t wantLen, bool back) {
264 if (xLen < wantLen) {
265 return 0;
266 }
267 if (xLen == 0) {
268 return 1; // wantLen is also 0, so trivial match
269 }
270 if (back) {
271 // If wantLen==0, returns xLen + 1 per standard (and all other compilers)
272 std::size_t at{xLen - wantLen + 1};
273 for (; at > 0; --at) {
274 std::size_t j{1};
275 for (; j <= wantLen; ++j) {
276 if (x[at + j - 2] != want[j - 1]) {
277 break;
278 }
279 }
280 if (j > wantLen) {
281 return at;
282 }
283 }
284 return 0;
285 }
286 // Non-trivial forward substring search: use a simplified form of
287 // Boyer-Moore substring searching.
288 for (std::size_t at{1}; at + wantLen - 1 <= xLen;) {
289 // Compare x(at:at+wantLen-1) with want(1:wantLen).
290 // The comparison proceeds from the ends of the substrings forward
291 // so that we can skip ahead by multiple positions on a miss.
292 std::size_t j{wantLen};
293 CHAR ch;
294 for (; j > 0; --j) {
295 ch = x[at + j - 2];
296 if (ch != want[j - 1]) {
297 break;
298 }
299 }
300 if (j == 0) {
301 return at; // found a match
302 }
303 // Suppose we have at==2:
304 // "THAT FORTRAN THAT I RAN" <- the string (x) in which we search
305 // "THAT I RAN" <- the string (want) for which we search
306 // ^------------------ j==7, ch=='T'
307 // We can shift ahead 3 positions to at==5 to align the 'T's:
308 // "THAT FORTRAN THAT I RAN"
309 // "THAT I RAN"
310 std::size_t shift{1};
311 for (; shift < j; ++shift) {
312 if (want[j - shift - 1] == ch) {
313 break;
314 }
315 }
316 at += shift;
317 }
318 return 0;
319 }
320
321 // SCAN and VERIFY implementation help. These intrinsic functions
322 // do pretty much the same thing, so they're templatized with a
323 // distinguishing flag.
324
325 enum class CharFunc { Index, Scan, Verify };
326
327 template <typename CHAR, CharFunc FUNC>
ScanVerify(const CHAR * x,std::size_t xLen,const CHAR * set,std::size_t setLen,bool back)328 inline std::size_t ScanVerify(const CHAR *x, std::size_t xLen, const CHAR *set,
329 std::size_t setLen, bool back) {
330 std::size_t at{back ? xLen : 1};
331 int increment{back ? -1 : 1};
332 for (; xLen-- > 0; at += increment) {
333 CHAR ch{x[at - 1]};
334 bool inSet{false};
335 // TODO: If set is sorted, could use binary search
336 for (std::size_t j{0}; j < setLen; ++j) {
337 if (set[j] == ch) {
338 inSet = true;
339 break;
340 }
341 }
342 if (inSet != (FUNC == CharFunc::Verify)) {
343 return at;
344 }
345 }
346 return 0;
347 }
348
349 // Specialization for one-byte characters
350 template <bool IS_VERIFY = false>
ScanVerify(const char * x,std::size_t xLen,const char * set,std::size_t setLen,bool back)351 inline std::size_t ScanVerify(const char *x, std::size_t xLen, const char *set,
352 std::size_t setLen, bool back) {
353 std::size_t at{back ? xLen : 1};
354 int increment{back ? -1 : 1};
355 if (xLen > 0) {
356 std::uint64_t bitSet[256 / 64]{0};
357 std::uint64_t one{1};
358 for (std::size_t j{0}; j < setLen; ++j) {
359 unsigned setCh{static_cast<unsigned char>(set[j])};
360 bitSet[setCh / 64] |= one << (setCh % 64);
361 }
362 for (; xLen-- > 0; at += increment) {
363 unsigned ch{static_cast<unsigned char>(x[at - 1])};
364 bool inSet{((bitSet[ch / 64] >> (ch % 64)) & 1) != 0};
365 if (inSet != IS_VERIFY) {
366 return at;
367 }
368 }
369 }
370 return 0;
371 }
372
373 template <typename INT, typename CHAR, CharFunc FUNC>
GeneralCharFunc(Descriptor & result,const Descriptor & string,const Descriptor & arg,const Descriptor * back,const Terminator & terminator)374 static void GeneralCharFunc(Descriptor &result, const Descriptor &string,
375 const Descriptor &arg, const Descriptor *back,
376 const Terminator &terminator) {
377 int rank{string.rank() ? string.rank()
378 : arg.rank() ? arg.rank()
379 : back ? back->rank()
380 : 0};
381 SubscriptValue ub[maxRank], stringAt[maxRank], argAt[maxRank],
382 backAt[maxRank];
383 SubscriptValue elements{1};
384 for (int j{0}; j < rank; ++j) {
385 ub[j] = string.rank() ? string.GetDimension(j).Extent()
386 : arg.rank() ? arg.GetDimension(j).Extent()
387 : back ? back->GetDimension(j).Extent()
388 : 1;
389 elements *= ub[j];
390 }
391 string.GetLowerBounds(stringAt);
392 arg.GetLowerBounds(argAt);
393 if (back) {
394 back->GetLowerBounds(backAt);
395 }
396 result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub,
397 CFI_attribute_allocatable);
398 for (int j{0}; j < rank; ++j) {
399 result.GetDimension(j).SetBounds(1, ub[j]);
400 }
401 if (result.Allocate() != CFI_SUCCESS) {
402 terminator.Crash("SCAN/VERIFY: could not allocate storage for result");
403 }
404 std::size_t stringElementChars{string.ElementBytes() >> shift<CHAR>};
405 std::size_t argElementChars{arg.ElementBytes() >> shift<CHAR>};
406 for (SubscriptValue resultAt{0}; elements-- > 0; resultAt += sizeof(INT),
407 string.IncrementSubscripts(stringAt), arg.IncrementSubscripts(argAt),
408 back && back->IncrementSubscripts(backAt)) {
409 if constexpr (FUNC == CharFunc::Index) {
410 *result.OffsetElement<INT>(resultAt) =
411 Index<CHAR>(string.Element<CHAR>(stringAt), stringElementChars,
412 arg.Element<CHAR>(argAt), argElementChars,
413 back && IsLogicalElementTrue(*back, backAt));
414 } else if constexpr (FUNC == CharFunc::Scan) {
415 *result.OffsetElement<INT>(resultAt) =
416 ScanVerify<CHAR, CharFunc::Scan>(string.Element<CHAR>(stringAt),
417 stringElementChars, arg.Element<CHAR>(argAt), argElementChars,
418 back && IsLogicalElementTrue(*back, backAt));
419 } else if constexpr (FUNC == CharFunc::Verify) {
420 *result.OffsetElement<INT>(resultAt) =
421 ScanVerify<CHAR, CharFunc::Verify>(string.Element<CHAR>(stringAt),
422 stringElementChars, arg.Element<CHAR>(argAt), argElementChars,
423 back && IsLogicalElementTrue(*back, backAt));
424 } else {
425 static_assert(FUNC == CharFunc::Index || FUNC == CharFunc::Scan ||
426 FUNC == CharFunc::Verify);
427 }
428 }
429 }
430
431 template <typename CHAR, CharFunc FUNC>
GeneralCharFuncKind(Descriptor & result,const Descriptor & string,const Descriptor & arg,const Descriptor * back,int kind,const Terminator & terminator)432 static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
433 const Descriptor &arg, const Descriptor *back, int kind,
434 const Terminator &terminator) {
435 switch (kind) {
436 case 1:
437 GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 1>, CHAR, FUNC>(
438 result, string, arg, back, terminator);
439 break;
440 case 2:
441 GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 2>, CHAR, FUNC>(
442 result, string, arg, back, terminator);
443 break;
444 case 4:
445 GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 4>, CHAR, FUNC>(
446 result, string, arg, back, terminator);
447 break;
448 case 8:
449 GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 8>, CHAR, FUNC>(
450 result, string, arg, back, terminator);
451 break;
452 case 16:
453 GeneralCharFunc<CppTypeFor<TypeCategory::Integer, 16>, CHAR, FUNC>(
454 result, string, arg, back, terminator);
455 break;
456 default:
457 terminator.Crash("not yet implemented: INDEX/SCAN/VERIFY: KIND=%d", kind);
458 }
459 }
460
461 template <typename TO, typename FROM>
CopyAndPad(TO * to,const FROM * from,std::size_t toChars,std::size_t fromChars)462 static void CopyAndPad(
463 TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
464 if constexpr (sizeof(TO) != sizeof(FROM)) {
465 std::size_t copyChars{std::min(toChars, fromChars)};
466 for (std::size_t j{0}; j < copyChars; ++j) {
467 to[j] = from[j];
468 }
469 for (std::size_t j{copyChars}; j < toChars; ++j) {
470 to[j] = static_cast<TO>(' ');
471 }
472 } else if (toChars <= fromChars) {
473 std::memcpy(to, from, toChars * sizeof(TO));
474 } else {
475 std::memcpy(to, from, fromChars * sizeof(TO));
476 for (std::size_t j{fromChars}; j < toChars; ++j) {
477 to[j] = static_cast<TO>(' ');
478 }
479 }
480 }
481
482 template <typename CHAR, bool ISMIN>
MaxMinHelper(Descriptor & accumulator,const Descriptor & x,const Terminator & terminator)483 static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
484 const Terminator &terminator) {
485 RUNTIME_CHECK(terminator,
486 accumulator.rank() == 0 || x.rank() == 0 ||
487 accumulator.rank() == x.rank());
488 SubscriptValue ub[maxRank], xAt[maxRank];
489 SubscriptValue elements{1};
490 std::size_t accumChars{accumulator.ElementBytes() >> shift<CHAR>};
491 std::size_t xChars{x.ElementBytes() >> shift<CHAR>};
492 std::size_t chars{std::max(accumChars, xChars)};
493 bool reallocate{accumulator.raw().base_addr == nullptr ||
494 accumChars != chars || (accumulator.rank() == 0 && x.rank() > 0)};
495 int rank{std::max(accumulator.rank(), x.rank())};
496 for (int j{0}; j < rank; ++j) {
497 if (x.rank() > 0) {
498 ub[j] = x.GetDimension(j).Extent();
499 if (accumulator.rank() > 0) {
500 SubscriptValue accumExt{accumulator.GetDimension(j).Extent()};
501 if (accumExt != ub[j]) {
502 terminator.Crash("Character MAX/MIN: operands are not "
503 "conforming on dimension %d (%jd != %jd)",
504 j + 1, static_cast<std::intmax_t>(accumExt),
505 static_cast<std::intmax_t>(ub[j]));
506 }
507 }
508 } else {
509 ub[j] = accumulator.GetDimension(j).Extent();
510 }
511 elements *= ub[j];
512 }
513 x.GetLowerBounds(xAt);
514 void *old{nullptr};
515 const CHAR *accumData{accumulator.OffsetElement<CHAR>()};
516 if (reallocate) {
517 old = accumulator.raw().base_addr;
518 accumulator.set_base_addr(nullptr);
519 accumulator.raw().elem_len = chars << shift<CHAR>;
520 for (int j{0}; j < rank; ++j) {
521 accumulator.GetDimension(j).SetBounds(1, ub[j]);
522 }
523 RUNTIME_CHECK(terminator, accumulator.Allocate() == CFI_SUCCESS);
524 }
525 for (CHAR *result{accumulator.OffsetElement<CHAR>()}; elements-- > 0;
526 accumData += accumChars, result += chars, x.IncrementSubscripts(xAt)) {
527 const CHAR *xData{x.Element<CHAR>(xAt)};
528 int cmp{CharacterScalarCompare(accumData, xData, accumChars, xChars)};
529 if constexpr (ISMIN) {
530 cmp = -cmp;
531 }
532 if (cmp < 0) {
533 CopyAndPad(result, xData, chars, xChars);
534 } else if (result != accumData) {
535 CopyAndPad(result, accumData, chars, accumChars);
536 }
537 }
538 FreeMemory(old);
539 }
540
541 template <bool ISMIN>
MaxMin(Descriptor & accumulator,const Descriptor & x,const char * sourceFile,int sourceLine)542 static void MaxMin(Descriptor &accumulator, const Descriptor &x,
543 const char *sourceFile, int sourceLine) {
544 Terminator terminator{sourceFile, sourceLine};
545 RUNTIME_CHECK(terminator, accumulator.raw().type == x.raw().type);
546 switch (accumulator.raw().type) {
547 case CFI_type_char:
548 MaxMinHelper<char, ISMIN>(accumulator, x, terminator);
549 break;
550 case CFI_type_char16_t:
551 MaxMinHelper<char16_t, ISMIN>(accumulator, x, terminator);
552 break;
553 case CFI_type_char32_t:
554 MaxMinHelper<char32_t, ISMIN>(accumulator, x, terminator);
555 break;
556 default:
557 terminator.Crash(
558 "Character MAX/MIN: result does not have a character type");
559 }
560 }
561
562 extern "C" {
563
RTNAME(CharacterConcatenate)564 void RTNAME(CharacterConcatenate)(Descriptor &accumulator,
565 const Descriptor &from, const char *sourceFile, int sourceLine) {
566 Terminator terminator{sourceFile, sourceLine};
567 RUNTIME_CHECK(terminator,
568 accumulator.rank() == 0 || from.rank() == 0 ||
569 accumulator.rank() == from.rank());
570 int rank{std::max(accumulator.rank(), from.rank())};
571 SubscriptValue ub[maxRank], fromAt[maxRank];
572 SubscriptValue elements{1};
573 for (int j{0}; j < rank; ++j) {
574 if (accumulator.rank() > 0 && from.rank() > 0) {
575 ub[j] = accumulator.GetDimension(j).Extent();
576 SubscriptValue fromUB{from.GetDimension(j).Extent()};
577 if (ub[j] != fromUB) {
578 terminator.Crash("Character array concatenation: operands are not "
579 "conforming on dimension %d (%jd != %jd)",
580 j + 1, static_cast<std::intmax_t>(ub[j]),
581 static_cast<std::intmax_t>(fromUB));
582 }
583 } else {
584 ub[j] =
585 (accumulator.rank() ? accumulator : from).GetDimension(j).Extent();
586 }
587 elements *= ub[j];
588 }
589 std::size_t oldBytes{accumulator.ElementBytes()};
590 void *old{accumulator.raw().base_addr};
591 accumulator.set_base_addr(nullptr);
592 std::size_t fromBytes{from.ElementBytes()};
593 accumulator.raw().elem_len += fromBytes;
594 std::size_t newBytes{accumulator.ElementBytes()};
595 for (int j{0}; j < rank; ++j) {
596 accumulator.GetDimension(j).SetBounds(1, ub[j]);
597 }
598 if (accumulator.Allocate() != CFI_SUCCESS) {
599 terminator.Crash(
600 "CharacterConcatenate: could not allocate storage for result");
601 }
602 const char *p{static_cast<const char *>(old)};
603 char *to{static_cast<char *>(accumulator.raw().base_addr)};
604 from.GetLowerBounds(fromAt);
605 for (; elements-- > 0;
606 to += newBytes, p += oldBytes, from.IncrementSubscripts(fromAt)) {
607 std::memcpy(to, p, oldBytes);
608 std::memcpy(to + oldBytes, from.Element<char>(fromAt), fromBytes);
609 }
610 FreeMemory(old);
611 }
612
RTNAME(CharacterConcatenateScalar1)613 void RTNAME(CharacterConcatenateScalar1)(
614 Descriptor &accumulator, const char *from, std::size_t chars) {
615 Terminator terminator{__FILE__, __LINE__};
616 RUNTIME_CHECK(terminator, accumulator.rank() == 0);
617 void *old{accumulator.raw().base_addr};
618 accumulator.set_base_addr(nullptr);
619 std::size_t oldLen{accumulator.ElementBytes()};
620 accumulator.raw().elem_len += chars;
621 RUNTIME_CHECK(terminator, accumulator.Allocate() == CFI_SUCCESS);
622 std::memcpy(accumulator.OffsetElement<char>(oldLen), from, chars);
623 FreeMemory(old);
624 }
625
RTNAME(CharacterAssign)626 void RTNAME(CharacterAssign)(Descriptor &lhs, const Descriptor &rhs,
627 const char *sourceFile, int sourceLine) {
628 Terminator terminator{sourceFile, sourceLine};
629 int rank{lhs.rank()};
630 RUNTIME_CHECK(terminator, rhs.rank() == 0 || rhs.rank() == rank);
631 SubscriptValue ub[maxRank], lhsAt[maxRank], rhsAt[maxRank];
632 SubscriptValue elements{1};
633 std::size_t lhsBytes{lhs.ElementBytes()};
634 std::size_t rhsBytes{rhs.ElementBytes()};
635 bool reallocate{lhs.IsAllocatable() &&
636 (lhs.raw().base_addr == nullptr || lhsBytes != rhsBytes)};
637 for (int j{0}; j < rank; ++j) {
638 lhsAt[j] = lhs.GetDimension(j).LowerBound();
639 if (rhs.rank() > 0) {
640 SubscriptValue lhsExt{lhs.GetDimension(j).Extent()};
641 SubscriptValue rhsExt{rhs.GetDimension(j).Extent()};
642 ub[j] = lhsAt[j] + rhsExt - 1;
643 if (lhsExt != rhsExt) {
644 if (lhs.IsAllocatable()) {
645 reallocate = true;
646 } else {
647 terminator.Crash("Character array assignment: operands are not "
648 "conforming on dimension %d (%jd != %jd)",
649 j + 1, static_cast<std::intmax_t>(lhsExt),
650 static_cast<std::intmax_t>(rhsExt));
651 }
652 }
653 rhsAt[j] = rhs.GetDimension(j).LowerBound();
654 } else {
655 ub[j] = lhs.GetDimension(j).UpperBound();
656 }
657 elements *= ub[j] - lhsAt[j] + 1;
658 }
659 void *old{nullptr};
660 if (reallocate) {
661 old = lhs.raw().base_addr;
662 lhs.set_base_addr(nullptr);
663 lhs.raw().elem_len = lhsBytes = rhsBytes;
664 if (rhs.rank() > 0) {
665 // When the RHS is not scalar, the LHS acquires its bounds.
666 for (int j{0}; j < rank; ++j) {
667 lhsAt[j] = rhsAt[j];
668 ub[j] = rhs.GetDimension(j).UpperBound();
669 lhs.GetDimension(j).SetBounds(lhsAt[j], ub[j]);
670 }
671 }
672 RUNTIME_CHECK(terminator, lhs.Allocate() == CFI_SUCCESS);
673 }
674 switch (lhs.raw().type) {
675 case CFI_type_char:
676 switch (rhs.raw().type) {
677 case CFI_type_char:
678 for (; elements-- > 0;
679 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
680 CopyAndPad(lhs.Element<char>(lhsAt), rhs.Element<char>(rhsAt), lhsBytes,
681 rhsBytes);
682 }
683 break;
684 case CFI_type_char16_t:
685 for (; elements-- > 0;
686 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
687 CopyAndPad(lhs.Element<char>(lhsAt), rhs.Element<char16_t>(rhsAt),
688 lhsBytes, rhsBytes >> 1);
689 }
690 break;
691 case CFI_type_char32_t:
692 for (; elements-- > 0;
693 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
694 CopyAndPad(lhs.Element<char>(lhsAt), rhs.Element<char32_t>(rhsAt),
695 lhsBytes, rhsBytes >> 2);
696 }
697 break;
698 default:
699 terminator.Crash(
700 "RHS of character assignment does not have a character type");
701 }
702 break;
703 case CFI_type_char16_t:
704 switch (rhs.raw().type) {
705 case CFI_type_char:
706 for (; elements-- > 0;
707 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
708 CopyAndPad(lhs.Element<char16_t>(lhsAt), rhs.Element<char>(rhsAt),
709 lhsBytes >> 1, rhsBytes);
710 }
711 break;
712 case CFI_type_char16_t:
713 for (; elements-- > 0;
714 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
715 CopyAndPad(lhs.Element<char16_t>(lhsAt), rhs.Element<char16_t>(rhsAt),
716 lhsBytes >> 1, rhsBytes >> 1);
717 }
718 break;
719 case CFI_type_char32_t:
720 for (; elements-- > 0;
721 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
722 CopyAndPad(lhs.Element<char16_t>(lhsAt), rhs.Element<char32_t>(rhsAt),
723 lhsBytes >> 1, rhsBytes >> 2);
724 }
725 break;
726 default:
727 terminator.Crash(
728 "RHS of character assignment does not have a character type");
729 }
730 break;
731 case CFI_type_char32_t:
732 switch (rhs.raw().type) {
733 case CFI_type_char:
734 for (; elements-- > 0;
735 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
736 CopyAndPad(lhs.Element<char32_t>(lhsAt), rhs.Element<char>(rhsAt),
737 lhsBytes >> 2, rhsBytes);
738 }
739 break;
740 case CFI_type_char16_t:
741 for (; elements-- > 0;
742 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
743 CopyAndPad(lhs.Element<char32_t>(lhsAt), rhs.Element<char16_t>(rhsAt),
744 lhsBytes >> 2, rhsBytes >> 1);
745 }
746 break;
747 case CFI_type_char32_t:
748 for (; elements-- > 0;
749 lhs.IncrementSubscripts(lhsAt), rhs.IncrementSubscripts(rhsAt)) {
750 CopyAndPad(lhs.Element<char32_t>(lhsAt), rhs.Element<char32_t>(rhsAt),
751 lhsBytes >> 2, rhsBytes >> 2);
752 }
753 break;
754 default:
755 terminator.Crash(
756 "RHS of character assignment does not have a character type");
757 }
758 break;
759 default:
760 terminator.Crash(
761 "LHS of character assignment does not have a character type");
762 }
763 if (reallocate) {
764 FreeMemory(old);
765 }
766 }
767
RTNAME(CharacterCompareScalar)768 int RTNAME(CharacterCompareScalar)(const Descriptor &x, const Descriptor &y) {
769 Terminator terminator{__FILE__, __LINE__};
770 RUNTIME_CHECK(terminator, x.rank() == 0);
771 RUNTIME_CHECK(terminator, y.rank() == 0);
772 RUNTIME_CHECK(terminator, x.raw().type == y.raw().type);
773 switch (x.raw().type) {
774 case CFI_type_char:
775 return CharacterScalarCompare<char>(x.OffsetElement<char>(),
776 y.OffsetElement<char>(), x.ElementBytes(), y.ElementBytes());
777 case CFI_type_char16_t:
778 return CharacterScalarCompare<char16_t>(x.OffsetElement<char16_t>(),
779 y.OffsetElement<char16_t>(), x.ElementBytes() >> 1,
780 y.ElementBytes() >> 1);
781 case CFI_type_char32_t:
782 return CharacterScalarCompare<char32_t>(x.OffsetElement<char32_t>(),
783 y.OffsetElement<char32_t>(), x.ElementBytes() >> 2,
784 y.ElementBytes() >> 2);
785 default:
786 terminator.Crash("CharacterCompareScalar: bad string type code %d",
787 static_cast<int>(x.raw().type));
788 }
789 return 0;
790 }
791
RTNAME(CharacterCompareScalar1)792 int RTNAME(CharacterCompareScalar1)(
793 const char *x, const char *y, std::size_t xChars, std::size_t yChars) {
794 return CharacterScalarCompare(x, y, xChars, yChars);
795 }
796
RTNAME(CharacterCompareScalar2)797 int RTNAME(CharacterCompareScalar2)(const char16_t *x, const char16_t *y,
798 std::size_t xChars, std::size_t yChars) {
799 return CharacterScalarCompare(x, y, xChars, yChars);
800 }
801
RTNAME(CharacterCompareScalar4)802 int RTNAME(CharacterCompareScalar4)(const char32_t *x, const char32_t *y,
803 std::size_t xChars, std::size_t yChars) {
804 return CharacterScalarCompare(x, y, xChars, yChars);
805 }
806
RTNAME(CharacterCompare)807 void RTNAME(CharacterCompare)(
808 Descriptor &result, const Descriptor &x, const Descriptor &y) {
809 Terminator terminator{__FILE__, __LINE__};
810 RUNTIME_CHECK(terminator, x.raw().type == y.raw().type);
811 switch (x.raw().type) {
812 case CFI_type_char:
813 Compare<char>(result, x, y, terminator);
814 break;
815 case CFI_type_char16_t:
816 Compare<char16_t>(result, x, y, terminator);
817 break;
818 case CFI_type_char32_t:
819 Compare<char32_t>(result, x, y, terminator);
820 break;
821 default:
822 terminator.Crash("CharacterCompareScalar: bad string type code %d",
823 static_cast<int>(x.raw().type));
824 }
825 }
826
RTNAME(CharacterAppend1)827 std::size_t RTNAME(CharacterAppend1)(char *lhs, std::size_t lhsBytes,
828 std::size_t offset, const char *rhs, std::size_t rhsBytes) {
829 if (auto n{std::min(lhsBytes - offset, rhsBytes)}) {
830 std::memcpy(lhs + offset, rhs, n);
831 offset += n;
832 }
833 return offset;
834 }
835
RTNAME(CharacterPad1)836 void RTNAME(CharacterPad1)(char *lhs, std::size_t bytes, std::size_t offset) {
837 if (bytes > offset) {
838 std::memset(lhs + offset, ' ', bytes - offset);
839 }
840 }
841
842 // Intrinsic function entry points
843
RTNAME(Adjustl)844 void RTNAME(Adjustl)(Descriptor &result, const Descriptor &string,
845 const char *sourceFile, int sourceLine) {
846 AdjustLR<false>(result, string, sourceFile, sourceLine);
847 }
848
RTNAME(Adjustr)849 void RTNAME(Adjustr)(Descriptor &result, const Descriptor &string,
850 const char *sourceFile, int sourceLine) {
851 AdjustLR<true>(result, string, sourceFile, sourceLine);
852 }
853
RTNAME(Index1)854 std::size_t RTNAME(Index1)(const char *x, std::size_t xLen, const char *set,
855 std::size_t setLen, bool back) {
856 return Index<char>(x, xLen, set, setLen, back);
857 }
RTNAME(Index2)858 std::size_t RTNAME(Index2)(const char16_t *x, std::size_t xLen,
859 const char16_t *set, std::size_t setLen, bool back) {
860 return Index<char16_t>(x, xLen, set, setLen, back);
861 }
RTNAME(Index4)862 std::size_t RTNAME(Index4)(const char32_t *x, std::size_t xLen,
863 const char32_t *set, std::size_t setLen, bool back) {
864 return Index<char32_t>(x, xLen, set, setLen, back);
865 }
866
RTNAME(Index)867 void RTNAME(Index)(Descriptor &result, const Descriptor &string,
868 const Descriptor &substring, const Descriptor *back, int kind,
869 const char *sourceFile, int sourceLine) {
870 Terminator terminator{sourceFile, sourceLine};
871 switch (string.raw().type) {
872 case CFI_type_char:
873 GeneralCharFuncKind<char, CharFunc::Index>(
874 result, string, substring, back, kind, terminator);
875 break;
876 case CFI_type_char16_t:
877 GeneralCharFuncKind<char16_t, CharFunc::Index>(
878 result, string, substring, back, kind, terminator);
879 break;
880 case CFI_type_char32_t:
881 GeneralCharFuncKind<char32_t, CharFunc::Index>(
882 result, string, substring, back, kind, terminator);
883 break;
884 default:
885 terminator.Crash(
886 "INDEX: bad string type code %d", static_cast<int>(string.raw().type));
887 }
888 }
889
RTNAME(LenTrim1)890 std::size_t RTNAME(LenTrim1)(const char *x, std::size_t chars) {
891 return LenTrim(x, chars);
892 }
RTNAME(LenTrim2)893 std::size_t RTNAME(LenTrim2)(const char16_t *x, std::size_t chars) {
894 return LenTrim(x, chars);
895 }
RTNAME(LenTrim4)896 std::size_t RTNAME(LenTrim4)(const char32_t *x, std::size_t chars) {
897 return LenTrim(x, chars);
898 }
899
RTNAME(LenTrim)900 void RTNAME(LenTrim)(Descriptor &result, const Descriptor &string, int kind,
901 const char *sourceFile, int sourceLine) {
902 Terminator terminator{sourceFile, sourceLine};
903 switch (string.raw().type) {
904 case CFI_type_char:
905 LenTrimKind<char>(result, string, kind, terminator);
906 break;
907 case CFI_type_char16_t:
908 LenTrimKind<char16_t>(result, string, kind, terminator);
909 break;
910 case CFI_type_char32_t:
911 LenTrimKind<char32_t>(result, string, kind, terminator);
912 break;
913 default:
914 terminator.Crash("LEN_TRIM: bad string type code %d",
915 static_cast<int>(string.raw().type));
916 }
917 }
918
RTNAME(Scan1)919 std::size_t RTNAME(Scan1)(const char *x, std::size_t xLen, const char *set,
920 std::size_t setLen, bool back) {
921 return ScanVerify<char, CharFunc::Scan>(x, xLen, set, setLen, back);
922 }
RTNAME(Scan2)923 std::size_t RTNAME(Scan2)(const char16_t *x, std::size_t xLen,
924 const char16_t *set, std::size_t setLen, bool back) {
925 return ScanVerify<char16_t, CharFunc::Scan>(x, xLen, set, setLen, back);
926 }
RTNAME(Scan4)927 std::size_t RTNAME(Scan4)(const char32_t *x, std::size_t xLen,
928 const char32_t *set, std::size_t setLen, bool back) {
929 return ScanVerify<char32_t, CharFunc::Scan>(x, xLen, set, setLen, back);
930 }
931
RTNAME(Scan)932 void RTNAME(Scan)(Descriptor &result, const Descriptor &string,
933 const Descriptor &set, const Descriptor *back, int kind,
934 const char *sourceFile, int sourceLine) {
935 Terminator terminator{sourceFile, sourceLine};
936 switch (string.raw().type) {
937 case CFI_type_char:
938 GeneralCharFuncKind<char, CharFunc::Scan>(
939 result, string, set, back, kind, terminator);
940 break;
941 case CFI_type_char16_t:
942 GeneralCharFuncKind<char16_t, CharFunc::Scan>(
943 result, string, set, back, kind, terminator);
944 break;
945 case CFI_type_char32_t:
946 GeneralCharFuncKind<char32_t, CharFunc::Scan>(
947 result, string, set, back, kind, terminator);
948 break;
949 default:
950 terminator.Crash(
951 "SCAN: bad string type code %d", static_cast<int>(string.raw().type));
952 }
953 }
954
RTNAME(Repeat)955 void RTNAME(Repeat)(Descriptor &result, const Descriptor &string,
956 std::int64_t ncopies, const char *sourceFile, int sourceLine) {
957 Terminator terminator{sourceFile, sourceLine};
958 if (ncopies < 0) {
959 terminator.Crash(
960 "REPEAT has negative NCOPIES=%jd", static_cast<std::intmax_t>(ncopies));
961 }
962 std::size_t origBytes{string.ElementBytes()};
963 result.Establish(string.type(), origBytes * ncopies, nullptr, 0, nullptr,
964 CFI_attribute_allocatable);
965 if (result.Allocate() != CFI_SUCCESS) {
966 terminator.Crash("REPEAT could not allocate storage for result");
967 }
968 const char *from{string.OffsetElement()};
969 for (char *to{result.OffsetElement()}; ncopies-- > 0; to += origBytes) {
970 std::memcpy(to, from, origBytes);
971 }
972 }
973
RTNAME(Trim)974 void RTNAME(Trim)(Descriptor &result, const Descriptor &string,
975 const char *sourceFile, int sourceLine) {
976 Terminator terminator{sourceFile, sourceLine};
977 std::size_t resultBytes{0};
978 switch (string.raw().type) {
979 case CFI_type_char:
980 resultBytes =
981 LenTrim(string.OffsetElement<const char>(), string.ElementBytes());
982 break;
983 case CFI_type_char16_t:
984 resultBytes = LenTrim(string.OffsetElement<const char16_t>(),
985 string.ElementBytes() >> 1)
986 << 1;
987 break;
988 case CFI_type_char32_t:
989 resultBytes = LenTrim(string.OffsetElement<const char32_t>(),
990 string.ElementBytes() >> 2)
991 << 2;
992 break;
993 default:
994 terminator.Crash(
995 "TRIM: bad string type code %d", static_cast<int>(string.raw().type));
996 }
997 result.Establish(string.type(), resultBytes, nullptr, 0, nullptr,
998 CFI_attribute_allocatable);
999 RUNTIME_CHECK(terminator, result.Allocate() == CFI_SUCCESS);
1000 std::memcpy(result.OffsetElement(), string.OffsetElement(), resultBytes);
1001 }
1002
RTNAME(Verify1)1003 std::size_t RTNAME(Verify1)(const char *x, std::size_t xLen, const char *set,
1004 std::size_t setLen, bool back) {
1005 return ScanVerify<char, CharFunc::Verify>(x, xLen, set, setLen, back);
1006 }
RTNAME(Verify2)1007 std::size_t RTNAME(Verify2)(const char16_t *x, std::size_t xLen,
1008 const char16_t *set, std::size_t setLen, bool back) {
1009 return ScanVerify<char16_t, CharFunc::Verify>(x, xLen, set, setLen, back);
1010 }
RTNAME(Verify4)1011 std::size_t RTNAME(Verify4)(const char32_t *x, std::size_t xLen,
1012 const char32_t *set, std::size_t setLen, bool back) {
1013 return ScanVerify<char32_t, CharFunc::Verify>(x, xLen, set, setLen, back);
1014 }
1015
RTNAME(Verify)1016 void RTNAME(Verify)(Descriptor &result, const Descriptor &string,
1017 const Descriptor &set, const Descriptor *back, int kind,
1018 const char *sourceFile, int sourceLine) {
1019 Terminator terminator{sourceFile, sourceLine};
1020 switch (string.raw().type) {
1021 case CFI_type_char:
1022 GeneralCharFuncKind<char, CharFunc::Verify>(
1023 result, string, set, back, kind, terminator);
1024 break;
1025 case CFI_type_char16_t:
1026 GeneralCharFuncKind<char16_t, CharFunc::Verify>(
1027 result, string, set, back, kind, terminator);
1028 break;
1029 case CFI_type_char32_t:
1030 GeneralCharFuncKind<char32_t, CharFunc::Verify>(
1031 result, string, set, back, kind, terminator);
1032 break;
1033 default:
1034 terminator.Crash(
1035 "VERIFY: bad string type code %d", static_cast<int>(string.raw().type));
1036 }
1037 }
1038
RTNAME(CharacterMax)1039 void RTNAME(CharacterMax)(Descriptor &accumulator, const Descriptor &x,
1040 const char *sourceFile, int sourceLine) {
1041 MaxMin<false>(accumulator, x, sourceFile, sourceLine);
1042 }
1043
RTNAME(CharacterMin)1044 void RTNAME(CharacterMin)(Descriptor &accumulator, const Descriptor &x,
1045 const char *sourceFile, int sourceLine) {
1046 MaxMin<true>(accumulator, x, sourceFile, sourceLine);
1047 }
1048 }
1049 } // namespace Fortran::runtime
1050