Skip to content
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

[Flang] [OpenMP] Add semantic checks for detach clause in task #119172

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Thirumalai-Shaktivel
Copy link
Member

Fixes:

  • Add semantic checks along with the tests
  • Move the detach clause to allowedOnceClauses list in Task construct

Restrictions:
OpenMP 5.0: Task construct

  • At most one detach clause can appear on the directive.
  • If a detach clause appears on the directive, then a mergeable clause cannot appear on the same directive.

OpenMP 5.2: Detach contruct

  • If a detach clause appears on a directive, then the encountering task must not be a final task.
  • A variable that appears in a detach clause cannot appear as a list item on a data-environment attribute clause on the same construct.
  • A variable that is part of another variable (as an array element or a structure element) cannot appear in a detach clause.
  • event-handle must not have the POINTER attribute.

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

llvmbot commented Dec 9, 2024

@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-flang-semantics

Author: Thirumalai Shaktivel (Thirumalai-Shaktivel)

Changes

Fixes:

  • Add semantic checks along with the tests
  • Move the detach clause to allowedOnceClauses list in Task construct

Restrictions:
OpenMP 5.0: Task construct

  • At most one detach clause can appear on the directive.
  • If a detach clause appears on the directive, then a mergeable clause cannot appear on the same directive.

OpenMP 5.2: Detach contruct

  • If a detach clause appears on a directive, then the encountering task must not be a final task.
  • A variable that appears in a detach clause cannot appear as a list item on a data-environment attribute clause on the same construct.
  • A variable that is part of another variable (as an array element or a structure element) cannot appear in a detach clause.
  • event-handle must not have the POINTER attribute.

Full diff: https://github.com/llvm/llvm-project/pull/119172.diff

4 Files Affected:

  • (modified) flang/lib/Semantics/check-omp-structure.cpp (+110-30)
  • (modified) flang/lib/Semantics/check-omp-structure.h (+2)
  • (added) flang/test/Semantics/OpenMP/detach01.f90 (+65)
  • (modified) llvm/include/llvm/Frontend/OpenMP/OMP.td (+1-1)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 3c9c5a02a338a6..23f7624182a556 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2739,6 +2739,58 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
         llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait});
   }
 
+  if (GetContext().directive == llvm::omp::Directive::OMPD_task) {
+    if (auto *d_clause{FindClause(llvm::omp::Clause::OMPC_detach)}) {
+      // OpenMP 5.0: Task construct restrictions
+      CheckNotAllowedIfClause(
+          llvm::omp::Clause::OMPC_detach, {llvm::omp::Clause::OMPC_mergeable});
+
+      // OpenMP 5.2: Task construct restrictions
+      if (FindClause(llvm::omp::Clause::OMPC_final)) {
+        context_.Say(GetContext().clauseSource,
+            "If a DETACH clause appears on a directive, then the encountering task must not be a FINAL task"_err_en_US);
+      }
+
+      const auto &detachClause{
+          std::get<parser::OmpClause::Detach>(d_clause->u)};
+      if (const auto *name{parser::Unwrap<parser::Name>(detachClause.v.v)}) {
+        if (name->symbol) {
+          std::string eventHandleSymName{name->ToString()};
+          auto checkVarAppearsInDataEnvClause = [&](const parser::OmpObjectList
+                                                        &objs,
+                                                    std::string clause) {
+            for (const auto &obj : objs.v) {
+              if (const parser::Name *name{parser::Unwrap<parser::Name>(obj)}) {
+                if (name->ToString() == eventHandleSymName) {
+                  context_.Say(GetContext().clauseSource,
+                      "A variable: `%s` that appears in a DETACH clause cannot appear on %s clause on the same construct"_err_en_US,
+                      eventHandleSymName, clause);
+                }
+              }
+            }
+          };
+          if (auto *dataEnvClause{
+                  FindClause(llvm::omp::Clause::OMPC_private)}) {
+            const auto &pClause{
+                std::get<parser::OmpClause::Private>(dataEnvClause->u)};
+            checkVarAppearsInDataEnvClause(pClause.v, "PRIVATE");
+          } else if (auto *dataEnvClause{
+                         FindClause(llvm::omp::Clause::OMPC_firstprivate)}) {
+            const auto &fpClause{
+                std::get<parser::OmpClause::Firstprivate>(dataEnvClause->u)};
+            checkVarAppearsInDataEnvClause(fpClause.v, "FIRSTPRIVATE");
+          } else if (auto *dataEnvClause{
+                         FindClause(llvm::omp::Clause::OMPC_in_reduction)}) {
+            const auto &irClause{
+                std::get<parser::OmpClause::InReduction>(dataEnvClause->u)};
+            checkVarAppearsInDataEnvClause(
+                std::get<parser::OmpObjectList>(irClause.v.t), "IN_REDUCTION");
+          }
+        }
+      }
+    }
+  }
+
   auto testThreadprivateVarErr = [&](Symbol sym, parser::Name name,
                                      llvmOmpClause clauseTy) {
     if (sym.test(Symbol::Flag::OmpThreadprivate))
@@ -2823,7 +2875,6 @@ CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
 CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains)
 CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
-CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach)
 CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type)
 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
 CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive)
@@ -3352,40 +3403,45 @@ void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
     const parser::CharBlock &source, const parser::OmpObjectList &objList,
     llvm::StringRef clause) {
   for (const auto &ompObject : objList.v) {
-    common::visit(
-        common::visitors{
-            [&](const parser::Designator &designator) {
-              if (const auto *dataRef{
-                      std::get_if<parser::DataRef>(&designator.u)}) {
-                if (IsDataRefTypeParamInquiry(dataRef)) {
+    CheckIsVarPartOfAnotherVar(source, ompObject, clause);
+  }
+}
+
+void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
+    const parser::CharBlock &source, const parser::OmpObject &ompObject,
+    llvm::StringRef clause) {
+  common::visit(
+      common::visitors{
+          [&](const parser::Designator &designator) {
+            if (const auto *dataRef{
+                    std::get_if<parser::DataRef>(&designator.u)}) {
+              if (IsDataRefTypeParamInquiry(dataRef)) {
+                context_.Say(source,
+                    "A type parameter inquiry cannot appear on the %s "
+                    "directive"_err_en_US,
+                    ContextDirectiveAsFortran());
+              } else if (parser::Unwrap<parser::StructureComponent>(
+                             ompObject) ||
+                  parser::Unwrap<parser::ArrayElement>(ompObject)) {
+                if (llvm::omp::nonPartialVarSet.test(GetContext().directive)) {
                   context_.Say(source,
-                      "A type parameter inquiry cannot appear on the %s "
+                      "A variable that is part of another variable (as an "
+                      "array or structure element) cannot appear on the %s "
                       "directive"_err_en_US,
                       ContextDirectiveAsFortran());
-                } else if (parser::Unwrap<parser::StructureComponent>(
-                               ompObject) ||
-                    parser::Unwrap<parser::ArrayElement>(ompObject)) {
-                  if (llvm::omp::nonPartialVarSet.test(
-                          GetContext().directive)) {
-                    context_.Say(source,
-                        "A variable that is part of another variable (as an "
-                        "array or structure element) cannot appear on the %s "
-                        "directive"_err_en_US,
-                        ContextDirectiveAsFortran());
-                  } else {
-                    context_.Say(source,
-                        "A variable that is part of another variable (as an "
-                        "array or structure element) cannot appear in a "
-                        "%s clause"_err_en_US,
-                        clause.data());
-                  }
+                } else {
+                  context_.Say(source,
+                      "A variable that is part of another variable (as an "
+                      "array or structure element) cannot appear in a "
+                      "%s clause"_err_en_US,
+                      clause.data());
                 }
               }
-            },
-            [&](const parser::Name &name) {},
-        },
-        ompObject.u);
-  }
+            }
+          },
+          [&](const parser::Name &name) {},
+      },
+      ompObject.u);
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
@@ -3711,6 +3767,30 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
       x.v.u);
 }
 
+void OmpStructureChecker::Enter(const parser::OmpClause::Detach &x) {
+  // OpenMP 5.0: Task construct restrictions
+  CheckAllowedClause(llvm::omp::Clause::OMPC_detach);
+
+  // OpenMP 5.2: Detach clause restrictions
+  CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v.v, "DETACH");
+  if (const auto *name{parser::Unwrap<parser::Name>(x.v.v)}) {
+    if (name->symbol) {
+      if (IsPointer(*name->symbol)) {
+        context_.Say(GetContext().clauseSource,
+            "The event-handle: `%s` must not have the POINTER attribute"_err_en_US,
+            name->ToString());
+      }
+    }
+    auto type{name->symbol->GetType()};
+    if (!name->symbol->GetType()->IsNumeric(TypeCategory::Integer) ||
+        evaluate::ToInt64(type->numericTypeSpec().kind()) != 8) {
+      context_.Say(GetContext().clauseSource,
+          "The event-handle: `%s` must be of type integer(kind=omp_event_handle_kind)"_err_en_US,
+          name->ToString());
+    }
+  }
+}
+
 void OmpStructureChecker::CheckAllowedMapTypes(
     const parser::OmpMapType::Value &type,
     const std::list<parser::OmpMapType::Value> &allowedMapTypeList) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 89af46d9171ad3..b8adbc77a8fce0 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -180,6 +180,8 @@ class OmpStructureChecker
       const common::Indirection<parser::ArrayElement> &, const parser::Name &);
   void CheckDoacross(const parser::OmpDoacross &doa);
   bool IsDataRefTypeParamInquiry(const parser::DataRef *dataRef);
+  void CheckIsVarPartOfAnotherVar(const parser::CharBlock &source,
+      const parser::OmpObject &obj, llvm::StringRef clause = "");
   void CheckIsVarPartOfAnotherVar(const parser::CharBlock &source,
       const parser::OmpObjectList &objList, llvm::StringRef clause = "");
   void CheckThreadprivateOrDeclareTargetVar(
diff --git a/flang/test/Semantics/OpenMP/detach01.f90 b/flang/test/Semantics/OpenMP/detach01.f90
new file mode 100644
index 00000000000000..e342fcd1b19b4a
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/detach01.f90
@@ -0,0 +1,65 @@
+! REQUIRES: openmp_runtime
+! RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=50
+
+! OpenMP Version 5.2
+! Various checks for DETACH Clause (12.5.2)
+
+program test_detach
+    use omp_lib
+    implicit none
+    integer :: e, x
+    integer(omp_event_handle_kind) :: event_01, event_02(2)
+    integer(omp_event_handle_kind), pointer :: event_03
+
+
+    type :: t
+        integer(omp_event_handle_kind) :: event
+    end type
+
+    type(t) :: t_01
+
+    !ERROR: The event-handle: `e` must be of type integer(kind=omp_event_handle_kind)
+    !$omp task detach(e)
+        x = x + 1
+    !$omp end task
+
+    !ERROR: At most one DETACH clause can appear on the TASK directive
+    !$omp task detach(event_01) detach(event_01)
+        x = x + 1
+    !$omp end task
+
+    !ERROR: Clause MERGEABLE is not allowed if clause DETACH appears on the TASK directive
+    !$omp task detach(event_01) mergeable
+        x = x + 1
+    !$omp end task
+
+    !ERROR: If a DETACH clause appears on a directive, then the encountering task must not be a FINAL task
+    !$omp task detach(event_01) final(.false.)
+        x = x + 1
+    !$omp end task
+
+    !ERROR: A variable: `event_01` that appears in a DETACH clause cannot appear on PRIVATE clause on the same construct
+    !$omp task detach(event_01) private(event_01)
+        x = x + 1
+    !$omp end task
+
+    !ERROR: A variable: `event_01` that appears in a DETACH clause cannot appear on IN_REDUCTION clause on the same construct
+    !$omp task detach(event_01) in_reduction(+:event_01)
+        x = x + 1
+    !$omp end task
+
+    !ERROR: A variable that is part of another variable (as an array or structure element) cannot appear in a DETACH clause
+    !$omp task detach(event_02(1))
+        x = x + 1
+    !$omp end task
+
+    !ERROR: A variable that is part of another variable (as an array or structure element) cannot appear in a DETACH clause
+    !$omp task detach(t_01%event)
+        x = x + 1
+    !$omp end task
+
+    !ERROR: The event-handle: `event_03` must not have the POINTER attribute
+    !$omp task detach(event_03)
+        x = x + 1
+    !$omp end task
+end program
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index bd7fb2361aaeb1..9ada11241d3a56 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1085,7 +1085,6 @@ def OMP_Task : Directive<"task"> {
     VersionedClause<OMPC_Affinity, 50>,
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
-    VersionedClause<OMPC_Detach, 50>,
     VersionedClause<OMPC_FirstPrivate>,
     VersionedClause<OMPC_InReduction>,
     VersionedClause<OMPC_Mergeable>,
@@ -1095,6 +1094,7 @@ def OMP_Task : Directive<"task"> {
   ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Default>,
+    VersionedClause<OMPC_Detach, 50>,
     VersionedClause<OMPC_Final>,
     VersionedClause<OMPC_If>,
     VersionedClause<OMPC_Priority>,

@Thirumalai-Shaktivel Thirumalai-Shaktivel added flang Flang issues not falling into any other category flang:openmp and removed flang Flang issues not falling into any other category flang:openmp clang:openmp OpenMP related changes to Clang labels Dec 9, 2024
Copy link

github-actions bot commented Dec 9, 2024

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff eaa1b05c48c88ed6da6b8fc21d07f8417575a051 414ea0c456c050f2c3523978c200e3828f0c70b4 --extensions cpp,h -- flang/lib/Semantics/check-omp-structure.cpp flang/lib/Semantics/check-omp-structure.h
View the diff from clang-format here.
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index c18bff2603..de0833df13 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2760,8 +2760,8 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
                                                         &objs,
                                                     std::string clause) {
             for (const auto &obj : objs.v) {
-              if (const parser::Name *objName{
-                      parser::Unwrap<parser::Name>(obj)}) {
+              if (const parser::Name *
+                  objName{parser::Unwrap<parser::Name>(obj)}) {
                 if (objName->ToString() == eventHandleSymName) {
                   context_.Say(GetContext().clauseSource,
                       "A variable: `%s` that appears in a DETACH clause cannot appear on %s clause on the same construct"_err_en_US,

@Thirumalai-Shaktivel
Copy link
Member Author

There seems to be some problem with Check code formatting.

I get the following locally

$ git-clang-format
clang-format did not modify any files

The failed line seems to be within the range (80 characters):

              if (const parser::Name *name{parser::Unwrap<parser::Name>(obj)}) {

Fixes:
- Add semantic checks along with the tests
- Move the detach clause to allowedOnceClauses list in
  Task construct

Restrictions:\
OpenMP 5.0: Task construct
- At most one detach clause can appear on the directive.
- If a detach clause appears on the directive, then a mergeable
  clause cannot appear on the same directive.

OpenMP 5.2: Detach contruct
- If a detach clause appears on a directive, then the encountering
  task must not be a final task.
- A variable that appears in a detach clause cannot appear as a
  list item on a data-environment attribute clause on the same
  construct.
- A variable that is part of another variable (as an array element or a
  structure element) cannot appear in a detach clause.
- event-handle must not have the POINTER attribute.
@llvmbot llvmbot added the clang:openmp OpenMP related changes to Clang label Dec 9, 2024
@Thirumalai-Shaktivel Thirumalai-Shaktivel removed the clang:openmp OpenMP related changes to Clang label Dec 9, 2024
@Thirumalai-Shaktivel Thirumalai-Shaktivel changed the title [OpenMP] [TASK] Add semantic checks for detach clause [Flang] [OpenMP] Add semantic checks for detach clause in task Dec 9, 2024
}
auto type{name->symbol->GetType()};
if (!name->symbol->GetType()->IsNumeric(TypeCategory::Integer) ||
evaluate::ToInt64(type->numericTypeSpec().kind()) != 8) {
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 check of the kind. We shouldn't be using hardcoded numbers.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I felt the same. But, the only info I had was the kind => 8 to throw an error for the following:

    !ERROR: The event-handle: `e` must be of type integer(kind=omp_event_handle_kind)
    !$omp task detach(e)
        x = x + 1
    !$omp end task

Do you know any other way to add a check for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't have a way of checking this, unfortunately. In other cases we limit it to checking that it has an INTEGER type, but without checking the kind.

@@ -2739,6 +2739,59 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait});
}

if (GetContext().directive == llvm::omp::Directive::OMPD_task) {
if (auto *d_clause{FindClause(llvm::omp::Clause::OMPC_detach)}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

No snake_case please (d_clause).

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, I forgot to update this.

Comment on lines +2744 to +2748
// OpenMP 5.0: Task construct restrictions
CheckNotAllowedIfClause(
llvm::omp::Clause::OMPC_detach, {llvm::omp::Clause::OMPC_mergeable});

// OpenMP 5.2: Task construct restrictions
Copy link
Contributor

Choose a reason for hiding this comment

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

If you're doing version-specific checks, please guard them with the actual version from -fopenmp-version. You can get it from the language options in context. This is done in a number of places in this file, you can check how to do it.

Comment on lines +2758 to +2772
std::string eventHandleSymName{name->ToString()};
auto checkVarAppearsInDataEnvClause = [&](const parser::OmpObjectList
&objs,
std::string clause) {
for (const auto &obj : objs.v) {
if (const parser::Name *objName{
parser::Unwrap<parser::Name>(obj)}) {
if (objName->ToString() == eventHandleSymName) {
context_.Say(GetContext().clauseSource,
"A variable: `%s` that appears in a DETACH clause cannot appear on %s clause on the same construct"_err_en_US,
eventHandleSymName, clause);
}
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Please compare symbols, not names. You can compare the results of &symbol->GetUltimate() to be accurate.

Comment on lines +3772 to +3775
// OpenMP 5.0: Task construct restrictions
CheckAllowedClause(llvm::omp::Clause::OMPC_detach);

// OpenMP 5.2: Detach clause restrictions
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here: please do version-specific checks based on the actual version.

@Thirumalai-Shaktivel
Copy link
Member Author

Thirumalai-Shaktivel commented Dec 11, 2024

Sure, thanks for the review. I will update it and report back soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:openmp flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants