Skip to content

Commit

Permalink
[flang] Allow compiler directives in macros
Browse files Browse the repository at this point in the history
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
  • Loading branch information
klausler committed May 18, 2023
1 parent 8fe9718 commit cbc5d42
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 18 deletions.
12 changes: 12 additions & 0 deletions flang/include/flang/Parser/char-block.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 1 addition & 2 deletions flang/lib/Parser/Fortran-parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))};
Expand All @@ -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,
Expand Down
20 changes: 10 additions & 10 deletions flang/lib/Parser/prescan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
Expand All @@ -220,7 +220,7 @@ void Prescanner::Statement() {
}
}
preprocessed->ToLowerCase()
.ClipComment()
.ClipComment(*this)
.CheckBadFortranCharacters(messages_)
.CheckBadParentheses(messages_)
.Emit(cooked_);
Expand Down Expand Up @@ -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}};
Expand Down Expand Up @@ -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}};
Expand All @@ -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();
}

Expand Down
2 changes: 1 addition & 1 deletion flang/lib/Parser/prescan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)...);
Expand Down Expand Up @@ -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 &&);

Expand Down
35 changes: 31 additions & 4 deletions flang/lib/Parser/token-sequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion flang/lib/Parser/token-sequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions flang/test/Parser/compiler-directives.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down

0 comments on commit cbc5d42

Please sign in to comment.