[flang-commits] [flang] cbc5d42 - [flang] Allow compiler directives in macros

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Thu May 18 11:04:46 PDT 2023


Author: Peter Klausler
Date: 2023-05-18T11:04:30-07:00
New Revision: cbc5d42fcedace0b9dcfa2e2a91d41e3ce84908c

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

LOG: [flang] Allow compiler directives in macros

Modify the prescanner to allow compiler directives to appear in
macro expansions, and adjust the parser to accept a semicolon
as a directive terminator.

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

Added: 
    

Modified: 
    flang/include/flang/Parser/char-block.h
    flang/lib/Parser/Fortran-parsers.cpp
    flang/lib/Parser/prescan.cpp
    flang/lib/Parser/prescan.h
    flang/lib/Parser/token-sequence.cpp
    flang/lib/Parser/token-sequence.h
    flang/test/Parser/compiler-directives.f90

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Parser/char-block.h b/flang/include/flang/Parser/char-block.h
index 0f5758f8c552c..152fdb41e0fc2 100644
--- a/flang/include/flang/Parser/char-block.h
+++ b/flang/include/flang/Parser/char-block.h
@@ -64,6 +64,18 @@ class CharBlock {
     return ' '; // non no-blank character
   }
 
+  std::size_t CountLeadingBlanks() const {
+    std::size_t n{size()};
+    std::size_t j{0};
+    for (; j < n; ++j) {
+      char ch{(*this)[j]};
+      if (ch != ' ' && ch != '\t') {
+        break;
+      }
+    }
+    return j;
+  }
+
   bool IsBlank() const { return FirstNonBlank() == ' '; }
 
   std::string ToString() const {

diff  --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index e7208e60afbf6..4236addf67c30 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -1217,7 +1217,6 @@ TYPE_PARSER(construct<StatOrErrmsg>("STAT =" >> statVariable) ||
 // !DIR$ LOOP COUNT (n1[, n2]...)
 // !DIR$ name...
 constexpr auto beginDirective{skipStuffBeforeStatement >> "!"_ch};
-constexpr auto endDirective{space >> endOfLine};
 constexpr auto ignore_tkr{
     "DIR$ IGNORE_TKR" >> optionalList(construct<CompilerDirective::IgnoreTKR>(
                              maybe(parenthesized(many(letter))), name))};
@@ -1231,7 +1230,7 @@ TYPE_PARSER(beginDirective >>
         construct<CompilerDirective>(
             "DIR$" >> many(construct<CompilerDirective::NameValue>(name,
                           maybe(("="_tok || ":"_tok) >> digitString64))))) /
-        endDirective)
+        endOfStmt)
 
 TYPE_PARSER(extension<LanguageFeature::CrayPointer>(
     "nonstandard usage: based POINTER"_port_en_US,

diff  --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 19a70f1f6e06c..27b5597db4259 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -204,7 +204,7 @@ void Prescanner::Statement() {
       NormalizeCompilerDirectiveCommentMarker(*preprocessed);
       preprocessed->ToLowerCase();
       SourceFormChange(preprocessed->ToString());
-      preprocessed->ClipComment(true /* skip first ! */)
+      preprocessed->ClipComment(*this, true /* skip first ! */)
           .CheckBadFortranCharacters(messages_)
           .CheckBadParentheses(messages_)
           .Emit(cooked_);
@@ -220,7 +220,7 @@ void Prescanner::Statement() {
         }
       }
       preprocessed->ToLowerCase()
-          .ClipComment()
+          .ClipComment(*this)
           .CheckBadFortranCharacters(messages_)
           .CheckBadParentheses(messages_)
           .Emit(cooked_);
@@ -1140,7 +1140,8 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
     return std::nullopt;
   }
   *sp = '\0';
-  if (const char *ss{IsCompilerDirectiveSentinel(sentinel)}) {
+  if (const char *ss{IsCompilerDirectiveSentinel(
+          sentinel, static_cast<std::size_t>(sp - sentinel))}) {
     std::size_t payloadOffset = p - start;
     return {LineClassification{
         LineClassification::Kind::CompilerDirective, payloadOffset, ss}};
@@ -1168,7 +1169,7 @@ Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
       if (*p == '!') {
         break;
       }
-      if (const char *sp{IsCompilerDirectiveSentinel(sentinel)}) {
+      if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
         std::size_t offset = p - start;
         return {LineClassification{
             LineClassification::Kind::CompilerDirective, offset, sp}};
@@ -1192,17 +1193,16 @@ Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) {
 }
 
 const char *Prescanner::IsCompilerDirectiveSentinel(
-    const char *sentinel) const {
+    const char *sentinel, std::size_t len) const {
   std::uint64_t packed{0};
-  std::size_t n{0};
-  for (; sentinel[n] != '\0'; ++n) {
-    packed = (packed << 8) | (sentinel[n] & 0xff);
+  for (std::size_t j{0}; j < len; ++j) {
+    packed = (packed << 8) | (sentinel[j] & 0xff);
   }
-  if (n == 0 || !compilerDirectiveBloomFilter_.test(packed % prime1) ||
+  if (len == 0 || !compilerDirectiveBloomFilter_.test(packed % prime1) ||
       !compilerDirectiveBloomFilter_.test(packed % prime2)) {
     return nullptr;
   }
-  const auto iter{compilerDirectiveSentinels_.find(std::string(sentinel, n))};
+  const auto iter{compilerDirectiveSentinels_.find(std::string(sentinel, len))};
   return iter == compilerDirectiveSentinels_.end() ? nullptr : iter->c_str();
 }
 

diff  --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index ad8b90b0560e5..69d3b590e1ec0 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -68,6 +68,7 @@ class Prescanner {
   bool IsNextLinePreprocessorDirective() const;
   TokenSequence TokenizePreprocessorDirective();
   Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
+  const char *IsCompilerDirectiveSentinel(const char *, std::size_t) const;
 
   template <typename... A> Message &Say(A &&...a) {
     return messages_.Say(std::forward<A>(a)...);
@@ -182,7 +183,6 @@ class Prescanner {
       const char *) const;
   std::optional<LineClassification> IsFreeFormCompilerDirectiveLine(
       const char *) const;
-  const char *IsCompilerDirectiveSentinel(const char *) const;
   LineClassification ClassifyLine(const char *) const;
   void SourceFormChange(std::string &&);
 

diff  --git a/flang/lib/Parser/token-sequence.cpp b/flang/lib/Parser/token-sequence.cpp
index a3b97d32fa229..eaa2bf3650589 100644
--- a/flang/lib/Parser/token-sequence.cpp
+++ b/flang/lib/Parser/token-sequence.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "token-sequence.h"
+#include "prescan.h"
 #include "flang/Parser/characters.h"
 #include "flang/Parser/message.h"
 #include "llvm/Support/raw_ostream.h"
@@ -244,11 +245,31 @@ TokenSequence &TokenSequence::RemoveRedundantBlanks(std::size_t firstChar) {
   return *this;
 }
 
-TokenSequence &TokenSequence::ClipComment(bool skipFirst) {
+TokenSequence &TokenSequence::ClipComment(
+    const Prescanner &prescanner, bool skipFirst) {
   std::size_t tokens{SizeInTokens()};
   for (std::size_t j{0}; j < tokens; ++j) {
-    if (TokenAt(j).FirstNonBlank() == '!') {
-      if (skipFirst) {
+    CharBlock tok{TokenAt(j)};
+    if (std::size_t blanks{tok.CountLeadingBlanks()};
+        blanks < tok.size() && tok[blanks] == '!') {
+      // Retain active compiler directive sentinels (e.g. "!dir$")
+      for (std::size_t k{j + 1}; k < tokens && tok.size() < blanks + 5; ++k) {
+        if (tok.begin() + tok.size() == TokenAt(k).begin()) {
+          tok.ExtendToCover(TokenAt(k));
+        } else {
+          break;
+        }
+      }
+      bool isSentinel{false};
+      if (tok.size() == blanks + 5) {
+        char sentinel[4];
+        for (int k{0}; k < 4; ++k) {
+          sentinel[k] = ToLowerCaseLetter(tok[blanks + k + 1]);
+        }
+        isSentinel = prescanner.IsCompilerDirectiveSentinel(sentinel, 4);
+      }
+      if (isSentinel) {
+      } else if (skipFirst) {
         skipFirst = false;
       } else {
         TokenSequence result;
@@ -315,11 +336,12 @@ ProvenanceRange TokenSequence::GetProvenanceRange() const {
 const TokenSequence &TokenSequence::CheckBadFortranCharacters(
     Messages &messages) const {
   std::size_t tokens{SizeInTokens()};
+  bool isBangOk{true};
   for (std::size_t j{0}; j < tokens; ++j) {
     CharBlock token{TokenAt(j)};
     char ch{token.FirstNonBlank()};
     if (ch != ' ' && !IsValidFortranTokenCharacter(ch)) {
-      if (ch == '!' && j == 0) {
+      if (ch == '!' && isBangOk) {
         // allow in !dir$
       } else if (ch < ' ' || ch >= '\x7f') {
         messages.Say(GetTokenProvenanceRange(j),
@@ -329,6 +351,11 @@ const TokenSequence &TokenSequence::CheckBadFortranCharacters(
             "bad character ('%c') in Fortran token"_err_en_US, ch);
       }
     }
+    if (ch == ';') {
+      isBangOk = true;
+    } else if (ch != ' ') {
+      isBangOk = false;
+    }
   }
   return *this;
 }

diff  --git a/flang/lib/Parser/token-sequence.h b/flang/lib/Parser/token-sequence.h
index c039126bee0ff..ab506da82dde6 100644
--- a/flang/lib/Parser/token-sequence.h
+++ b/flang/lib/Parser/token-sequence.h
@@ -28,6 +28,7 @@ class raw_ostream;
 namespace Fortran::parser {
 
 class Messages;
+class Prescanner;
 
 // Buffers a contiguous sequence of characters that has been partitioned into
 // a sequence of preprocessing tokens with provenances.
@@ -115,7 +116,7 @@ class TokenSequence {
   bool HasRedundantBlanks(std::size_t firstChar = 0) const;
   TokenSequence &RemoveBlanks(std::size_t firstChar = 0);
   TokenSequence &RemoveRedundantBlanks(std::size_t firstChar = 0);
-  TokenSequence &ClipComment(bool skipFirst = false);
+  TokenSequence &ClipComment(const Prescanner &, bool skipFirst = false);
   const TokenSequence &CheckBadFortranCharacters(Messages &) const;
   const TokenSequence &CheckBadParentheses(Messages &) const;
   void Emit(CookedSource &) const;

diff  --git a/flang/test/Parser/compiler-directives.f90 b/flang/test/Parser/compiler-directives.f90
index 1e6c67dff3c1c..88cfd0944faf0 100644
--- a/flang/test/Parser/compiler-directives.f90
+++ b/flang/test/Parser/compiler-directives.f90
@@ -2,6 +2,12 @@
 
 ! Test that compiler directives can appear in various places.
 
+#define PROC(KIND) \
+  interface; integer(KIND) function foo(a); \
+    integer(KIND), intent(in) :: a; \
+    !dir$ ignore_tkr a; \
+  end; end interface
+
 !dir$ integer
 module m
   !dir$ integer
@@ -11,6 +17,7 @@ module m
   !dir$ integer
   !dir$ integer=64
   !dir$ integer = 64
+  PROC(4)
   !dir$ optimize:1
   !dir$ optimize : 1
   !dir$ loop count (10000)


        


More information about the flang-commits mailing list