Skip to content

[FLANG][OpenMP]Add frontend support for ASSUME and ASSUMES #120770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 25, 2025

Conversation

Leporacanthicus
Copy link
Contributor

Enough suport to parse correctly formed directives of !$OMP ASSUME and !$OMP ASSUMES with teh related clauses that go with them: ABSENT, CONTAINS, NO_OPENPP, NO_OPENMP_ROUTINES, NO_PARALLELISM and HOLDS.

Tests added for unparsing and dump parse-tree.

Semantics support is very minimal and no specific tests added.

The lowering will hit a TODO, and there are tests in Lower/OpenMP/Todo to make it clear that this is currently expected behaviour.

@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir flang:openmp flang:semantics flang:parser clang:openmp OpenMP related changes to Clang labels Dec 20, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 20, 2024

@llvm/pr-subscribers-flang-parser
@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-flang-semantics

Author: Mats Petersson (Leporacanthicus)

Changes

Enough suport to parse correctly formed directives of !$OMP ASSUME and !$OMP ASSUMES with teh related clauses that go with them: ABSENT, CONTAINS, NO_OPENPP, NO_OPENMP_ROUTINES, NO_PARALLELISM and HOLDS.

Tests added for unparsing and dump parse-tree.

Semantics support is very minimal and no specific tests added.

The lowering will hit a TODO, and there are tests in Lower/OpenMP/Todo to make it clear that this is currently expected behaviour.


Patch is 22.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/120770.diff

12 Files Affected:

  • (modified) flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp (+4)
  • (modified) flang/include/flang/Parser/dump-parse-tree.h (+12)
  • (modified) flang/include/flang/Parser/parse-tree.h (+84-1)
  • (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+18)
  • (modified) flang/lib/Parser/openmp-parsers.cpp (+37-1)
  • (modified) flang/lib/Parser/unparse.cpp (+19-1)
  • (modified) flang/lib/Semantics/check-omp-structure.cpp (+17)
  • (modified) flang/lib/Semantics/check-omp-structure.h (+4)
  • (added) flang/test/Lower/OpenMP/Todo/assume.f90 (+6)
  • (added) flang/test/Lower/OpenMP/Todo/assumes.f90 (+9)
  • (added) flang/test/Parser/OpenMP/assumption.f90 (+66)
  • (modified) llvm/include/llvm/Frontend/OpenMP/OMP.td (+19)
diff --git a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
index 665b92be008986..498cd2d7e33b24 100644
--- a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
+++ b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
@@ -129,6 +129,10 @@ std::string OpenMPCounterVisitor::getName(const OpenMPConstruct &c) {
             const CharBlock &source{std::get<0>(c.t).source};
             return normalize_construct_name(source.ToString());
           },
+          [&](const OpenMPAssumeConstruct &c) -> std::string {
+            const CharBlock &source{std::get<0>(c.t).source};
+            return normalize_construct_name(source.ToString());
+          },
           [&](const OpenMPAllocatorsConstruct &c) -> std::string {
             const CharBlock &source{std::get<0>(c.t).source};
             return normalize_construct_name(source.ToString());
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 940caaeea9c3b7..4c88b9583c25f9 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -483,6 +483,7 @@ class ParseTreeDumper {
   NODE_ENUM(OmpMapTypeModifier, Value)
   NODE(parser, OmpIteratorSpecifier)
   NODE(parser, OmpIterator)
+  NODE(parser, OmpAbsentClause)
   NODE(parser, OmpAffinityClause)
   NODE(OmpAffinityClause, Modifier)
   NODE(parser, OmpAlignment)
@@ -516,6 +517,7 @@ class ParseTreeDumper {
 #include "llvm/Frontend/OpenMP/OMP.inc"
   NODE(parser, OmpClauseList)
   NODE(parser, OmpCriticalDirective)
+  NODE(parser, OmpContainsClause)
   NODE(parser, OmpDeclareTargetSpecifier)
   NODE(parser, OmpDeclareTargetWithClause)
   NODE(parser, OmpDeclareTargetWithList)
@@ -555,6 +557,8 @@ class ParseTreeDumper {
   NODE(parser, OmpExpectation)
   NODE_ENUM(OmpExpectation, Value)
   NODE(parser, OmpDirectiveNameModifier)
+  NODE(parser, OmpDirectiveNameEntry)
+  NODE(parser, OmpHoldsClause)
   NODE(parser, OmpIfClause)
   NODE(OmpIfClause, Modifier)
   NODE(parser, OmpLastprivateClause)
@@ -578,6 +582,9 @@ class ParseTreeDumper {
   }
   NODE(parser, OmpObject)
   NODE(parser, OmpObjectList)
+  NODE(parser, OmpNoOpenMPClause)
+  NODE(parser, OmpNoOpenMPRoutinesClause)
+  NODE(parser, OmpNoParallelismClause)
   NODE(parser, OmpOrderClause)
   NODE(OmpOrderClause, Modifier)
   NODE_ENUM(OmpOrderClause, Ordering)
@@ -643,6 +650,11 @@ class ParseTreeDumper {
   NODE(parser, OpenACCStandaloneDeclarativeConstruct)
   NODE(parser, OpenACCStandaloneConstruct)
   NODE(parser, OpenACCWaitConstruct)
+  NODE(parser, OpenMPAssumeConstruct)
+  NODE(parser, OpenMPAssumesConstruct)
+  NODE(parser, OpenMPAssumesPartConstruct)
+  NODE(parser, OmpAssumesDirective)
+  NODE(parser, OmpEndAssumesDirective)
   NODE(parser, OpenMPAtomicConstruct)
   NODE(parser, OpenMPBlockConstruct)
   NODE(parser, OpenMPCancelConstruct)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 1d97126d17dbc4..a89938036d9c3f 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3740,6 +3740,19 @@ struct OmpVariableCategory {
 
 // --- Clauses
 
+struct OmpDirectiveNameEntry {
+  WRAPPER_CLASS_BOILERPLATE(OmpDirectiveNameEntry, llvm::omp::Directive);
+};
+using OmpDirectiveList = std::list<OmpDirectiveNameEntry>;
+
+// Ref: [5.2:214]
+//
+// absent-clause ->
+//   ABSENT(directive-name[, directive-name])
+struct OmpAbsentClause {
+  WRAPPER_CLASS_BOILERPLATE(OmpAbsentClause, OmpDirectiveList);
+};
+
 // Ref: [5.0:135-140], [5.1:161-166], [5.2:264-265]
 //
 // affinity-clause ->
@@ -3808,6 +3821,14 @@ struct OmpBindClause {
   WRAPPER_CLASS_BOILERPLATE(OmpBindClause, Binding);
 };
 
+// Ref: [5.2:214]
+//
+// contains-clause ->
+//   CONTAINS(directive-name[, directive-name])
+struct OmpContainsClause {
+  WRAPPER_CLASS_BOILERPLATE(OmpContainsClause, OmpDirectiveList);
+};
+
 // Ref: [4.5:46-50], [5.0:74-78], [5.1:92-96], [5.2:109]
 //
 // default-clause ->
@@ -3971,6 +3992,15 @@ struct OmpGrainsizeClause {
   std::tuple<MODIFIERS(), ScalarIntExpr> t;
 };
 
+// Ref: [5.2: 214]
+//
+// holds-clause ->
+//   HOLDS(expr[, expr])
+struct OmpHoldsClause {
+  using ExprList = std::list<common::Indirection<Expr>>;
+  WRAPPER_CLASS_BOILERPLATE(OmpHoldsClause, ExprList);
+};
+
 // Ref: [5.2:72-73], in 4.5-5.1 it's scattered over individual directives
 // that allow the IF clause.
 //
@@ -4042,6 +4072,21 @@ struct OmpMessageClause {
   WRAPPER_CLASS_BOILERPLATE(OmpMessageClause, Expr);
 };
 
+// Ref: [5.2: 214]
+//
+// no_openmp_clause -> NO_OPENMP
+EMPTY_CLASS(OmpNoOpenMPClause);
+
+// Ref: [5.2: 214]
+//
+// no_openmp_routines_clause -> NO_OPENMP_ROUTINES
+EMPTY_CLASS(OmpNoOpenMPRoutinesClause);
+
+// Ref: [5.2: 214]
+//
+// no_parallelism_clause -> NO_PARALELISM
+EMPTY_CLASS(OmpNoParallelismClause);
+
 // Ref: [4.5:87-91], [5.0:140-146], [5.1:166-171], [5.2:270]
 //
 // num-tasks-clause ->
@@ -4174,6 +4219,44 @@ struct OmpClauseList {
 
 // --- Directives and constructs
 
+// Ref: [5.2: 213-216]
+//
+// assume-construct ->
+//   ASSUME absent-clause | contains-clause | holds-clause | no-openmp-clause |
+//          no-openmp-routines-clause | no-parallelism-clause
+struct OpenMPAssumeConstruct {
+  TUPLE_CLASS_BOILERPLATE(OpenMPAssumeConstruct);
+  std::tuple<Verbatim, OmpClauseList> t;
+  CharBlock source;
+};
+
+struct OmpAssumesDirective {
+  TUPLE_CLASS_BOILERPLATE(OmpAssumesDirective);
+  std::tuple<Verbatim, OmpClauseList> t;
+  CharBlock source;
+};
+
+struct OmpEndAssumesDirective {
+  TUPLE_CLASS_BOILERPLATE(OmpEndAssumesDirective);
+  std::tuple<Verbatim> t;
+  CharBlock source;
+};
+
+//    structured-block
+// ...
+struct OpenMPAssumesPartConstruct {
+  WRAPPER_CLASS_BOILERPLATE(OpenMPAssumesPartConstruct, Block);
+  CharBlock source;
+};
+
+struct OpenMPAssumesConstruct {
+  TUPLE_CLASS_BOILERPLATE(OpenMPAssumesConstruct);
+  std::tuple<OmpAssumesDirective, OpenMPAssumesPartConstruct,
+      OmpEndAssumesDirective>
+      t;
+  CharBlock source;
+};
+
 // 2.7.2 SECTIONS
 // 2.11.2 PARALLEL SECTIONS
 struct OmpSectionsDirective {
@@ -4580,7 +4663,7 @@ struct OpenMPConstruct {
       OpenMPSectionConstruct, OpenMPLoopConstruct, OpenMPBlockConstruct,
       OpenMPAtomicConstruct, OpenMPDeclarativeAllocate, OpenMPErrorConstruct,
       OpenMPExecutableAllocate, OpenMPAllocatorsConstruct,
-      OpenMPCriticalConstruct>
+      OpenMPAssumeConstruct, OpenMPAssumesConstruct, OpenMPCriticalConstruct>
       u;
 };
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index c61ab67d95a957..cc3cd95f69d77e 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2893,6 +2893,24 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                  queue.begin());
 }
 
+static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
+                   semantics::SemanticsContext &semaCtx,
+                   lower::pft::Evaluation &eval,
+                   const parser::OpenMPAssumeConstruct &assumeConstruct) {
+  const auto &verbatim = std::get<parser::Verbatim>(assumeConstruct.t);
+  mlir::Location clauseLocation = converter.genLocation(verbatim.source);
+  TODO(clauseLocation, "OpenMP ASSUME construct");
+}
+
+static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
+                   semantics::SemanticsContext &semaCtx,
+                   lower::pft::Evaluation &eval,
+                   const parser::OpenMPAssumesConstruct &assumesConstruct) {
+  mlir::Location clauseLocation =
+      converter.genLocation(assumesConstruct.source);
+  TODO(clauseLocation, "OpenMP ASSUMES construct");
+}
+
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                    semantics::SemanticsContext &semaCtx,
                    lower::pft::Evaluation &eval,
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 67385c03f66c80..a9b995bbf5e2bb 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -577,7 +577,14 @@ TYPE_PARSER(construct<OmpSeverityClause>(
 
 TYPE_PARSER(construct<OmpMessageClause>(expr))
 
-TYPE_PARSER(
+TYPE_PARSER(construct<OmpHoldsClause>(many(maybe(","_tok) >> indirect(expr))))
+TYPE_PARSER(construct<OmpAbsentClause>(many(maybe(","_tok) >>
+    construct<OmpDirectiveNameEntry>(OmpDirectiveNameParser{}))))
+TYPE_PARSER(construct<OmpContainsClause>(many(maybe(","_tok) >>
+    construct<OmpDirectiveNameEntry>(OmpDirectiveNameParser{}))))
+
+TYPE_PARSER("ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(
+                            parenthesized(Parser<OmpAbsentClause>{}))) ||
     "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
     "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
     "AFFINITY" >> construct<OmpClause>(construct<OmpClause::Affinity>(
@@ -597,6 +604,8 @@ TYPE_PARSER(
                   parenthesized(Parser<OmpBindClause>{}))) ||
     "COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(
                       parenthesized(scalarIntConstantExpr))) ||
+    "CONTAINS" >> construct<OmpClause>(construct<OmpClause::Contains>(
+                      parenthesized(Parser<OmpContainsClause>{}))) ||
     "COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>(
                     parenthesized(Parser<OmpObjectList>{}))) ||
     "COPYPRIVATE" >> construct<OmpClause>(construct<OmpClause::Copyprivate>(
@@ -641,6 +650,8 @@ TYPE_PARSER(
             parenthesized(Parser<OmpObjectList>{}))) ||
     "HINT" >> construct<OmpClause>(
                   construct<OmpClause::Hint>(parenthesized(constantExpr))) ||
+    "HOLDS" >> construct<OmpClause>(construct<OmpClause::Holds>(
+                   parenthesized(Parser<OmpHoldsClause>{}))) ||
     "IF" >> construct<OmpClause>(construct<OmpClause::If>(
                 parenthesized(Parser<OmpIfClause>{}))) ||
     "INBRANCH" >> construct<OmpClause>(construct<OmpClause::Inbranch>()) ||
@@ -665,6 +676,11 @@ TYPE_PARSER(
     "NOTINBRANCH" >>
         construct<OmpClause>(construct<OmpClause::Notinbranch>()) ||
     "NOWAIT" >> construct<OmpClause>(construct<OmpClause::Nowait>()) ||
+    "NO_OPENMP"_id >> construct<OmpClause>(construct<OmpClause::NoOpenmp>()) ||
+    "NO_OPENMP_ROUTINES" >>
+        construct<OmpClause>(construct<OmpClause::NoOpenmpRoutines>()) ||
+    "NO_PARALLELISM" >>
+        construct<OmpClause>(construct<OmpClause::NoParallelism>()) ||
     "NUM_TASKS" >> construct<OmpClause>(construct<OmpClause::NumTasks>(
                        parenthesized(Parser<OmpNumTasksClause>{}))) ||
     "NUM_TEAMS" >> construct<OmpClause>(construct<OmpClause::NumTeams>(
@@ -1085,6 +1101,24 @@ TYPE_PARSER(startOmpLine >>
                 Parser<OpenMPThreadprivate>{})) /
             endOmpLine))
 
+// Assume Construct
+TYPE_PARSER(sourced(construct<OpenMPAssumeConstruct>(
+                        verbatim("ASSUME"_tok), Parser<OmpClauseList>{}) /
+    endOmpLine))
+// Assumes Construct
+TYPE_PARSER(sourced(construct<OmpAssumesDirective>(
+    verbatim("ASSUMES"_tok), Parser<OmpClauseList>{})))
+
+TYPE_PARSER(sourced(construct<OmpEndAssumesDirective>(
+    verbatim(startOmpLine >> "END ASSUMES"_tok))))
+
+TYPE_PARSER(construct<OpenMPAssumesPartConstruct>(block))
+
+TYPE_PARSER(sourced(construct<OpenMPAssumesConstruct>(
+    maybe("BEGIN"_tok) >> Parser<OmpAssumesDirective>{} / endOmpLine,
+    Parser<OpenMPAssumesPartConstruct>{},
+    Parser<OmpEndAssumesDirective>{} / endOmpLine)))
+
 // Block Construct
 TYPE_PARSER(construct<OpenMPBlockConstruct>(
     Parser<OmpBeginBlockDirective>{} / endOmpLine, block,
@@ -1131,6 +1165,8 @@ TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
                 construct<OpenMPConstruct>(Parser<OpenMPExecutableAllocate>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPDeclarativeAllocate>{}),
+                construct<OpenMPConstruct>(Parser<OpenMPAssumesConstruct>{}),
+                construct<OpenMPConstruct>(Parser<OpenMPAssumeConstruct>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))))
 
 // END OMP Block directives
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 0a6af7435b4a22..a3887cd42cd850 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2077,6 +2077,9 @@ class UnparseVisitor {
   void Unparse(const OmpDirectiveNameModifier &x) {
     Word(llvm::omp::getOpenMPDirectiveName(x.v));
   }
+  void Unparse(const OmpDirectiveNameEntry &x) {
+    Word(llvm::omp::getOpenMPDirectiveName(x.v));
+  }
   void Unparse(const OmpIteratorSpecifier &x) {
     Walk(std::get<TypeDeclarationStmt>(x.t));
     Put(" = ");
@@ -2113,6 +2116,8 @@ class UnparseVisitor {
     Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
     Walk(std::get<ScalarIntExpr>(x.t));
   }
+  void Unparse(const OmpAbsentClause &x) { Walk("", x.v, ","); }
+  void Unparse(const OmpContainsClause &x) { Walk("", x.v, ","); }
   void Unparse(const OmpAffinityClause &x) {
     using Modifier = OmpAffinityClause::Modifier;
     Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
@@ -2590,6 +2595,13 @@ class UnparseVisitor {
       Walk(*end);
     }
   }
+  void Unparse(const OpenMPAssumeConstruct &x) {
+    BeginOpenMP();
+    Word("!$OMP ASSUME");
+    Walk(std::get<OmpClauseList>(x.t), ", ");
+    Put("\n");
+    EndOpenMP();
+  }
   void Unparse(const OmpCriticalDirective &x) {
     BeginOpenMP();
     Word("!$OMP CRITICAL");
@@ -2806,7 +2818,9 @@ class UnparseVisitor {
     Put("\n");
     EndOpenMP();
   }
-  void Unparse(const OmpClauseList &x) { Walk(" ", x.v, " "); }
+  void Unparse(const OmpClauseList &x, const char *sep = " ") {
+    Walk(" ", x.v, sep);
+  }
   void Unparse(const OpenMPSimpleStandaloneConstruct &x) {
     BeginOpenMP();
     Word("!$OMP ");
@@ -3065,6 +3079,10 @@ class UnparseVisitor {
     return Walk("", list, comma, suffix);
   }
 
+  void Walk(const OmpClauseList &x, const char *sep = " ") {
+    return Walk(" ", x.v, sep);
+  }
+
   // Traverse a std::tuple<>, with an optional separator.
   template <std::size_t J = 0, typename T>
   void WalkTupleElements(const T &tuple, const char *separator) {
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 414753305a6e37..775b6bcfb76fdc 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1185,6 +1185,23 @@ void OmpStructureChecker::CheckMasterNesting(
   }
 }
 
+void OmpStructureChecker::Enter(const parser::OpenMPAssumeConstruct &x) {
+  const auto &dir{std::get<parser::Verbatim>(x.t)};
+  PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_assume);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPAssumeConstruct &) {
+  dirContext_.pop_back();
+}
+
+void OmpStructureChecker::Enter(const parser::OpenMPAssumesConstruct &x) {
+  PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_assumes);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPAssumesConstruct &) {
+  dirContext_.pop_back();
+}
+
 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
   if (GetDirectiveNest(TargetBlockOnlyTeams)) {
     ExitDirectiveNest(TargetBlockOnlyTeams);
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 346a7bed9138f0..ffd36e9cd3324c 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -78,6 +78,10 @@ class OmpStructureChecker
   void Enter(const parser::OmpEndLoopDirective &);
   void Leave(const parser::OmpEndLoopDirective &);
 
+  void Enter(const parser::OpenMPAssumeConstruct &);
+  void Leave(const parser::OpenMPAssumeConstruct &);
+  void Enter(const parser::OpenMPAssumesConstruct &);
+  void Leave(const parser::OpenMPAssumesConstruct &);
   void Enter(const parser::OpenMPBlockConstruct &);
   void Leave(const parser::OpenMPBlockConstruct &);
   void Leave(const parser::OmpBeginBlockDirective &);
diff --git a/flang/test/Lower/OpenMP/Todo/assume.f90 b/flang/test/Lower/OpenMP/Todo/assume.f90
new file mode 100644
index 00000000000000..6942c7774663ed
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/assume.f90
@@ -0,0 +1,6 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: OpenMP ASSUME construct
+program p
+  !$omp assume no_openmp
+end program p
diff --git a/flang/test/Lower/OpenMP/Todo/assumes.f90 b/flang/test/Lower/OpenMP/Todo/assumes.f90
new file mode 100644
index 00000000000000..73ad1526e83cc4
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/assumes.f90
@@ -0,0 +1,9 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: OpenMP ASSUMES construct
+program p
+  integer r
+  !$omp assumes no_openmp
+  print *,r
+  !$omp end assumes
+end program p
diff --git a/flang/test/Parser/OpenMP/assumption.f90 b/flang/test/Parser/OpenMP/assumption.f90
new file mode 100644
index 00000000000000..74c10126a76c61
--- /dev/null
+++ b/flang/test/Parser/OpenMP/assumption.f90
@@ -0,0 +1,66 @@
+! RUN: %flang_fc1  -fopenmp-version=51 -fopenmp -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
+!!! RUN: %flang_fc1  -fopenmp-version=51 -fopenmp -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s --check-prefix="PARSE-TREE"
+
+subroutine sub1
+  integer :: r
+!CHECK: !$OMP ASSUME NO_OPENMP
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> NoOpenmp
+  !$omp assume no_openmp
+!CHECK: !$OMP ASSUME NO_PARALLELISM
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> NoParallelism
+  !$omp assume no_parallelism
+!CHECK: !$OMP ASSUME NO_OPENMP_ROUTINES
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> NoOpenmpRoutines  
+  !$omp assume no_openmp_routines
+!CHECK: !$OMP ASSUME ABSENT(ALLOCATE), CONTAINS(WORKSHARE,TASK)
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> Absent -> OmpAbsentClause -> OmpDirectiveNameEntry -> llvm::omp::Directive = allocate
+!PARSE-TREE:   OmpClause -> Contains -> OmpContainsClause -> OmpDirectiveNameEntry -> llvm::omp::Directive = workshare
+!PARSE-TREE:   OmpDirectiveNameEntry -> llvm::omp::Directive = task
+ !$omp assume absent(allocate), contains(workshare, task)
+!CHECK: !$OMP ASSUME HOLDS(1==1)
+  !$omp assume holds(1.eq.1)
+  print *, r
+end subroutine sub1
+
+subroutine sub2
+  integer :: r
+!CHECK !$OMP ASSUMES NO_OPENMP
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumesConstruct
+!PARSE-TREE: OmpAssumesDirective
+!PARSE-TREE: Verbatim
+!PARSE-TREE: OmpClauseList -> OmpClause -> NoOpenmp
+!PARSE-TREE: OpenMPAssumesPartConstruct -> Block
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt
+!PARSE-TREE: Expr -> Add
+!PARSE-TREE: OmpEndAssumesDirective
+  !$omp assumes no_openmp
+  r = r + 1
+!CHECK !$OMP END ASSUMES
+  !$omp end assumes
+end subroutine sub2
+
+subroutine sub3
+  integer :: r
+!CHECK !$OMP ASSUMES NO_OPENMP
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumesConstruct
+!PARSE-TREE: OmpAssumesDirective
+!PARSE-TREE: Verbatim
+!PARSE-TREE: OmpClauseList -> OmpClause -> NoOpenmp
+!PARSE-TREE: OpenMPAssumesPartConstruct -> Block
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt
+!PARSE-TREE: Expr -> Add
+!PARSE-TREE: OmpEndAssumesDirective
+  !$omp begin assumes no_openmp
+  r = r + 1
+!CHECK !$OMP END ASSUMES
+  !$omp end assumes
+end subroutine sub3
+  
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llv...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Dec 20, 2024

@llvm/pr-subscribers-flang-fir-hlfir

Author: Mats Petersson (Leporacanthicus)

Changes

Enough suport to parse correctly formed directives of !$OMP ASSUME and !$OMP ASSUMES with teh related clauses that go with them: ABSENT, CONTAINS, NO_OPENPP, NO_OPENMP_ROUTINES, NO_PARALLELISM and HOLDS.

Tests added for unparsing and dump parse-tree.

Semantics support is very minimal and no specific tests added.

The lowering will hit a TODO, and there are tests in Lower/OpenMP/Todo to make it clear that this is currently expected behaviour.


Patch is 22.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/120770.diff

12 Files Affected:

  • (modified) flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp (+4)
  • (modified) flang/include/flang/Parser/dump-parse-tree.h (+12)
  • (modified) flang/include/flang/Parser/parse-tree.h (+84-1)
  • (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+18)
  • (modified) flang/lib/Parser/openmp-parsers.cpp (+37-1)
  • (modified) flang/lib/Parser/unparse.cpp (+19-1)
  • (modified) flang/lib/Semantics/check-omp-structure.cpp (+17)
  • (modified) flang/lib/Semantics/check-omp-structure.h (+4)
  • (added) flang/test/Lower/OpenMP/Todo/assume.f90 (+6)
  • (added) flang/test/Lower/OpenMP/Todo/assumes.f90 (+9)
  • (added) flang/test/Parser/OpenMP/assumption.f90 (+66)
  • (modified) llvm/include/llvm/Frontend/OpenMP/OMP.td (+19)
diff --git a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
index 665b92be008986..498cd2d7e33b24 100644
--- a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
+++ b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
@@ -129,6 +129,10 @@ std::string OpenMPCounterVisitor::getName(const OpenMPConstruct &c) {
             const CharBlock &source{std::get<0>(c.t).source};
             return normalize_construct_name(source.ToString());
           },
+          [&](const OpenMPAssumeConstruct &c) -> std::string {
+            const CharBlock &source{std::get<0>(c.t).source};
+            return normalize_construct_name(source.ToString());
+          },
           [&](const OpenMPAllocatorsConstruct &c) -> std::string {
             const CharBlock &source{std::get<0>(c.t).source};
             return normalize_construct_name(source.ToString());
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 940caaeea9c3b7..4c88b9583c25f9 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -483,6 +483,7 @@ class ParseTreeDumper {
   NODE_ENUM(OmpMapTypeModifier, Value)
   NODE(parser, OmpIteratorSpecifier)
   NODE(parser, OmpIterator)
+  NODE(parser, OmpAbsentClause)
   NODE(parser, OmpAffinityClause)
   NODE(OmpAffinityClause, Modifier)
   NODE(parser, OmpAlignment)
@@ -516,6 +517,7 @@ class ParseTreeDumper {
 #include "llvm/Frontend/OpenMP/OMP.inc"
   NODE(parser, OmpClauseList)
   NODE(parser, OmpCriticalDirective)
+  NODE(parser, OmpContainsClause)
   NODE(parser, OmpDeclareTargetSpecifier)
   NODE(parser, OmpDeclareTargetWithClause)
   NODE(parser, OmpDeclareTargetWithList)
@@ -555,6 +557,8 @@ class ParseTreeDumper {
   NODE(parser, OmpExpectation)
   NODE_ENUM(OmpExpectation, Value)
   NODE(parser, OmpDirectiveNameModifier)
+  NODE(parser, OmpDirectiveNameEntry)
+  NODE(parser, OmpHoldsClause)
   NODE(parser, OmpIfClause)
   NODE(OmpIfClause, Modifier)
   NODE(parser, OmpLastprivateClause)
@@ -578,6 +582,9 @@ class ParseTreeDumper {
   }
   NODE(parser, OmpObject)
   NODE(parser, OmpObjectList)
+  NODE(parser, OmpNoOpenMPClause)
+  NODE(parser, OmpNoOpenMPRoutinesClause)
+  NODE(parser, OmpNoParallelismClause)
   NODE(parser, OmpOrderClause)
   NODE(OmpOrderClause, Modifier)
   NODE_ENUM(OmpOrderClause, Ordering)
@@ -643,6 +650,11 @@ class ParseTreeDumper {
   NODE(parser, OpenACCStandaloneDeclarativeConstruct)
   NODE(parser, OpenACCStandaloneConstruct)
   NODE(parser, OpenACCWaitConstruct)
+  NODE(parser, OpenMPAssumeConstruct)
+  NODE(parser, OpenMPAssumesConstruct)
+  NODE(parser, OpenMPAssumesPartConstruct)
+  NODE(parser, OmpAssumesDirective)
+  NODE(parser, OmpEndAssumesDirective)
   NODE(parser, OpenMPAtomicConstruct)
   NODE(parser, OpenMPBlockConstruct)
   NODE(parser, OpenMPCancelConstruct)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 1d97126d17dbc4..a89938036d9c3f 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3740,6 +3740,19 @@ struct OmpVariableCategory {
 
 // --- Clauses
 
+struct OmpDirectiveNameEntry {
+  WRAPPER_CLASS_BOILERPLATE(OmpDirectiveNameEntry, llvm::omp::Directive);
+};
+using OmpDirectiveList = std::list<OmpDirectiveNameEntry>;
+
+// Ref: [5.2:214]
+//
+// absent-clause ->
+//   ABSENT(directive-name[, directive-name])
+struct OmpAbsentClause {
+  WRAPPER_CLASS_BOILERPLATE(OmpAbsentClause, OmpDirectiveList);
+};
+
 // Ref: [5.0:135-140], [5.1:161-166], [5.2:264-265]
 //
 // affinity-clause ->
@@ -3808,6 +3821,14 @@ struct OmpBindClause {
   WRAPPER_CLASS_BOILERPLATE(OmpBindClause, Binding);
 };
 
+// Ref: [5.2:214]
+//
+// contains-clause ->
+//   CONTAINS(directive-name[, directive-name])
+struct OmpContainsClause {
+  WRAPPER_CLASS_BOILERPLATE(OmpContainsClause, OmpDirectiveList);
+};
+
 // Ref: [4.5:46-50], [5.0:74-78], [5.1:92-96], [5.2:109]
 //
 // default-clause ->
@@ -3971,6 +3992,15 @@ struct OmpGrainsizeClause {
   std::tuple<MODIFIERS(), ScalarIntExpr> t;
 };
 
+// Ref: [5.2: 214]
+//
+// holds-clause ->
+//   HOLDS(expr[, expr])
+struct OmpHoldsClause {
+  using ExprList = std::list<common::Indirection<Expr>>;
+  WRAPPER_CLASS_BOILERPLATE(OmpHoldsClause, ExprList);
+};
+
 // Ref: [5.2:72-73], in 4.5-5.1 it's scattered over individual directives
 // that allow the IF clause.
 //
@@ -4042,6 +4072,21 @@ struct OmpMessageClause {
   WRAPPER_CLASS_BOILERPLATE(OmpMessageClause, Expr);
 };
 
+// Ref: [5.2: 214]
+//
+// no_openmp_clause -> NO_OPENMP
+EMPTY_CLASS(OmpNoOpenMPClause);
+
+// Ref: [5.2: 214]
+//
+// no_openmp_routines_clause -> NO_OPENMP_ROUTINES
+EMPTY_CLASS(OmpNoOpenMPRoutinesClause);
+
+// Ref: [5.2: 214]
+//
+// no_parallelism_clause -> NO_PARALELISM
+EMPTY_CLASS(OmpNoParallelismClause);
+
 // Ref: [4.5:87-91], [5.0:140-146], [5.1:166-171], [5.2:270]
 //
 // num-tasks-clause ->
@@ -4174,6 +4219,44 @@ struct OmpClauseList {
 
 // --- Directives and constructs
 
+// Ref: [5.2: 213-216]
+//
+// assume-construct ->
+//   ASSUME absent-clause | contains-clause | holds-clause | no-openmp-clause |
+//          no-openmp-routines-clause | no-parallelism-clause
+struct OpenMPAssumeConstruct {
+  TUPLE_CLASS_BOILERPLATE(OpenMPAssumeConstruct);
+  std::tuple<Verbatim, OmpClauseList> t;
+  CharBlock source;
+};
+
+struct OmpAssumesDirective {
+  TUPLE_CLASS_BOILERPLATE(OmpAssumesDirective);
+  std::tuple<Verbatim, OmpClauseList> t;
+  CharBlock source;
+};
+
+struct OmpEndAssumesDirective {
+  TUPLE_CLASS_BOILERPLATE(OmpEndAssumesDirective);
+  std::tuple<Verbatim> t;
+  CharBlock source;
+};
+
+//    structured-block
+// ...
+struct OpenMPAssumesPartConstruct {
+  WRAPPER_CLASS_BOILERPLATE(OpenMPAssumesPartConstruct, Block);
+  CharBlock source;
+};
+
+struct OpenMPAssumesConstruct {
+  TUPLE_CLASS_BOILERPLATE(OpenMPAssumesConstruct);
+  std::tuple<OmpAssumesDirective, OpenMPAssumesPartConstruct,
+      OmpEndAssumesDirective>
+      t;
+  CharBlock source;
+};
+
 // 2.7.2 SECTIONS
 // 2.11.2 PARALLEL SECTIONS
 struct OmpSectionsDirective {
@@ -4580,7 +4663,7 @@ struct OpenMPConstruct {
       OpenMPSectionConstruct, OpenMPLoopConstruct, OpenMPBlockConstruct,
       OpenMPAtomicConstruct, OpenMPDeclarativeAllocate, OpenMPErrorConstruct,
       OpenMPExecutableAllocate, OpenMPAllocatorsConstruct,
-      OpenMPCriticalConstruct>
+      OpenMPAssumeConstruct, OpenMPAssumesConstruct, OpenMPCriticalConstruct>
       u;
 };
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index c61ab67d95a957..cc3cd95f69d77e 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2893,6 +2893,24 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                  queue.begin());
 }
 
+static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
+                   semantics::SemanticsContext &semaCtx,
+                   lower::pft::Evaluation &eval,
+                   const parser::OpenMPAssumeConstruct &assumeConstruct) {
+  const auto &verbatim = std::get<parser::Verbatim>(assumeConstruct.t);
+  mlir::Location clauseLocation = converter.genLocation(verbatim.source);
+  TODO(clauseLocation, "OpenMP ASSUME construct");
+}
+
+static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
+                   semantics::SemanticsContext &semaCtx,
+                   lower::pft::Evaluation &eval,
+                   const parser::OpenMPAssumesConstruct &assumesConstruct) {
+  mlir::Location clauseLocation =
+      converter.genLocation(assumesConstruct.source);
+  TODO(clauseLocation, "OpenMP ASSUMES construct");
+}
+
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                    semantics::SemanticsContext &semaCtx,
                    lower::pft::Evaluation &eval,
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 67385c03f66c80..a9b995bbf5e2bb 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -577,7 +577,14 @@ TYPE_PARSER(construct<OmpSeverityClause>(
 
 TYPE_PARSER(construct<OmpMessageClause>(expr))
 
-TYPE_PARSER(
+TYPE_PARSER(construct<OmpHoldsClause>(many(maybe(","_tok) >> indirect(expr))))
+TYPE_PARSER(construct<OmpAbsentClause>(many(maybe(","_tok) >>
+    construct<OmpDirectiveNameEntry>(OmpDirectiveNameParser{}))))
+TYPE_PARSER(construct<OmpContainsClause>(many(maybe(","_tok) >>
+    construct<OmpDirectiveNameEntry>(OmpDirectiveNameParser{}))))
+
+TYPE_PARSER("ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(
+                            parenthesized(Parser<OmpAbsentClause>{}))) ||
     "ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
     "ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
     "AFFINITY" >> construct<OmpClause>(construct<OmpClause::Affinity>(
@@ -597,6 +604,8 @@ TYPE_PARSER(
                   parenthesized(Parser<OmpBindClause>{}))) ||
     "COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(
                       parenthesized(scalarIntConstantExpr))) ||
+    "CONTAINS" >> construct<OmpClause>(construct<OmpClause::Contains>(
+                      parenthesized(Parser<OmpContainsClause>{}))) ||
     "COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>(
                     parenthesized(Parser<OmpObjectList>{}))) ||
     "COPYPRIVATE" >> construct<OmpClause>(construct<OmpClause::Copyprivate>(
@@ -641,6 +650,8 @@ TYPE_PARSER(
             parenthesized(Parser<OmpObjectList>{}))) ||
     "HINT" >> construct<OmpClause>(
                   construct<OmpClause::Hint>(parenthesized(constantExpr))) ||
+    "HOLDS" >> construct<OmpClause>(construct<OmpClause::Holds>(
+                   parenthesized(Parser<OmpHoldsClause>{}))) ||
     "IF" >> construct<OmpClause>(construct<OmpClause::If>(
                 parenthesized(Parser<OmpIfClause>{}))) ||
     "INBRANCH" >> construct<OmpClause>(construct<OmpClause::Inbranch>()) ||
@@ -665,6 +676,11 @@ TYPE_PARSER(
     "NOTINBRANCH" >>
         construct<OmpClause>(construct<OmpClause::Notinbranch>()) ||
     "NOWAIT" >> construct<OmpClause>(construct<OmpClause::Nowait>()) ||
+    "NO_OPENMP"_id >> construct<OmpClause>(construct<OmpClause::NoOpenmp>()) ||
+    "NO_OPENMP_ROUTINES" >>
+        construct<OmpClause>(construct<OmpClause::NoOpenmpRoutines>()) ||
+    "NO_PARALLELISM" >>
+        construct<OmpClause>(construct<OmpClause::NoParallelism>()) ||
     "NUM_TASKS" >> construct<OmpClause>(construct<OmpClause::NumTasks>(
                        parenthesized(Parser<OmpNumTasksClause>{}))) ||
     "NUM_TEAMS" >> construct<OmpClause>(construct<OmpClause::NumTeams>(
@@ -1085,6 +1101,24 @@ TYPE_PARSER(startOmpLine >>
                 Parser<OpenMPThreadprivate>{})) /
             endOmpLine))
 
+// Assume Construct
+TYPE_PARSER(sourced(construct<OpenMPAssumeConstruct>(
+                        verbatim("ASSUME"_tok), Parser<OmpClauseList>{}) /
+    endOmpLine))
+// Assumes Construct
+TYPE_PARSER(sourced(construct<OmpAssumesDirective>(
+    verbatim("ASSUMES"_tok), Parser<OmpClauseList>{})))
+
+TYPE_PARSER(sourced(construct<OmpEndAssumesDirective>(
+    verbatim(startOmpLine >> "END ASSUMES"_tok))))
+
+TYPE_PARSER(construct<OpenMPAssumesPartConstruct>(block))
+
+TYPE_PARSER(sourced(construct<OpenMPAssumesConstruct>(
+    maybe("BEGIN"_tok) >> Parser<OmpAssumesDirective>{} / endOmpLine,
+    Parser<OpenMPAssumesPartConstruct>{},
+    Parser<OmpEndAssumesDirective>{} / endOmpLine)))
+
 // Block Construct
 TYPE_PARSER(construct<OpenMPBlockConstruct>(
     Parser<OmpBeginBlockDirective>{} / endOmpLine, block,
@@ -1131,6 +1165,8 @@ TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
                 construct<OpenMPConstruct>(Parser<OpenMPExecutableAllocate>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPDeclarativeAllocate>{}),
+                construct<OpenMPConstruct>(Parser<OpenMPAssumesConstruct>{}),
+                construct<OpenMPConstruct>(Parser<OpenMPAssumeConstruct>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))))
 
 // END OMP Block directives
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 0a6af7435b4a22..a3887cd42cd850 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2077,6 +2077,9 @@ class UnparseVisitor {
   void Unparse(const OmpDirectiveNameModifier &x) {
     Word(llvm::omp::getOpenMPDirectiveName(x.v));
   }
+  void Unparse(const OmpDirectiveNameEntry &x) {
+    Word(llvm::omp::getOpenMPDirectiveName(x.v));
+  }
   void Unparse(const OmpIteratorSpecifier &x) {
     Walk(std::get<TypeDeclarationStmt>(x.t));
     Put(" = ");
@@ -2113,6 +2116,8 @@ class UnparseVisitor {
     Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
     Walk(std::get<ScalarIntExpr>(x.t));
   }
+  void Unparse(const OmpAbsentClause &x) { Walk("", x.v, ","); }
+  void Unparse(const OmpContainsClause &x) { Walk("", x.v, ","); }
   void Unparse(const OmpAffinityClause &x) {
     using Modifier = OmpAffinityClause::Modifier;
     Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
@@ -2590,6 +2595,13 @@ class UnparseVisitor {
       Walk(*end);
     }
   }
+  void Unparse(const OpenMPAssumeConstruct &x) {
+    BeginOpenMP();
+    Word("!$OMP ASSUME");
+    Walk(std::get<OmpClauseList>(x.t), ", ");
+    Put("\n");
+    EndOpenMP();
+  }
   void Unparse(const OmpCriticalDirective &x) {
     BeginOpenMP();
     Word("!$OMP CRITICAL");
@@ -2806,7 +2818,9 @@ class UnparseVisitor {
     Put("\n");
     EndOpenMP();
   }
-  void Unparse(const OmpClauseList &x) { Walk(" ", x.v, " "); }
+  void Unparse(const OmpClauseList &x, const char *sep = " ") {
+    Walk(" ", x.v, sep);
+  }
   void Unparse(const OpenMPSimpleStandaloneConstruct &x) {
     BeginOpenMP();
     Word("!$OMP ");
@@ -3065,6 +3079,10 @@ class UnparseVisitor {
     return Walk("", list, comma, suffix);
   }
 
+  void Walk(const OmpClauseList &x, const char *sep = " ") {
+    return Walk(" ", x.v, sep);
+  }
+
   // Traverse a std::tuple<>, with an optional separator.
   template <std::size_t J = 0, typename T>
   void WalkTupleElements(const T &tuple, const char *separator) {
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 414753305a6e37..775b6bcfb76fdc 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1185,6 +1185,23 @@ void OmpStructureChecker::CheckMasterNesting(
   }
 }
 
+void OmpStructureChecker::Enter(const parser::OpenMPAssumeConstruct &x) {
+  const auto &dir{std::get<parser::Verbatim>(x.t)};
+  PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_assume);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPAssumeConstruct &) {
+  dirContext_.pop_back();
+}
+
+void OmpStructureChecker::Enter(const parser::OpenMPAssumesConstruct &x) {
+  PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_assumes);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPAssumesConstruct &) {
+  dirContext_.pop_back();
+}
+
 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
   if (GetDirectiveNest(TargetBlockOnlyTeams)) {
     ExitDirectiveNest(TargetBlockOnlyTeams);
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 346a7bed9138f0..ffd36e9cd3324c 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -78,6 +78,10 @@ class OmpStructureChecker
   void Enter(const parser::OmpEndLoopDirective &);
   void Leave(const parser::OmpEndLoopDirective &);
 
+  void Enter(const parser::OpenMPAssumeConstruct &);
+  void Leave(const parser::OpenMPAssumeConstruct &);
+  void Enter(const parser::OpenMPAssumesConstruct &);
+  void Leave(const parser::OpenMPAssumesConstruct &);
   void Enter(const parser::OpenMPBlockConstruct &);
   void Leave(const parser::OpenMPBlockConstruct &);
   void Leave(const parser::OmpBeginBlockDirective &);
diff --git a/flang/test/Lower/OpenMP/Todo/assume.f90 b/flang/test/Lower/OpenMP/Todo/assume.f90
new file mode 100644
index 00000000000000..6942c7774663ed
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/assume.f90
@@ -0,0 +1,6 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: OpenMP ASSUME construct
+program p
+  !$omp assume no_openmp
+end program p
diff --git a/flang/test/Lower/OpenMP/Todo/assumes.f90 b/flang/test/Lower/OpenMP/Todo/assumes.f90
new file mode 100644
index 00000000000000..73ad1526e83cc4
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/assumes.f90
@@ -0,0 +1,9 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: OpenMP ASSUMES construct
+program p
+  integer r
+  !$omp assumes no_openmp
+  print *,r
+  !$omp end assumes
+end program p
diff --git a/flang/test/Parser/OpenMP/assumption.f90 b/flang/test/Parser/OpenMP/assumption.f90
new file mode 100644
index 00000000000000..74c10126a76c61
--- /dev/null
+++ b/flang/test/Parser/OpenMP/assumption.f90
@@ -0,0 +1,66 @@
+! RUN: %flang_fc1  -fopenmp-version=51 -fopenmp -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
+!!! RUN: %flang_fc1  -fopenmp-version=51 -fopenmp -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s --check-prefix="PARSE-TREE"
+
+subroutine sub1
+  integer :: r
+!CHECK: !$OMP ASSUME NO_OPENMP
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> NoOpenmp
+  !$omp assume no_openmp
+!CHECK: !$OMP ASSUME NO_PARALLELISM
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> NoParallelism
+  !$omp assume no_parallelism
+!CHECK: !$OMP ASSUME NO_OPENMP_ROUTINES
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> NoOpenmpRoutines  
+  !$omp assume no_openmp_routines
+!CHECK: !$OMP ASSUME ABSENT(ALLOCATE), CONTAINS(WORKSHARE,TASK)
+!PARSE-TREE:   ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumeConstruct
+!PARSE-TREE:   Verbatim
+!PARSE-TREE:   OmpClauseList -> OmpClause -> Absent -> OmpAbsentClause -> OmpDirectiveNameEntry -> llvm::omp::Directive = allocate
+!PARSE-TREE:   OmpClause -> Contains -> OmpContainsClause -> OmpDirectiveNameEntry -> llvm::omp::Directive = workshare
+!PARSE-TREE:   OmpDirectiveNameEntry -> llvm::omp::Directive = task
+ !$omp assume absent(allocate), contains(workshare, task)
+!CHECK: !$OMP ASSUME HOLDS(1==1)
+  !$omp assume holds(1.eq.1)
+  print *, r
+end subroutine sub1
+
+subroutine sub2
+  integer :: r
+!CHECK !$OMP ASSUMES NO_OPENMP
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumesConstruct
+!PARSE-TREE: OmpAssumesDirective
+!PARSE-TREE: Verbatim
+!PARSE-TREE: OmpClauseList -> OmpClause -> NoOpenmp
+!PARSE-TREE: OpenMPAssumesPartConstruct -> Block
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt
+!PARSE-TREE: Expr -> Add
+!PARSE-TREE: OmpEndAssumesDirective
+  !$omp assumes no_openmp
+  r = r + 1
+!CHECK !$OMP END ASSUMES
+  !$omp end assumes
+end subroutine sub2
+
+subroutine sub3
+  integer :: r
+!CHECK !$OMP ASSUMES NO_OPENMP
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPAssumesConstruct
+!PARSE-TREE: OmpAssumesDirective
+!PARSE-TREE: Verbatim
+!PARSE-TREE: OmpClauseList -> OmpClause -> NoOpenmp
+!PARSE-TREE: OpenMPAssumesPartConstruct -> Block
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt
+!PARSE-TREE: Expr -> Add
+!PARSE-TREE: OmpEndAssumesDirective
+  !$omp begin assumes no_openmp
+  r = r + 1
+!CHECK !$OMP END ASSUMES
+  !$omp end assumes
+end subroutine sub3
+  
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llv...
[truncated]

Comment on lines 3997 to 4165
// holds-clause ->
// HOLDS(expr[, expr])
struct OmpHoldsClause {
using ExprList = std::list<common::Indirection<Expr>>;
WRAPPER_CLASS_BOILERPLATE(OmpHoldsClause, ExprList);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOLDS takes only one expression. Is this an extension?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, me being a bit enthusiastic about what that should do! :)


struct OmpEndAssumesDirective {
TUPLE_CLASS_BOILERPLATE(OmpEndAssumesDirective);
std::tuple<Verbatim> t;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is only one element in the tuple, then this should be a wrapper class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

struct OmpDirectiveNameEntry {
WRAPPER_CLASS_BOILERPLATE(OmpDirectiveNameEntry, llvm::omp::Directive);
};
using OmpDirectiveList = std::list<OmpDirectiveNameEntry>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the OmpDirectiveNameEntry class and use llvm::omp::Directive directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@kiranchandramohan
Copy link
Contributor

Assumes directive cannot occur in the execution part of Fortran. See the following in Section 8.3.2 of OpenMP 5.2 standard. So it is not an executable directive.

The assumes directive may only appear in the specification part of a module or subprogram after any USE statement, any IMPORT statement, and any IMPLICIT statement.

@Leporacanthicus
Copy link
Contributor Author

Assumes directive cannot occur in the execution part of Fortran. See the following in Section 8.3.2 of OpenMP 5.2 standard. So it is not an executable directive.

The assumes directive may only appear in the specification part of a module or subprogram after any USE statement, any IMPORT statement, and any IMPLICIT statement.

Thanks for pointing this out, I completely missed that this was the distinction between BEGIN ASSUMES and ASSUMES - I thought it was a bit weird that there was two different ways to express something very similar. :)

Now fixed.

Comment on lines 4253 to 4480
struct OpenMPBeginAssumesConstruct {
TUPLE_CLASS_BOILERPLATE(OpenMPBeginAssumesConstruct);
std::tuple<OmpBeginAssumesDirective, OpenMPAssumesPartConstruct,
OmpEndAssumesDirective>
t;
CharBlock source;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the standard begin assumes looks it is only for C++.

Comment on lines 4253 to 4479
struct OpenMPAssumesConstruct {
TUPLE_CLASS_BOILERPLATE(OpenMPAssumesConstruct);
std::tuple<OmpAssumesDirective, OmpAssumesPartConstruct,
OmpEndAssumesDirective>
t;
CharBlock source;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Assume (note no s at the end). The ASSUME directive is associated with a block (and it has END ASSUME counterpart).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 4248 to 4467
struct OmpAssumesPartConstruct {
WRAPPER_CLASS_BOILERPLATE(OmpAssumesPartConstruct, Block);
CharBlock source;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this class? Could we just have a Block in the construct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 4223 to 4431
struct OpenMPAssumeConstruct {
TUPLE_CLASS_BOILERPLATE(OpenMPAssumeConstruct);
std::tuple<Verbatim, OmpClauseList> t;
CharBlock source;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenMPDeclarativeAssumes below is what we need (based on the name, that is). The ASSUMES (with S at the end) is the declarative thing, which applies to the entire compilation unit. There is no END ASSUMES.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Leporacanthicus and others added 5 commits January 31, 2025 18:30
Enough suport to parse correctly formed directives of !$OMP ASSUME
and !$OMP ASSUMES with teh related clauses that go with them:
ABSENT, CONTAINS, NO_OPENPP, NO_OPENMP_ROUTINES, NO_PARALLELISM
and HOLDS.

Tests added for unparsing and dump parse-tree.

Semantics support is very minimal and no specific tests added.

The lowering will hit a TODO, and there are tests in Lower/OpenMP/Todo
to make it clear that this is currently expected behaviour.
This is only used in C++, so do not use it in the Fortran compiler.

Updated tests to suit.
Copy link

github-actions bot commented Feb 1, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@@ -0,0 +1,59 @@
! RUN: %flang_fc1 -fopenmp-version=51 -fopenmp -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
!!! RUN: %flang_fc1 -fopenmp-version=51 -fopenmp -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s --check-prefix="PARSE-TREE"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is this commented? Removing the extra ! looks OK now.

@@ -0,0 +1,8 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: duplicate test

Comment on lines 3142 to 3144
void Walk(const OmpClauseList &x, const char *sep = " ") {
return Walk(" ", x.v, sep);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is this required?

Also rewrite some code to avoid needing a new function.
Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG. Please wait for @kparzysz.

Copy link
Contributor

@kparzysz kparzysz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Leporacanthicus Leporacanthicus merged commit 24b7759 into llvm:main Feb 25, 2025
9 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:openmp OpenMP related changes to Clang flang:fir-hlfir flang:openmp flang:parser flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants