Author: Ferran Toda
Date: 2026-07-09T10:51:35-05:00
New Revision: 3b2fe66a4eb5fc569cc9a9822440ebe3a1a36f34

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

LOG: [Flang][OpenMP] add Apply clause for OpenMP Flang (#204225)

This patch adds a parser and basic checks for the Apply clause from
OpenMP 6.0 in Flang.
It is missing semantic checks for nesting loop transformations that are
being worked on.

Added: 
    flang/test/Parser/OpenMP/apply01.f90
    flang/test/Parser/OpenMP/apply02.f90
    flang/test/Semantics/OpenMP/apply01.f90
    flang/test/Semantics/OpenMP/apply02.f90

Modified: 
    clang/test/OpenMP/unimplemented_clause_messages.cpp
    flang/include/flang/Parser/dump-parse-tree.h
    flang/include/flang/Parser/parse-tree.h
    flang/include/flang/Semantics/openmp-modifiers.h
    flang/lib/Parser/openmp-parsers.cpp
    flang/lib/Parser/unparse.cpp
    flang/lib/Semantics/check-omp-loop.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/lib/Semantics/check-omp-variant.cpp
    flang/lib/Semantics/openmp-modifiers.cpp
    llvm/include/llvm/Frontend/Directive/DirectiveBase.td
    llvm/include/llvm/Frontend/OpenMP/OMP.td
    llvm/include/llvm/TableGen/DirectiveEmitter.h
    llvm/test/TableGen/directive1.td
    llvm/test/TableGen/directive2.td
    llvm/utils/TableGen/Basic/DirectiveEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/test/OpenMP/unimplemented_clause_messages.cpp 
b/clang/test/OpenMP/unimplemented_clause_messages.cpp
index 172203ea5d040..0f51c916093ff 100644
--- a/clang/test/OpenMP/unimplemented_clause_messages.cpp
+++ b/clang/test/OpenMP/unimplemented_clause_messages.cpp
@@ -16,8 +16,7 @@ void test_induction_basic() {
 }
 
 void test_apply() {
-  // omp60-warning@+4{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
-  // omp60-error@+3{{unexpected OpenMP clause 'apply' in directive '#pragma 
omp tile'}}
+  // omp60-warning@+3{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
   // omp51-error@+2{{unexpected OpenMP clause 'apply' in directive '#pragma 
omp tile'}}
   // omp51-warning@+1{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
 #pragma omp tile sizes(10) apply(intratile: unroll)
@@ -26,8 +25,7 @@ void test_apply() {
 }
 
 void test_empty_apply() {
- // omp60-warning@+4{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
-  // omp60-error@+3{{unexpected OpenMP clause 'apply' in directive '#pragma 
omp tile'}}
+ // omp60-warning@+3{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
   // omp51-error@+2{{unexpected OpenMP clause 'apply' in directive '#pragma 
omp tile'}}
   // omp51-warning@+1{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
 #pragma omp tile sizes(10) apply()
@@ -37,7 +35,6 @@ void test_empty_apply() {
 
 void test_nested_apply()
 {
-  // omp60-error@+5{{unexpected OpenMP clause 'apply' in directive '#pragma 
omp tile'}}
   // omp60-warning@+4{{extra tokens at the end of '#pragma omp tile' are 
ignored}}
   //omp51-error@+3{{unexpected OpenMP clause 'apply' in directive '#pragma omp 
tile'}}
   // omp51-warning@+2{{extra tokens at the end of '#pragma omp tile' are 
ignored}}

diff  --git a/flang/include/flang/Parser/dump-parse-tree.h 
b/flang/include/flang/Parser/dump-parse-tree.h
index ceba23d7d4706..030b0cc6f5301 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -556,6 +556,15 @@ class ParseTreeDumper {
   NODE_ENUM(OmpAlwaysModifier, Value)
   NODE(parser, OmpAppendArgsClause)
   NODE(OmpAppendArgsClause, OmpAppendOp)
+  NODE(parser, OmpLoopModifier)
+
+  static std::string GetNodeName(const llvm::omp::LoopModifier &x) {
+    return llvm::Twine(
+        "llvm::omp::LoopModifier = ", llvm::omp::getLoopModifierName(x))
+        .str();
+  }
+  NODE(parser, OmpApplyClause)
+  NODE(OmpApplyClause, Modifier)
   NODE(parser, OmpArgument)
   NODE(parser, OmpArgumentList)
   NODE(parser, OmpAssumeDirective)

diff  --git a/flang/include/flang/Parser/parse-tree.h 
b/flang/include/flang/Parser/parse-tree.h
index a6fecf61593cb..f1e483922eee4 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4155,6 +4155,24 @@ struct OmpLinearModifier {
   WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Value);
 };
 
+// Ref: [6.0:372-373]
+//
+// loop-modifier ->
+//    FLATTENED |
+//    FUSED | GRID | IDENTITY |
+//    INTERCHANGED |
+//    INTRATILE | OFFSETS |
+//    REVERSED | SPLIT |
+//    UNROLLED
+//    [( ScalarIntConstantExpr-list )]
+struct OmpLoopModifier {
+  TUPLE_CLASS_BOILERPLATE(OmpLoopModifier);
+  std::tuple<llvm::omp::LoopModifier,
+      std::optional<std::list<ScalarIntConstantExpr>>>
+      t;
+  CharBlock source;
+};
+
 // Ref: [5.1:100-104], [5.2:277], [6.0:452-453]
 //
 // lower-bound ->
@@ -4485,6 +4503,16 @@ struct OmpContainsClause {
   WRAPPER_CLASS_BOILERPLATE(OmpContainsClause, OmpDirectiveList);
 };
 
+// Ref: [6.0:372-373]
+//
+// apply-clause ->
+//    APPLY( [loop-modifier :] directive-specification-list )
+struct OmpApplyClause {
+  TUPLE_CLASS_BOILERPLATE(OmpApplyClause);
+  MODIFIER_BOILERPLATE(OmpLoopModifier);
+  std::tuple<MODIFIERS(), std::list<OmpDirectiveSpecification>> t;
+};
+
 // Ref: [4.5:46-50], [5.0:74-78], [5.1:92-96], [5.2:109]
 //
 // When used as a data-sharing clause:

diff  --git a/flang/include/flang/Semantics/openmp-modifiers.h 
b/flang/include/flang/Semantics/openmp-modifiers.h
index 8dcac9b179924..66a4f3319c4d1 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -90,6 +90,7 @@ DECLARE_DESCRIPTOR(parser::OmpInteropType);
 DECLARE_DESCRIPTOR(parser::OmpIterator);
 DECLARE_DESCRIPTOR(parser::OmpLastprivateModifier);
 DECLARE_DESCRIPTOR(parser::OmpLinearModifier);
+DECLARE_DESCRIPTOR(parser::OmpLoopModifier);
 DECLARE_DESCRIPTOR(parser::OmpLowerBound);
 DECLARE_DESCRIPTOR(parser::OmpMapper);
 DECLARE_DESCRIPTOR(parser::OmpMapType);

diff  --git a/flang/lib/Parser/openmp-parsers.cpp 
b/flang/lib/Parser/openmp-parsers.cpp
index 2c5f9c19a944e..105a72de5cb29 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1133,6 +1133,20 @@ TYPE_PARSER(construct<OmpAdjustArgsClause::OmpAdjustOp>(
     "NEED_DEVICE_PTR" >>
         pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Need_Device_Ptr)))
 
+TYPE_PARSER(construct<OmpApplyClause::Modifier>(Parser<OmpLoopModifier>{}))
+
+TYPE_PARSER(sourced(construct<OmpLoopModifier>(
+    "FUSED" >> pure(llvm::omp::LoopModifier::OMPLM_fused) ||
+        "GRID" >> pure(llvm::omp::LoopModifier::OMPLM_grid) ||
+        "IDENTITY" >> pure(llvm::omp::LoopModifier::OMPLM_identity) ||
+        "INTERCHANGED" >> pure(llvm::omp::LoopModifier::OMPLM_interchanged) ||
+        "INTRATILE" >> pure(llvm::omp::LoopModifier::OMPLM_intratile) ||
+        "OFFSETS" >> pure(llvm::omp::LoopModifier::OMPLM_offsets) ||
+        "REVERSED" >> pure(llvm::omp::LoopModifier::OMPLM_reversed) ||
+        "SPLIT" >> pure(llvm::omp::LoopModifier::OMPLM_split) ||
+        "UNROLLED" >> pure(llvm::omp::LoopModifier::OMPLM_unrolled),
+    maybe(parenthesized(nonemptyList(scalarIntConstantExpr))))))
+
 // --- Parsers for clauses --------------------------------------------
 
 // Declaration of the ODS parser. This type must be complete for some of
@@ -1175,6 +1189,10 @@ TYPE_PARSER(construct<OmpAffinityClause>(
     maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),
     Parser<OmpObjectList>{}))
 
+TYPE_PARSER(construct<OmpApplyClause>(
+    maybe(nonemptyList(Parser<OmpApplyClause::Modifier>{} / ":")),
+    nonemptyList(OmpDirectiveSpecificationParser(/*allowCommas=*/false))))
+
 // 2.4 Requires construct [OpenMP 5.0]
 //        atomic-default-mem-order-clause ->
 //                               acq_rel
@@ -1483,6 +1501,8 @@ TYPE_PARSER( //
                       parenthesized(Parser<OmpAllocateClause>{}))) ||
     "APPEND_ARGS" >> construct<OmpClause>(construct<OmpClause::AppendArgs>(
                          parenthesized(Parser<OmpAppendArgsClause>{}))) ||
+    "APPLY" >> construct<OmpClause>(construct<OmpClause::Apply>(
+                   parenthesized(Parser<OmpApplyClause>{}))) ||
     "ALLOCATOR" >> construct<OmpClause>(construct<OmpClause::Allocator>(
                        parenthesized(scalarIntExpr))) ||
     "AT" >> construct<OmpClause>(construct<OmpClause::At>(

diff  --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 7d0038767a9c4..c7fc6da4b8268 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2213,6 +2213,17 @@ class UnparseVisitor {
   }
   void Unparse(const OmpAppendArgsClause &x) { Walk(x.v, ","); }
   void Unparse(const OmpArgumentList &x) { Walk(x.v, ", "); }
+  void Unparse(const OmpLoopModifier &x) {
+    Word(
+        
llvm::omp::getLoopModifierName(std::get<llvm::omp::LoopModifier>(x.t)));
+    Walk("(", std::get<std::optional<std::list<ScalarIntConstantExpr>>>(x.t),
+        ")");
+  }
+  void Unparse(const OmpApplyClause &x) {
+    using Modifier = OmpApplyClause::Modifier;
+    Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
+    Walk(std::get<std::list<OmpDirectiveSpecification>>(x.t));
+  }
   void Unparse(const OmpAttachModifier &x) {
     Word("ATTACH(");
     Walk(x.v);

diff  --git a/flang/lib/Semantics/check-omp-loop.cpp 
b/flang/lib/Semantics/check-omp-loop.cpp
index 38b77157e33b8..b05ae4d0d8946 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -869,6 +869,52 @@ void OmpStructureChecker::Enter(const parser::DoConstruct 
&x) {
   constructStack_.push_back(&x);
 }
 
+void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
+  DirectiveContext &dirCtx = GetContext();
+  llvm::omp::Directive dir{dirCtx.directive};
+  unsigned version{context_.langOptions().OpenMPVersion};
+  auto &m{std::get<llvm::omp::LoopModifier>(x.t)};
+  if (!llvm::omp::isAllowedLoopModifier(dir, m)) {
+    llvm::StringRef name = llvm::omp::getLoopModifierName(m);
+    context_.Say(x.source,
+        "%s modifier is not allowed on %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(name),
+        parser::omp::GetUpperName(dir, version));
+  }
+  if (const auto &il{
+          std::get<std::optional<std::list<parser::ScalarIntConstantExpr>>>(
+              x.t)}) {
+    int64_t last = -1;
+    for (auto &i : il.value()) {
+      if (const auto v{GetIntValue(i)}) {
+        if (*v <= 0) {
+          context_.Say(x.source,
+              "The loop modifier indexes of the %s clause must be constant 
positive integer expressions"_err_en_US,
+              parser::ToUpperCaseLetters(
+                  getClauseName(llvm::omp::Clause::OMPC_apply).str()));
+        } else if (*v <= last) {
+          context_.Say(x.source,
+              "The loop modifier indexes of the %s clause must be in ascending 
order"_err_en_US,
+              parser::ToUpperCaseLetters(
+                  getClauseName(llvm::omp::Clause::OMPC_apply).str()));
+        } else {
+          last = *v;
+        }
+      }
+    }
+  }
+}
+
+void OmpStructureChecker::Enter(const parser::OmpClause::Apply &x) {
+  EnterDirectiveNest(ApplyNest);
+  OmpVerifyModifiers(
+      x.v, llvm::omp::Clause::OMPC_apply, GetContext().clauseSource, context_);
+}
+
+void OmpStructureChecker::Leave(const parser::OmpClause::Apply &x) {
+  ExitDirectiveNest(ApplyNest);
+}
+
 void OmpStructureChecker::Leave(const parser::DoConstruct &x) {
   assert(!constructStack_.empty() && "Expecting non-empty construct stack");
 #ifndef NDEBUG

diff  --git a/flang/lib/Semantics/check-omp-structure.h 
b/flang/lib/Semantics/check-omp-structure.h
index 011b29d370ba3..97906e636298e 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -144,6 +144,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase 
{
   void Enter(const parser::OmpContextSelector &);
   void Leave(const parser::OmpContextSelector &);
 
+  void Enter(const parser::OmpLoopModifier &);
+
+  void Enter(const parser::OmpClause::Apply &);
+  void Leave(const parser::OmpClause::Apply &);
+
   template <typename A> void Enter(const parser::Statement<A> &);
   void Leave(const parser::GotoStmt &);
   void Leave(const parser::ComputedGotoStmt &);
@@ -437,6 +442,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
 
   bool deviceConstructFound_{false};
   enum directiveNestType : int {
+    ApplyNest,
     SIMDNest,
     TargetBlockOnlyTeams,
     TargetNest,

diff  --git a/flang/lib/Semantics/check-omp-variant.cpp 
b/flang/lib/Semantics/check-omp-variant.cpp
index a60f46ba760d9..1eff064dcbc80 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -549,7 +549,7 @@ void OmpStructureChecker::Enter(const 
parser::OmpDirectiveSpecification &x) {
   // METADIRECTIVE.
   // In other cases it's a part of other constructs that handle directive
   // context stack by themselves.
-  if (!GetDirectiveNest(MetadirectiveNest)) {
+  if (!GetDirectiveNest(MetadirectiveNest) && !GetDirectiveNest(ApplyNest)) {
     return;
   }
 
@@ -578,7 +578,7 @@ void OmpStructureChecker::Enter(const 
parser::OmpDirectiveSpecification &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &x) {
-  if (GetDirectiveNest(MetadirectiveNest)) {
+  if (GetDirectiveNest(MetadirectiveNest) || GetDirectiveNest(ApplyNest)) {
     dirContext_.pop_back();
   }
 }

diff  --git a/flang/lib/Semantics/openmp-modifiers.cpp 
b/flang/lib/Semantics/openmp-modifiers.cpp
index b6a6d476ceec6..fe8e087cd5a94 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -456,6 +456,22 @@ const OmpModifierDescriptor 
&OmpGetDescriptor<parser::OmpLinearModifier>() {
   return desc;
 }
 
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLoopModifier>() {
+  static const OmpModifierDescriptor desc{
+      /*name=*/"loop-modifier",
+      /*props=*/
+      {
+          {60, {}},
+      },
+      /*clauses=*/
+      {
+          {60, {Clause::OMPC_apply}},
+      },
+  };
+  return desc;
+}
+
 template <>
 const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLowerBound>() {
   static const OmpModifierDescriptor desc{

diff  --git a/flang/test/Parser/OpenMP/apply01.f90 
b/flang/test/Parser/OpenMP/apply01.f90
new file mode 100644
index 0000000000000..555ef769e22b3
--- /dev/null
+++ b/flang/test/Parser/OpenMP/apply01.f90
@@ -0,0 +1,137 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck 
--ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | 
FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine apply_1(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp interchange apply(nothing, reverse)
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP INTERCHANGE APPLY(NOTHING, REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = nothing
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_modifier(x)
+  implicit none
+  integer :: x, i
+
+  !$omp tile sizes(2) apply(grid: reverse)
+  do i = 1, 10
+    x = x + 1
+  end do
+end
+
+!UNPARSE: !$OMP TILE SIZES(2_4) APPLY(GRID: REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> 
Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_2_clauses(x)
+  implicit none
+  integer :: x, i
+
+  !$omp tile sizes(2) apply(intratile: unroll) apply(grid: reverse)
+  do i = 1, 10
+    x = x + 1
+  end do
+end
+
+!UNPARSE: !$OMP TILE SIZES(2_4) APPLY(INTRATILE: UNROLL) APPLY(GRID: REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> 
Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = intratile
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = unroll
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+
+subroutine apply_inside_apply(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp fuse apply(fused: tile sizes(2) apply(grid: reverse))
+  do i = 1, 10
+    x = x + 1
+  end do
+  do j = 1, 10
+    x = x - 1
+  end do
+  !$omp end fuse
+end
+
+!UNPARSE: !$OMP FUSE APPLY(FUSED: TILE SIZES(2_4) APPLY(GRID: REVERSE))
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:  END DO
+!UNPARSE:  DO j=1_4,10_4
+!UNPARSE:    x=x-1_4
+!UNPARSE:  END DO
+!UNPARSE: !$OMP END FUSE
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = fuse
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = fused
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | | | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> 
Expr = '2_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | | | OmpDirectiveSpecification
+!PARSE-TREE: | | | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | | | OmpClauseList ->
+!PARSE-TREE: | | | | | Flags = {}
+!PARSE-TREE: | | | Flags = {}
+

diff  --git a/flang/test/Parser/OpenMP/apply02.f90 
b/flang/test/Parser/OpenMP/apply02.f90
new file mode 100644
index 0000000000000..e2160796bf40e
--- /dev/null
+++ b/flang/test/Parser/OpenMP/apply02.f90
@@ -0,0 +1,114 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck 
--ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | 
FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine apply_all(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp interchange apply(interchanged(1,2): nothing, reverse)
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP INTERCHANGE APPLY(INTERCHANGED(1_4, 2_4): NOTHING, REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:     x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = interchanged
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = nothing
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_one(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp interchange apply(interchanged(2): reverse)
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP INTERCHANGE APPLY(INTERCHANGED(2_4): REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:     x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = interchanged
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_inside_apply(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp tile sizes(2,2) apply(grid(1): interchange apply(interchanged(2): 
reverse))
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP TILE SIZES(2_4,2_4) APPLY(GRID(1_4): INTERCHANGE 
APPLY(INTERCHANGED(2_4): REVERSE))
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:     x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> 
Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | Scalar -> Integer -> Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | | | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | | | Modifier -> OmpLoopModifier
+!PARSE-TREE: | | | | | llvm::omp::LoopModifier = interchanged
+!PARSE-TREE: | | | | | Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | | | OmpDirectiveSpecification
+!PARSE-TREE: | | | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | | | OmpClauseList ->
+!PARSE-TREE: | | | | | Flags = {}
+!PARSE-TREE: | | | Flags = {}
+

diff  --git a/flang/test/Semantics/OpenMP/apply01.f90 
b/flang/test/Semantics/OpenMP/apply01.f90
new file mode 100644
index 0000000000000..f146c4e7730d8
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/apply01.f90
@@ -0,0 +1,34 @@
+! Testing the Semantics of tile
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+
+subroutine wrong_modifier(x)
+  implicit none
+  integer i, j, x
+
+  !ERROR: INTERCHANGED modifier is not allowed on TILE directive
+  !$omp tile sizes(2,2) apply(interchanged: nothing, reverse)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: INTERCHANGED modifier is not allowed on TILE directive
+  !ERROR: FUSED modifier is not allowed on TILE directive
+  !$omp tile sizes(2,2) apply(interchanged: nothing, reverse) apply(fused: 
reverse)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: INTRATILE modifier is not allowed on UNROLL directive
+  !$omp tile sizes(2,2) apply(grid: nothing, unroll partial(2) 
apply(intratile: reverse))
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+end subroutine
+

diff  --git a/flang/test/Semantics/OpenMP/apply02.f90 
b/flang/test/Semantics/OpenMP/apply02.f90
new file mode 100644
index 0000000000000..81fc436f96fe6
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/apply02.f90
@@ -0,0 +1,34 @@
+! Testing the Semantics of tile
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+
+subroutine wrong_modifier(x)
+  implicit none
+  integer i, j, x
+
+  !ERROR: Must be a constant value
+  !$omp tile sizes(2,2) apply(grid(x): unroll)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: The loop modifier indexes of the APPLY clause must be constant 
positive integer expressions
+  !$omp tile sizes(2,2) apply(grid(-1): unroll)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: The loop modifier indexes of the APPLY clause must be in ascending 
order
+  !$omp tile sizes(2,2) apply(grid(2,1): unroll, nothing)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+end subroutine
+

diff  --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td 
b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 2aa0649479023..56d5457481bc3 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -33,6 +33,10 @@ class DirectiveLanguage {
   // enum.
   string clausePrefix = "";
 
+  // Optional prefix used for the generation of the enumerator in the
+  // LoopModifier enum.
+  string loopModifierPrefix = "";
+
   // Make the enum values available in the namespace. This allows us to
   // write something like Enum_X if we have a `using namespace cppNamespace`.
   bit makeEnumAvailableInNamespace = false;
@@ -201,6 +205,23 @@ class SourceLanguage<string n> {
 def L_C : SourceLanguage<"C"> {}
 def L_Fortran : SourceLanguage<"Fortran"> {}
 
+// Loop Modifier for Apply clause
+class LoopModifier<list<Spelling> ss> {
+  // Spellings of the loop modifer.
+  list<Spelling> spellings = ss;
+}
+
+def LM_Flattened: LoopModifier<[Spelling<"flattened">]> {}
+def LM_Fused: LoopModifier<[Spelling<"fused">]> {}
+def LM_Grid: LoopModifier<[Spelling<"grid">]> {}
+def LM_Identity: LoopModifier<[Spelling<"identity">]> {}
+def LM_Interchanged: LoopModifier<[Spelling<"interchanged">]> {}
+def LM_Intratile: LoopModifier<[Spelling<"intratile">]> {}
+def LM_Offsets: LoopModifier<[Spelling<"offsets">]> {}
+def LM_Reversed: LoopModifier<[Spelling<"reversed">]> {}
+def LM_Split: LoopModifier<[Spelling<"split">]> {}
+def LM_Unrolled: LoopModifier<[Spelling<"unrolled">]> {}
+
 // Information about a specific directive.
 class Directive<list<Spelling> ss> {
   // Spellings of the directive.
@@ -235,6 +256,9 @@ class Directive<list<Spelling> ss> {
   // The category of the directive.
   Category category = ?;
 
+  // List of allowed loop modifiers for the apply clause.
+  list<LoopModifier> allowedLoopModifiers = [];
+
   // The languages that allow this directive. Default: all languages.
   list<SourceLanguage> languages = [L_C, L_Fortran];
 }

diff  --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td 
b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 189bdaeb4f8d4..bbd55962ba455 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -21,6 +21,7 @@ def OpenMP : DirectiveLanguage {
   let cppNamespace = "omp"; // final namespace will be llvm::omp
   let directivePrefix = "OMPD_";
   let clausePrefix = "OMPC_";
+  let loopModifierPrefix = "OMPLM_";
   let makeEnumAvailableInNamespace = true;
   let enableBitmaskEnumInNamespace = true;
   let clauseEnumSetClass = "OmpClauseSet";
@@ -66,6 +67,7 @@ def OMPC_Allocator : Clause<[Spelling<"allocator">]> {
   let flangClass = "ScalarIntExpr";
 }
 def OMPC_Apply : Clause<[Spelling<"apply">]> {
+  let flangClass = "OmpApplyClause";
 }
 def OMPC_AppendArgs : Clause<[Spelling<"append_args">]> {
   let flangClass = "OmpAppendArgsClause";
@@ -938,6 +940,9 @@ def OMP_Flatten : Directive<[Spelling<"flatten">]> {
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Flattened,
+  ];
 }
 def OMP_Flush : Directive<[Spelling<"flush">]> {
   let allowedOnceClauses = [
@@ -981,19 +986,31 @@ def OMP_Groupprivate : 
Directive<[Spelling<"groupprivate">]> {
   let languages = [L_C, L_Fortran];
 }
 def OMP_Fuse : Directive<[Spelling<"fuse">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Depth, 61>,
     VersionedClause<OMPC_LoopRange, 60>,
   ];
   let association = AS_LoopSeq;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Fused,
+  ];
 }
 def OMP_Interchange : Directive<[Spelling<"interchange">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Permutation>,
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Interchanged,
+  ];
 }
 def OMP_interop : Directive<[Spelling<"interop">]> {
   let allowedClauses = [
@@ -1046,8 +1063,14 @@ def OMP_Metadirective : 
Directive<[Spelling<"metadirective">]> {
   let category = CA_Meta;
 }
 def OMP_Nothing : Directive<[Spelling<"nothing">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let association = AS_None;
   let category = CA_Utility;
+  let allowedLoopModifiers = [
+    LM_Identity,
+  ];
 }
 def OMP_Ordered : Directive<[Spelling<"ordered">]> {
   let allowedClauses = [
@@ -1097,8 +1120,14 @@ def OMP_Requires : Directive<[Spelling<"requires">]> {
   let category = CA_Informational;
 }
 def OMP_Reverse : Directive<[Spelling<"reverse">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Reversed,
+  ];
 }
 def OMP_Scan : Directive<[Spelling<"scan">]> {
   let allowedOnceClauses = [
@@ -1382,6 +1411,9 @@ def OMP_ThreadPrivate : 
Directive<[Spelling<"threadprivate">]> {
   let category = CA_Declarative;
 }
 def OMP_Tile : Directive<[Spelling<"tile">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 51>,
   ];
@@ -1390,15 +1422,29 @@ def OMP_Tile : Directive<[Spelling<"tile">]> {
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Grid,
+    LM_Intratile,
+  ];
 }
 def OMP_Stripe : Directive<[Spelling<"stripe">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 60>,
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Grid,
+    LM_Offsets,
+  ];
 }
 def OMP_Split : Directive<[Spelling<"split">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Counts, 60>,
   ];
@@ -1407,6 +1453,9 @@ def OMP_Split : Directive<[Spelling<"split">]> {
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Split,
+  ];
 }
 def OMP_Unknown : Directive<[Spelling<"unknown">]> {
   let isDefault = true;
@@ -1414,12 +1463,18 @@ def OMP_Unknown : Directive<[Spelling<"unknown">]> {
   let category = CA_Utility;
 }
 def OMP_Unroll : Directive<[Spelling<"unroll">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Full, 51>,
     VersionedClause<OMPC_Partial, 51>,
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Unrolled,
+  ];
 }
 def OMP_Workshare : Directive<[Spelling<"workshare">]> {
   let allowedOnceClauses = [

diff  --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h 
b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 2080f75eb8cfc..d111f666ff31a 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -48,6 +48,10 @@ class DirectiveLanguage {
     return Def->getValueAsString("clausePrefix");
   }
 
+  StringRef getLoopModifierPrefix() const {
+    return Def->getValueAsString("loopModifierPrefix");
+  }
+
   StringRef getClauseEnumSetClass() const {
     return Def->getValueAsString("clauseEnumSetClass");
   }
@@ -84,6 +88,10 @@ class DirectiveLanguage {
     return Records.getAllDerivedDefinitions("Clause");
   }
 
+  ArrayRef<const Record *> getLoopModifiers() const {
+    return Records.getAllDerivedDefinitions("LoopModifier");
+  }
+
   bool HasValidityErrors() const;
 
 private:
@@ -262,6 +270,10 @@ class Directive : public BaseRecord {
     return Def->getValueAsListOfDefs("languages");
   }
 
+  std::vector<const Record *> getAllowedLoopModifiers() const {
+    return Def->getValueAsListOfDefs("allowedLoopModifiers");
+  }
+
   // Clang uses a 
diff erent format for names of its directives enum.
   std::string getClangAccSpelling() const {
     StringRef Name = getSpellingForIdentifier();

diff  --git a/llvm/test/TableGen/directive1.td 
b/llvm/test/TableGen/directive1.td
index 7dca15e908665..a82ca6e2451ce 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -126,6 +126,34 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:  constexpr auto TDLC_clauseb = Clause::TDLC_clauseb;
 // CHECK-NEXT:  constexpr auto TDLC_clausec = Clause::TDLC_clausec;
 // CHECK-EMPTY:
+// CHECK-NEXT:  enum class LoopModifier {
+// CHECK-NEXT:    flattened,
+// CHECK-NEXT:    First_ = flattened,
+// CHECK-NEXT:    fused,
+// CHECK-NEXT:    grid,
+// CHECK-NEXT:    identity,
+// CHECK-NEXT:    interchanged,
+// CHECK-NEXT:    intratile,
+// CHECK-NEXT:    offsets,
+// CHECK-NEXT:    reversed,
+// CHECK-NEXT:    split,
+// CHECK-NEXT:    unrolled,
+// CHECK-NEXT:    Last_ = unrolled,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr std::size_t LoopModifier_enumSize = 10;
+// CHECK-EMPTY:
+// CHECK-NEXT:  constexpr auto flattened = LoopModifier::flattened;
+// CHECK-NEXT:  constexpr auto fused = LoopModifier::fused;
+// CHECK-NEXT:  constexpr auto grid = LoopModifier::grid;
+// CHECK-NEXT:  constexpr auto identity = LoopModifier::identity;
+// CHECK-NEXT:  constexpr auto interchanged = LoopModifier::interchanged;
+// CHECK-NEXT:  constexpr auto intratile = LoopModifier::intratile;
+// CHECK-NEXT:  constexpr auto offsets = LoopModifier::offsets;
+// CHECK-NEXT:  constexpr auto reversed = LoopModifier::reversed;
+// CHECK-NEXT:  constexpr auto split = LoopModifier::split;
+// CHECK-NEXT:  constexpr auto unrolled = LoopModifier::unrolled;
+// CHECK-EMPTY:
 // CHECK-NEXT:  enum class AKind {
 // CHECK-NEXT:    TDLCV_vala=1,
 // CHECK-NEXT:    TDLCV_valb=2,
@@ -194,6 +222,8 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:  LLVM_ABI bool isAllowedClauseForDirective(Directive D, Clause 
C, unsigned Version);
 // CHECK-EMPTY:
 // CHECK-NEXT:  constexpr std::size_t getMaxLeafCount() { return 0; }
+// CHECK-NEXT:  LLVM_ABI bool isAllowedLoopModifier(Directive D, LoopModifier 
LM);
+// CHECK-NEXT:  LLVM_ABI StringRef getLoopModifierName(LoopModifier LM, 
unsigned Ver = 0);
 // CHECK-NEXT:  LLVM_ABI AKind getAKind(StringRef Str);
 // CHECK-NEXT:  LLVM_ABI StringRef getTdlAKindName(AKind x);
 // CHECK-EMPTY:
@@ -215,6 +245,10 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:    static constexpr bool is_iterable = true;
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
+// CHECK-NEXT:  template <> struct enum_iteration_traits<tdl::LoopModifier> {
+// CHECK-NEXT:    static constexpr bool is_iterable = true;
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
 // CHECK-NEXT:  } // namespace llvm
 // CHECK-EMPTY:
 // CHECK-NEXT:  #endif // LLVM_Tdl_INC
@@ -467,6 +501,42 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
 // IMPL-NEXT:  }
 // IMPL-EMPTY:
+// IMPL-NEXT:  bool llvm::tdl::isAllowedLoopModifier(llvm::tdl::Directive D, 
llvm::tdl::LoopModifier LM) {
+// IMPL-NEXT:    assert(unsigned(D) <= Directive_enumSize);
+// IMPL-NEXT:    switch (D) {
+// IMPL-NEXT:      case TDLD_dira:
+// IMPL-NEXT:        return false;
+// IMPL-NEXT:        break;
+// IMPL-NEXT:    }
+// IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
+// IMPL-NEXT:  llvm::StringRef 
llvm::tdl::getLoopModifierName(llvm::tdl::LoopModifier Kind, unsigned Version) {
+// IMPL-NEXT:    switch (Kind) {
+// IMPL-NEXT:      case flattened:
+// IMPL-NEXT:        return "flattened";
+// IMPL-NEXT:      case fused:
+// IMPL-NEXT:        return "fused";
+// IMPL-NEXT:      case grid:
+// IMPL-NEXT:        return "grid";
+// IMPL-NEXT:      case identity:
+// IMPL-NEXT:        return "identity";
+// IMPL-NEXT:      case interchanged:
+// IMPL-NEXT:        return "interchanged";
+// IMPL-NEXT:      case intratile:
+// IMPL-NEXT:        return "intratile";
+// IMPL-NEXT:      case offsets:
+// IMPL-NEXT:        return "offsets";
+// IMPL-NEXT:      case reversed:
+// IMPL-NEXT:        return "reversed";
+// IMPL-NEXT:      case split:
+// IMPL-NEXT:        return "split";
+// IMPL-NEXT:      case unrolled:
+// IMPL-NEXT:        return "unrolled";
+// IMPL-NEXT:    }
+// IMPL-NEXT:    llvm_unreachable("Invalid  LoopModifier kind");
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
 // IMPL-NEXT:  static_assert(sizeof(llvm::tdl::Directive) == sizeof(int));
 // IMPL-NEXT:  {{.*}} static const llvm::tdl::Directive 
LeafConstructTable[][2] = {
 // IMPL-NEXT:    {llvm::tdl::TDLD_dira, static_cast<llvm::tdl::Directive>(0),},

diff  --git a/llvm/test/TableGen/directive2.td 
b/llvm/test/TableGen/directive2.td
index 31a3bc907988c..76252e5f92303 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -112,6 +112,23 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-EMPTY:
 // CHECK-NEXT:  static constexpr std::size_t Clause_enumSize = 4;
 // CHECK-EMPTY:
+// CHECK-NEXT:  enum class LoopModifier {
+// CHECK-NEXT:    flattened,
+// CHECK-NEXT:    First_ = flattened,
+// CHECK-NEXT:    fused,
+// CHECK-NEXT:    grid,
+// CHECK-NEXT:    identity,
+// CHECK-NEXT:    interchanged,
+// CHECK-NEXT:    intratile,
+// CHECK-NEXT:    offsets,
+// CHECK-NEXT:    reversed,
+// CHECK-NEXT:    split,
+// CHECK-NEXT:    unrolled,
+// CHECK-NEXT:    Last_ = unrolled,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr std::size_t LoopModifier_enumSize = 10;
+// CHECK-EMPTY:
 // CHECK-NEXT:  // Constexpr functions.
 // CHECK-EMPTY:
 // CHECK-NEXT:  constexpr Association getDirectiveAssociation(Directive Dir) {
@@ -170,6 +187,8 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:  LLVM_ABI bool isAllowedClauseForDirective(Directive D, Clause 
C, unsigned Version);
 // CHECK-EMPTY:
 // CHECK-NEXT:  constexpr std::size_t getMaxLeafCount() { return 0; }
+// CHECK-NEXT:  LLVM_ABI bool isAllowedLoopModifier(Directive D, LoopModifier 
LM);
+// CHECK-NEXT:  LLVM_ABI StringRef getLoopModifierName(LoopModifier LM, 
unsigned Ver = 0);
 // CHECK-EMPTY:
 // CHECK-NEXT:  } // namespace tdl
 // CHECK-EMPTY:
@@ -189,6 +208,10 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:    static constexpr bool is_iterable = true;
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
+// CHECK-NEXT:  template <> struct enum_iteration_traits<tdl::LoopModifier> {
+// CHECK-NEXT:    static constexpr bool is_iterable = true;
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
 // CHECK-NEXT:  } // namespace llvm
 // CHECK-EMPTY:
 // CHECK-NEXT:  #endif // LLVM_Tdl_INC
@@ -392,6 +415,42 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
 // IMPL-NEXT:  }
 // IMPL-EMPTY:
+// IMPL-NEXT:  bool llvm::tdl::isAllowedLoopModifier(llvm::tdl::Directive D, 
llvm::tdl::LoopModifier LM) {
+// IMPL-NEXT:    assert(unsigned(D) <= Directive_enumSize);
+// IMPL-NEXT:    switch (D) {
+// IMPL-NEXT:      case TDLD_dira:
+// IMPL-NEXT:        return false;
+// IMPL-NEXT:        break;
+// IMPL-NEXT:    }
+// IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
+// IMPL-NEXT:  llvm::StringRef 
llvm::tdl::getLoopModifierName(llvm::tdl::LoopModifier Kind, unsigned Version) {
+// IMPL-NEXT:    switch (Kind) {
+// IMPL-NEXT:      case flattened:
+// IMPL-NEXT:        return "flattened";
+// IMPL-NEXT:      case fused:
+// IMPL-NEXT:        return "fused";
+// IMPL-NEXT:      case grid:
+// IMPL-NEXT:        return "grid";
+// IMPL-NEXT:      case identity:
+// IMPL-NEXT:        return "identity";
+// IMPL-NEXT:      case interchanged:
+// IMPL-NEXT:        return "interchanged";
+// IMPL-NEXT:      case intratile:
+// IMPL-NEXT:        return "intratile";
+// IMPL-NEXT:      case offsets:
+// IMPL-NEXT:        return "offsets";
+// IMPL-NEXT:      case reversed:
+// IMPL-NEXT:        return "reversed";
+// IMPL-NEXT:      case split:
+// IMPL-NEXT:        return "split";
+// IMPL-NEXT:      case unrolled:
+// IMPL-NEXT:        return "unrolled";
+// IMPL-NEXT:    }
+// IMPL-NEXT:    llvm_unreachable("Invalid  LoopModifier kind");
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
 // IMPL-NEXT:  static_assert(sizeof(llvm::tdl::Directive) == sizeof(int));
 // IMPL-NEXT:  {{.*}} static const llvm::tdl::Directive 
LeafConstructTable[][2] = {
 // IMPL-NEXT:    {llvm::tdl::TDLD_dira, static_cast<llvm::tdl::Directive>(0),},

diff  --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp 
b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 55675fdec8448..93d83f909bfd2 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -311,6 +311,11 @@ static void emitDirectivesDecl(const RecordKeeper 
&Records, raw_ostream &OS) {
                       DirLang.getClausePrefix(),
                       DirLang.hasMakeEnumAvailableInNamespace());
 
+    // Emit LoopModifier
+    generateEnumClass(DirLang.getLoopModifiers(), OS, "LoopModifier",
+                      DirLang.getLoopModifierPrefix(),
+                      DirLang.hasMakeEnumAvailableInNamespace());
+
     // Emit ClauseVals enumeration
     std::string EnumHelperFuncs;
     generateClauseEnumVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);
@@ -354,11 +359,16 @@ static void emitDirectivesDecl(const RecordKeeper 
&Records, raw_ostream &OS) {
     OS << "\n";
     OS << "constexpr std::size_t getMaxLeafCount() { return "
        << getMaxLeafCount(DirLang) << "; }\n";
+    OS << "LLVM_ABI bool isAllowedLoopModifier(Directive D, LoopModifier "
+          "LM);\n";
+    OS << "LLVM_ABI StringRef getLoopModifierName(LoopModifier LM, unsigned "
+          "Ver = 0);\n";
     OS << EnumHelperFuncs;
   } // close DirLangNS
 
   // These specializations need to be in ::llvm.
-  for (StringRef Enum : {"Association", "Category", "Directive", "Clause"}) {
+  for (StringRef Enum :
+       {"Association", "Category", "Directive", "Clause", "LoopModifier"}) {
     OS << "\n";
     OS << "template <> struct enum_iteration_traits<"
        << DirLang.getCppNamespace() << "::" << Enum << "> {\n";
@@ -384,12 +394,11 @@ orderSpellings(ArrayRef<Spelling::Value> Spellings) {
 // Generate function implementation for get<Enum>Name(StringRef Str)
 static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS,
                             StringRef Enum, const DirectiveLanguage &DirLang,
-                            StringRef Prefix) {
-  StringRef Lang = DirLang.getName();
+                            StringRef LangName, StringRef Prefix) {
   std::string Qual = getQualifier(DirLang);
   OS << "\n";
-  OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual
-     << Enum << " Kind, unsigned Version) {\n";
+  OS << "llvm::StringRef " << Qual << "get" << LangName << Enum << "Name("
+     << Qual << Enum << " Kind, unsigned Version) {\n";
   OS << "  switch (Kind) {\n";
   for (const Record *R : Records) {
     BaseRecord Rec(R);
@@ -416,7 +425,8 @@ static void generateGetName(ArrayRef<const Record *> 
Records, raw_ostream &OS,
     }
   }
   OS << "  }\n"; // switch
-  OS << "  llvm_unreachable(\"Invalid " << Lang << " " << Enum << " 
kind\");\n";
+  OS << "  llvm_unreachable(\"Invalid " << LangName << " " << Enum
+     << " kind\");\n";
   OS << "}\n";
 }
 
@@ -909,6 +919,47 @@ static void generateGetDirectiveLanguages(const 
DirectiveLanguage &DirLang,
   OS << "}\n";
 }
 
+// Generate the isAllowedLoopModifier function implementation.
+static void generateIsAllowedLoopModifier(const DirectiveLanguage &DirLang,
+                                          raw_ostream &OS) {
+  std::string Qual = getQualifier(DirLang);
+
+  OS << "\n";
+  OS << "bool " << Qual << "isAllowedLoopModifier(" << Qual << "Directive D, "
+     << Qual << "LoopModifier LM) {\n";
+  OS << "  assert(unsigned(D) <= Directive_enumSize);\n";
+
+  OS << "  switch (D) {\n";
+
+  StringRef DPrefix = DirLang.getDirectivePrefix();
+  StringRef LMPrefix = DirLang.getLoopModifierPrefix();
+  for (const Record *R : DirLang.getDirectives()) {
+    Directive Dir(R);
+    OS << "    case " << getIdentifierName(R, DPrefix) << ":\n";
+    if (Dir.getAllowedLoopModifiers().empty()) {
+      OS << "      return false;\n";
+    } else {
+      OS << "      switch (LM) {\n";
+
+      for (const Record *LMR : Dir.getAllowedLoopModifiers()) {
+        std::string Name = getIdentifierName(LMR, LMPrefix);
+        OS << "        case LoopModifier::" << Name << ":\n";
+        OS << "          return true;\n";
+      }
+
+      OS << "        default:\n";
+      OS << "          return false;\n";
+      OS << "      }\n"; // End of modifier switch
+    }
+    OS << "      break;\n";
+  }
+
+  OS << "  }\n"; // End of directives switch
+  OS << "  llvm_unreachable(\"Invalid " << DirLang.getName()
+     << " Directive kind\");\n";
+  OS << "}\n"; // End of function isAllowedLoopModifier
+}
+
 // Generate a simple enum set with the give clauses.
 static void generateClauseSet(ArrayRef<const Record *> VerClauses,
                               raw_ostream &OS, StringRef ClauseSetPrefix,
@@ -1344,14 +1395,16 @@ void emitDirectivesBasicImpl(const DirectiveLanguage 
&DirLang,
                   /*ImplicitAsUnknown=*/false);
 
   // getDirectiveName(Directive Kind)
-  generateGetName(DirLang.getDirectives(), OS, "Directive", DirLang, DPrefix);
+  generateGetName(DirLang.getDirectives(), OS, "Directive", DirLang,
+                  DirLang.getName(), DPrefix);
 
   // getClauseKind(StringRef Str)
   generateGetKind(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix,
                   /*ImplicitAsUnknown=*/true);
 
   // getClauseName(Clause Kind)
-  generateGetName(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix);
+  generateGetName(DirLang.getClauses(), OS, "Clause", DirLang,
+                  DirLang.getName(), CPrefix);
 
   // <enumClauseValue> get<enumClauseValue>(StringRef Str) ; string -> value
   // StringRef get<enumClauseValue>Name(<enumClauseValue>) ; value -> string
@@ -1360,6 +1413,13 @@ void emitDirectivesBasicImpl(const DirectiveLanguage 
&DirLang,
   // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
   generateIsAllowedClause(DirLang, OS);
 
+  // isAllowedLoopModifier(Directive D, LoopModifier LM)
+  generateIsAllowedLoopModifier(DirLang, OS);
+
+  // getLoopModifierName(LoopModifier Kind)
+  generateGetName(DirLang.getLoopModifiers(), OS, "LoopModifier", DirLang, "",
+                  DirLang.getLoopModifierPrefix());
+
   // Leaf table for getLeafConstructs, etc.
   emitLeafTable(DirLang, OS, "LeafConstructTable");
 }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to