https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/146933
>From 0e9eab649f7a515c0697c3fe58309c478108f6b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 2 Jul 2025 09:43:32 -0500 Subject: [PATCH 01/12] [Frontend][OpenMP] Implement directive name parser Implement a state machine that consumes tokens (words delimited by white space), and returns the corresponding directive id, or fails if the tokens did not form a valid name. --- .../Frontend/OpenMP/DirectiveNameParser.h | 76 ++++++++ llvm/lib/Frontend/OpenMP/CMakeLists.txt | 1 + .../Frontend/OpenMP/DirectiveNameParser.cpp | 93 ++++++++++ llvm/unittests/Frontend/CMakeLists.txt | 1 + .../OpenMPDirectiveNameParserTest.cpp | 171 ++++++++++++++++++ 5 files changed, 342 insertions(+) create mode 100644 llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h create mode 100644 llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp create mode 100644 llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h new file mode 100644 index 0000000000000..db8986601b2ca --- /dev/null +++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h @@ -0,0 +1,76 @@ +//===- DirectiveNameParser.h ------------------------------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FRONTEND_OPENMP_DIRECTIVENAMEPARSER_H +#define LLVM_FRONTEND_OPENMP_DIRECTIVENAMEPARSER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Frontend/OpenMP/OMP.h" + +#include <memory> + +namespace llvm::omp { +/// Parser class for OpenMP directive names. It only recognizes names listed +/// in OMP.td, in particular it does not recognize Fortran's end-directives +/// if they are not explicitly listed in OMP.td. +/// +/// The class itself may be a singleton, once it's constructed it never +/// changes. +/// +/// Usage: +/// { +/// DirectiveNameParser Parser; // Could be static const. +/// +/// DirectiveNameParser::State *S = Parser.initial(); +/// for (StringRef Token : Tokens) +/// S = Parser.apply(S, Token); // Passing nullptr is ok. +/// +/// if (S == nullptr) { +/// // Error: ended up in a state from which there is no possible path +/// // to a successful parse. +/// } else if (S->Value == OMPD_unknown) +/// // Parsed a sequence of tokens that are not a complete name, but +/// // parsing more tokens could lead to a successful parse. +/// } else { +/// // Success. +/// ParsedId = S->Value; +/// } +/// } +struct DirectiveNameParser { + DirectiveNameParser(SourceLanguage L = SourceLanguage::C); + + struct State { + Directive Value = Directive::OMPD_unknown; + + private: + using TransitionMapTy = StringMap<State>; + std::unique_ptr<TransitionMapTy> Transition; + + State *next(StringRef Tok); + bool isValid() const { + return Value != Directive::OMPD_unknown || !Transition->empty(); + } + friend struct DirectiveNameParser; + }; + + const State *initial() const { return &InitialState; } + const State *apply(const State *Current, StringRef Tok) const; + + static SmallVector<StringRef> tokenize(StringRef N); + +private: + void insertName(StringRef Name, Directive D); + State *insertTransition(State *From, StringRef Tok); + + State InitialState; +}; +} // namespace llvm::omp + +#endif // LLVM_FRONTEND_OPENMP_DIRECTIVENAMEPARSER_H diff --git a/llvm/lib/Frontend/OpenMP/CMakeLists.txt b/llvm/lib/Frontend/OpenMP/CMakeLists.txt index 5bf15ca3a8991..e60b59c1203b9 100644 --- a/llvm/lib/Frontend/OpenMP/CMakeLists.txt +++ b/llvm/lib/Frontend/OpenMP/CMakeLists.txt @@ -2,6 +2,7 @@ add_llvm_component_library(LLVMFrontendOpenMP OMP.cpp OMPContext.cpp OMPIRBuilder.cpp + DirectiveNameParser.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp new file mode 100644 index 0000000000000..02ff8327a3054 --- /dev/null +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -0,0 +1,93 @@ +//===- DirectiveNameParser.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Frontend/OpenMP/DirectiveNameParser.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Frontend/OpenMP/OMP.h" + +#include <cassert> +#include <memory> + +namespace llvm::omp { +DirectiveNameParser::DirectiveNameParser(SourceLanguage L) { + // Take every directive, get its name in every version, break the name up + // into whitespace-separated tokens, and insert each token. + for (size_t I = 0, E = Directive_enumSize; I != E; ++I) { + auto D = static_cast<Directive>(I); + if (D == Directive::OMPD_unknown || !(getDirectiveLanguages(D) & L)) + continue; + for (unsigned Ver : getOpenMPVersions()) + insertName(getOpenMPDirectiveName(D, Ver), D); + } +} + +const DirectiveNameParser::State * +DirectiveNameParser::apply(const State *Current, StringRef Tok) const { + if (!Current) + return Current; + assert(Current->isValid() && "Invalid input state"); + if (const State *Next = const_cast<State *>(Current)->next(Tok)) + return Next->isValid() ? Next : nullptr; + return nullptr; +} + +SmallVector<StringRef> DirectiveNameParser::tokenize(StringRef Str) { + SmallVector<StringRef> Tokens; + + auto nextChar = [](StringRef N, size_t I) { + while (I < N.size() && N[I] == ' ') + ++I; + return I; + }; + auto nextSpace = [](StringRef N, size_t I) { + size_t S = N.find(' ', I); + return S != StringRef::npos ? S : N.size(); + }; + + size_t From = nextChar(Str, 0); + size_t To = 0; + + while (From != Str.size()) { + To = nextSpace(Str, From); + Tokens.push_back(Str.substr(From, To - From)); + From = nextChar(Str, To); + } + + return Tokens; +} + +void DirectiveNameParser::insertName(StringRef Name, Directive D) { + State *Where = &InitialState; + + for (StringRef Tok : tokenize(Name)) + Where = insertTransition(Where, Tok); + + Where->Value = D; +} + +DirectiveNameParser::State * +DirectiveNameParser::insertTransition(State *From, StringRef Tok) { + assert(From && "Expecting state"); + if (!From->Transition) { + From->Transition = std::make_unique<State::TransitionMapTy>(); + } + if (State *Next = From->next(Tok)) + return Next; + + auto [Where, DidIt] = From->Transition->try_emplace(Tok, State()); + assert(DidIt && "Map insertion failed"); + return &Where->second; +} + +DirectiveNameParser::State *DirectiveNameParser::State::next(StringRef Tok) { + if (!Transition) + return nullptr; + auto F = Transition->find(Tok); + return F != Transition->end() ? &F->second : nullptr; +} +} // namespace llvm::omp diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt index 2412cc9d26c7a..281d509227a46 100644 --- a/llvm/unittests/Frontend/CMakeLists.txt +++ b/llvm/unittests/Frontend/CMakeLists.txt @@ -20,6 +20,7 @@ add_llvm_unittest(LLVMFrontendTests OpenMPCompositionTest.cpp OpenMPDecompositionTest.cpp OpenMPDirectiveNameTest.cpp + OpenMPDirectiveNameParserTest.cpp DEPENDS acc_gen diff --git a/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp new file mode 100644 index 0000000000000..11fef684dec4c --- /dev/null +++ b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp @@ -0,0 +1,171 @@ +//===- llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Frontend/OpenMP/DirectiveNameParser.h" +#include "llvm/Frontend/OpenMP/OMP.h" +#include "gtest/gtest.h" + +#include <cctype> +#include <sstream> +#include <string> +#include <tuple> +#include <vector> + +using namespace llvm; + +static const omp::DirectiveNameParser &getParser() { + static omp::DirectiveNameParser Parser(omp::SourceLanguage::C | + omp::SourceLanguage::Fortran); + return Parser; +} + +static std::vector<std::string> tokenize(StringRef S) { + std::vector<std::string> Tokens; + + using TokenIterator = std::istream_iterator<std::string>; + std::string Copy = S.str(); + std::istringstream Stream(Copy); + + for (auto I = TokenIterator(Stream), E = TokenIterator(); I != E; ++I) + Tokens.push_back(*I); + return Tokens; +} + +static std::string &prepareParamName(std::string &Name) { + for (size_t I = 0, E = Name.size(); I != E; ++I) { + // The parameter name must only have alphanumeric characters. + if (!isalnum(Name[I])) + Name[I] = 'X'; + } + return Name; +} + +namespace llvm { +template <> struct enum_iteration_traits<omp::Directive> { + static constexpr bool is_iterable = true; +}; +} // namespace llvm + +// Test tokenizing. + +class Tokenize : public testing::TestWithParam<omp::Directive> {}; + +static bool isEqual(const SmallVector<StringRef> &A, + const std::vector<std::string> &B) { + if (A.size() != B.size()) + return false; + + for (size_t I = 0, E = A.size(); I != E; ++I) { + if (A[I] != StringRef(B[I])) + return false; + } + return true; +} + +TEST_P(Tokenize, T) { + omp::Directive DirId = GetParam(); + StringRef Name = omp::getOpenMPDirectiveName(DirId, omp::FallbackVersion); + + SmallVector<StringRef> tokens1 = omp::DirectiveNameParser::tokenize(Name); + std::vector<std::string> tokens2 = tokenize(Name); + ASSERT_TRUE(isEqual(tokens1, tokens2)); +} + +static std::string +getParamName1(const testing::TestParamInfo<Tokenize::ParamType> &Info) { + omp::Directive DirId = Info.param; + std::string Name = + omp::getOpenMPDirectiveName(DirId, omp::FallbackVersion).str(); + return prepareParamName(Name); +} + +INSTANTIATE_TEST_SUITE_P( + DirectiveNameParserTest, Tokenize, + testing::ValuesIn( + llvm::enum_seq(static_cast<omp::Directive>(0), + static_cast<omp::Directive>(omp::Directive_enumSize))), + getParamName1); + +// Test parsing of valid names. + +using ValueType = std::tuple<omp::Directive, unsigned>; + +class ParseValid : public testing::TestWithParam<ValueType> {}; + +TEST_P(ParseValid, T) { + auto [DirId, Version] = GetParam(); + if (DirId == omp::Directive::OMPD_unknown) + return; + + std::string Name = omp::getOpenMPDirectiveName(DirId, Version).str(); + + // Tokenize and parse + auto &Parser = getParser(); + auto *State = Parser.initial(); + ASSERT_TRUE(State != nullptr); + + std::vector<std::string> Tokens = tokenize(Name); + for (auto &Tok : Tokens) { + State = Parser.apply(State, Tok); + ASSERT_TRUE(State != nullptr); + } + + ASSERT_EQ(State->Value, DirId); +} + +static std::string +getParamName2(const testing::TestParamInfo<ParseValid::ParamType> &Info) { + auto [DirId, Version] = Info.param; + std::string Name = omp::getOpenMPDirectiveName(DirId, Version).str() + "v" + + std::to_string(Version); + return prepareParamName(Name); +} + +INSTANTIATE_TEST_SUITE_P( + DirectiveNameParserTest, ParseValid, + testing::Combine(testing::ValuesIn(llvm::enum_seq( + static_cast<omp::Directive>(0), + static_cast<omp::Directive>(omp::Directive_enumSize))), + testing::ValuesIn(omp::getOpenMPVersions())), + getParamName2); + +// Test parsing of invalid names + +class ParseInvalid : public testing::TestWithParam<std::string> {}; + +TEST_P(ParseInvalid, T) { + std::string Name = GetParam(); + + auto &Parser = getParser(); + auto *State = Parser.initial(); + ASSERT_TRUE(State != nullptr); + + std::vector<std::string> Tokens = tokenize(Name); + for (auto &Tok : Tokens) + State = Parser.apply(State, Tok); + + ASSERT_TRUE(State == nullptr || State->Value == omp::Directive::OMPD_unknown); +} + +namespace { +using namespace std; + +INSTANTIATE_TEST_SUITE_P(DirectiveNameParserTest, ParseInvalid, + testing::Values( + // Names that contain invalid tokens + "bad"s, "target teams invalid"s, + "target sections parallel"s, + "target teams distribute parallel for wrong"s, + // Valid beginning, but not a complete name + "begin declare"s, + // Complete name with extra tokens + "distribute simd target"s)); +} // namespace >From ab3f0cc9d240f12e07be452effa75b5c7d010d9b Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 2 Jul 2025 11:25:00 -0500 Subject: [PATCH 02/12] [clang][OpenMP] Use DirectiveNameParser to parse directive names This simplifies the parsing code in clang quite a bit. --- clang/lib/Parse/ParseOpenMP.cpp | 181 ++++---------------------------- 1 file changed, 19 insertions(+), 162 deletions(-) diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index f694ae1d0d112..c0a17d0e9537d 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -25,6 +25,7 @@ #include "clang/Sema/SemaOpenMP.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Frontend/OpenMP/DirectiveNameParser.h" #include "llvm/Frontend/OpenMP/OMPAssume.h" #include "llvm/Frontend/OpenMP/OMPContext.h" #include <optional> @@ -37,48 +38,6 @@ using namespace llvm::omp; //===----------------------------------------------------------------------===// namespace { -enum OpenMPDirectiveKindEx { - OMPD_cancellation = llvm::omp::Directive_enumSize + 1, - OMPD_data, - OMPD_declare, - OMPD_end, - OMPD_end_declare, - OMPD_enter, - OMPD_exit, - OMPD_point, - OMPD_reduction, - OMPD_target_enter, - OMPD_target_exit, - OMPD_update, - OMPD_distribute_parallel, - OMPD_teams_distribute_parallel, - OMPD_target_teams_distribute_parallel, - OMPD_mapper, - OMPD_variant, - OMPD_begin, - OMPD_begin_declare, -}; - -// Helper to unify the enum class OpenMPDirectiveKind with its extension -// the OpenMPDirectiveKindEx enum which allows to use them together as if they -// are unsigned values. -struct OpenMPDirectiveKindExWrapper { - OpenMPDirectiveKindExWrapper(unsigned Value) : Value(Value) {} - OpenMPDirectiveKindExWrapper(OpenMPDirectiveKind DK) : Value(unsigned(DK)) {} - bool operator==(OpenMPDirectiveKindExWrapper V) const { - return Value == V.Value; - } - bool operator!=(OpenMPDirectiveKindExWrapper V) const { - return Value != V.Value; - } - bool operator==(OpenMPDirectiveKind V) const { return Value == unsigned(V); } - bool operator!=(OpenMPDirectiveKind V) const { return Value != unsigned(V); } - bool operator<(OpenMPDirectiveKind V) const { return Value < unsigned(V); } - operator unsigned() const { return Value; } - operator OpenMPDirectiveKind() const { return OpenMPDirectiveKind(Value); } - unsigned Value; -}; - class DeclDirectiveListParserHelper final { SmallVector<Expr *, 4> Identifiers; Parser *P; @@ -97,130 +56,32 @@ class DeclDirectiveListParserHelper final { }; } // namespace -// Map token string to extended OMP token kind that are -// OpenMPDirectiveKind + OpenMPDirectiveKindEx. -static unsigned getOpenMPDirectiveKindEx(StringRef S) { - OpenMPDirectiveKindExWrapper DKind = getOpenMPDirectiveKind(S); - if (DKind != OMPD_unknown) - return DKind; - - return llvm::StringSwitch<OpenMPDirectiveKindExWrapper>(S) - .Case("cancellation", OMPD_cancellation) - .Case("data", OMPD_data) - .Case("declare", OMPD_declare) - .Case("end", OMPD_end) - .Case("enter", OMPD_enter) - .Case("exit", OMPD_exit) - .Case("point", OMPD_point) - .Case("reduction", OMPD_reduction) - .Case("update", OMPD_update) - .Case("mapper", OMPD_mapper) - .Case("variant", OMPD_variant) - .Case("begin", OMPD_begin) - .Default(OMPD_unknown); -} +static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { + static const DirectiveNameParser DNP; + + const DirectiveNameParser::State *S = DNP.initial(); -static OpenMPDirectiveKindExWrapper parseOpenMPDirectiveKind(Parser &P) { - // Array of foldings: F[i][0] F[i][1] ===> F[i][2]. - // E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd - // TODO: add other combined directives in topological order. - static const OpenMPDirectiveKindExWrapper F[][3] = { - {OMPD_begin, OMPD_declare, OMPD_begin_declare}, - {OMPD_begin, OMPD_assumes, OMPD_begin_assumes}, - {OMPD_end, OMPD_declare, OMPD_end_declare}, - {OMPD_end, OMPD_assumes, OMPD_end_assumes}, - {OMPD_cancellation, OMPD_point, OMPD_cancellation_point}, - {OMPD_declare, OMPD_reduction, OMPD_declare_reduction}, - {OMPD_declare, OMPD_mapper, OMPD_declare_mapper}, - {OMPD_declare, OMPD_simd, OMPD_declare_simd}, - {OMPD_declare, OMPD_target, OMPD_declare_target}, - {OMPD_declare, OMPD_variant, OMPD_declare_variant}, - {OMPD_begin_declare, OMPD_target, OMPD_begin_declare_target}, - {OMPD_begin_declare, OMPD_variant, OMPD_begin_declare_variant}, - {OMPD_end_declare, OMPD_variant, OMPD_end_declare_variant}, - {OMPD_distribute, OMPD_parallel, OMPD_distribute_parallel}, - {OMPD_distribute_parallel, OMPD_for, OMPD_distribute_parallel_for}, - {OMPD_distribute_parallel_for, OMPD_simd, - OMPD_distribute_parallel_for_simd}, - {OMPD_distribute, OMPD_simd, OMPD_distribute_simd}, - {OMPD_end_declare, OMPD_target, OMPD_end_declare_target}, - {OMPD_target, OMPD_data, OMPD_target_data}, - {OMPD_target, OMPD_enter, OMPD_target_enter}, - {OMPD_target, OMPD_exit, OMPD_target_exit}, - {OMPD_target, OMPD_update, OMPD_target_update}, - {OMPD_target_enter, OMPD_data, OMPD_target_enter_data}, - {OMPD_target_exit, OMPD_data, OMPD_target_exit_data}, - {OMPD_for, OMPD_simd, OMPD_for_simd}, - {OMPD_parallel, OMPD_for, OMPD_parallel_for}, - {OMPD_parallel_for, OMPD_simd, OMPD_parallel_for_simd}, - {OMPD_parallel, OMPD_loop, OMPD_parallel_loop}, - {OMPD_parallel, OMPD_sections, OMPD_parallel_sections}, - {OMPD_taskloop, OMPD_simd, OMPD_taskloop_simd}, - {OMPD_target, OMPD_parallel, OMPD_target_parallel}, - {OMPD_target, OMPD_simd, OMPD_target_simd}, - {OMPD_target_parallel, OMPD_loop, OMPD_target_parallel_loop}, - {OMPD_target_parallel, OMPD_for, OMPD_target_parallel_for}, - {OMPD_target_parallel_for, OMPD_simd, OMPD_target_parallel_for_simd}, - {OMPD_teams, OMPD_distribute, OMPD_teams_distribute}, - {OMPD_teams_distribute, OMPD_simd, OMPD_teams_distribute_simd}, - {OMPD_teams_distribute, OMPD_parallel, OMPD_teams_distribute_parallel}, - {OMPD_teams_distribute_parallel, OMPD_for, - OMPD_teams_distribute_parallel_for}, - {OMPD_teams_distribute_parallel_for, OMPD_simd, - OMPD_teams_distribute_parallel_for_simd}, - {OMPD_teams, OMPD_loop, OMPD_teams_loop}, - {OMPD_target, OMPD_teams, OMPD_target_teams}, - {OMPD_target_teams, OMPD_distribute, OMPD_target_teams_distribute}, - {OMPD_target_teams, OMPD_loop, OMPD_target_teams_loop}, - {OMPD_target_teams_distribute, OMPD_parallel, - OMPD_target_teams_distribute_parallel}, - {OMPD_target_teams_distribute, OMPD_simd, - OMPD_target_teams_distribute_simd}, - {OMPD_target_teams_distribute_parallel, OMPD_for, - OMPD_target_teams_distribute_parallel_for}, - {OMPD_target_teams_distribute_parallel_for, OMPD_simd, - OMPD_target_teams_distribute_parallel_for_simd}, - {OMPD_master, OMPD_taskloop, OMPD_master_taskloop}, - {OMPD_masked, OMPD_taskloop, OMPD_masked_taskloop}, - {OMPD_master_taskloop, OMPD_simd, OMPD_master_taskloop_simd}, - {OMPD_masked_taskloop, OMPD_simd, OMPD_masked_taskloop_simd}, - {OMPD_parallel, OMPD_master, OMPD_parallel_master}, - {OMPD_parallel, OMPD_masked, OMPD_parallel_masked}, - {OMPD_parallel_master, OMPD_taskloop, OMPD_parallel_master_taskloop}, - {OMPD_parallel_masked, OMPD_taskloop, OMPD_parallel_masked_taskloop}, - {OMPD_parallel_master_taskloop, OMPD_simd, - OMPD_parallel_master_taskloop_simd}, - {OMPD_parallel_masked_taskloop, OMPD_simd, - OMPD_parallel_masked_taskloop_simd}}; - enum { CancellationPoint = 0, DeclareReduction = 1, TargetData = 2 }; Token Tok = P.getCurToken(); - OpenMPDirectiveKindExWrapper DKind = - Tok.isAnnotation() - ? static_cast<unsigned>(OMPD_unknown) - : getOpenMPDirectiveKindEx(P.getPreprocessor().getSpelling(Tok)); - if (DKind == OMPD_unknown) + if (Tok.isAnnotation()) return OMPD_unknown; - for (const auto &I : F) { - if (DKind != I[0]) - continue; + S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok)); + if (S == nullptr) + return OMPD_unknown; + while (!Tok.isAnnotation()) { + OpenMPDirectiveKind DKind = S->Value; Tok = P.getPreprocessor().LookAhead(0); - OpenMPDirectiveKindExWrapper SDKind = - Tok.isAnnotation() - ? static_cast<unsigned>(OMPD_unknown) - : getOpenMPDirectiveKindEx(P.getPreprocessor().getSpelling(Tok)); - if (SDKind == OMPD_unknown) - continue; - - if (SDKind == I[1]) { + if (!Tok.isAnnotation()) { + S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok)); + if (S == nullptr) + return DKind; P.ConsumeToken(); - DKind = I[2]; } } - return unsigned(DKind) < llvm::omp::Directive_enumSize - ? static_cast<OpenMPDirectiveKind>(DKind) - : OMPD_unknown; + + assert(S && "Should have exited early"); + return S->Value; } static DeclarationName parseOpenMPReductionId(Parser &P) { @@ -2629,10 +2490,6 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( Diag(Tok, diag::err_omp_unknown_directive); return StmtError(); } - if (!(getDirectiveLanguages(DKind) & SourceLanguage::C)) { - // Treat directives that are not allowed in C/C++ as unknown. - DKind = OMPD_unknown; - } StmtResult Directive = StmtError(); @@ -4014,7 +3871,7 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind, KLoc.push_back(Tok.getLocation()); TentativeParsingAction TPA(*this); auto DK = parseOpenMPDirectiveKind(*this); - Arg.push_back(DK); + Arg.push_back(static_cast<unsigned>(DK)); if (DK != OMPD_unknown) { ConsumeToken(); if (Tok.is(tok::colon) && getLangOpts().OpenMP > 40) { >From f49ba8b84a8218e52c09713be49a4a04bfa36920 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 2 Jul 2025 16:49:32 -0500 Subject: [PATCH 03/12] Use llvm::seq --- llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp index 02ff8327a3054..b560858b44df6 100644 --- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Frontend/OpenMP/DirectiveNameParser.h" +#include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringRef.h" #include "llvm/Frontend/OpenMP/OMP.h" @@ -17,7 +18,7 @@ namespace llvm::omp { DirectiveNameParser::DirectiveNameParser(SourceLanguage L) { // Take every directive, get its name in every version, break the name up // into whitespace-separated tokens, and insert each token. - for (size_t I = 0, E = Directive_enumSize; I != E; ++I) { + for (size_t I : llvm::seq<size_t>(Directive_enumSize)) { auto D = static_cast<Directive>(I); if (D == Directive::OMPD_unknown || !(getDirectiveLanguages(D) & L)) continue; >From 538cfcdd285c34d08ff6a67253e20343547982f1 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 2 Jul 2025 16:49:36 -0500 Subject: [PATCH 04/12] Add const version of next() --- .../include/llvm/Frontend/OpenMP/DirectiveNameParser.h | 1 + llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h index db8986601b2ca..9996375c9023e 100644 --- a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h +++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h @@ -54,6 +54,7 @@ struct DirectiveNameParser { std::unique_ptr<TransitionMapTy> Transition; State *next(StringRef Tok); + const State *next(StringRef Tok) const; bool isValid() const { return Value != Directive::OMPD_unknown || !Transition->empty(); } diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp index b560858b44df6..4aaf25cc32e05 100644 --- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -32,7 +32,7 @@ DirectiveNameParser::apply(const State *Current, StringRef Tok) const { if (!Current) return Current; assert(Current->isValid() && "Invalid input state"); - if (const State *Next = const_cast<State *>(Current)->next(Tok)) + if (const State *Next = Current->next(Tok)) return Next->isValid() ? Next : nullptr; return nullptr; } @@ -85,6 +85,14 @@ DirectiveNameParser::insertTransition(State *From, StringRef Tok) { return &Where->second; } +const DirectiveNameParser::State * +DirectiveNameParser::State::next(StringRef Tok) const { + if (!Transition) + return nullptr; + auto F = Transition->find(Tok); + return F != Transition->end() ? &F->second : nullptr; +} + DirectiveNameParser::State *DirectiveNameParser::State::next(StringRef Tok) { if (!Transition) return nullptr; >From 9d35e461829665e3eb1d9c4cc03548b02b2c0925 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 2 Jul 2025 16:52:09 -0500 Subject: [PATCH 05/12] Add missing brace in comment --- llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h index 9996375c9023e..b1ff34c003295 100644 --- a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h +++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h @@ -35,7 +35,7 @@ namespace llvm::omp { /// if (S == nullptr) { /// // Error: ended up in a state from which there is no possible path /// // to a successful parse. -/// } else if (S->Value == OMPD_unknown) +/// } else if (S->Value == OMPD_unknown) { /// // Parsed a sequence of tokens that are not a complete name, but /// // parsing more tokens could lead to a successful parse. /// } else { >From 5d2a8a367eb4e13b298897f85120e785dd97e39b Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 3 Jul 2025 10:03:58 -0500 Subject: [PATCH 06/12] Rename apply to consume --- llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h | 4 ++-- llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 2 +- llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h index b1ff34c003295..898c32aa4b8f2 100644 --- a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h +++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h @@ -30,7 +30,7 @@ namespace llvm::omp { /// /// DirectiveNameParser::State *S = Parser.initial(); /// for (StringRef Token : Tokens) -/// S = Parser.apply(S, Token); // Passing nullptr is ok. +/// S = Parser.consume(S, Token); // Passing nullptr is ok. /// /// if (S == nullptr) { /// // Error: ended up in a state from which there is no possible path @@ -62,7 +62,7 @@ struct DirectiveNameParser { }; const State *initial() const { return &InitialState; } - const State *apply(const State *Current, StringRef Tok) const; + const State *consume(const State *Current, StringRef Tok) const; static SmallVector<StringRef> tokenize(StringRef N); diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp index 4aaf25cc32e05..037ffe2c13f1e 100644 --- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -28,7 +28,7 @@ DirectiveNameParser::DirectiveNameParser(SourceLanguage L) { } const DirectiveNameParser::State * -DirectiveNameParser::apply(const State *Current, StringRef Tok) const { +DirectiveNameParser::consume(const State *Current, StringRef Tok) const { if (!Current) return Current; assert(Current->isValid() && "Invalid input state"); diff --git a/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp index 11fef684dec4c..0363a08cc0f03 100644 --- a/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp +++ b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp @@ -114,7 +114,7 @@ TEST_P(ParseValid, T) { std::vector<std::string> Tokens = tokenize(Name); for (auto &Tok : Tokens) { - State = Parser.apply(State, Tok); + State = Parser.consume(State, Tok); ASSERT_TRUE(State != nullptr); } @@ -150,7 +150,7 @@ TEST_P(ParseInvalid, T) { std::vector<std::string> Tokens = tokenize(Name); for (auto &Tok : Tokens) - State = Parser.apply(State, Tok); + State = Parser.consume(State, Tok); ASSERT_TRUE(State == nullptr || State->Value == omp::Directive::OMPD_unknown); } >From cde46dc3d14d224c4f4f1ec9ef8eafb4d9d12828 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 3 Jul 2025 10:04:23 -0500 Subject: [PATCH 07/12] Use isspace instead of ' ' --- .../Frontend/OpenMP/DirectiveNameParser.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp index 037ffe2c13f1e..d42a3e98adb37 100644 --- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -12,6 +12,7 @@ #include "llvm/Frontend/OpenMP/OMP.h" #include <cassert> +#include <cctype> #include <memory> namespace llvm::omp { @@ -40,23 +41,24 @@ DirectiveNameParser::consume(const State *Current, StringRef Tok) const { SmallVector<StringRef> DirectiveNameParser::tokenize(StringRef Str) { SmallVector<StringRef> Tokens; - auto nextChar = [](StringRef N, size_t I) { - while (I < N.size() && N[I] == ' ') + auto NextChar = [](StringRef N, size_t I) { + while (I < N.size() && isspace(N[I])) ++I; return I; }; - auto nextSpace = [](StringRef N, size_t I) { - size_t S = N.find(' ', I); - return S != StringRef::npos ? S : N.size(); + auto NextSpace = [](StringRef N, size_t I) { + while (I < N.size() && !isspace(N[I])) + ++I; + return I; }; - size_t From = nextChar(Str, 0); + size_t From = NextChar(Str, 0); size_t To = 0; while (From != Str.size()) { - To = nextSpace(Str, From); + To = NextSpace(Str, From); Tokens.push_back(Str.substr(From, To - From)); - From = nextChar(Str, To); + From = NextChar(Str, To); } return Tokens; >From e86ac032518655d19f0f5b00d6987a20edbf42ba Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 3 Jul 2025 10:40:35 -0500 Subject: [PATCH 08/12] Use StringSplit --- .../Frontend/OpenMP/DirectiveNameParser.cpp | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp index d42a3e98adb37..2246fed78aa72 100644 --- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -8,11 +8,11 @@ #include "llvm/Frontend/OpenMP/DirectiveNameParser.h" #include "llvm/ADT/Sequence.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Frontend/OpenMP/OMP.h" #include <cassert> -#include <cctype> #include <memory> namespace llvm::omp { @@ -40,27 +40,7 @@ DirectiveNameParser::consume(const State *Current, StringRef Tok) const { SmallVector<StringRef> DirectiveNameParser::tokenize(StringRef Str) { SmallVector<StringRef> Tokens; - - auto NextChar = [](StringRef N, size_t I) { - while (I < N.size() && isspace(N[I])) - ++I; - return I; - }; - auto NextSpace = [](StringRef N, size_t I) { - while (I < N.size() && !isspace(N[I])) - ++I; - return I; - }; - - size_t From = NextChar(Str, 0); - size_t To = 0; - - while (From != Str.size()) { - To = NextSpace(Str, From); - Tokens.push_back(Str.substr(From, To - From)); - From = NextChar(Str, To); - } - + SplitString(Str, Tokens); return Tokens; } >From 2768326ecd62c2831cda66f1123f302c377fe79d Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 3 Jul 2025 10:42:06 -0500 Subject: [PATCH 09/12] Remove {} around single statement --- llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp index 2246fed78aa72..62e24825111f6 100644 --- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp +++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp @@ -56,9 +56,8 @@ void DirectiveNameParser::insertName(StringRef Name, Directive D) { DirectiveNameParser::State * DirectiveNameParser::insertTransition(State *From, StringRef Tok) { assert(From && "Expecting state"); - if (!From->Transition) { + if (!From->Transition) From->Transition = std::make_unique<State::TransitionMapTy>(); - } if (State *Next = From->next(Tok)) return Next; >From ca31af4bbb783cbecab3d4c7af3c2f240e435fc1 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 3 Jul 2025 11:16:03 -0500 Subject: [PATCH 10/12] Use consume instead ofof apply --- clang/lib/Parse/ParseOpenMP.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index c0a17d0e9537d..c3c6149d9de1a 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -65,7 +65,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { if (Tok.isAnnotation()) return OMPD_unknown; - S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok)); + S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok)); if (S == nullptr) return OMPD_unknown; @@ -73,7 +73,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { OpenMPDirectiveKind DKind = S->Value; Tok = P.getPreprocessor().LookAhead(0); if (!Tok.isAnnotation()) { - S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok)); + S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok)); if (S == nullptr) return DKind; P.ConsumeToken(); >From 49ef393290309ea9c0caed965b0b182c02356529 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 3 Jul 2025 12:18:55 -0500 Subject: [PATCH 11/12] Rename DNP to DirParser --- clang/lib/Parse/ParseOpenMP.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index c3c6149d9de1a..5256d08259b60 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -57,15 +57,15 @@ class DeclDirectiveListParserHelper final { } // namespace static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { - static const DirectiveNameParser DNP; + static const DirectiveNameParser DirParser; - const DirectiveNameParser::State *S = DNP.initial(); + const DirectiveNameParser::State *S = DirParser.initial(); Token Tok = P.getCurToken(); if (Tok.isAnnotation()) return OMPD_unknown; - S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok)); + S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok)); if (S == nullptr) return OMPD_unknown; @@ -73,7 +73,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { OpenMPDirectiveKind DKind = S->Value; Tok = P.getPreprocessor().LookAhead(0); if (!Tok.isAnnotation()) { - S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok)); + S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok)); if (S == nullptr) return DKind; P.ConsumeToken(); >From 5ad103e08e8a06cfc3708ba83601e073a022bb7e Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 2 Jul 2025 12:49:04 -0500 Subject: [PATCH 12/12] [clang][OpenMP] Issue a warning when parsing future directive spelling OpenMP 6.0 introduced alternative spelling for some directives, with the previous spellings still being allowed. Warn the user when a new spelling is encountered with OpenMP version set to an older value. --- clang/include/clang/Basic/DiagnosticGroups.td | 4 +- .../clang/Basic/DiagnosticParseKinds.td | 3 + clang/lib/Parse/ParseOpenMP.cpp | 28 ++++++++-- .../test/OpenMP/openmp-6-future-spellings.cpp | 55 +++++++++++++++++++ 4 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 clang/test/OpenMP/openmp-6-future-spellings.cpp diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 36fa3227fd6a6..ace8663b73a4a 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1530,9 +1530,11 @@ def OpenMPPre51Compat : DiagGroup<"pre-openmp-51-compat">; def OpenMP51Ext : DiagGroup<"openmp-51-extensions">; def OpenMPExtensions : DiagGroup<"openmp-extensions">; def OpenMPTargetException : DiagGroup<"openmp-target-exception">; +def OpenMPFuture : DiagGroup<"openmp-future">; def OpenMP : DiagGroup<"openmp", [ SourceUsesOpenMP, OpenMPClauses, OpenMPLoopForm, OpenMPTarget, - OpenMPMapping, OpenMP51Ext, OpenMPExtensions, OpenMPTargetException + OpenMPMapping, OpenMP51Ext, OpenMPExtensions, OpenMPTargetException, + OpenMPFuture ]>; // OpenACC warnings. diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 6c30da376dafb..87eb2b724b297 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1488,6 +1488,9 @@ def err_omp_multiple_step_or_linear_modifier : Error< "multiple %select{'step size'|'linear modifier'}0 found in linear clause">; def err_omp_deprecate_old_syntax: Error< "old syntax '%0' on '%1' clause was deprecated, use new syntax '%2'">; +def warn_omp_future_directive_spelling: Warning< + "directive spelling '%0' is introduced in a later OpenMP version">, + InGroup<OpenMPFuture>; def warn_pragma_expected_colon_r_paren : Warning< "missing ':' or ')' after %0 - ignoring">, InGroup<IgnoredPragmas>; def err_omp_unknown_directive : Error< diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 5256d08259b60..cb9eb3304c317 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -56,6 +56,21 @@ class DeclDirectiveListParserHelper final { }; } // namespace +static OpenMPDirectiveKind checkOpenMPDirectiveName(Parser &P, + SourceLocation Loc, + OpenMPDirectiveKind Kind, + StringRef Name) { + unsigned Version = P.getLangOpts().OpenMP; + auto [D, VR] = getOpenMPDirectiveKindAndVersions(Name); + assert(D == Kind && "Directive kind mismatch"); + // Ignore the case Version > VR.Max: In OpenMP 6.0 all prior spellings + // are explicitly allowed. + if (Version < VR.Min) + P.Diag(Loc, diag::warn_omp_future_directive_spelling) << Name; + + return Kind; +} + static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { static const DirectiveNameParser DirParser; @@ -65,7 +80,10 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { if (Tok.isAnnotation()) return OMPD_unknown; - S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok)); + std::string Concat = P.getPreprocessor().getSpelling(Tok); + SourceLocation Loc = Tok.getLocation(); + + S = DirParser.consume(S, Concat); if (S == nullptr) return OMPD_unknown; @@ -73,15 +91,17 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { OpenMPDirectiveKind DKind = S->Value; Tok = P.getPreprocessor().LookAhead(0); if (!Tok.isAnnotation()) { - S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok)); + std::string TS = P.getPreprocessor().getSpelling(Tok); + S = DirParser.consume(S, TS); if (S == nullptr) - return DKind; + return checkOpenMPDirectiveName(P, Loc, DKind, Concat); + Concat += ' ' + TS; P.ConsumeToken(); } } assert(S && "Should have exited early"); - return S->Value; + return checkOpenMPDirectiveName(P, Loc, S->Value, Concat); } static DeclarationName parseOpenMPReductionId(Parser &P) { diff --git a/clang/test/OpenMP/openmp-6-future-spellings.cpp b/clang/test/OpenMP/openmp-6-future-spellings.cpp new file mode 100644 index 0000000000000..642ed3502d475 --- /dev/null +++ b/clang/test/OpenMP/openmp-6-future-spellings.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -ferror-limit 100 -o - %s + +// expected-warning@+1 {{directive spelling 'begin declare_target' is introduced in a later OpenMP version}} +#pragma omp begin declare_target +void f0(); +// expected-warning@+1 {{directive spelling 'end declare_target' is introduced in a later OpenMP version}} +#pragma omp end declare_target + +// expected-warning@+1 {{directive spelling 'begin declare_variant' is introduced in a later OpenMP version}} +#pragma omp begin declare_variant match(user={condition(true)}) +void f1(); +// expected-warning@+1 {{directive spelling 'end declare_variant' is introduced in a later OpenMP version}} +#pragma omp end declare_variant + +int x; +// expected-warning@+1 {{directive spelling 'declare_target' is introduced in a later OpenMP version}} +#pragma omp declare_target(x) + +struct A { + int x, y; +}; +// expected-warning@+1 {{directive spelling 'declare_mapper' is introduced in a later OpenMP version}} +#pragma omp declare_mapper(mymapper: A a) map(tofrom:a.x, a.y) +A add(A, A); +// expected-warning@+1 {{directive spelling 'declare_reduction' is introduced in a later OpenMP version}} +#pragma omp declare_reduction(+: A: omp_out = add(omp_in, omp_out)) + +// expected-warning@+1 {{directive spelling 'declare_simd' is introduced in a later OpenMP version}} +#pragma omp declare_simd +void f2(); + +void g3(); +// expected-warning@+1 {{directive spelling 'declare_variant' is introduced in a later OpenMP version}} +#pragma omp declare_variant(g3) match(user={condition(true)}) +void f3() {} + +void fred() { + #pragma omp parallel + { + // expected-warning@+1 {{directive spelling 'cancellation_point' is introduced in a later OpenMP version}} + #pragma omp cancellation_point parallel + } + + // expected-warning@+1 {{directive spelling 'target_data' is introduced in a later OpenMP version}} + #pragma omp target_data map(tofrom: x) + {} + + // expected-warning@+1 {{directive spelling 'target_enter_data' is introduced in a later OpenMP version}} + #pragma omp target_enter_data map(to: x) + // expected-warning@+1 {{directive spelling 'target_exit_data' is introduced in a later OpenMP version}} + #pragma omp target_exit_data map(from: x) + // expected-warning@+1 {{directive spelling 'target_update' is introduced in a later OpenMP version}} + #pragma omp target_update from(x) +} + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits