[llvm] 7f0d54b - [flang][openmp] Allocators construct semantic checks

Ethan Luis McDonough via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 2 10:59:15 PDT 2023


Author: Ethan Luis McDonough
Date: 2023-08-02T12:59:06-05:00
New Revision: 7f0d54b4299547856073632c460bed53e63f40c4

URL: https://github.com/llvm/llvm-project/commit/7f0d54b4299547856073632c460bed53e63f40c4
DIFF: https://github.com/llvm/llvm-project/commit/7f0d54b4299547856073632c460bed53e63f40c4.diff

LOG: [flang][openmp] Allocators construct semantic checks

This patch applies the semantic checks for executable allocation directives to the new allocators construct.  It also introduces a new check that ensures all items in the list appear in the corresponding Fortran allocate statement.

Reviewed By: kiranchandramohan

Differential Revision: https://reviews.llvm.org/D150428

Added: 
    flang/test/Semantics/OpenMP/allocate-clause01.f90
    flang/test/Semantics/OpenMP/allocators01.f90
    flang/test/Semantics/OpenMP/allocators02.f90
    flang/test/Semantics/OpenMP/allocators03.f90
    flang/test/Semantics/OpenMP/allocators04.f90
    flang/test/Semantics/OpenMP/allocators05.f90
    flang/test/Semantics/OpenMP/allocators06.f90

Modified: 
    flang/include/flang/Parser/parse-tree.h
    flang/lib/Semantics/check-omp-structure.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/lib/Semantics/resolve-directives.cpp
    llvm/include/llvm/Frontend/OpenMP/OMP.td

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 73956338a31fef..5ee7ecc61a4e2e 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3544,17 +3544,17 @@ struct OmpInReductionClause {
 //                                   variable-name-list)
 //                allocate-modifier -> allocator | align
 struct OmpAllocateClause {
-  TUPLE_CLASS_BOILERPLATE(OmpAllocateClause);
   struct AllocateModifier {
-    UNION_CLASS_BOILERPLATE(AllocateModifier);
     WRAPPER_CLASS(Allocator, ScalarIntExpr);
     WRAPPER_CLASS(Align, ScalarIntExpr);
     struct ComplexModifier {
       TUPLE_CLASS_BOILERPLATE(ComplexModifier);
       std::tuple<Allocator, Align> t;
     };
+    UNION_CLASS_BOILERPLATE(AllocateModifier);
     std::variant<Allocator, ComplexModifier, Align> u;
   };
+  TUPLE_CLASS_BOILERPLATE(OmpAllocateClause);
   std::tuple<std::optional<AllocateModifier>, OmpObjectList> t;
 };
 

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index b4b838a45f7eae..b26f3e23f9ec73 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -311,11 +311,12 @@ void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
         (IsSaved(*symbol) || commonBlock ||
             containingScope.kind() == Scope::Kind::Module)) {
       context_.Say(source,
-          "If list items within the ALLOCATE directive have the "
+          "If list items within the %s directive have the "
           "SAVE attribute, are a common block name, or are "
           "declared in the scope of a module, then only "
           "predefined memory allocator parameters can be used "
-          "in the allocator clause"_err_en_US);
+          "in the allocator clause"_err_en_US,
+          ContextDirectiveAsFortran());
     }
   }
 }
@@ -1140,6 +1141,39 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) {
   RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v);
 }
 
+void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) {
+  CheckAllowed(llvm::omp::Clause::OMPC_allocate);
+  if (const auto &modifier{
+          std::get<std::optional<parser::OmpAllocateClause::AllocateModifier>>(
+              x.v.t)}) {
+    common::visit(
+        common::visitors{
+            [&](const parser::OmpAllocateClause::AllocateModifier::Allocator
+                    &y) {
+              RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocate, y.v);
+              isPredefinedAllocator = GetIntValue(y.v).has_value();
+            },
+            [&](const parser::OmpAllocateClause::AllocateModifier::
+                    ComplexModifier &y) {
+              const auto &alloc = std::get<
+                  parser::OmpAllocateClause::AllocateModifier::Allocator>(y.t);
+              const auto &align =
+                  std::get<parser::OmpAllocateClause::AllocateModifier::Align>(
+                      y.t);
+              RequiresPositiveParameter(
+                  llvm::omp::Clause::OMPC_allocate, alloc.v);
+              RequiresPositiveParameter(
+                  llvm::omp::Clause::OMPC_allocate, align.v);
+              isPredefinedAllocator = GetIntValue(alloc.v).has_value();
+            },
+            [&](const parser::OmpAllocateClause::AllocateModifier::Align &y) {
+              RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocate, y.v);
+            },
+        },
+        modifier->u);
+  }
+}
+
 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
   const auto &dir{std::get<parser::Verbatim>(x.t)};
   PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
@@ -1218,6 +1252,33 @@ void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) {
   dirContext_.pop_back();
 }
 
+void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
+  isPredefinedAllocator = true;
+  const auto &dir{std::get<parser::Verbatim>(x.t)};
+  PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocators);
+  const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
+  for (const auto &clause : clauseList.v) {
+    if (const auto *allocClause{
+            parser::Unwrap<parser::OmpClause::Allocate>(clause)}) {
+      CheckIsVarPartOfAnotherVar(
+          dir.source, std::get<parser::OmpObjectList>(allocClause->v.t));
+    }
+  }
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) {
+  const auto &dir{std::get<parser::Verbatim>(x.t)};
+  const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
+  for (const auto &clause : clauseList.v) {
+    if (const auto *allocClause{
+            std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
+      CheckPredefinedAllocatorRestriction(
+          dir.source, std::get<parser::OmpObjectList>(allocClause->v.t));
+    }
+  }
+  dirContext_.pop_back();
+}
+
 void OmpStructureChecker::CheckBarrierNesting(
     const parser::OpenMPSimpleStandaloneConstruct &x) {
   // A barrier region may not be `closely nested` inside a worksharing, loop,
@@ -1893,7 +1954,6 @@ CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
 CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order)
 CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity)
-CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate)
 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
 CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
@@ -2189,6 +2249,7 @@ bool OmpStructureChecker::IsDataRefTypeParamInquiry(
 void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
     const parser::CharBlock &source, const parser::OmpObjectList &objList) {
   OmpDirectiveSet nonPartialVarSet{llvm::omp::Directive::OMPD_allocate,
+      llvm::omp::Directive::OMPD_allocators,
       llvm::omp::Directive::OMPD_threadprivate,
       llvm::omp::Directive::OMPD_declare_target};
   for (const auto &ompObject : objList.v) {

diff  --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index b99a19013c8c12..195b0c2326f548 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -152,6 +152,8 @@ class OmpStructureChecker
   void Leave(const parser::OpenMPDeclareTargetConstruct &);
   void Enter(const parser::OpenMPExecutableAllocate &);
   void Leave(const parser::OpenMPExecutableAllocate &);
+  void Enter(const parser::OpenMPAllocatorsConstruct &);
+  void Leave(const parser::OpenMPAllocatorsConstruct &);
   void Enter(const parser::OpenMPRequiresConstruct &);
   void Leave(const parser::OpenMPRequiresConstruct &);
   void Enter(const parser::OpenMPThreadprivate &);

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 401ffee6badc92..747f1e414eb21f 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -364,6 +364,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   bool Pre(const parser::OpenMPExecutableAllocate &);
   void Post(const parser::OpenMPExecutableAllocate &);
 
+  bool Pre(const parser::OpenMPAllocatorsConstruct &);
+  void Post(const parser::OpenMPAllocatorsConstruct &);
+
   // 2.15.3 Data-Sharing Attribute Clauses
   void Post(const parser::OmpDefaultClause &);
   bool Pre(const parser::OmpClause::Shared &x) {
@@ -606,6 +609,11 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
     sourceLabels_.clear();
     targetLabels_.clear();
   };
+  void CheckAllNamesInAllocateStmt(const parser::CharBlock &source,
+      const parser::OmpObjectList &ompObjectList,
+      const parser::AllocateStmt &allocate);
+  void CheckNameInAllocateStmt(const parser::CharBlock &source,
+      const parser::Name &ompObject, const parser::AllocateStmt &allocate);
 
   bool HasSymbolInEnclosingScope(const Symbol &, Scope &);
   std::int64_t ordCollapseLevel{0};
@@ -1548,6 +1556,19 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
   return true;
 }
 
+bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) {
+  PushContext(x.source, llvm::omp::Directive::OMPD_allocators);
+  const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
+  for (const auto &clause : clauseList.v) {
+    if (const auto *allocClause{
+            std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
+      ResolveOmpObjectList(std::get<parser::OmpObjectList>(allocClause->v.t),
+          Symbol::Flag::OmpExecutableAllocateDirective);
+    }
+  }
+  return true;
+}
+
 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) {
   if (!dirContext_.empty()) {
     switch (x.v) {
@@ -1600,6 +1621,36 @@ void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) {
   PopContext();
 }
 
+void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
+  const auto &dir{std::get<parser::Verbatim>(x.t)};
+  const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
+  for (const auto &clause : clauseList.v) {
+    if (const auto *alloc{
+            std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
+      CheckAllNamesInAllocateStmt(dir.source,
+          std::get<parser::OmpObjectList>(alloc->v.t),
+          std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement);
+
+      const auto &allocMod{
+          std::get<std::optional<parser::OmpAllocateClause::AllocateModifier>>(
+              alloc->v.t)};
+      // TODO: As with allocate directive, exclude the case when a requires
+      //       directive with the dynamic_allocators clause is present in
+      //       the same compilation unit (OMP5.0 2.11.3).
+      if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) &&
+          (!allocMod.has_value() ||
+              std::holds_alternative<
+                  parser::OmpAllocateClause::AllocateModifier::Align>(
+                  allocMod->u))) {
+        context_.Say(x.source,
+            "ALLOCATORS directives that appear in a TARGET region "
+            "must specify an allocator"_err_en_US);
+      }
+    }
+  }
+  PopContext();
+}
+
 // For OpenMP constructs, check all the data-refs within the constructs
 // and adjust the symbol for each Name if necessary
 void OmpAttributeVisitor::Post(const parser::Name &name) {
@@ -1772,7 +1823,11 @@ void OmpAttributeVisitor::ResolveOmpObject(
                     ResolveOmpObjectScope(name) == nullptr) {
                   context_.Say(designator.source, // 2.15.3
                       "List items must be declared in the same scoping unit "
-                      "in which the ALLOCATE directive appears"_err_en_US);
+                      "in which the %s directive appears"_err_en_US,
+                      parser::ToUpperCaseLetters(
+                          llvm::omp::getOpenMPDirectiveName(
+                              GetContext().directive)
+                              .str()));
                 }
               }
             } else {
@@ -2021,4 +2076,39 @@ bool OmpAttributeVisitor::HasSymbolInEnclosingScope(
   return llvm::is_contained(symbols, symbol);
 }
 
+// Goes through the names in an OmpObjectList and checks if each name appears
+// in the given allocate statement
+void OmpAttributeVisitor::CheckAllNamesInAllocateStmt(
+    const parser::CharBlock &source, const parser::OmpObjectList &ompObjectList,
+    const parser::AllocateStmt &allocate) {
+  for (const auto &obj : ompObjectList.v) {
+    if (const auto *d{std::get_if<parser::Designator>(&obj.u)}) {
+      if (const auto *ref{std::get_if<parser::DataRef>(&d->u)}) {
+        if (const auto *n{std::get_if<parser::Name>(&ref->u)}) {
+          CheckNameInAllocateStmt(source, *n, allocate);
+        }
+      }
+    }
+  }
+}
+
+void OmpAttributeVisitor::CheckNameInAllocateStmt(
+    const parser::CharBlock &source, const parser::Name &name,
+    const parser::AllocateStmt &allocate) {
+  for (const auto &allocation :
+      std::get<std::list<parser::Allocation>>(allocate.t)) {
+    const auto &allocObj = std::get<parser::AllocateObject>(allocation.t);
+    if (const auto *n{std::get_if<parser::Name>(&allocObj.u)}) {
+      if (n->source == name.source) {
+        return;
+      }
+    }
+  }
+  context_.Say(source,
+      "Object '%s' in %s directive not "
+      "found in corresponding ALLOCATE statement"_err_en_US,
+      name.ToString(),
+      parser::ToUpperCaseLetters(
+          llvm::omp::getOpenMPDirectiveName(GetContext().directive).str()));
+}
 } // namespace Fortran::semantics

diff  --git a/flang/test/Semantics/OpenMP/allocate-clause01.f90 b/flang/test/Semantics/OpenMP/allocate-clause01.f90
new file mode 100644
index 00000000000000..7e2f64397f738d
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocate-clause01.f90
@@ -0,0 +1,22 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! The allocate clause's allocator modifier must be of type allocator_handle
+! and the align modifier must be constant, positive integer expression
+
+subroutine allocate()
+    use omp_lib
+
+    integer, allocatable :: a, b, c
+
+    !ERROR: The parameter of the ALLOCATE clause must be a positive integer expression
+    !$omp allocators allocate(-1: a)
+        allocate(a)
+
+    !ERROR: The parameter of the ALLOCATE clause must be a positive integer expression
+    !$omp allocators allocate(allocator(-2), align(-3): b)
+        allocate(b)
+
+    !ERROR: The parameter of the ALLOCATE clause must be a positive integer expression
+    !$omp allocators allocate(align(-4): c)
+        allocate(c)
+end subroutine

diff  --git a/flang/test/Semantics/OpenMP/allocators01.f90 b/flang/test/Semantics/OpenMP/allocators01.f90
new file mode 100644
index 00000000000000..d674438b2ab207
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocators01.f90
@@ -0,0 +1,21 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! 6.7 allocators construct
+! A list item that appears in an allocate clause must appear as
+! one of the variables that is allocated by the allocate-stmt in
+! the associated allocator structured block.
+
+subroutine allocate()
+use omp_lib
+
+  integer, allocatable :: arr1(:), arr2(:, :), arr3(:), arr4(:, :)
+
+  !$omp allocators allocate(arr3)
+    allocate(arr3(3), arr4(4, 4))
+  !$omp end allocators
+
+  !ERROR: Object 'arr1' in ALLOCATORS directive not found in corresponding ALLOCATE statement
+  !$omp allocators allocate(omp_default_mem_alloc: arr1, arr2)
+    allocate(arr2(2, 2))
+
+end subroutine allocate

diff  --git a/flang/test/Semantics/OpenMP/allocators02.f90 b/flang/test/Semantics/OpenMP/allocators02.f90
new file mode 100644
index 00000000000000..32e7bc18ad99b0
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocators02.f90
@@ -0,0 +1,20 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! 6.7 allocators construct
+! A variable that is part of another variable (as an array or
+! structure element) cannot appear in an allocatprs construct.
+
+subroutine allocate()
+use omp_lib
+
+  type my_type
+    integer, allocatable :: array(:)
+  end type my_type
+
+  type(my_type) :: my_var
+
+  !ERROR: A variable that is part of another variable (as an array or structure element) cannot appear on the ALLOCATORS directive
+  !$omp allocators allocate(my_var%array)
+    allocate(my_var%array(10))
+
+end subroutine allocate

diff  --git a/flang/test/Semantics/OpenMP/allocators03.f90 b/flang/test/Semantics/OpenMP/allocators03.f90
new file mode 100644
index 00000000000000..533434c2ace0f1
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocators03.f90
@@ -0,0 +1,15 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! 6.7 allocators construct
+! Only the allocate clause is allowed on the allocators construct
+
+subroutine allocate()
+use omp_lib
+
+  integer, allocatable :: arr1(:), arr2(:)
+
+  !ERROR: PRIVATE clause is not allowed on the ALLOCATORS directive
+  !$omp allocators allocate(arr1) private(arr2)
+    allocate(arr1(23), arr2(2))
+
+end subroutine allocate

diff  --git a/flang/test/Semantics/OpenMP/allocators04.f90 b/flang/test/Semantics/OpenMP/allocators04.f90
new file mode 100644
index 00000000000000..d4467a2052a7dc
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocators04.f90
@@ -0,0 +1,29 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! Inherited from 2.11.3 allocate Directive
+! If list items within the ALLOCATE directive have the SAVE attribute, are a common block name, or are declared in the scope of a
+! module, then only predefined memory allocator parameters can be used in the allocator clause
+! SAVE and common block names can't be declared as allocatable, only module scope variables are tested
+
+module AllocateModule
+    integer, allocatable :: a, b
+end module
+
+subroutine allocate()
+    use omp_lib
+    use AllocateModule
+
+    integer(kind=omp_allocator_handle_kind) :: custom_allocator
+    type(omp_alloctrait) :: trait(1)
+
+    trait(1)%key = fallback
+    trait(1)%value = default_mem_fb
+    custom_allocator = omp_init_allocator(omp_default_mem_space, 1, trait)
+
+    !$omp allocators allocate(omp_default_mem_alloc: a)
+        allocate(a)
+
+    !ERROR: If list items within the ALLOCATORS directive have the SAVE attribute, are a common block name, or are declared in the scope of a module, then only predefined memory allocator parameters can be used in the allocator clause
+    !$omp allocators allocate(custom_allocator: b)
+        allocate(b)
+end subroutine

diff  --git a/flang/test/Semantics/OpenMP/allocators05.f90 b/flang/test/Semantics/OpenMP/allocators05.f90
new file mode 100644
index 00000000000000..684eef69504951
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocators05.f90
@@ -0,0 +1,24 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! Inherited from 2.11.3 allocate directive
+! allocate directives that appear in a target region must specify an
+! allocator clause unless a requires directive with the dynamic_allocators
+! clause is present in the same compilation unit.
+
+subroutine allocate()
+    use omp_lib
+
+    integer :: i
+    integer, allocatable :: a(:), b(:)
+    integer, parameter :: LEN = 2
+
+    !$omp target private(a, b)
+    !ERROR: List items must be declared in the same scoping unit in which the ALLOCATORS directive appears
+    !$omp allocators allocate(omp_default_mem_alloc: a)
+        allocate(a(LEN))
+    !ERROR: ALLOCATORS directives that appear in a TARGET region must specify an allocator
+    !ERROR: List items must be declared in the same scoping unit in which the ALLOCATORS directive appears
+    !$omp allocators allocate(b)
+        allocate(b(LEN))
+    !$omp end target
+end subroutine

diff  --git a/flang/test/Semantics/OpenMP/allocators06.f90 b/flang/test/Semantics/OpenMP/allocators06.f90
new file mode 100644
index 00000000000000..17e168d4cfe0cb
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/allocators06.f90
@@ -0,0 +1,16 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! OpenMP Version 5.2
+! Inherited from 2.11.3 allocate directive
+! The allocate directive must appear in the same scope as the declarations of
+! each of its list items and must follow all such declarations.
+
+subroutine allocate()
+    use omp_lib
+    integer, allocatable :: a
+contains
+    subroutine test()
+        !ERROR: List items must be declared in the same scoping unit in which the ALLOCATORS directive appears
+        !$omp allocators allocate(omp_default_mem_alloc: a)
+            allocate(a)
+    end subroutine
+end subroutine

diff  --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 68f7eca4daffbf..f7a980476bac0c 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1727,6 +1727,11 @@ def OMP_Allocate : Directive<"allocate"> {
     VersionedClause<OMPC_Align, 51>
   ];
 }
+def OMP_Allocators : Directive<"allocators"> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Allocate>
+  ];
+}
 def OMP_DeclareVariant : Directive<"declare variant"> {
   let allowedClauses = [
     VersionedClause<OMPC_Match>


        


More information about the llvm-commits mailing list