diff --git a/flang/runtime/character.h b/flang/runtime/character.h --- a/flang/runtime/character.h +++ b/flang/runtime/character.h @@ -108,6 +108,16 @@ int dim = 0, const Descriptor *mask = nullptr, int kind = sizeof(int), bool back = false, const char *sourceFile = nullptr, int sourceLine = 0); +std::size_t RTNAME(Index1)(const char *, std::size_t, const char *substring, + std::size_t, bool back = false); +std::size_t RTNAME(Index2)(const char16_t *, std::size_t, + const char16_t *substring, std::size_t, bool back = false); +std::size_t RTNAME(Index4)(const char32_t *, std::size_t, + const char32_t *substring, std::size_t, bool back = false); +void RTNAME(Index)(Descriptor &result, const Descriptor &string, + const Descriptor &substring, const Descriptor *back /*can be null*/, + int kind, const char *sourceFile = nullptr, int sourceLine = 0); + std::size_t RTNAME(Scan1)( const char *, std::size_t, const char *set, std::size_t, bool back = false); std::size_t RTNAME(Scan2)(const char16_t *, std::size_t, const char16_t *set, diff --git a/flang/runtime/character.cpp b/flang/runtime/character.cpp --- a/flang/runtime/character.cpp +++ b/flang/runtime/character.cpp @@ -235,11 +235,87 @@ } } +// Utility for dealing with elemental LOGICAL arguments +static 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(at)}; + for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) { + if (*p) { + return true; + } + } + return false; +} + +// INDEX implementation +template +inline std::size_t Index(const CHAR *x, std::size_t xLen, const CHAR *want, + std::size_t wantLen, bool back) { + if (xLen < wantLen) { + return 0; + } + if (xLen == 0) { + return 1; // wantLen is also 0, so trivial match + } + if (back) { + // If wantLen==0, returns xLen + 1 per standard (and all other compilers) + std::size_t at{xLen - wantLen + 1}; + for (; at > 0; --at) { + std::size_t j{1}; + for (; j <= wantLen; ++j) { + if (x[at + j - 2] != want[j - 1]) { + break; + } + } + if (j > wantLen) { + return at; + } + } + return 0; + } + // Non-trivial forward substring search: use a simplified form of + // Boyer-Moore substring searching. + for (std::size_t at{1}; at + wantLen - 1 <= xLen;) { + // Compare x(at:at+wantLen-1) with want(1:wantLen). + // The comparison proceeds from the ends of the substrings forward + // so that we can skip ahead by multiple positions on a miss. + std::size_t j{wantLen}; + CHAR ch; + for (; j > 0; --j) { + ch = x[at + j - 2]; + if (ch != want[j - 1]) { + break; + } + } + if (j == 0) { + return at; // found a match + } + // Suppose we have at==2: + // "THAT FORTRAN THAT I RAN" <- the string (x) in which we search + // "THAT I RAN" <- the string (want) for which we search + // ^------------------ j==7, ch=='T' + // We can shift ahead 3 positions to at==5 to align the 'T's: + // "THAT FORTRAN THAT I RAN" + // "THAT I RAN" + std::size_t shift{1}; + for (; shift < j; ++shift) { + if (want[j - shift - 1] == ch) { + break; + } + } + at += shift; + } + return 0; +} + // SCAN and VERIFY implementation help. These intrinsic functions // do pretty much the same thing, so they're templatized with a // distinguishing flag. -template +enum class CharFunc { Index, Scan, Verify }; + +template inline std::size_t ScanVerify(const CHAR *x, std::size_t xLen, const CHAR *set, std::size_t setLen, bool back) { std::size_t at{back ? xLen : 1}; @@ -254,7 +330,7 @@ break; } } - if (inSet != IS_VERIFY) { + if (inSet != (FUNC == CharFunc::Verify)) { return at; } } @@ -285,35 +361,25 @@ return 0; } -static 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(at)}; - for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) { - if (*p) { - return true; - } - } - return false; -} - -template -static void ScanVerify(Descriptor &result, const Descriptor &string, - const Descriptor &set, const Descriptor *back, +template +static void GeneralCharFunc(Descriptor &result, const Descriptor &string, + const Descriptor &arg, const Descriptor *back, const Terminator &terminator) { int rank{string.rank() ? string.rank() - : set.rank() ? set.rank() : back ? back->rank() : 0}; - SubscriptValue lb[maxRank], ub[maxRank], stringAt[maxRank], setAt[maxRank], + : arg.rank() ? arg.rank() + : back ? back->rank() + : 0}; + SubscriptValue lb[maxRank], ub[maxRank], stringAt[maxRank], argAt[maxRank], backAt[maxRank]; SubscriptValue elements{1}; for (int j{0}; j < rank; ++j) { lb[j] = 1; - ub[j] = string.rank() - ? string.GetDimension(j).Extent() - : set.rank() ? set.GetDimension(j).Extent() - : back ? back->GetDimension(j).Extent() : 1; + ub[j] = string.rank() ? string.GetDimension(j).Extent() + : arg.rank() ? arg.GetDimension(j).Extent() + : back ? back->GetDimension(j).Extent() + : 1; elements *= ub[j]; - stringAt[j] = setAt[j] = backAt[j] = 1; + stringAt[j] = argAt[j] = backAt[j] = 1; } result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub, CFI_attribute_allocatable); @@ -321,44 +387,59 @@ terminator.Crash("SCAN/VERIFY: could not allocate storage for result"); } std::size_t stringElementChars{string.ElementBytes() >> shift}; - std::size_t setElementChars{set.ElementBytes() >> shift}; + std::size_t argElementChars{arg.ElementBytes() >> shift}; for (SubscriptValue resultAt{0}; elements-- > 0; resultAt += sizeof(INT), - string.IncrementSubscripts(stringAt), set.IncrementSubscripts(setAt), + string.IncrementSubscripts(stringAt), arg.IncrementSubscripts(argAt), back && back->IncrementSubscripts(backAt)) { - *result.OffsetElement(resultAt) = - ScanVerify(string.Element(stringAt), - stringElementChars, set.Element(setAt), setElementChars, - back && IsLogicalElementTrue(*back, backAt)); + if constexpr (FUNC == CharFunc::Index) { + *result.OffsetElement(resultAt) = + Index(string.Element(stringAt), stringElementChars, + arg.Element(argAt), argElementChars, + back && IsLogicalElementTrue(*back, backAt)); + } else if constexpr (FUNC == CharFunc::Scan) { + *result.OffsetElement(resultAt) = + ScanVerify(string.Element(stringAt), + stringElementChars, arg.Element(argAt), argElementChars, + back && IsLogicalElementTrue(*back, backAt)); + } else if constexpr (FUNC == CharFunc::Verify) { + *result.OffsetElement(resultAt) = + ScanVerify(string.Element(stringAt), + stringElementChars, arg.Element(argAt), argElementChars, + back && IsLogicalElementTrue(*back, backAt)); + } else { + static_assert(FUNC == CharFunc::Index || FUNC == CharFunc::Scan || + FUNC == CharFunc::Verify); + } } } -template -static void ScanVerifyKind(Descriptor &result, const Descriptor &string, - const Descriptor &set, const Descriptor *back, int kind, +template +static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string, + const Descriptor &arg, const Descriptor *back, int kind, const Terminator &terminator) { switch (kind) { case 1: - ScanVerify( - result, string, set, back, terminator); + GeneralCharFunc( + result, string, arg, back, terminator); break; case 2: - ScanVerify( - result, string, set, back, terminator); + GeneralCharFunc( + result, string, arg, back, terminator); break; case 4: - ScanVerify( - result, string, set, back, terminator); + GeneralCharFunc( + result, string, arg, back, terminator); break; case 8: - ScanVerify( - result, string, set, back, terminator); + GeneralCharFunc( + result, string, arg, back, terminator); break; case 16: - ScanVerify( - result, string, set, back, terminator); + GeneralCharFunc( + result, string, arg, back, terminator); break; default: - terminator.Crash("SCAN/VERIFY: bad KIND=%d", kind); + terminator.Crash("INDEX/SCAN/VERIFY: bad KIND=%d", kind); } } @@ -750,6 +831,42 @@ AdjustLR(result, string, sourceFile, sourceLine); } +std::size_t RTNAME(Index1)(const char *x, std::size_t xLen, const char *set, + std::size_t setLen, bool back) { + return Index(x, xLen, set, setLen, back); +} +std::size_t RTNAME(Index2)(const char16_t *x, std::size_t xLen, + const char16_t *set, std::size_t setLen, bool back) { + return Index(x, xLen, set, setLen, back); +} +std::size_t RTNAME(Index4)(const char32_t *x, std::size_t xLen, + const char32_t *set, std::size_t setLen, bool back) { + return Index(x, xLen, set, setLen, back); +} + +void RTNAME(Index)(Descriptor &result, const Descriptor &string, + const Descriptor &substring, const Descriptor *back, int kind, + const char *sourceFile, int sourceLine) { + Terminator terminator{sourceFile, sourceLine}; + switch (string.raw().type) { + case CFI_type_char: + GeneralCharFuncKind( + result, string, substring, back, kind, terminator); + break; + case CFI_type_char16_t: + GeneralCharFuncKind( + result, string, substring, back, kind, terminator); + break; + case CFI_type_char32_t: + GeneralCharFuncKind( + result, string, substring, back, kind, terminator); + break; + default: + terminator.Crash( + "INDEX: bad string type code %d", static_cast(string.raw().type)); + } +} + std::size_t RTNAME(LenTrim1)(const char *x, std::size_t chars) { return LenTrim(x, chars); } @@ -781,15 +898,15 @@ std::size_t RTNAME(Scan1)(const char *x, std::size_t xLen, const char *set, std::size_t setLen, bool back) { - return ScanVerify(x, xLen, set, setLen, back); + return ScanVerify(x, xLen, set, setLen, back); } std::size_t RTNAME(Scan2)(const char16_t *x, std::size_t xLen, const char16_t *set, std::size_t setLen, bool back) { - return ScanVerify(x, xLen, set, setLen, back); + return ScanVerify(x, xLen, set, setLen, back); } std::size_t RTNAME(Scan4)(const char32_t *x, std::size_t xLen, const char32_t *set, std::size_t setLen, bool back) { - return ScanVerify(x, xLen, set, setLen, back); + return ScanVerify(x, xLen, set, setLen, back); } void RTNAME(Scan)(Descriptor &result, const Descriptor &string, @@ -798,14 +915,15 @@ Terminator terminator{sourceFile, sourceLine}; switch (string.raw().type) { case CFI_type_char: - ScanVerifyKind(result, string, set, back, kind, terminator); + GeneralCharFuncKind( + result, string, set, back, kind, terminator); break; case CFI_type_char16_t: - ScanVerifyKind( + GeneralCharFuncKind( result, string, set, back, kind, terminator); break; case CFI_type_char32_t: - ScanVerifyKind( + GeneralCharFuncKind( result, string, set, back, kind, terminator); break; default: @@ -860,15 +978,15 @@ std::size_t RTNAME(Verify1)(const char *x, std::size_t xLen, const char *set, std::size_t setLen, bool back) { - return ScanVerify(x, xLen, set, setLen, back); + return ScanVerify(x, xLen, set, setLen, back); } std::size_t RTNAME(Verify2)(const char16_t *x, std::size_t xLen, const char16_t *set, std::size_t setLen, bool back) { - return ScanVerify(x, xLen, set, setLen, back); + return ScanVerify(x, xLen, set, setLen, back); } std::size_t RTNAME(Verify4)(const char32_t *x, std::size_t xLen, const char32_t *set, std::size_t setLen, bool back) { - return ScanVerify(x, xLen, set, setLen, back); + return ScanVerify(x, xLen, set, setLen, back); } void RTNAME(Verify)(Descriptor &result, const Descriptor &string, @@ -877,13 +995,16 @@ Terminator terminator{sourceFile, sourceLine}; switch (string.raw().type) { case CFI_type_char: - ScanVerifyKind(result, string, set, back, kind, terminator); + GeneralCharFuncKind( + result, string, set, back, kind, terminator); break; case CFI_type_char16_t: - ScanVerifyKind(result, string, set, back, kind, terminator); + GeneralCharFuncKind( + result, string, set, back, kind, terminator); break; case CFI_type_char32_t: - ScanVerifyKind(result, string, set, back, kind, terminator); + GeneralCharFuncKind( + result, string, set, back, kind, terminator); break; default: terminator.Crash( diff --git a/flang/test/Evaluate/folding05.f90 b/flang/test/Evaluate/folding05.f90 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ @@ -136,147 +139,86 @@ } } -//------------------------------------------------------------------------------ -/// Tests and infrastructure for Scan functions -//------------------------------------------------------------------------------ +// Test search functions INDEX(), SCAN(), and VERIFY() template -using ScanFuncTy = std::function; - -using ScanFuncsTy = - std::tuple, ScanFuncTy, ScanFuncTy>; - -// These functions are the systems under test in CharacterScanTests test cases. -static ScanFuncsTy scanFuncs{ - RTNAME(Scan1), - RTNAME(Scan2), - RTNAME(Scan4), +template