[flang-commits] [flang] 83ae5cc - [flang][openacc] allow duplicate data sharing clauses (#197019)

via flang-commits flang-commits at lists.llvm.org
Wed May 13 22:18:50 PDT 2026


Author: Andre Kuhlenschmidt
Date: 2026-05-13T22:18:45-07:00
New Revision: 83ae5ccb300eea674352716239ee57c959b3e14c

URL: https://github.com/llvm/llvm-project/commit/83ae5ccb300eea674352716239ee57c959b3e14c
DIFF: https://github.com/llvm/llvm-project/commit/83ae5ccb300eea674352716239ee57c959b3e14c.diff

LOG: [flang][openacc] allow duplicate data sharing clauses (#197019)

This PR allows duplicate OpenACC `private` and `firstprivate` clauses.
While maintaining the restriction on `reduction` clauses.

Added: 
    flang/test/Lower/OpenACC/acc-dedup-private.f90
    flang/test/Parser/acc-dedup-unparse.f90
    flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90

Modified: 
    flang/docs/OpenACC.md
    flang/include/flang/Semantics/semantics.h
    flang/lib/Semantics/resolve-directives.cpp
    flang/lib/Semantics/rewrite-parse-tree.cpp

Removed: 
    


################################################################################
diff  --git a/flang/docs/OpenACC.md b/flang/docs/OpenACC.md
index 9a166aa9bdde4..720afa5c830e4 100644
--- a/flang/docs/OpenACC.md
+++ b/flang/docs/OpenACC.md
@@ -33,7 +33,7 @@ local:
   or module, but it is allowed with a warning when same clause is used.
 * The OpenACC specification does not prohibit the same variable from appearing
   in multiple data clauses, but this is disallowed for variables appearing in
-  `private`, `firstprivate`, or `reduction` clauses.
+  `reduction` clauses.
 * The OpenACC specification does not prohibit the same variable from appearing
   multiple times in a `use_device` clause on a `host_data` construct, but this
   is disallowed.

diff  --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 0abfd150cefe0..6893f51c97122 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -33,6 +33,7 @@ class IntrinsicTypeDefaultKinds;
 }
 
 namespace Fortran::parser {
+struct AccObject;
 struct Name;
 struct Program;
 class AllCookedSources;
@@ -336,6 +337,15 @@ class SemanticsContext {
   void NoteUsedSymbols(const UnorderedSymbolSet &);
   bool IsSymbolUsed(const Symbol &) const;
 
+  // Track same-kind duplicate AccObjects between resolve-directives and
+  // rewrite-parse-tree (e.g. the second `x` in `private(x, x)`).
+  void MarkAccObjectDuplicate(const parser::AccObject *o) {
+    accObjectDuplicates_.insert(o);
+  }
+  bool IsAccObjectDuplicate(const parser::AccObject *o) const {
+    return accObjectDuplicates_.count(o) != 0;
+  }
+
   void DumpSymbols(llvm::raw_ostream &);
 
   // Top-level ProgramTrees are owned by the SemanticsContext for persistence.
@@ -395,6 +405,7 @@ class SemanticsContext {
   std::map<const Symbol *, SourceName> moduleFileOutputRenamings_;
   UnorderedSymbolSet isDefined_;
   UnorderedSymbolSet isUsed_;
+  std::set<const parser::AccObject *> accObjectDuplicates_;
   std::list<ProgramTree> programTrees_;
 };
 

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 7ae867e19f276..25fb489d57475 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -384,8 +384,8 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   Symbol *ResolveAccCommonBlockName(const parser::Name *);
   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
-  void CheckMultipleAppearances(
-      const parser::Name &, const Symbol &, Symbol::Flag);
+  void CheckMultipleAppearances(const parser::Name &, const Symbol &,
+      Symbol::Flag, const parser::AccObject *occurrence = nullptr);
   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
   void AllowOnlyVariable(const parser::AccObject &object);
@@ -1875,7 +1875,7 @@ void AccAttributeVisitor::ResolveAccObject(
               if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) {
                 AddToContextObjectWithDSA(*symbol, accFlag);
                 if (dataSharingAttributeFlags.test(accFlag)) {
-                  CheckMultipleAppearances(*name, *symbol, accFlag);
+                  CheckMultipleAppearances(*name, *symbol, accFlag, &accObject);
                 }
               }
             } else {
@@ -1940,20 +1940,31 @@ Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
   return &object;
 }
 
-static bool WithMultipleAppearancesAccException(
-    const Symbol &symbol, Symbol::Flag flag) {
-  return false; // Place holder
-}
-
-void AccAttributeVisitor::CheckMultipleAppearances(
-    const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) {
+void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
+    const Symbol &symbol, Symbol::Flag accFlag,
+    const parser::AccObject *occurrence) {
   const auto *target{&symbol};
-  if (HasDataSharingAttributeObject(*target) &&
-      !WithMultipleAppearancesAccException(symbol, accFlag)) {
-    context_.Say(name.source,
-        "'%s' appears in more than one data-sharing clause "
-        "on the same OpenACC directive"_err_en_US,
-        name.ToString());
+  if (HasDataSharingAttributeObject(*target)) {
+    // A same-kind duplicate (e.g. private(x, x) or private(x) private(x))
+    // is benign: warn and tag this AccObject occurrence so rewrite-parse-tree
+    // can drop it from the clause list. Cross-kind duplicates (e.g.
+    // private(x) firstprivate(x)) remain hard errors.
+    //
+    // Reduction is excluded from the benign case: two reduction clauses
+    // with the same Symbol::Flag may still 
diff er in operator, which is a
+    // real conflict that dedup would silently hide.
+    auto firstFlag{GetContext().FindSymbolWithDSA(*target)};
+    if (occurrence && firstFlag && *firstFlag == accFlag &&
+        accFlag != Symbol::Flag::AccReduction) {
+      context_.Warn(common::UsageWarning::OpenAccUsage, name.source,
+          "'%s' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored"_warn_en_US,
+          name.ToString());
+      context_.MarkAccObjectDuplicate(occurrence);
+    } else {
+      context_.Say(name.source,
+          "'%s' appears in more than one data-sharing clause on the same OpenACC directive"_err_en_US,
+          name.ToString());
+    }
   } else {
     AddDataSharingAttributeObject(*target);
   }

diff  --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp
index 7352c2a324616..500dd3f225889 100644
--- a/flang/lib/Semantics/rewrite-parse-tree.cpp
+++ b/flang/lib/Semantics/rewrite-parse-tree.cpp
@@ -62,6 +62,7 @@ class RewriteMutator {
   void Post(parser::IfConstruct &);
   void Post(parser::ReadStmt &);
   void Post(parser::WriteStmt &);
+  void Post(parser::AccObjectList &);
 
   // Name resolution yet implemented:
   // TODO: Can some/all of these now be enabled?
@@ -496,6 +497,15 @@ void RewriteMutator::Post(parser::WriteStmt &x) {
   FixMisparsedUntaggedNamelistName(x);
 }
 
+// Erase AccObjects recorded in the context by resolve-directives as same-kind
+// data-sharing duplicates. Cross-kind duplicates remain hard errors and never
+// reach this pass.
+void RewriteMutator::Post(parser::AccObjectList &x) {
+  x.v.remove_if([this](const parser::AccObject &o) {
+    return context_.IsAccObjectDuplicate(&o);
+  });
+}
+
 bool RewriteParseTree(SemanticsContext &context, parser::Program &program) {
   RewriteMutator mutator{context};
   parser::Walk(program, mutator);

diff  --git a/flang/test/Lower/OpenACC/acc-dedup-private.f90 b/flang/test/Lower/OpenACC/acc-dedup-private.f90
new file mode 100644
index 0000000000000..6399324070831
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-dedup-private.f90
@@ -0,0 +1,63 @@
+! RUN: bbc -fopenacc -emit-hlfir %s -o - 2>/dev/null | FileCheck %s
+
+! Check that same-kind duplicate variables in OpenACC private/firstprivate
+! clauses lower without failure, and that each variable produces exactly one
+! acc.private / acc.firstprivate op (deduplication by rewrite-parse-tree).
+
+! -----------------------------------------------------------------------
+! private(x, x) -- duplicate within one clause
+
+subroutine test_private_pair(i)
+  integer :: x, i
+  !$acc parallel loop private(x, x)
+  do i = 1, 10
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_pair
+! x is privatized exactly once.
+! CHECK: acc.private varPtr({{.*}}) recipe(@privatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+! CHECK-NOT: acc.private varPtr({{.*}}) recipe(@privatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+
+! -----------------------------------------------------------------------
+! private(x, x, x) -- two duplicates (from the triple-occurrence review note)
+
+subroutine test_private_triple(i)
+  integer :: x, i
+  !$acc parallel loop private(x, x, x)
+  do i = 1, 10
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_triple
+! x is privatized exactly once even with three source occurrences.
+! CHECK: acc.private varPtr({{.*}}) recipe(@privatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+! CHECK-NOT: acc.private varPtr({{.*}}) recipe(@privatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+
+! -----------------------------------------------------------------------
+! private(x) private(x) -- duplicate across two separate clauses
+
+subroutine test_private_two_clauses(i)
+  integer :: x, i
+  !$acc parallel loop private(x) private(x)
+  do i = 1, 10
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_two_clauses
+! CHECK: acc.private varPtr({{.*}}) recipe(@privatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+! CHECK-NOT: acc.private varPtr({{.*}}) recipe(@privatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+
+! -----------------------------------------------------------------------
+! firstprivate(x, x)
+
+subroutine test_firstprivate_pair(i)
+  integer :: x, i
+  !$acc parallel loop firstprivate(x, x)
+  do i = 1, 10
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_firstprivate_pair
+! CHECK: acc.firstprivate varPtr({{.*}}) recipe(@firstprivatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+! CHECK-NOT: acc.firstprivate varPtr({{.*}}) recipe(@firstprivatization_ref_i32) -> !fir.ref<i32> {name = "x"}

diff  --git a/flang/test/Parser/acc-dedup-unparse.f90 b/flang/test/Parser/acc-dedup-unparse.f90
new file mode 100644
index 0000000000000..26fa422ff6aad
--- /dev/null
+++ b/flang/test/Parser/acc-dedup-unparse.f90
@@ -0,0 +1,28 @@
+! RUN: %flang_fc1 -fopenacc -fdebug-unparse -w %s | FileCheck %s
+
+! Verify that same-kind duplicate variables in OpenACC data-sharing clauses are
+! removed by rewrite-parse-tree, so each variable appears at most once when
+! unparsed.
+
+subroutine dedup_pair(x, i)
+  integer, intent(inout) :: x, i
+  !$acc parallel loop private(x, x)
+  do i = 1, 10
+  end do
+end subroutine
+! CHECK-LABEL: SUBROUTINE dedup_pair
+! CHECK: PRIVATE(x)
+! CHECK-NOT: PRIVATE(x,x)
+! CHECK-NOT: PRIVATE(x, x)
+
+subroutine dedup_triple(x, i)
+  integer, intent(inout) :: x, i
+  !$acc parallel loop private(x, x, x)
+  do i = 1, 10
+  end do
+end subroutine
+! CHECK-LABEL: SUBROUTINE dedup_triple
+! Three occurrences reduce to one.
+! CHECK: PRIVATE(x)
+! CHECK-NOT: PRIVATE(x,x)
+! CHECK-NOT: PRIVATE(x, x)

diff  --git a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90 b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
new file mode 100644
index 0000000000000..e29332578a0bf
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
@@ -0,0 +1,122 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenacc
+
+! Same-kind data-sharing duplicates on an OpenACC directive (e.g.
+! private(x, x), private(x) private(x), copyin(x, x) ...) are not errors:
+! resolve-directives warns and rewrite-parse-tree drops the duplicate
+! occurrences from the clause object lists. Cross-kind duplicates
+! (e.g. private(x) firstprivate(x)) and reduction duplicates remain
+! hard errors.
+
+program test_dataclause_dedup
+  implicit none
+  integer :: x, y, z, i
+
+  ! passThis1.f90 pattern: duplicate within a single PRIVATE clause.
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc parallel loop private(x, x)
+  do i = 1, 10
+  end do
+
+  ! passThis2.f90 pattern: duplicate within a single PRIVATE clause across
+  ! a continuation, with another variable in between.
+  !$acc parallel loop private(x, &
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc&               y, x)
+  do i = 1, 10
+  end do
+
+  ! passThis3.f90 pattern: duplicate across two separate PRIVATE clauses
+  ! on the same directive.
+  !$acc parallel loop private(x) &
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc&              private(y, x)
+  do i = 1, 10
+  end do
+
+  ! Same patterns generalize to FIRSTPRIVATE.
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc parallel loop firstprivate(x, x)
+  do i = 1, 10
+  end do
+
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc parallel loop firstprivate(x) firstprivate(y, x)
+  do i = 1, 10
+  end do
+
+  ! Multiple distinct duplicates on a single directive.
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !WARNING: 'y' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc parallel loop private(x, y, x, y)
+  do i = 1, 10
+  end do
+
+  ! Triple occurrence: two duplicates, both warned, only one survives dedup.
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !WARNING: 'x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+  !$acc parallel loop private(x, x, x)
+  do i = 1, 10
+  end do
+
+  ! Cross-kind duplicates on the same directive remain hard errors.
+  !ERROR: 'x' appears in more than one data-sharing clause on the same OpenACC directive
+  !$acc parallel loop private(x) firstprivate(x)
+  do i = 1, 10
+  end do
+
+  ! Reduction is excluded from the benign case: same-flag duplicates may
+  ! 
diff er in operator, which is a real conflict.
+  !ERROR: 'x' appears in more than one data-sharing clause on the same OpenACC directive
+  !$acc parallel loop reduction(+:x) reduction(*:x)
+  do i = 1, 10
+  end do
+
+  ! Regression coverage for non-bare designators: the dedup machinery only
+  ! examines simple-Name DataRefs, so distinct array elements and array
+  ! sections must pass through untouched, with no warning and no erasure.
+  block
+    integer :: arr(10)
+    integer, target :: t1, t2
+    integer, pointer :: p
+    type :: pt
+      integer :: a
+      integer :: b
+    end type
+    type(pt) :: s
+
+    ! Different array elements -- not duplicates.
+    !$acc parallel loop private(arr(1), arr(2))
+    do i = 1, 10
+    end do
+
+    ! Different array sections -- not duplicates.
+    !$acc parallel loop private(arr(1:5), arr(6:10))
+    do i = 1, 10
+    end do
+
+    ! Same array element listed twice -- not deduped, since GetDesignatorName-
+    ! IfDataRef returns null for ArrayElement and CheckMultipleAppearances
+    ! is never invoked. Compiles without diagnostics.
+    !$acc parallel loop private(arr(1), arr(1))
+    do i = 1, 10
+    end do
+
+    ! Same array section listed twice -- same reasoning, no diagnostic.
+    !$acc parallel loop private(arr(1:5), arr(1:5))
+    do i = 1, 10
+    end do
+
+    ! Distinct structure components -- not duplicates.
+    !$acc parallel loop private(s%a, s%b)
+    do i = 1, 10
+    end do
+
+    ! Mixing a bare-name designator and an array-element designator on the
+    ! same symbol must not trigger dedup -- the array element doesn't go
+    ! through the duplicate check at all.
+    !$acc parallel loop private(arr, arr(1))
+    do i = 1, 10
+    end do
+  end block
+
+end program


        


More information about the flang-commits mailing list