[clang] 67c82d6 - [Frontend] Add leaf constructs and association to OpenMP/ACC directives (#83625)

via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 6 08:46:31 PST 2024


Author: Krzysztof Parzyszek
Date: 2024-03-06T10:46:26-06:00
New Revision: 67c82d6ffb4bbc21212116a11f390761d859297a

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

LOG: [Frontend] Add leaf constructs and association to OpenMP/ACC directives (#83625)

Add members "leafConstructs" and "association" to .td describing
OpenMP/ACC directives. The naming follows the terminology used in the
OpenMP standard: a "leaf" construct is a construct that is itself not a
composition or a combination of other constructs, and "association" is
the source language construct to which the directive applies (e.g. loop,
block, etc.)

The tblgen-generated output then contains two additional functions
- getLeafConstructs(D), and
- getDirectiveAssociation(D)
plus "enum class Association", all in namespaces "llvm::omp" and
"llvm::acc".

Note: getLeafConstructs returns an empty sequence for a construct that
is itself a leaf construct.

Use the new functions to simplify a few OpenMP-related functions in
clang.

Added: 
    

Modified: 
    clang/lib/Basic/OpenMPKinds.cpp
    llvm/include/llvm/Frontend/Directive/DirectiveBase.td
    llvm/include/llvm/Frontend/OpenACC/ACC.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/DirectiveEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 6c31b0824eb8a4..b3e9affbb3e58a 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -574,31 +574,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
 }
 
 bool clang::isOpenMPLoopDirective(OpenMPDirectiveKind DKind) {
-  return DKind == OMPD_simd || DKind == OMPD_for || DKind == OMPD_for_simd ||
-         DKind == OMPD_parallel_for || DKind == OMPD_parallel_for_simd ||
-         DKind == OMPD_taskloop || DKind == OMPD_taskloop_simd ||
-         DKind == OMPD_master_taskloop || DKind == OMPD_master_taskloop_simd ||
-         DKind == OMPD_parallel_master_taskloop ||
-         DKind == OMPD_parallel_master_taskloop_simd ||
-         DKind == OMPD_masked_taskloop || DKind == OMPD_masked_taskloop_simd ||
-         DKind == OMPD_parallel_masked_taskloop || DKind == OMPD_distribute ||
-         DKind == OMPD_parallel_masked_taskloop_simd ||
-         DKind == OMPD_target_parallel_for ||
-         DKind == OMPD_distribute_parallel_for ||
-         DKind == OMPD_distribute_parallel_for_simd ||
-         DKind == OMPD_distribute_simd ||
-         DKind == OMPD_target_parallel_for_simd || DKind == OMPD_target_simd ||
-         DKind == OMPD_teams_distribute ||
-         DKind == OMPD_teams_distribute_simd ||
-         DKind == OMPD_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_teams_distribute_parallel_for ||
-         DKind == OMPD_target_teams_distribute ||
-         DKind == OMPD_target_teams_distribute_parallel_for ||
-         DKind == OMPD_target_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_target_teams_distribute_simd || DKind == OMPD_tile ||
-         DKind == OMPD_unroll || DKind == OMPD_loop ||
-         DKind == OMPD_teams_loop || DKind == OMPD_target_teams_loop ||
-         DKind == OMPD_parallel_loop || DKind == OMPD_target_parallel_loop;
+  return getDirectiveAssociation(DKind) == Association::Loop;
 }
 
 bool clang::isOpenMPWorksharingDirective(OpenMPDirectiveKind DKind) {
@@ -619,44 +595,20 @@ bool clang::isOpenMPWorksharingDirective(OpenMPDirectiveKind DKind) {
 }
 
 bool clang::isOpenMPTaskLoopDirective(OpenMPDirectiveKind DKind) {
-  return DKind == OMPD_taskloop || DKind == OMPD_taskloop_simd ||
-         DKind == OMPD_master_taskloop || DKind == OMPD_master_taskloop_simd ||
-         DKind == OMPD_parallel_master_taskloop ||
-         DKind == OMPD_masked_taskloop || DKind == OMPD_masked_taskloop_simd ||
-         DKind == OMPD_parallel_masked_taskloop ||
-         DKind == OMPD_parallel_masked_taskloop_simd ||
-         DKind == OMPD_parallel_master_taskloop_simd;
+  return DKind == OMPD_taskloop ||
+         llvm::is_contained(getLeafConstructs(DKind), OMPD_taskloop);
 }
 
 bool clang::isOpenMPParallelDirective(OpenMPDirectiveKind DKind) {
-  return DKind == OMPD_parallel || DKind == OMPD_parallel_for ||
-         DKind == OMPD_parallel_for_simd || DKind == OMPD_parallel_sections ||
-         DKind == OMPD_target_parallel || DKind == OMPD_target_parallel_for ||
-         DKind == OMPD_distribute_parallel_for ||
-         DKind == OMPD_distribute_parallel_for_simd ||
-         DKind == OMPD_target_parallel_for_simd ||
-         DKind == OMPD_teams_distribute_parallel_for ||
-         DKind == OMPD_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_target_teams_distribute_parallel_for ||
-         DKind == OMPD_target_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_parallel_master || DKind == OMPD_parallel_masked ||
-         DKind == OMPD_parallel_master_taskloop ||
-         DKind == OMPD_parallel_master_taskloop_simd ||
-         DKind == OMPD_parallel_masked_taskloop ||
-         DKind == OMPD_parallel_masked_taskloop_simd ||
-         DKind == OMPD_parallel_loop || DKind == OMPD_target_parallel_loop ||
-         DKind == OMPD_teams_loop;
+  if (DKind == OMPD_teams_loop)
+    return true;
+  return DKind == OMPD_parallel ||
+         llvm::is_contained(getLeafConstructs(DKind), OMPD_parallel);
 }
 
 bool clang::isOpenMPTargetExecutionDirective(OpenMPDirectiveKind DKind) {
-  return DKind == OMPD_target || DKind == OMPD_target_parallel ||
-         DKind == OMPD_target_parallel_for ||
-         DKind == OMPD_target_parallel_for_simd || DKind == OMPD_target_simd ||
-         DKind == OMPD_target_teams || DKind == OMPD_target_teams_distribute ||
-         DKind == OMPD_target_teams_distribute_parallel_for ||
-         DKind == OMPD_target_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_target_teams_distribute_simd ||
-         DKind == OMPD_target_teams_loop || DKind == OMPD_target_parallel_loop;
+  return DKind == OMPD_target ||
+         llvm::is_contained(getLeafConstructs(DKind), OMPD_target);
 }
 
 bool clang::isOpenMPTargetDataManagementDirective(OpenMPDirectiveKind DKind) {
@@ -665,60 +617,45 @@ bool clang::isOpenMPTargetDataManagementDirective(OpenMPDirectiveKind DKind) {
 }
 
 bool clang::isOpenMPNestingTeamsDirective(OpenMPDirectiveKind DKind) {
-  return DKind == OMPD_teams || DKind == OMPD_teams_distribute ||
-         DKind == OMPD_teams_distribute_simd ||
-         DKind == OMPD_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_teams_distribute_parallel_for ||
-         DKind == OMPD_teams_loop;
+  if (DKind == OMPD_teams)
+    return true;
+  ArrayRef<Directive> Leaves = getLeafConstructs(DKind);
+  return !Leaves.empty() && Leaves.front() == OMPD_teams;
 }
 
 bool clang::isOpenMPTeamsDirective(OpenMPDirectiveKind DKind) {
-  return isOpenMPNestingTeamsDirective(DKind) || DKind == OMPD_target_teams ||
-         DKind == OMPD_target_teams_distribute ||
-         DKind == OMPD_target_teams_distribute_parallel_for ||
-         DKind == OMPD_target_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_target_teams_distribute_simd ||
-         DKind == OMPD_target_teams_loop;
+  return DKind == OMPD_teams ||
+         llvm::is_contained(getLeafConstructs(DKind), OMPD_teams);
 }
 
 bool clang::isOpenMPSimdDirective(OpenMPDirectiveKind DKind) {
-  return DKind == OMPD_simd || DKind == OMPD_for_simd ||
-         DKind == OMPD_parallel_for_simd || DKind == OMPD_taskloop_simd ||
-         DKind == OMPD_master_taskloop_simd ||
-         DKind == OMPD_masked_taskloop_simd ||
-         DKind == OMPD_parallel_master_taskloop_simd ||
-         DKind == OMPD_parallel_masked_taskloop_simd ||
-         DKind == OMPD_distribute_parallel_for_simd ||
-         DKind == OMPD_distribute_simd || DKind == OMPD_target_simd ||
-         DKind == OMPD_teams_distribute_simd ||
-         DKind == OMPD_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_target_teams_distribute_parallel_for_simd ||
-         DKind == OMPD_target_teams_distribute_simd ||
-         DKind == OMPD_target_parallel_for_simd;
+  // Avoid OMPD_declare_simd
+  if (getDirectiveAssociation(DKind) != Association::Loop)
+    return false;
+  // Formally, OMPD_end_do_simd also has a loop association, but
+  // it's a Fortran-specific directive.
+
+  return DKind == OMPD_simd ||
+         llvm::is_contained(getLeafConstructs(DKind), OMPD_simd);
 }
 
 bool clang::isOpenMPNestingDistributeDirective(OpenMPDirectiveKind Kind) {
-  return Kind == OMPD_distribute || Kind == OMPD_distribute_parallel_for ||
-         Kind == OMPD_distribute_parallel_for_simd ||
-         Kind == OMPD_distribute_simd;
-  // TODO add next directives.
+  if (Kind == OMPD_distribute)
+    return true;
+  ArrayRef<Directive> Leaves = getLeafConstructs(Kind);
+  return !Leaves.empty() && Leaves.front() == OMPD_distribute;
 }
 
 bool clang::isOpenMPDistributeDirective(OpenMPDirectiveKind Kind) {
-  return isOpenMPNestingDistributeDirective(Kind) ||
-         Kind == OMPD_teams_distribute || Kind == OMPD_teams_distribute_simd ||
-         Kind == OMPD_teams_distribute_parallel_for_simd ||
-         Kind == OMPD_teams_distribute_parallel_for ||
-         Kind == OMPD_target_teams_distribute ||
-         Kind == OMPD_target_teams_distribute_parallel_for ||
-         Kind == OMPD_target_teams_distribute_parallel_for_simd ||
-         Kind == OMPD_target_teams_distribute_simd;
+  return Kind == OMPD_distribute ||
+         llvm::is_contained(getLeafConstructs(Kind), OMPD_distribute);
 }
 
 bool clang::isOpenMPGenericLoopDirective(OpenMPDirectiveKind Kind) {
-  return Kind == OMPD_loop || Kind == OMPD_teams_loop ||
-         Kind == OMPD_target_teams_loop || Kind == OMPD_parallel_loop ||
-         Kind == OMPD_target_parallel_loop;
+  if (Kind == OMPD_loop)
+    return true;
+  ArrayRef<Directive> Leaves = getLeafConstructs(Kind);
+  return !Leaves.empty() && Leaves.back() == OMPD_loop;
 }
 
 bool clang::isOpenMPPrivate(OpenMPClauseKind Kind) {

diff  --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 31578710365b21..ce532e0cfae297 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -127,6 +127,35 @@ class VersionedClause<Clause c, int min = 1, int max = 0x7FFFFFFF> {
   int maxVersion = max;
 }
 
+// Kinds of directive associations.
+class Association<string n> {
+  string name = n;  // Name of the enum value in enum class Association.
+}
+// All of the AS_Xyz names are recognized by TableGen in order to calculate
+// the association in the AS_FromLeaves case.
+def AS_None : Association<"None"> {}              // No association
+def AS_Block : Association<"Block"> {}            // Block (incl. single
+                                                  // statement)
+def AS_Declaration : Association<"Declaration"> {}   // Declaration
+def AS_Delimited : Association<"Delimited"> {}    // Region delimited with
+                                                  // begin/end
+def AS_Loop : Association<"Loop"> {}              // Loop
+def AS_Separating : Association<"Separating"> {}  // Separates parts of a
+                                                  // construct
+
+def AS_FromLeaves : Association<"FromLeaves"> {}    // See below
+// AS_FromLeaves can be used for combined/composite directives, and the actual
+// association will be computed based on associations of the leaf constructs:
+//   (x + y) + z = x + (y + z)
+//   x + y = y + x
+//   x + x = x
+//   AS_None + x = x
+//   AS_Block + AS_Loop = AS_Loop
+// 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
+// generated for it.
+
 // Information about a specific directive.
 class Directive<string d> {
   // Name of the directive. Can be composite directive sepearted by whitespace.
@@ -152,6 +181,13 @@ class Directive<string d> {
   // List of clauses that are required.
   list<VersionedClause> requiredClauses = [];
 
+  // List of leaf constituent directives in the order in which they appear
+  // in the combined/composite directive.
+  list<Directive> leafConstructs = [];
+
   // Set directive used by default when unknown.
   bit isDefault = false;
+
+  // What the directive is associated with.
+  Association association = AS_FromLeaves;
 }

diff  --git a/llvm/include/llvm/Frontend/OpenACC/ACC.td b/llvm/include/llvm/Frontend/OpenACC/ACC.td
index 0dbd934d83f064..dfa6a222e9f77c 100644
--- a/llvm/include/llvm/Frontend/OpenACC/ACC.td
+++ b/llvm/include/llvm/Frontend/OpenACC/ACC.td
@@ -266,7 +266,9 @@ def ACCC_Unknown : Clause<"unknown"> {
 //===----------------------------------------------------------------------===//
 
 // 2.12
-def ACC_Atomic : Directive<"atomic"> {}
+def ACC_Atomic : Directive<"atomic"> {
+  let association = AS_Block;
+}
 
 // 2.6.5
 def ACC_Data : Directive<"data"> {
@@ -290,6 +292,7 @@ def ACC_Data : Directive<"data"> {
     VersionedClause<ACCC_NoCreate>,
     VersionedClause<ACCC_Present>
   ];
+  let association = AS_Block;
 }
 
 // 2.13
@@ -304,6 +307,7 @@ def ACC_Declare : Directive<"declare"> {
     VersionedClause<ACCC_DeviceResident>,
     VersionedClause<ACCC_Link>
   ];
+  let association = AS_None;
 }
 
 // 2.5.3
@@ -329,6 +333,7 @@ def ACC_Kernels : Directive<"kernels"> {
     VersionedClause<ACCC_Self>,
     VersionedClause<ACCC_VectorLength>
   ];
+  let association = AS_Block;
 }
 
 // 2.5.1
@@ -357,6 +362,7 @@ def ACC_Parallel : Directive<"parallel"> {
     VersionedClause<ACCC_If>,
     VersionedClause<ACCC_Self>
   ];
+  let association = AS_Block;
 }
 
 // 2.5.2
@@ -384,6 +390,7 @@ def ACC_Serial : Directive<"serial"> {
     VersionedClause<ACCC_If>,
     VersionedClause<ACCC_Self>
   ];
+  let association = AS_Block;
 }
 
 // 2.9
@@ -403,10 +410,13 @@ def ACC_Loop : Directive<"loop"> {
     VersionedClause<ACCC_Independent>,
     VersionedClause<ACCC_Seq>
   ];
+  let association = AS_Loop;
 }
 
 // 2.10
-def ACC_Cache : Directive<"cache"> {}
+def ACC_Cache : Directive<"cache"> {
+  let association = AS_None;
+}
 
 // 2.14.1
 def ACC_Init : Directive<"init"> {
@@ -415,6 +425,7 @@ def ACC_Init : Directive<"init"> {
     VersionedClause<ACCC_DeviceType>,
     VersionedClause<ACCC_If>
   ];
+  let association = AS_None;
 }
 
 // 2.15.1
@@ -430,6 +441,7 @@ def ACC_Routine : Directive<"routine"> {
   let allowedOnceClauses = [
     VersionedClause<ACCC_NoHost>
   ];
+  let association = AS_Declaration;
 }
 
 // 2.14.3
@@ -448,6 +460,7 @@ def ACC_Set : Directive<"set"> {
     VersionedClause<ACCC_DeviceNum>,
     VersionedClause<ACCC_DeviceType>
   ];
+  let association = AS_None;
 }
 
 // 2.14.2
@@ -457,6 +470,7 @@ def ACC_Shutdown : Directive<"shutdown"> {
     VersionedClause<ACCC_DeviceType>,
     VersionedClause<ACCC_If>
   ];
+  let association = AS_None;
 }
 
 // 2.14.4
@@ -475,6 +489,7 @@ def ACC_Update : Directive<"update"> {
     VersionedClause<ACCC_Host>,
     VersionedClause<ACCC_Self>
   ];
+  let association = AS_None;
 }
 
 // 2.16.3
@@ -483,6 +498,7 @@ def ACC_Wait : Directive<"wait"> {
     VersionedClause<ACCC_Async>,
     VersionedClause<ACCC_If>
   ];
+  let association = AS_None;
 }
 
 // 2.14.6
@@ -499,6 +515,7 @@ def ACC_EnterData : Directive<"enter data"> {
     VersionedClause<ACCC_Create>,
     VersionedClause<ACCC_Copyin>
   ];
+  let association = AS_None;
 }
 
 // 2.14.7
@@ -516,6 +533,7 @@ def ACC_ExitData : Directive<"exit data"> {
     VersionedClause<ACCC_Delete>,
     VersionedClause<ACCC_Detach>
   ];
+  let association = AS_None;
 }
 
 // 2.8
@@ -527,6 +545,7 @@ def ACC_HostData : Directive<"host_data"> {
   let requiredClauses = [
     VersionedClause<ACCC_UseDevice>
   ];
+  let association = AS_Block;
 }
 
 // 2.11
@@ -564,6 +583,7 @@ def ACC_KernelsLoop : Directive<"kernels loop"> {
     VersionedClause<ACCC_Independent>,
     VersionedClause<ACCC_Seq>
   ];
+  let leafConstructs = [ACC_Kernels, ACC_Loop];
 }
 
 // 2.11
@@ -602,6 +622,7 @@ def ACC_ParallelLoop : Directive<"parallel loop"> {
     VersionedClause<ACCC_Independent>,
     VersionedClause<ACCC_Seq>
   ];
+  let leafConstructs = [ACC_Parallel, ACC_Loop];
 }
 
 // 2.11
@@ -637,8 +658,10 @@ def ACC_SerialLoop : Directive<"serial loop"> {
     VersionedClause<ACCC_Independent>,
     VersionedClause<ACCC_Seq>
   ];
+  let leafConstructs = [ACC_Serial, ACC_Loop];
 }
 
 def ACC_Unknown : Directive<"unknown"> {
   let isDefault = true;
+  let association = AS_None;
 }

diff  --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 77d207f2b10a83..d9a931438b4292 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -463,7 +463,9 @@ def OMPC_OMX_Bare : Clause<"ompx_bare"> {
 // Definition of OpenMP directives
 //===----------------------------------------------------------------------===//
 
-def OMP_ThreadPrivate : Directive<"threadprivate"> {}
+def OMP_ThreadPrivate : Directive<"threadprivate"> {
+  let association = AS_None;
+}
 def OMP_Parallel : Directive<"parallel"> {
   let allowedClauses = [
     VersionedClause<OMPC_Private>,
@@ -480,6 +482,7 @@ def OMP_Parallel : Directive<"parallel"> {
     VersionedClause<OMPC_NumThreads>,
     VersionedClause<OMPC_ProcBind>,
   ];
+  let association = AS_Block;
 }
 def OMP_Task : Directive<"task"> {
   let allowedClauses = [
@@ -500,6 +503,7 @@ def OMP_Task : Directive<"task"> {
     VersionedClause<OMPC_Final>,
     VersionedClause<OMPC_Priority>
   ];
+  let association = AS_Block;
 }
 def OMP_Simd : Directive<"simd"> {
   let allowedClauses = [
@@ -518,17 +522,20 @@ def OMP_Simd : Directive<"simd"> {
     VersionedClause<OMPC_If, 50>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let association = AS_Loop;
 }
 def OMP_Tile : Directive<"tile"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 51>,
   ];
+  let association = AS_Loop;
 }
 def OMP_Unroll : Directive<"unroll"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Full, 51>,
     VersionedClause<OMPC_Partial, 51>,
   ];
+  let association = AS_Loop;
 }
 def OMP_For : Directive<"for"> {
   let allowedClauses = [
@@ -544,6 +551,7 @@ def OMP_For : Directive<"for"> {
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let association = AS_Loop;
 }
 def OMP_Do : Directive<"do"> {
   let allowedClauses = [
@@ -560,6 +568,7 @@ def OMP_Do : Directive<"do"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let association = AS_Loop;
 }
 def OMP_Sections : Directive<"sections"> {
   let allowedClauses = [
@@ -570,8 +579,11 @@ def OMP_Sections : Directive<"sections"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_Allocate>
   ];
+  let association = AS_Block;
+}
+def OMP_Section : Directive<"section"> {
+  let association = AS_Separating;
 }
-def OMP_Section : Directive<"section"> {}
 def OMP_Single : Directive<"single"> {
   let allowedClauses = [
     VersionedClause<OMPC_Private>,
@@ -580,33 +592,44 @@ def OMP_Single : Directive<"single"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_Allocate>
   ];
+  let association = AS_Block;
+}
+def OMP_Master : Directive<"master"> {
+  let association = AS_Block;
 }
-def OMP_Master : Directive<"master"> {}
 def OMP_Critical : Directive<"critical"> {
   let allowedClauses = [
     VersionedClause<OMPC_Hint>
   ];
+  let association = AS_Block;
+}
+def OMP_TaskYield : Directive<"taskyield"> {
+  let association = AS_None;
+}
+def OMP_Barrier : Directive<"barrier"> {
+  let association = AS_None;
 }
-def OMP_TaskYield : Directive<"taskyield"> {}
-def OMP_Barrier : Directive<"barrier"> {}
 def OMP_Error : Directive<"error"> {
   let allowedClauses = [
     VersionedClause<OMPC_At, 51>,
     VersionedClause<OMPC_Severity, 51>,
     VersionedClause<OMPC_Message, 51>
   ];
+  let association = AS_None;
 }
 def OMP_TaskWait : Directive<"taskwait"> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend, 50>,
     VersionedClause<OMPC_NoWait, 51>
   ];
+  let association = AS_None;
 }
 def OMP_TaskGroup : Directive<"taskgroup"> {
   let allowedClauses = [
     VersionedClause<OMPC_TaskReduction, 50>,
     VersionedClause<OMPC_Allocate, 50>
   ];
+  let association = AS_Block;
 }
 def OMP_Flush : Directive<"flush"> {
   let allowedOnceClauses = [
@@ -617,6 +640,7 @@ def OMP_Flush : Directive<"flush"> {
     // OMPKinds.def.
     VersionedClause<OMPC_Flush>
   ];
+  let association = AS_None;
 }
 def OMP_Ordered : Directive<"ordered"> {
   let allowedClauses = [
@@ -627,6 +651,8 @@ def OMP_Ordered : Directive<"ordered"> {
     VersionedClause<OMPC_Threads>,
     VersionedClause<OMPC_Simd>
   ];
+  let association = AS_None;
+  // There is also a block-associated "ordered" directive.
 }
 def OMP_Atomic : Directive<"atomic"> {
   let allowedClauses = [
@@ -646,6 +672,7 @@ def OMP_Atomic : Directive<"atomic"> {
     VersionedClause<OMPC_Fail, 51>,
     VersionedClause<OMPC_Weak, 51>
   ];
+  let association = AS_Block;
 }
 def OMP_Target : Directive<"target"> {
   let allowedClauses = [
@@ -669,6 +696,7 @@ def OMP_Target : Directive<"target"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
   ];
+  let association = AS_Block;
 }
 def OMP_Teams : Directive<"teams"> {
   let allowedClauses = [
@@ -685,11 +713,13 @@ def OMP_Teams : Directive<"teams"> {
     VersionedClause<OMPC_NumTeams>,
     VersionedClause<OMPC_ThreadLimit>
   ];
+  let association = AS_Block;
 }
 def OMP_Cancel : Directive<"cancel"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_If>
   ];
+  let association = AS_None;
 }
 def OMP_Requires : Directive<"requires"> {
   let allowedOnceClauses = [
@@ -707,8 +737,11 @@ def OMP_Requires : Directive<"requires"> {
     VersionedClause<OMPC_DynamicAllocators>,
     VersionedClause<OMPC_AtomicDefaultMemOrder>
   ];
+  let association = AS_None;
+}
+def OMP_Nothing : Directive<"nothing"> {
+  let association = AS_None;
 }
-def OMP_Nothing : Directive<"nothing"> {}
 def OMP_TargetData : Directive<"target data"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Device>,
@@ -719,6 +752,7 @@ def OMP_TargetData : Directive<"target data"> {
     VersionedClause<OMPC_UseDevicePtr>,
     VersionedClause<OMPC_UseDeviceAddr, 50>
   ];
+  let association = AS_Block;
 }
 def OMP_TargetEnterData : Directive<"target enter data"> {
   let allowedClauses = [
@@ -732,6 +766,7 @@ def OMP_TargetEnterData : Directive<"target enter data"> {
   let requiredClauses = [
     VersionedClause<OMPC_Map>
   ];
+  let association = AS_None;
 }
 def OMP_TargetExitData : Directive<"target exit data"> {
   let allowedClauses = [
@@ -745,6 +780,7 @@ def OMP_TargetExitData : Directive<"target exit data"> {
   let requiredClauses = [
     VersionedClause<OMPC_Map>
   ];
+  let association = AS_None;
 }
 def OMP_TargetParallel : Directive<"target parallel"> {
   let allowedClauses = [
@@ -771,6 +807,7 @@ def OMP_TargetParallel : Directive<"target parallel"> {
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
     VersionedClause<OMPC_ThreadLimit, 51>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Parallel];
 }
 def OMP_TargetParallelFor : Directive<"target parallel for"> {
   let allowedClauses = [
@@ -803,6 +840,7 @@ def OMP_TargetParallelFor : Directive<"target parallel for"> {
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
     VersionedClause<OMPC_ThreadLimit, 51>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Parallel, OMP_For];
 }
 def OMP_TargetParallelDo : Directive<"target parallel do"> {
   let allowedClauses = [
@@ -833,6 +871,7 @@ def OMP_TargetParallelDo : Directive<"target parallel do"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Target, OMP_Parallel, OMP_Do];
 }
 def OMP_TargetUpdate : Directive<"target update"> {
   let allowedClauses = [
@@ -845,6 +884,13 @@ def OMP_TargetUpdate : Directive<"target update"> {
     VersionedClause<OMPC_If>,
     VersionedClause<OMPC_NoWait>
   ];
+  let association = AS_None;
+}
+def OMP_masked : Directive<"masked"> {
+  let allowedOnceClauses = [
+    VersionedClause<OMPC_Filter>
+  ];
+  let association = AS_Block;
 }
 def OMP_ParallelFor : Directive<"parallel for"> {
   let allowedClauses = [
@@ -866,6 +912,7 @@ def OMP_ParallelFor : Directive<"parallel for"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_For];
 }
 def OMP_ParallelDo : Directive<"parallel do"> {
   let allowedClauses = [
@@ -887,6 +934,7 @@ def OMP_ParallelDo : Directive<"parallel do"> {
     VersionedClause<OMPC_Collapse>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Do];
 }
 def OMP_ParallelForSimd : Directive<"parallel for simd"> {
   let allowedClauses = [
@@ -912,6 +960,7 @@ def OMP_ParallelForSimd : Directive<"parallel for simd"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_For, OMP_Simd];
 }
 def OMP_ParallelDoSimd : Directive<"parallel do simd"> {
   let allowedClauses = [
@@ -938,6 +987,7 @@ def OMP_ParallelDoSimd : Directive<"parallel do simd"> {
     VersionedClause<OMPC_SimdLen>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Do, OMP_Simd];
 }
 def OMP_ParallelMaster : Directive<"parallel master"> {
   let allowedClauses = [
@@ -953,6 +1003,7 @@ def OMP_ParallelMaster : Directive<"parallel master"> {
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Master];
 }
 def OMP_ParallelMasked : Directive<"parallel masked"> {
   let allowedClauses = [
@@ -969,6 +1020,7 @@ def OMP_ParallelMasked : Directive<"parallel masked"> {
     VersionedClause<OMPC_Filter>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_masked];
 }
 def OMP_ParallelSections : Directive<"parallel sections"> {
   let allowedClauses = [
@@ -987,6 +1039,7 @@ def OMP_ParallelSections : Directive<"parallel sections"> {
     VersionedClause<OMPC_If>,
     VersionedClause<OMPC_NumThreads>
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Sections];
 }
 def OMP_ForSimd : Directive<"for simd"> {
   let allowedClauses = [
@@ -1007,6 +1060,7 @@ def OMP_ForSimd : Directive<"for simd"> {
     VersionedClause<OMPC_NonTemporal, 50>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_For, OMP_Simd];
 }
 def OMP_DoSimd : Directive<"do simd"> {
   let allowedClauses = [
@@ -1027,13 +1081,19 @@ def OMP_DoSimd : Directive<"do simd"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Do, OMP_Simd];
+}
+def OMP_CancellationPoint : Directive<"cancellation point"> {
+  let association = AS_None;
+}
+def OMP_DeclareReduction : Directive<"declare reduction"> {
+  let association = AS_None;
 }
-def OMP_CancellationPoint : Directive<"cancellation point"> {}
-def OMP_DeclareReduction : Directive<"declare reduction"> {}
 def OMP_DeclareMapper : Directive<"declare mapper"> {
   let allowedClauses = [
     VersionedClause<OMPC_Map>
   ];
+  let association = AS_None;
 }
 def OMP_DeclareSimd : Directive<"declare simd"> {
   let allowedClauses = [
@@ -1048,6 +1108,7 @@ def OMP_DeclareSimd : Directive<"declare simd"> {
     VersionedClause<OMPC_Inbranch>,
     VersionedClause<OMPC_Notinbranch>
   ];
+  let association = AS_Declaration;
 }
 def OMP_TaskLoop : Directive<"taskloop"> {
   let allowedClauses = [
@@ -1073,6 +1134,7 @@ def OMP_TaskLoop : Directive<"taskloop"> {
     VersionedClause<OMPC_GrainSize>,
     VersionedClause<OMPC_NumTasks>
   ];
+  let association = AS_Loop;
 }
 def OMP_TaskLoopSimd : Directive<"taskloop simd"> {
   let allowedClauses = [
@@ -1104,6 +1166,7 @@ def OMP_TaskLoopSimd : Directive<"taskloop simd"> {
     VersionedClause<OMPC_GrainSize>,
     VersionedClause<OMPC_NumTasks>
   ];
+  let leafConstructs = [OMP_TaskLoop, OMP_Simd];
 }
 def OMP_Distribute : Directive<"distribute"> {
   let allowedClauses = [
@@ -1116,6 +1179,7 @@ def OMP_Distribute : Directive<"distribute"> {
     VersionedClause<OMPC_Collapse>,
     VersionedClause<OMPC_DistSchedule>
   ];
+  let association = AS_Loop;
 }
 def OMP_BeginDeclareTarget : Directive<"begin declare target"> {
   let allowedClauses = [
@@ -1124,6 +1188,7 @@ def OMP_BeginDeclareTarget : Directive<"begin declare target"> {
     VersionedClause<OMPC_DeviceType>,
     VersionedClause<OMPC_Indirect>
   ];
+  let association = AS_Delimited;
 }
 def OMP_DeclareTarget : Directive<"declare target"> {
   let allowedClauses = [
@@ -1135,8 +1200,11 @@ def OMP_DeclareTarget : Directive<"declare target"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_DeviceType, 50>
   ];
+  let association = AS_None;
+}
+def OMP_EndDeclareTarget : Directive<"end declare target"> {
+  let association = AS_Delimited;
 }
-def OMP_EndDeclareTarget : Directive<"end declare target"> {}
 def OMP_DistributeParallelFor : Directive<"distribute parallel for"> {
   let allowedClauses = [
     VersionedClause<OMPC_FirstPrivate>,
@@ -1156,6 +1224,7 @@ def OMP_DistributeParallelFor : Directive<"distribute parallel for"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Distribute, OMP_Parallel, OMP_For];
 }
 def OMP_DistributeParallelDo : Directive<"distribute parallel do"> {
   let allowedClauses = [
@@ -1179,6 +1248,7 @@ def OMP_DistributeParallelDo : Directive<"distribute parallel do"> {
     VersionedClause<OMPC_Ordered>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Distribute, OMP_Parallel, OMP_Do];
 }
 def OMP_DistributeParallelForSimd : Directive<"distribute parallel for simd"> {
   let allowedClauses = [
@@ -1204,6 +1274,7 @@ def OMP_DistributeParallelForSimd : Directive<"distribute parallel for simd"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Distribute, OMP_Parallel, OMP_For, OMP_Simd];
 }
 def OMP_DistributeParallelDoSimd : Directive<"distribute parallel do simd"> {
   let allowedClauses = [
@@ -1228,6 +1299,7 @@ def OMP_DistributeParallelDoSimd : Directive<"distribute parallel do simd"> {
     VersionedClause<OMPC_NonTemporal>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Distribute, OMP_Parallel, OMP_Do, OMP_Simd];
 }
 def OMP_DistributeSimd : Directive<"distribute simd"> {
   let allowedClauses = [
@@ -1254,6 +1326,7 @@ def OMP_DistributeSimd : Directive<"distribute simd"> {
     VersionedClause<OMPC_SimdLen>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Distribute, OMP_Simd];
 }
 
 def OMP_TargetParallelForSimd : Directive<"target parallel for simd"> {
@@ -1291,6 +1364,7 @@ def OMP_TargetParallelForSimd : Directive<"target parallel for simd"> {
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
     VersionedClause<OMPC_ThreadLimit, 51>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Parallel, OMP_For, OMP_Simd];
 }
 def OMP_TargetParallelDoSimd : Directive<"target parallel do simd"> {
   let allowedClauses = [
@@ -1322,6 +1396,7 @@ def OMP_TargetParallelDoSimd : Directive<"target parallel do simd"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_UsesAllocators>
   ];
+  let leafConstructs = [OMP_Target, OMP_Parallel, OMP_Do, OMP_Simd];
 }
 def OMP_TargetSimd : Directive<"target simd"> {
   let allowedClauses = [
@@ -1356,6 +1431,7 @@ def OMP_TargetSimd : Directive<"target simd"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_ThreadLimit, 51>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Simd];
 }
 def OMP_TeamsDistribute : Directive<"teams distribute"> {
   let allowedClauses = [
@@ -1375,6 +1451,7 @@ def OMP_TeamsDistribute : Directive<"teams distribute"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_If>
   ];
+  let leafConstructs = [OMP_Teams, OMP_Distribute];
 }
 def OMP_TeamsDistributeSimd : Directive<"teams distribute simd"> {
   let allowedClauses = [
@@ -1400,6 +1477,7 @@ def OMP_TeamsDistributeSimd : Directive<"teams distribute simd"> {
     VersionedClause<OMPC_ThreadLimit>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Teams, OMP_Distribute, OMP_Simd];
 }
 
 def OMP_TeamsDistributeParallelForSimd :
@@ -1428,6 +1506,7 @@ def OMP_TeamsDistributeParallelForSimd :
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_For, OMP_Simd];
 }
 def OMP_TeamsDistributeParallelDoSimd :
     Directive<"teams distribute parallel do simd"> {
@@ -1456,6 +1535,7 @@ def OMP_TeamsDistributeParallelDoSimd :
     VersionedClause<OMPC_SimdLen>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_Do, OMP_Simd];
 }
 def OMP_TeamsDistributeParallelFor :
     Directive<"teams distribute parallel for"> {
@@ -1479,6 +1559,7 @@ def OMP_TeamsDistributeParallelFor :
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_For];
 }
 def OMP_TeamsDistributeParallelDo :
     Directive<"teams distribute parallel do"> {
@@ -1505,6 +1586,7 @@ let allowedOnceClauses = [
     VersionedClause<OMPC_Schedule>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_Do];
 }
 def OMP_TargetTeams : Directive<"target teams"> {
   let allowedClauses = [
@@ -1532,6 +1614,7 @@ def OMP_TargetTeams : Directive<"target teams"> {
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
     VersionedClause<OMPC_OMX_Bare>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams];
 }
 def OMP_TargetTeamsDistribute : Directive<"target teams distribute"> {
   let allowedClauses = [
@@ -1560,6 +1643,7 @@ def OMP_TargetTeamsDistribute : Directive<"target teams distribute"> {
     VersionedClause<OMPC_DistSchedule>,
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute];
 }
 
 def OMP_TargetTeamsDistributeParallelFor :
@@ -1594,6 +1678,7 @@ def OMP_TargetTeamsDistributeParallelFor :
   let allowedOnceClauses = [
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_For];
 }
 def OMP_TargetTeamsDistributeParallelDo :
     Directive<"target teams distribute parallel do"> {
@@ -1628,6 +1713,7 @@ def OMP_TargetTeamsDistributeParallelDo :
     VersionedClause<OMPC_Schedule>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_Do];
 }
 def OMP_TargetTeamsDistributeParallelForSimd :
     Directive<"target teams distribute parallel for simd"> {
@@ -1666,6 +1752,7 @@ def OMP_TargetTeamsDistributeParallelForSimd :
   let allowedOnceClauses = [
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_For, OMP_Simd];
 }
 def OMP_TargetTeamsDistributeParallelDoSimd :
     Directive<"target teams distribute parallel do simd"> {
@@ -1704,6 +1791,7 @@ def OMP_TargetTeamsDistributeParallelDoSimd :
     VersionedClause<OMPC_SimdLen>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute, OMP_Parallel, OMP_Do, OMP_Simd];
 }
 def OMP_TargetTeamsDistributeSimd :
     Directive<"target teams distribute simd"> {
@@ -1738,17 +1826,20 @@ def OMP_TargetTeamsDistributeSimd :
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute, OMP_Simd];
 }
 def OMP_Allocate : Directive<"allocate"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Allocator>,
     VersionedClause<OMPC_Align, 51>
   ];
+  let association = AS_None;
 }
 def OMP_Allocators : Directive<"allocators"> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>
   ];
+  let association = AS_Block;
 }
 def OMP_DeclareVariant : Directive<"declare variant"> {
   let allowedClauses = [
@@ -1758,6 +1849,7 @@ def OMP_DeclareVariant : Directive<"declare variant"> {
     VersionedClause<OMPC_AdjustArgs, 51>,
     VersionedClause<OMPC_AppendArgs, 51>
   ];
+  let association = AS_Declaration;
 }
 def OMP_MasterTaskloop : Directive<"master taskloop"> {
   let allowedClauses = [
@@ -1779,6 +1871,7 @@ def OMP_MasterTaskloop : Directive<"master taskloop"> {
     VersionedClause<OMPC_InReduction>,
     VersionedClause<OMPC_Allocate>
   ];
+  let leafConstructs = [OMP_Master, OMP_TaskLoop];
 }
 def OMP_MaskedTaskloop : Directive<"masked taskloop"> {
   let allowedClauses = [
@@ -1801,6 +1894,7 @@ def OMP_MaskedTaskloop : Directive<"masked taskloop"> {
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Filter>
   ];
+  let leafConstructs = [OMP_masked, OMP_TaskLoop];
 }
 def OMP_ParallelMasterTaskloop :
     Directive<"parallel master taskloop"> {
@@ -1826,6 +1920,7 @@ def OMP_ParallelMasterTaskloop :
     VersionedClause<OMPC_Copyin>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Master, OMP_TaskLoop];
 }
 def OMP_ParallelMaskedTaskloop :
     Directive<"parallel masked taskloop"> {
@@ -1852,6 +1947,7 @@ def OMP_ParallelMaskedTaskloop :
     VersionedClause<OMPC_Filter>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_masked, OMP_TaskLoop];
 }
 def OMP_MasterTaskloopSimd : Directive<"master taskloop simd"> {
   let allowedClauses = [
@@ -1879,6 +1975,7 @@ def OMP_MasterTaskloopSimd : Directive<"master taskloop simd"> {
     VersionedClause<OMPC_NonTemporal, 50>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let leafConstructs = [OMP_Master, OMP_TaskLoop, OMP_Simd];
 }
 def OMP_MaskedTaskloopSimd : Directive<"masked taskloop simd"> {
   let allowedClauses = [
@@ -1907,6 +2004,7 @@ def OMP_MaskedTaskloopSimd : Directive<"masked taskloop simd"> {
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_Filter>
   ];
+  let leafConstructs = [OMP_masked, OMP_TaskLoop, OMP_Simd];
 }
 def OMP_ParallelMasterTaskloopSimd :
     Directive<"parallel master taskloop simd"> {
@@ -1938,6 +2036,7 @@ def OMP_ParallelMasterTaskloopSimd :
     VersionedClause<OMPC_Order, 50>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Master, OMP_TaskLoop, OMP_Simd];
 }
 def OMP_ParallelMaskedTaskloopSimd :
     Directive<"parallel masked taskloop simd"> {
@@ -1970,6 +2069,7 @@ def OMP_ParallelMaskedTaskloopSimd :
     VersionedClause<OMPC_Filter>,
     VersionedClause<OMPC_OMPX_Attribute>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_masked, OMP_TaskLoop, OMP_Simd];
 }
 def OMP_Depobj : Directive<"depobj"> {
   let allowedClauses = [
@@ -1980,18 +2080,30 @@ def OMP_Depobj : Directive<"depobj"> {
     // OMPKinds.def.
     VersionedClause<OMPC_Depobj, 50>
   ];
+  let association = AS_None;
 }
 def OMP_Scan : Directive<"scan"> {
   let allowedClauses = [
     VersionedClause<OMPC_Inclusive, 50>,
     VersionedClause<OMPC_Exclusive, 50>
   ];
+  let association = AS_Separating;
+}
+def OMP_Assumes : Directive<"assumes"> {
+  let association = AS_None;
+}
+def OMP_BeginAssumes : Directive<"begin assumes"> {
+  let association = AS_Delimited;
+}
+def OMP_EndAssumes : Directive<"end assumes"> {
+  let association = AS_Delimited;
+}
+def OMP_BeginDeclareVariant : Directive<"begin declare variant"> {
+  let association = AS_Delimited;
+}
+def OMP_EndDeclareVariant : Directive<"end declare variant"> {
+  let association = AS_Delimited;
 }
-def OMP_Assumes : Directive<"assumes"> {}
-def OMP_BeginAssumes : Directive<"begin assumes"> {}
-def OMP_EndAssumes : Directive<"end assumes"> {}
-def OMP_BeginDeclareVariant : Directive<"begin declare variant"> {}
-def OMP_EndDeclareVariant : Directive<"end declare variant"> {}
 def OMP_scope : Directive<"scope"> {
   let allowedClauses = [
     VersionedClause<OMPC_Private, 51>,
@@ -2000,6 +2112,10 @@ def OMP_scope : Directive<"scope"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait, 51>
   ];
+  let association = AS_Block;
+}
+def OMP_Workshare : Directive<"workshare"> {
+  let association = AS_Block;
 }
 def OMP_ParallelWorkshare : Directive<"parallel workshare"> {
   let allowedClauses = [
@@ -2016,22 +2132,29 @@ def OMP_ParallelWorkshare : Directive<"parallel workshare"> {
     VersionedClause<OMPC_NumThreads>,
     VersionedClause<OMPC_ProcBind>
   ];
+  let leafConstructs = [OMP_Parallel, OMP_Workshare];
 }
-def OMP_Workshare : Directive<"workshare"> {}
 def OMP_EndDo : Directive<"end do"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>
   ];
+  // Needed for association computation, since OMP_Do has it "from leafConstructs".
+  let leafConstructs = OMP_Do.leafConstructs;
+  let association = OMP_Do.association;
 }
 def OMP_EndDoSimd : Directive<"end do simd"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>
   ];
+  let leafConstructs = OMP_DoSimd.leafConstructs;
+  let association = OMP_DoSimd.association;
 }
 def OMP_EndSections : Directive<"end sections"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>
   ];
+  let leafConstructs = OMP_Sections.leafConstructs;
+  let association = OMP_Sections.association;
 }
 def OMP_EndSingle : Directive<"end single"> {
   let allowedClauses = [
@@ -2040,11 +2163,15 @@ def OMP_EndSingle : Directive<"end single"> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>
   ];
+  let leafConstructs = OMP_Single.leafConstructs;
+  let association = OMP_Single.association;
 }
 def OMP_EndWorkshare : Directive<"end workshare"> {
   let allowedClauses = [
     VersionedClause<OMPC_NoWait>
   ];
+  let leafConstructs = OMP_Workshare.leafConstructs;
+  let association = OMP_Workshare.association;
 }
 def OMP_interop : Directive<"interop"> {
   let allowedClauses = [
@@ -2055,6 +2182,7 @@ def OMP_interop : Directive<"interop"> {
     VersionedClause<OMPC_NoWait>,
     VersionedClause<OMPC_Use>,
   ];
+  let association = AS_None;
 }
 def OMP_dispatch : Directive<"dispatch"> {
   let allowedClauses = [
@@ -2066,11 +2194,7 @@ def OMP_dispatch : Directive<"dispatch"> {
     VersionedClause<OMPC_Novariants>,
     VersionedClause<OMPC_Nocontext>
   ];
-}
-def OMP_masked : Directive<"masked"> {
-  let allowedOnceClauses = [
-    VersionedClause<OMPC_Filter>
-  ];
+  let association = AS_Block;
 }
 def OMP_loop : Directive<"loop"> {
   let allowedClauses = [
@@ -2083,6 +2207,7 @@ def OMP_loop : Directive<"loop"> {
     VersionedClause<OMPC_Collapse>,
     VersionedClause<OMPC_Order, 50>
   ];
+  let association = AS_Loop;
 }
 def OMP_teams_loop : Directive<"teams loop"> {
   let allowedClauses = [
@@ -2102,6 +2227,7 @@ def OMP_teams_loop : Directive<"teams loop"> {
     VersionedClause<OMPC_Order>,
     VersionedClause<OMPC_ThreadLimit>,
   ];
+  let leafConstructs = [OMP_Teams, OMP_loop];
 }
 def OMP_target_teams_loop : Directive<"target teams loop"> {
   let allowedClauses = [
@@ -2131,6 +2257,7 @@ def OMP_target_teams_loop : Directive<"target teams loop"> {
     VersionedClause<OMPC_ThreadLimit>,
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Teams, OMP_loop];
 }
 def OMP_parallel_loop : Directive<"parallel loop"> {
   let allowedClauses = [
@@ -2152,6 +2279,7 @@ def OMP_parallel_loop : Directive<"parallel loop"> {
     VersionedClause<OMPC_Order>,
     VersionedClause<OMPC_ProcBind>,
   ];
+  let leafConstructs = [OMP_Parallel, OMP_loop];
 }
 def OMP_target_parallel_loop : Directive<"target parallel loop"> {
   let allowedClauses = [
@@ -2183,11 +2311,15 @@ def OMP_target_parallel_loop : Directive<"target parallel loop"> {
     VersionedClause<OMPC_OMPX_DynCGroupMem>,
     VersionedClause<OMPC_ThreadLimit, 51>,
   ];
+  let leafConstructs = [OMP_Target, OMP_Parallel, OMP_loop];
 }
 def OMP_Metadirective : Directive<"metadirective"> {
   let allowedClauses = [VersionedClause<OMPC_When>];
   let allowedOnceClauses = [VersionedClause<OMPC_Default>];
+  let association = AS_None;
 }
+
 def OMP_Unknown : Directive<"unknown"> {
   let isDefault = true;
+  let association = AS_None;
 }

diff  --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index c86018715a48a1..bd536d4a2b7f8c 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -51,6 +51,10 @@ class DirectiveLanguage {
     return Def->getValueAsBit("enableBitmaskEnumInNamespace");
   }
 
+  std::vector<Record *> getAssociations() const {
+    return Records.getAllDerivedDefinitions("Association");
+  }
+
   std::vector<Record *> getDirectives() const {
     return Records.getAllDerivedDefinitions("Directive");
   }
@@ -121,6 +125,12 @@ class Directive : public BaseRecord {
   std::vector<Record *> getRequiredClauses() const {
     return Def->getValueAsListOfDefs("requiredClauses");
   }
+
+  std::vector<Record *> getLeafConstructs() const {
+    return Def->getValueAsListOfDefs("leafConstructs");
+  }
+
+  Record *getAssociation() const { return Def->getValueAsDef("association"); }
 };
 
 // Wrapper class that contains Clause's information defined in DirectiveBase.td

diff  --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index bcd9fa34c99d3a..aa47d7ee666d02 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -44,11 +44,13 @@ def TDL_DirA : Directive<"dira"> {
     VersionedClause<TDLC_ClauseB>
   ];
   let isDefault = 1;
+  let association = AS_None;
 }
 
 // CHECK:       #ifndef LLVM_Tdl_INC
 // CHECK-NEXT:  #define LLVM_Tdl_INC
 // CHECK-EMPTY:
+// CHECK-NEXT:  #include "llvm/ADT/ArrayRef.h"
 // CHECK-NEXT:  #include "llvm/ADT/BitmaskEnum.h"
 // CHECK-EMPTY:
 // CHECK-NEXT:  namespace llvm {
@@ -57,6 +59,17 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-EMPTY:
 // CHECK-NEXT:  LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
 // CHECK-EMPTY:
+// CHECK-NEXT:  enum class Association {
+// CHECK-NEXT:    Block,
+// CHECK-NEXT:    Declaration,
+// CHECK-NEXT:    Delimited,
+// CHECK-NEXT:    Loop,
+// CHECK-NEXT:    None,
+// CHECK-NEXT:    Separating,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr std::size_t Association_enumSize = 6;
+// CHECK-EMPTY:
 // CHECK-NEXT:  enum class Directive {
 // CHECK-NEXT:    TDLD_dira,
 // CHECK-NEXT:  };
@@ -99,6 +112,8 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-NEXT:  /// Return true if \p C is a valid clause for \p D in version \p Version.
 // CHECK-NEXT:  bool isAllowedClauseForDirective(Directive D, Clause C, unsigned Version);
 // CHECK-EMPTY:
+// CHECK-NEXT:  llvm::ArrayRef<Directive> getLeafConstructs(Directive D);
+// CHECK-NEXT:  Association getDirectiveAssociation(Directive D);
 // CHECK-NEXT:  AKind getAKind(StringRef);
 // CHECK-NEXT:  llvm::StringRef getTdlAKindName(AKind);
 // CHECK-EMPTY:
@@ -270,6 +285,8 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL:       #ifdef GEN_DIRECTIVES_IMPL
 // IMPL-NEXT:  #undef GEN_DIRECTIVES_IMPL
 // IMPL-EMPTY:
+// IMPL-NEXT:  #include "llvm/Support/ErrorHandling.h"
+// IMPL-EMPTY:
 // IMPL-NEXT:  Directive llvm::tdl::getTdlDirectiveKind(llvm::StringRef Str) {
 // IMPL-NEXT:    return llvm::StringSwitch<Directive>(Str)
 // IMPL-NEXT:      .Case("dira",TDLD_dira)
@@ -342,4 +359,20 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
 // IMPL-NEXT:  }
 // IMPL-EMPTY:
+// IMPL-NEXT:  llvm::ArrayRef<llvm::tdl::Directive> llvm::tdl::getLeafConstructs(llvm::tdl::Directive Dir) {
+// IMPL-NEXT:    switch (Dir) {
+// IMPL-NEXT:    default:
+// IMPL-NEXT:      return ArrayRef<llvm::tdl::Directive>{};
+// IMPL-NEXT:    } // switch (Dir)
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
+// IMPL-NEXT:  llvm::tdl::Association llvm::tdl::getDirectiveAssociation(llvm::tdl::Directive Dir) {
+// IMPL-NEXT:    switch (Dir) {
+// IMPL-NEXT:    case llvm::tdl::Directive::TDLD_dira:
+// IMPL-NEXT:      return llvm::tdl::Association::None;
+// IMPL-NEXT:    default:
+// IMPL-NEXT:      llvm_unreachable("Unexpected directive");
+// IMPL-NEXT:    } // switch(Dir)
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
 // IMPL-NEXT:  #endif // GEN_DIRECTIVES_IMPL

diff  --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td
index 01741152956d22..90f9c45b5e15bd 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -38,15 +38,29 @@ def TDL_DirA : Directive<"dira"> {
     VersionedClause<TDLC_ClauseB, 2>
   ];
   let isDefault = 1;
+  let association = AS_Block;
 }
 
 // CHECK:       #ifndef LLVM_Tdl_INC
 // CHECK-NEXT:  #define LLVM_Tdl_INC
 // CHECK-EMPTY:
+// CHECK-NEXT:  #include "llvm/ADT/ArrayRef.h"
+// CHECK-EMPTY:
 // CHECK-NEXT:  namespace llvm {
 // CHECK-NEXT:  class StringRef;
 // CHECK-NEXT:  namespace tdl {
 // CHECK-EMPTY:
+// CHECK-NEXT:  enum class Association {
+// CHECK-NEXT:    Block,
+// CHECK-NEXT:    Declaration,
+// CHECK-NEXT:    Delimited,
+// CHECK-NEXT:    Loop,
+// CHECK-NEXT:    None,
+// CHECK-NEXT:    Separating,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr std::size_t Association_enumSize = 6;
+// CHECK-EMPTY:
 // CHECK-NEXT:  enum class Directive {
 // CHECK-NEXT:    TDLD_dira,
 // CHECK-NEXT:  };
@@ -74,6 +88,8 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-NEXT:  /// Return true if \p C is a valid clause for \p D in version \p Version.
 // CHECK-NEXT:  bool isAllowedClauseForDirective(Directive D, Clause C, unsigned Version);
 // CHECK-EMPTY:
+// CHECK-NEXT:  llvm::ArrayRef<Directive> getLeafConstructs(Directive D);
+// CHECK-NEXT:  Association getDirectiveAssociation(Directive D);
 // CHECK-NEXT:  } // namespace tdl
 // CHECK-NEXT:  } // namespace llvm
 // CHECK-NEXT:  #endif // LLVM_Tdl_INC
@@ -217,6 +233,8 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL:       #ifdef GEN_DIRECTIVES_IMPL
 // IMPL-NEXT:  #undef GEN_DIRECTIVES_IMPL
 // IMPL-EMPTY:
+// IMPL-NEXT:  #include "llvm/Support/ErrorHandling.h"
+// IMPL-EMPTY:
 // IMPL-NEXT:  Directive llvm::tdl::getTdlDirectiveKind(llvm::StringRef Str) {
 // IMPL-NEXT:    return llvm::StringSwitch<Directive>(Str)
 // IMPL-NEXT:      .Case("dira",TDLD_dira)
@@ -272,4 +290,20 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
 // IMPL-NEXT:  }
 // IMPL-EMPTY:
+// IMPL-NEXT:  llvm::ArrayRef<llvm::tdl::Directive> llvm::tdl::getLeafConstructs(llvm::tdl::Directive Dir) {
+// IMPL-NEXT:    switch (Dir) {
+// IMPL-NEXT:    default:
+// IMPL-NEXT:      return ArrayRef<llvm::tdl::Directive>{};
+// IMPL-NEXT:    } // switch (Dir)
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
+// IMPL-NEXT:  llvm::tdl::Association llvm::tdl::getDirectiveAssociation(llvm::tdl::Directive Dir) {
+// IMPL-NEXT:    switch (Dir) {
+// IMPL-NEXT:    case llvm::tdl::Directive::TDLD_dira:
+// IMPL-NEXT:      return llvm::tdl::Association::Block;
+// IMPL-NEXT:    default:
+// IMPL-NEXT:      llvm_unreachable("Unexpected directive");
+// IMPL-NEXT:    } // switch(Dir)
+// IMPL-NEXT:  }
+// IMPL-EMPTY:
 // IMPL-NEXT:  #endif // GEN_DIRECTIVES_IMPL

diff  --git a/llvm/utils/TableGen/DirectiveEmitter.cpp b/llvm/utils/TableGen/DirectiveEmitter.cpp
index 99eebacd6b3f32..5a35d320227d5b 100644
--- a/llvm/utils/TableGen/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/DirectiveEmitter.cpp
@@ -42,7 +42,8 @@ class IfDefScope {
 // Generate enum class
 static void GenerateEnumClass(const std::vector<Record *> &Records,
                               raw_ostream &OS, StringRef Enum, StringRef Prefix,
-                              const DirectiveLanguage &DirLang) {
+                              const DirectiveLanguage &DirLang,
+                              bool ExportEnums) {
   OS << "\n";
   OS << "enum class " << Enum << " {\n";
   for (const auto &R : Records) {
@@ -59,7 +60,7 @@ static void GenerateEnumClass(const std::vector<Record *> &Records,
   // At the same time we do not loose the strong type guarantees of the enum
   // class, that is we cannot pass an unsigned as Directive without an explicit
   // cast.
-  if (DirLang.hasMakeEnumAvailableInNamespace()) {
+  if (ExportEnums) {
     OS << "\n";
     for (const auto &R : Records) {
       BaseRecord Rec{R};
@@ -183,9 +184,10 @@ static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
 
   OS << "#ifndef LLVM_" << DirLang.getName() << "_INC\n";
   OS << "#define LLVM_" << DirLang.getName() << "_INC\n";
+  OS << "\n#include \"llvm/ADT/ArrayRef.h\"\n";
 
   if (DirLang.hasEnableBitmaskEnumInNamespace())
-    OS << "\n#include \"llvm/ADT/BitmaskEnum.h\"\n";
+    OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n";
 
   OS << "\n";
   OS << "namespace llvm {\n";
@@ -200,13 +202,24 @@ static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
   if (DirLang.hasEnableBitmaskEnumInNamespace())
     OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n";
 
+  // Emit Directive associations
+  std::vector<Record *> associations;
+  llvm::copy_if(
+      DirLang.getAssociations(), std::back_inserter(associations),
+      // Skip the "special" value
+      [](const Record *Def) { return Def->getName() != "AS_FromLeaves"; });
+  GenerateEnumClass(associations, OS, "Association",
+                    /*Prefix=*/"", DirLang, /*ExportEnums=*/false);
+
   // Emit Directive enumeration
   GenerateEnumClass(DirLang.getDirectives(), OS, "Directive",
-                    DirLang.getDirectivePrefix(), DirLang);
+                    DirLang.getDirectivePrefix(), DirLang,
+                    DirLang.hasMakeEnumAvailableInNamespace());
 
   // Emit Clause enumeration
   GenerateEnumClass(DirLang.getClauses(), OS, "Clause",
-                    DirLang.getClausePrefix(), DirLang);
+                    DirLang.getClausePrefix(), DirLang,
+                    DirLang.hasMakeEnumAvailableInNamespace());
 
   // Emit ClauseVal enumeration
   std::string EnumHelperFuncs;
@@ -231,6 +244,8 @@ static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
   OS << "bool isAllowedClauseForDirective(Directive D, "
      << "Clause C, unsigned Version);\n";
   OS << "\n";
+  OS << "llvm::ArrayRef<Directive> getLeafConstructs(Directive D);\n";
+  OS << "Association getDirectiveAssociation(Directive D);\n";
   if (EnumHelperFuncs.length() > 0) {
     OS << EnumHelperFuncs;
     OS << "\n";
@@ -435,6 +450,217 @@ static void GenerateIsAllowedClause(const DirectiveLanguage &DirLang,
   OS << "}\n"; // End of function isAllowedClauseForDirective
 }
 
+// Generate the getLeafConstructs function implementation.
+static void GenerateGetLeafConstructs(const DirectiveLanguage &DirLang,
+                                      raw_ostream &OS) {
+  auto getQualifiedName = [&](StringRef Formatted) -> std::string {
+    return (llvm::Twine("llvm::") + DirLang.getCppNamespace() +
+            "::Directive::" + DirLang.getDirectivePrefix() + Formatted)
+        .str();
+  };
+
+  // For each list of leaves, generate a static local object, then
+  // return a reference to that object for a given directive, e.g.
+  //
+  //   static ListTy leafConstructs_A_B = { A, B };
+  //   static ListTy leafConstructs_C_D_E = { C, D, E };
+  //   switch (Dir) {
+  //     case A_B:
+  //       return leafConstructs_A_B;
+  //     case C_D_E:
+  //       return leafConstructs_C_D_E;
+  //   }
+
+  // Map from a record that defines a directive to the name of the
+  // local object with the list of its leaves.
+  DenseMap<Record *, std::string> ListNames;
+
+  std::string DirectiveTypeName =
+      std::string("llvm::") + DirLang.getCppNamespace().str() + "::Directive";
+
+  OS << '\n';
+
+  // ArrayRef<...> llvm::<ns>::GetLeafConstructs(llvm::<ns>::Directive Dir)
+  OS << "llvm::ArrayRef<" << DirectiveTypeName
+     << "> llvm::" << DirLang.getCppNamespace() << "::getLeafConstructs("
+     << DirectiveTypeName << " Dir) ";
+  OS << "{\n";
+
+  // Generate the locals.
+  for (Record *R : DirLang.getDirectives()) {
+    Directive Dir{R};
+
+    std::vector<Record *> LeafConstructs = Dir.getLeafConstructs();
+    if (LeafConstructs.empty())
+      continue;
+
+    std::string ListName = "leafConstructs_" + Dir.getFormattedName();
+    OS << "  static const " << DirectiveTypeName << ' ' << ListName
+       << "[] = {\n";
+    for (Record *L : LeafConstructs) {
+      Directive LeafDir{L};
+      OS << "    " << getQualifiedName(LeafDir.getFormattedName()) << ",\n";
+    }
+    OS << "  };\n";
+    ListNames.insert(std::make_pair(R, std::move(ListName)));
+  }
+
+  if (!ListNames.empty())
+    OS << '\n';
+  OS << "  switch (Dir) {\n";
+  for (Record *R : DirLang.getDirectives()) {
+    auto F = ListNames.find(R);
+    if (F == ListNames.end())
+      continue;
+
+    Directive Dir{R};
+    OS << "  case " << getQualifiedName(Dir.getFormattedName()) << ":\n";
+    OS << "    return " << F->second << ";\n";
+  }
+  OS << "  default:\n";
+  OS << "    return ArrayRef<" << DirectiveTypeName << ">{};\n";
+  OS << "  } // switch (Dir)\n";
+  OS << "}\n";
+}
+
+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,
+    Delimited,
+    Loop,
+    Separating,
+    FromLeaves,
+    Invalid,
+  };
+
+  std::vector<Record *> associations = DirLang.getAssociations();
+
+  auto getAssocValue = [](StringRef name) -> Association {
+    return StringSwitch<Association>(name)
+        .Case("AS_Block", Association::Block)
+        .Case("AS_Declaration", Association::Declaration)
+        .Case("AS_Delimited", Association::Delimited)
+        .Case("AS_Loop", Association::Loop)
+        .Case("AS_None", Association::None)
+        .Case("AS_Separating", Association::Separating)
+        .Case("AS_FromLeaves", Association::FromLeaves)
+        .Default(Association::Invalid);
+  };
+
+  auto getAssocName = [&](Association A) -> StringRef {
+    if (A != Association::Invalid && A != Association::FromLeaves) {
+      auto F = llvm::find_if(associations, [&](const Record *R) {
+        return getAssocValue(R->getName()) == A;
+      });
+      if (F != associations.end())
+        return (*F)->getValueAsString("name"); // enum name
+    }
+    llvm_unreachable("Unexpected association value");
+  };
+
+  auto errorPrefixFor = [&](Directive D) -> std::string {
+    return (Twine("Directive '") + D.getName() + "' in namespace '" +
+            DirLang.getCppNamespace() + "' ")
+        .str();
+  };
+
+  auto reduce = [&](Association A, Association B) -> Association {
+    if (A > B)
+      std::swap(A, B);
+
+    // Calculate the result using the following rules:
+    //   x + x = x
+    //   AS_None + x = x
+    //   AS_Block + AS_Loop = AS_Loop
+    if (A == Association::None || A == B)
+      return B;
+    if (A == Association::Block && B == Association::Loop)
+      return B;
+    if (A == Association::Loop && B == Association::Block)
+      return A;
+    return Association::Invalid;
+  };
+
+  llvm::DenseMap<const Record *, Association> AsMap;
+
+  auto compAssocImpl = [&](const Record *R, auto &&Self) -> Association {
+    if (auto F = AsMap.find(R); F != AsMap.end())
+      return F->second;
+
+    Directive D{R};
+    Association AS = getAssocValue(D.getAssociation()->getName());
+    if (AS == Association::Invalid) {
+      PrintFatalError(errorPrefixFor(D) +
+                      "has an unrecognized value for association: '" +
+                      D.getAssociation()->getName() + "'");
+    }
+    if (AS != Association::FromLeaves) {
+      AsMap.insert(std::make_pair(R, AS));
+      return AS;
+    }
+    // Compute the association from leaf constructs.
+    std::vector<Record *> leaves = D.getLeafConstructs();
+    if (leaves.empty()) {
+      llvm::errs() << D.getName() << '\n';
+      PrintFatalError(errorPrefixFor(D) +
+                      "requests association to be computed from leaves, "
+                      "but it has no leaves");
+    }
+
+    Association Result = Self(leaves[0], Self);
+    for (int I = 1, E = leaves.size(); I < E; ++I) {
+      Association A = Self(leaves[I], Self);
+      Association R = reduce(Result, A);
+      if (R == Association::Invalid) {
+        PrintFatalError(errorPrefixFor(D) +
+                        "has leaves with incompatible association values: " +
+                        getAssocName(A) + " and " + getAssocName(R));
+      }
+      Result = R;
+    }
+
+    assert(Result != Association::Invalid);
+    assert(Result != Association::FromLeaves);
+    AsMap.insert(std::make_pair(R, Result));
+    return Result;
+  };
+
+  for (Record *R : DirLang.getDirectives())
+    compAssocImpl(R, compAssocImpl); // Updates AsMap.
+
+  OS << '\n';
+
+  auto getQualifiedName = [&](StringRef Formatted) -> std::string {
+    return (llvm::Twine("llvm::") + DirLang.getCppNamespace() +
+            "::Directive::" + DirLang.getDirectivePrefix() + Formatted)
+        .str();
+  };
+
+  std::string DirectiveTypeName =
+      std::string("llvm::") + DirLang.getCppNamespace().str() + "::Directive";
+  std::string AssociationTypeName =
+      std::string("llvm::") + DirLang.getCppNamespace().str() + "::Association";
+
+  OS << AssociationTypeName << " llvm::" << DirLang.getCppNamespace()
+     << "::getDirectiveAssociation(" << DirectiveTypeName << " Dir) {\n";
+  OS << "  switch (Dir) {\n";
+  for (Record *R : DirLang.getDirectives()) {
+    if (auto F = AsMap.find(R); F != AsMap.end()) {
+      Directive Dir{R};
+      OS << "  case " << getQualifiedName(Dir.getFormattedName()) << ":\n";
+      OS << "    return " << AssociationTypeName
+         << "::" << getAssocName(F->second) << ";\n";
+    }
+  }
+  OS << "  default:\n";
+  OS << "    llvm_unreachable(\"Unexpected directive\");\n";
+  OS << "  } // switch(Dir)\n";
+  OS << "}\n";
+}
+
 // Generate a simple enum set with the give clauses.
 static void GenerateClauseSet(const std::vector<Record *> &Clauses,
                               raw_ostream &OS, StringRef ClauseSetPrefix,
@@ -855,6 +1081,8 @@ void EmitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
                              raw_ostream &OS) {
   IfDefScope Scope("GEN_DIRECTIVES_IMPL", OS);
 
+  OS << "\n#include \"llvm/Support/ErrorHandling.h\"\n";
+
   // getDirectiveKind(StringRef Str)
   GenerateGetKind(DirLang.getDirectives(), OS, "Directive", DirLang,
                   DirLang.getDirectivePrefix(), /*ImplicitAsUnknown=*/false);
@@ -877,6 +1105,12 @@ void EmitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
 
   // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
   GenerateIsAllowedClause(DirLang, OS);
+
+  // getLeafConstructs(Directive D)
+  GenerateGetLeafConstructs(DirLang, OS);
+
+  // getDirectiveAssociation(Directive D)
+  GenerateGetDirectiveAssociation(DirLang, OS);
 }
 
 // Generate the implemenation section for the enumeration in the directive


        


More information about the cfe-commits mailing list