[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