[flang-commits] [flang] 5a451a4 - [flang] Runtime: SCAN and VERIFY

peter klausler via flang-commits flang-commits at lists.llvm.org
Mon Mar 1 12:15:58 PST 2021


Author: peter klausler
Date: 2021-03-01T12:15:29-08:00
New Revision: 5a451a428904168a02483510235c66a4c82fdaab

URL: https://github.com/llvm/llvm-project/commit/5a451a428904168a02483510235c66a4c82fdaab
DIFF: https://github.com/llvm/llvm-project/commit/5a451a428904168a02483510235c66a4c82fdaab.diff

LOG: [flang] Runtime: SCAN and VERIFY

Implement the related character intrinsic functions
SCAN and VERIFY.

Differential Revision: https://reviews.llvm.org/D97580

Added: 
    

Modified: 
    flang/runtime/character.cpp
    flang/runtime/character.h
    flang/unittests/Runtime/character.cpp

Removed: 
    


################################################################################
diff  --git a/flang/runtime/character.cpp b/flang/runtime/character.cpp
index 9476bde63dc6..21a8bb8d99b1 100644
--- a/flang/runtime/character.cpp
+++ b/flang/runtime/character.cpp
@@ -94,7 +94,8 @@ static void Compare(Descriptor &result, const Descriptor &x,
     elements *= ub[j];
     xAt[j] = yAt[j] = 1;
   }
-  result.Establish(TypeCategory::Logical, 1, ub, rank);
+  result.Establish(
+      TypeCategory::Logical, 1, nullptr, rank, ub, CFI_attribute_allocatable);
   if (result.Allocate(lb, ub) != CFI_SUCCESS) {
     terminator.Crash("Compare: could not allocate storage for result");
   }
@@ -145,7 +146,8 @@ static void AdjustLRHelper(Descriptor &result, const Descriptor &string,
     stringAt[j] = 1;
   }
   std::size_t elementBytes{string.ElementBytes()};
-  result.Establish(string.type(), elementBytes, ub, rank);
+  result.Establish(string.type(), elementBytes, nullptr, rank, ub,
+      CFI_attribute_allocatable);
   if (result.Allocate(lb, ub) != CFI_SUCCESS) {
     terminator.Crash("ADJUSTL/R: could not allocate storage for result");
   }
@@ -196,7 +198,8 @@ static void LenTrim(Descriptor &result, const Descriptor &string,
     elements *= ub[j];
     stringAt[j] = 1;
   }
-  result.Establish(TypeCategory::Integer, sizeof(INT), ub, rank);
+  result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub,
+      CFI_attribute_allocatable);
   if (result.Allocate(lb, ub) != CFI_SUCCESS) {
     terminator.Crash("LEN_TRIM: could not allocate storage for result");
   }
@@ -232,6 +235,133 @@ static void LenTrimKind(Descriptor &result, const Descriptor &string, int kind,
   }
 }
 
+// SCAN and VERIFY implementation help.  These intrinsic functions
+// do pretty much the same thing, so they're templatized with a
+// distinguishing flag.
+
+template <typename CHAR, bool IS_VERIFY = false>
+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};
+  int increment{back ? -1 : 1};
+  for (; xLen-- > 0; at += increment) {
+    CHAR ch{x[at - 1]};
+    bool inSet{false};
+    // TODO: If set is sorted, could use binary search
+    for (std::size_t j{0}; j < setLen; ++j) {
+      if (set[j] == ch) {
+        inSet = true;
+        break;
+      }
+    }
+    if (inSet != IS_VERIFY) {
+      return at;
+    }
+  }
+  return 0;
+}
+
+// Specialization for one-byte characters
+template <bool IS_VERIFY = false>
+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};
+  int increment{back ? -1 : 1};
+  if (xLen > 0) {
+    std::uint64_t bitSet[256 / 64]{0};
+    std::uint64_t one{1};
+    for (std::size_t j{0}; j < setLen; ++j) {
+      unsigned setCh{static_cast<unsigned char>(set[j])};
+      bitSet[setCh / 64] |= one << (setCh % 64);
+    }
+    for (; xLen-- > 0; at += increment) {
+      unsigned ch{static_cast<unsigned char>(x[at - 1])};
+      bool inSet{((bitSet[ch / 64] >> (ch % 64)) & 1) != 0};
+      if (inSet != IS_VERIFY) {
+        return at;
+      }
+    }
+  }
+  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<char>(at)};
+  for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) {
+    if (*p) {
+      return true;
+    }
+  }
+  return false;
+}
+
+template <typename INT, typename CHAR, bool IS_VERIFY = false>
+static void ScanVerify(Descriptor &result, const Descriptor &string,
+    const Descriptor &set, 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],
+      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;
+    elements *= ub[j];
+    stringAt[j] = setAt[j] = backAt[j] = 1;
+  }
+  result.Establish(TypeCategory::Integer, sizeof(INT), nullptr, rank, ub,
+      CFI_attribute_allocatable);
+  if (result.Allocate(lb, ub) != CFI_SUCCESS) {
+    terminator.Crash("SCAN/VERIFY: could not allocate storage for result");
+  }
+  std::size_t stringElementChars{string.ElementBytes() >> shift<CHAR>};
+  std::size_t setElementChars{set.ElementBytes() >> shift<CHAR>};
+  for (SubscriptValue resultAt{0}; elements-- > 0; resultAt += sizeof(INT),
+       string.IncrementSubscripts(stringAt), set.IncrementSubscripts(setAt),
+       back && back->IncrementSubscripts(backAt)) {
+    *result.OffsetElement<INT>(resultAt) =
+        ScanVerify<CHAR, IS_VERIFY>(string.Element<CHAR>(stringAt),
+            stringElementChars, set.Element<CHAR>(setAt), setElementChars,
+            back && IsLogicalElementTrue(*back, backAt));
+  }
+}
+
+template <typename CHAR, bool IS_VERIFY = false>
+static void ScanVerifyKind(Descriptor &result, const Descriptor &string,
+    const Descriptor &set, const Descriptor *back, int kind,
+    const Terminator &terminator) {
+  switch (kind) {
+  case 1:
+    ScanVerify<std::int8_t, CHAR, IS_VERIFY>(
+        result, string, set, back, terminator);
+    break;
+  case 2:
+    ScanVerify<std::int16_t, CHAR, IS_VERIFY>(
+        result, string, set, back, terminator);
+    break;
+  case 4:
+    ScanVerify<std::int32_t, CHAR, IS_VERIFY>(
+        result, string, set, back, terminator);
+    break;
+  case 8:
+    ScanVerify<std::int64_t, CHAR, IS_VERIFY>(
+        result, string, set, back, terminator);
+    break;
+  case 16:
+    ScanVerify<common::uint128_t, CHAR, IS_VERIFY>(
+        result, string, set, back, terminator);
+    break;
+  default:
+    terminator.Crash("SCAN/VERIFY: bad KIND=%d", kind);
+  }
+}
+
 template <typename TO, typename FROM>
 static void CopyAndPad(
     TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
@@ -608,7 +738,7 @@ void RTNAME(CharacterPad1)(char *lhs, std::size_t bytes, std::size_t offset) {
   }
 }
 
-// Intrinsic functions
+// Intrinsic function entry points
 
 void RTNAME(AdjustL)(Descriptor &result, const Descriptor &string,
     const char *sourceFile, int sourceLine) {
@@ -649,11 +779,47 @@ void RTNAME(LenTrim)(Descriptor &result, const Descriptor &string, int kind,
   }
 }
 
+std::size_t RTNAME(Scan1)(const char *x, std::size_t xLen, const char *set,
+    std::size_t setLen, bool back) {
+  return ScanVerify<char, false>(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<char16_t, false>(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<char32_t, false>(x, xLen, set, setLen, back);
+}
+
+void RTNAME(Scan)(Descriptor &result, const Descriptor &string,
+    const Descriptor &set, const Descriptor *back, int kind,
+    const char *sourceFile, int sourceLine) {
+  Terminator terminator{sourceFile, sourceLine};
+  switch (string.raw().type) {
+  case CFI_type_char:
+    ScanVerifyKind<char, false>(result, string, set, back, kind, terminator);
+    break;
+  case CFI_type_char16_t:
+    ScanVerifyKind<char16_t, false>(
+        result, string, set, back, kind, terminator);
+    break;
+  case CFI_type_char32_t:
+    ScanVerifyKind<char32_t, false>(
+        result, string, set, back, kind, terminator);
+    break;
+  default:
+    terminator.Crash(
+        "SCAN: bad string type code %d", static_cast<int>(string.raw().type));
+  }
+}
+
 void RTNAME(Repeat)(Descriptor &result, const Descriptor &string,
     std::size_t ncopies, const char *sourceFile, int sourceLine) {
   Terminator terminator{sourceFile, sourceLine};
   std::size_t origBytes{string.ElementBytes()};
-  result.Establish(string.type(), origBytes * ncopies, nullptr, 0);
+  result.Establish(string.type(), origBytes * ncopies, nullptr, 0, nullptr,
+      CFI_attribute_allocatable);
   if (result.Allocate(nullptr, nullptr) != CFI_SUCCESS) {
     terminator.Crash("REPEAT could not allocate storage for result");
   }
@@ -692,6 +858,39 @@ void RTNAME(Trim)(Descriptor &result, const Descriptor &string,
   std::memcpy(result.OffsetElement(), string.OffsetElement(), resultBytes);
 }
 
+std::size_t RTNAME(Verify1)(const char *x, std::size_t xLen, const char *set,
+    std::size_t setLen, bool back) {
+  return ScanVerify<char, true>(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<char16_t, true>(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<char32_t, true>(x, xLen, set, setLen, back);
+}
+
+void RTNAME(Verify)(Descriptor &result, const Descriptor &string,
+    const Descriptor &set, const Descriptor *back, int kind,
+    const char *sourceFile, int sourceLine) {
+  Terminator terminator{sourceFile, sourceLine};
+  switch (string.raw().type) {
+  case CFI_type_char:
+    ScanVerifyKind<char, true>(result, string, set, back, kind, terminator);
+    break;
+  case CFI_type_char16_t:
+    ScanVerifyKind<char16_t, true>(result, string, set, back, kind, terminator);
+    break;
+  case CFI_type_char32_t:
+    ScanVerifyKind<char32_t, true>(result, string, set, back, kind, terminator);
+    break;
+  default:
+    terminator.Crash(
+        "VERIFY: bad string type code %d", static_cast<int>(string.raw().type));
+  }
+}
+
 void RTNAME(CharacterMax)(Descriptor &accumulator, const Descriptor &x,
     const char *sourceFile, int sourceLine) {
   MaxMin<false>(accumulator, x, sourceFile, sourceLine);

diff  --git a/flang/runtime/character.h b/flang/runtime/character.h
index 6230495691dd..8879f3e25c37 100644
--- a/flang/runtime/character.h
+++ b/flang/runtime/character.h
@@ -107,6 +107,26 @@ void RTNAME(CharacterMaxLoc)(Descriptor &result, const Descriptor &x,
 void RTNAME(CharacterMinLoc)(Descriptor &result, const Descriptor &x,
     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(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,
+    std::size_t, bool back = false);
+std::size_t RTNAME(Scan4)(const char32_t *, std::size_t, const char32_t *set,
+    std::size_t, bool back = false);
+void RTNAME(Scan)(Descriptor &result, const Descriptor &string,
+    const Descriptor &set, const Descriptor *back /*can be null*/, int kind,
+    const char *sourceFile = nullptr, int sourceLine = 0);
+
+std::size_t RTNAME(Verify1)(
+    const char *, std::size_t, const char *set, std::size_t, bool back = false);
+std::size_t RTNAME(Verify2)(const char16_t *, std::size_t, const char16_t *set,
+    std::size_t, bool back = false);
+std::size_t RTNAME(Verify4)(const char32_t *, std::size_t, const char32_t *set,
+    std::size_t, bool back = false);
+void RTNAME(Verify)(Descriptor &result, const Descriptor &string,
+    const Descriptor &set, const Descriptor *back /*can be null*/, int kind,
+    const char *sourceFile = nullptr, int sourceLine = 0);
 }
 } // namespace Fortran::runtime
 #endif // FORTRAN_RUNTIME_CHARACTER_H_

diff  --git a/flang/unittests/Runtime/character.cpp b/flang/unittests/Runtime/character.cpp
index fb023473f64a..486f79b4052c 100644
--- a/flang/unittests/Runtime/character.cpp
+++ b/flang/unittests/Runtime/character.cpp
@@ -46,6 +46,24 @@ static void Compare(const char *x, const char *y, std::size_t xBytes,
   TestCharCompare(y, x, yBytes, xBytes, -expect);
 }
 
+static void Scan(
+    const char *str, const char *set, bool back, std::size_t expect) {
+  auto res{RTNAME(Scan1)(str, std::strlen(str), set, std::strlen(set), back)};
+  if (res != expect) {
+    Fail() << "Scan(" << str << ',' << set << ",back=" << back << "): got "
+           << res << ", should be " << expect << '\n';
+  }
+}
+
+static void Verify(
+    const char *str, const char *set, bool back, std::size_t expect) {
+  auto res{RTNAME(Verify1)(str, std::strlen(str), set, std::strlen(set), back)};
+  if (res != expect) {
+    Fail() << "Verify(" << str << ',' << set << ",back=" << back << "): got "
+           << res << ", should be " << expect << '\n';
+  }
+}
+
 int main() {
   StartTests();
   for (std::size_t j{0}; j < 8; ++j) {
@@ -55,5 +73,17 @@ int main() {
   Compare("abc", "def", 3, 3, -1);
   Compare("ab ", "abc", 3, 2, 0);
   Compare("abc", "abc", 2, 3, -1);
+  Scan("abc", "abc", false, 1);
+  Scan("abc", "abc", true, 3);
+  Scan("abc", "cde", false, 3);
+  Scan("abc", "cde", true, 3);
+  Scan("abc", "x", false, 0);
+  Scan("", "x", false, 0);
+  Verify("abc", "abc", false, 0);
+  Verify("abc", "abc", true, 0);
+  Verify("abc", "cde", false, 1);
+  Verify("abc", "cde", true, 2);
+  Verify("abc", "x", false, 1);
+  Verify("", "x", false, 0);
   return EndTests();
 }


        


More information about the flang-commits mailing list