[clang] [flang] [llvm] [OpenMP] Introduce "loop sequence" as directive association (PR #168934)

Krzysztof Parzyszek via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 20 11:21:06 PST 2025


https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/168934

OpenMP 6.0 introduced a `fuse` directive, and with it a "loop sequence" as the associated code. What used to be "loop association" has become "loop-nest association".

Rename Association::Loop to LoopNest, add Association::LoopSeq to represent the "loop sequence" association.

Change the association of fuse from "block" to "loop sequence".

>From 01a417701b5a758910d17ecfb1f4e99fca0f5e9d Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 20 Nov 2025 13:14:16 -0600
Subject: [PATCH] [OpenMP] Introduce "loop sequence" as directive association

OpenMP 6.0 introduced a `fuse` directive, and with it a "loop sequence"
as the associated code. What used to be "loop association" has become
"loop-nest association".

Rename Association::Loop to LoopNest, add Association::LoopSeq to
represent the "loop sequence" association.

Change the association of fuse from "block" to "loop sequence".
---
 clang/lib/Basic/OpenMPKinds.cpp               |  4 ++--
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  4 ++--
 .../llvm/Frontend/Directive/DirectiveBase.td  |  5 ++--
 llvm/include/llvm/Frontend/OpenACC/ACC.td     |  2 +-
 llvm/include/llvm/Frontend/OpenMP/OMP.td      | 24 +++++++++----------
 llvm/lib/Frontend/OpenMP/OMP.cpp              |  4 ++--
 llvm/test/TableGen/directive1.td              |  5 ++--
 llvm/test/TableGen/directive2.td              |  5 ++--
 .../utils/TableGen/Basic/DirectiveEmitter.cpp | 19 ++++++++-------
 9 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 8e60fc26a7947..03485b7e81abc 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -680,7 +680,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
 }
 
 bool clang::isOpenMPLoopDirective(OpenMPDirectiveKind DKind) {
-  return getDirectiveAssociation(DKind) == Association::Loop;
+  return getDirectiveAssociation(DKind) == Association::LoopNest;
 }
 
 bool clang::isOpenMPWorksharingDirective(OpenMPDirectiveKind DKind) {
@@ -741,7 +741,7 @@ bool clang::isOpenMPTeamsDirective(OpenMPDirectiveKind DKind) {
 
 bool clang::isOpenMPSimdDirective(OpenMPDirectiveKind DKind) {
   // Avoid OMPD_declare_simd
-  if (getDirectiveAssociation(DKind) != Association::Loop)
+  if (getDirectiveAssociation(DKind) != Association::LoopNest)
     return false;
   // Formally, OMPD_end_do_simd also has a loop association, but
   // it's a Fortran-specific directive.
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index c6487349c4056..2e43e3d94cb8c 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1202,7 +1202,7 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
   // Start with privatization, so that the lowering of the nested
   // code will use the right symbols.
   bool isLoop = llvm::omp::getDirectiveAssociation(info.dir) ==
-                llvm::omp::Association::Loop;
+                llvm::omp::Association::LoopNest;
   bool privatize = info.clauses && info.privatize;
 
   firOpBuilder.setInsertionPoint(marker);
@@ -3398,7 +3398,7 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
   };
 
   bool loopLeaf = llvm::omp::getDirectiveAssociation(item->id) ==
-                  llvm::omp::Association::Loop;
+                  llvm::omp::Association::LoopNest;
   if (loopLeaf) {
     symTable.pushScope();
     if (genOMPCompositeDispatch(converter, symTable, stmtCtx, semaCtx, eval,
diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 301a7cce59627..2aa0649479023 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -162,7 +162,8 @@ def AS_Block : Association<"Block"> {}            // Block (incl. single
 def AS_Declaration : Association<"Declaration"> {}   // Declaration
 def AS_Delimited : Association<"Delimited"> {}    // Region delimited with
                                                   // begin/end
-def AS_Loop : Association<"Loop"> {}              // Loop
+def AS_LoopNest : Association<"LoopNest"> {}      // Loop nest
+def AS_LoopSeq : Association<"LoopSeq"> {}        // Loop sequence
 def AS_Separating : Association<"Separating"> {}  // Separates parts of a
                                                   // construct
 
@@ -173,7 +174,7 @@ def AS_FromLeaves : Association<"FromLeaves"> {}    // See below
 //   x + y = y + x
 //   x + x = x
 //   AS_None + x = x
-//   AS_Block + AS_Loop = AS_Loop
+//   AS_Block + AS_Loop{Nest|Seq} = AS_Loop{Nest|Seq}
 // Other combinations are not allowed.
 // This association is not valid for leaf constructs.
 // The name "AS_FromLeaves" is recognized by TableGen, and there is no enum
diff --git a/llvm/include/llvm/Frontend/OpenACC/ACC.td b/llvm/include/llvm/Frontend/OpenACC/ACC.td
index 65751839ceb09..7381ec7bff0c4 100644
--- a/llvm/include/llvm/Frontend/OpenACC/ACC.td
+++ b/llvm/include/llvm/Frontend/OpenACC/ACC.td
@@ -419,7 +419,7 @@ def ACC_Loop : Directive<[Spelling<"loop">]> {
     VersionedClause<ACCC_Independent>,
     VersionedClause<ACCC_Seq>
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index a01858fb220f1..ade00e7ca27d5 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -859,7 +859,7 @@ def OMP_Distribute : Directive<[Spelling<"distribute">]> {
     VersionedClause<OMPC_DistSchedule>,
     VersionedClause<OMPC_Order, 50>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_Do : Directive<[Spelling<"do">]> {
@@ -877,7 +877,7 @@ def OMP_Do : Directive<[Spelling<"do">]> {
     VersionedClause<OMPC_Ordered>,
     VersionedClause<OMPC_Schedule>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
@@ -926,7 +926,7 @@ def OMP_For : Directive<[Spelling<"for">]> {
     VersionedClause<OMPC_Reduction>,
     VersionedClause<OMPC_Schedule>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
   let languages = [L_C];
 }
@@ -940,14 +940,14 @@ def OMP_Groupprivate : Directive<[Spelling<"groupprivate">]> {
 }
 def OMP_Fuse : Directive<[Spelling<"fuse">]> {
   let allowedOnceClauses = [VersionedClause<OMPC_LoopRange, 60>];
-  let association = AS_Block;
+  let association = AS_LoopSeq;
   let category = CA_Executable;
 }
 def OMP_Interchange : Directive<[Spelling<"interchange">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Permutation>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_interop : Directive<[Spelling<"interop">]> {
@@ -973,7 +973,7 @@ def OMP_loop : Directive<[Spelling<"loop">]> {
     VersionedClause<OMPC_Collapse>,
     VersionedClause<OMPC_Order, 50>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_masked : Directive<[Spelling<"masked">]> {
@@ -1056,7 +1056,7 @@ def OMP_Requires : Directive<[Spelling<"requires">]> {
   let category = CA_Informational;
 }
 def OMP_Reverse : Directive<[Spelling<"reverse">]> {
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_Scan : Directive<[Spelling<"scan">]> {
@@ -1131,7 +1131,7 @@ def OMP_Simd : Directive<[Spelling<"simd">]> {
     VersionedClause<OMPC_SafeLen>,
     VersionedClause<OMPC_SimdLen>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_Single : Directive<[Spelling<"single">]> {
@@ -1323,7 +1323,7 @@ def OMP_TaskLoop : Directive<[Spelling<"taskloop">]> {
     VersionedClause<OMPC_GrainSize>,
     VersionedClause<OMPC_NumTasks>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_TaskWait : Directive<[Spelling<"taskwait">]> {
@@ -1371,14 +1371,14 @@ def OMP_Tile : Directive<[Spelling<"tile">]> {
   let requiredClauses = [
     VersionedClause<OMPC_Sizes, 51>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_Stripe : Directive<[Spelling<"stripe">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 60>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_Unknown : Directive<[Spelling<"unknown">]> {
@@ -1391,7 +1391,7 @@ def OMP_Unroll : Directive<[Spelling<"unroll">]> {
     VersionedClause<OMPC_Full, 51>,
     VersionedClause<OMPC_Partial, 51>,
   ];
-  let association = AS_Loop;
+  let association = AS_LoopNest;
   let category = CA_Executable;
 }
 def OMP_Workshare : Directive<[Spelling<"workshare">]> {
diff --git a/llvm/lib/Frontend/OpenMP/OMP.cpp b/llvm/lib/Frontend/OpenMP/OMP.cpp
index f12941492547e..97aecf92ca5f3 100644
--- a/llvm/lib/Frontend/OpenMP/OMP.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMP.cpp
@@ -53,7 +53,7 @@ getFirstCompositeRange(iterator_range<ArrayRef<Directive>::iterator> Leafs) {
   auto firstLoopAssociated =
       [](iterator_range<ArrayRef<Directive>::iterator> List) {
         for (auto It = List.begin(), End = List.end(); It != End; ++It) {
-          if (getDirectiveAssociation(*It) == Association::Loop)
+          if (getDirectiveAssociation(*It) == Association::LoopNest)
             return It;
         }
         return List.end();
@@ -71,7 +71,7 @@ getFirstCompositeRange(iterator_range<ArrayRef<Directive>::iterator> Leafs) {
     return Empty;
 
   for (; End != Leafs.end(); ++End) {
-    if (getDirectiveAssociation(*End) != Association::Loop)
+    if (getDirectiveAssociation(*End) != Association::LoopNest)
       break;
   }
   return llvm::make_range(Begin, End);
diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index 5bd7890e0ddd1..3a9e8701c06eb 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -71,13 +71,14 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:    First_ = Block,
 // CHECK-NEXT:    Declaration,
 // CHECK-NEXT:    Delimited,
-// CHECK-NEXT:    Loop,
+// CHECK-NEXT:    LoopNest,
+// CHECK-NEXT:    LoopSeq,
 // CHECK-NEXT:    None,
 // CHECK-NEXT:    Separating,
 // CHECK-NEXT:    Last_ = Separating,
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
-// CHECK-NEXT:  static constexpr std::size_t Association_enumSize = 6;
+// CHECK-NEXT:  static constexpr std::size_t Association_enumSize = 7;
 // CHECK-EMPTY:
 // CHECK-NEXT:  enum class Category {
 // CHECK-NEXT:    Declarative,
diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td
index eaaf82ddaaf41..392423faed36f 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -62,13 +62,14 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
 // CHECK-NEXT:    First_ = Block,
 // CHECK-NEXT:    Declaration,
 // CHECK-NEXT:    Delimited,
-// CHECK-NEXT:    Loop,
+// CHECK-NEXT:    LoopNest,
+// CHECK-NEXT:    LoopSeq,
 // CHECK-NEXT:    None,
 // CHECK-NEXT:    Separating,
 // CHECK-NEXT:    Last_ = Separating,
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
-// CHECK-NEXT:  static constexpr std::size_t Association_enumSize = 6;
+// CHECK-NEXT:  static constexpr std::size_t Association_enumSize = 7;
 // CHECK-EMPTY:
 // CHECK-NEXT:  enum class Category {
 // CHECK-NEXT:    Declarative,
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 3a488ed952210..1126c83034b54 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -729,11 +729,12 @@ static void emitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS,
 static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang,
                                             raw_ostream &OS) {
   enum struct Association {
-    None = 0, // None should be the smallest value.
-    Block,    // The values of the rest don't matter.
-    Declaration,
+    None = 0,    // None should be the smallest value.
+    Block,       // If the order of the rest of these changes, update the
+    Declaration, // 'Reduce' function below.
     Delimited,
-    Loop,
+    LoopNest,
+    LoopSeq,
     Separating,
     FromLeaves,
     Invalid,
@@ -746,7 +747,8 @@ static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang,
         .Case("AS_Block", Association::Block)
         .Case("AS_Declaration", Association::Declaration)
         .Case("AS_Delimited", Association::Delimited)
-        .Case("AS_Loop", Association::Loop)
+        .Case("AS_LoopNest", Association::LoopNest)
+        .Case("AS_LoopSeq", Association::LoopSeq)
         .Case("AS_None", Association::None)
         .Case("AS_Separating", Association::Separating)
         .Case("AS_FromLeaves", Association::FromLeaves)
@@ -777,13 +779,12 @@ static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang,
     // Calculate the result using the following rules:
     //   x + x = x
     //   AS_None + x = x
-    //   AS_Block + AS_Loop = AS_Loop
+    //   AS_Block + AS_Loop{Nest|Seq} = AS_Loop{Nest|Seq}
     if (A == Association::None || A == B)
       return B;
-    if (A == Association::Block && B == Association::Loop)
+    if (A == Association::Block &&
+        (B == Association::LoopNest || B == Association::LoopSeq))
       return B;
-    if (A == Association::Loop && B == Association::Block)
-      return A;
     return Association::Invalid;
   };
 



More information about the llvm-commits mailing list