[flang-commits] [flang] [flang][OpenMP] Fix EQUIVALENCE variable privatization in OpenMP (PR #197726)
Caroline Newcombe via flang-commits
flang-commits at lists.llvm.org
Fri May 15 07:21:13 PDT 2026
https://github.com/cenewcombe updated https://github.com/llvm/llvm-project/pull/197726
>From b744cf0321b7004e66f596c52971430b7eeb1cf9 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Thu, 14 May 2026 10:37:46 -0500
Subject: [PATCH 1/2] [flang][OpenMP] Fix EQUIVALENCE variable privatization in
OpenMP
---
flang/lib/Lower/Support/Utils.cpp | 5 +-
.../DelayedPrivatization/equivalence.f90 | 23 ++++------
.../Lower/OpenMP/lastprivate-equivalence.f90 | 46 +++++++++++++++++++
3 files changed, 60 insertions(+), 14 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/lastprivate-equivalence.f90
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index 280968975ea96..865c614cdc6db 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -726,7 +726,10 @@ void privatizeSymbol(
mlir::Value privVal = hsb.getAddr();
mlir::Type allocType = privVal.getType();
- if (!mlir::isa<fir::PointerType>(privVal.getType()))
+ // Only preserve fir.ptr wrapping for true Fortran POINTERs; EQUIVALENCE
+ // aliases also use fir.ptr but need the underlying type for allocation.
+ if (!mlir::isa<fir::PointerType>(privVal.getType()) ||
+ !semantics::IsPointer(ultimate))
allocType = fir::unwrapRefType(privVal.getType());
if (auto poly = mlir::dyn_cast<fir::ClassType>(allocType)) {
diff --git a/flang/test/Lower/OpenMP/DelayedPrivatization/equivalence.f90 b/flang/test/Lower/OpenMP/DelayedPrivatization/equivalence.f90
index 5234862feaa76..4012df8219bb9 100644
--- a/flang/test/Lower/OpenMP/DelayedPrivatization/equivalence.f90
+++ b/flang/test/Lower/OpenMP/DelayedPrivatization/equivalence.f90
@@ -13,24 +13,21 @@ subroutine private_common
!$omp end parallel
end subroutine
-! TODO: the copy region for pointers is incorrect. OpenMP 5.2 says
-!
-! > If the original list item has the POINTER attribute, the new list items
-! > receive the same association status as the original list item
-!
-! Currently the original pointer is unconditionally loaded, which is undefined
-! behavior if that pointer is not associated.
+! Verify that the privatizer's alloc type is the underlying data type (f32),
+! not fir.ptr<f32>. Variables in EQUIVALENCE are lowered with fir.ptr addresses
+! (see castAliasToPointer in ConvertVariable.cpp), but they are not Fortran
+! POINTERs. The privatizer must allocate storage for the actual data type.
-! CHECK: omp.private {type = firstprivate} @[[X_PRIVATIZER:.*]] : ![[X_TYPE:fir.ptr<f32>]] copy {
-! CHECK: ^bb0(%[[ORIG_PTR:.*]]: ![[X_TYPE]], %[[PRIV_REF:.*]]: ![[X_TYPE]]):
+! CHECK: omp.private {type = firstprivate} @[[X_PRIVATIZER:.*]] : [[ALLOC_TYPE:f32]] copy {
+! CHECK: ^bb0(%[[ORIG_PTR:.*]]: ![[PTR_TYPE:fir.ptr<f32>]], %[[PRIV_REF:.*]]: ![[PTR_TYPE]]):
! CHECK: %[[ORIG_VAL:.*]] = fir.load %[[ORIG_PTR]] : !fir.ptr<f32>
-! CHECK: hlfir.assign %[[ORIG_VAL]] to %[[PRIV_REF]] : f32, ![[X_TYPE]]
-! CHECK: omp.yield(%[[PRIV_REF]] : ![[X_TYPE]])
+! CHECK: hlfir.assign %[[ORIG_VAL]] to %[[PRIV_REF]] : f32, ![[PTR_TYPE]]
+! CHECK: omp.yield(%[[PRIV_REF]] : ![[PTR_TYPE]])
! CHECK: }
! CHECK: func.func @_QPprivate_common() {
-! CHECK: omp.parallel private(@[[X_PRIVATIZER]] %{{.*}}#0 -> %[[PRIV_ARG:.*]] : ![[X_TYPE]]) {
-! CHECK: %[[REG_DECL:.*]]:2 = hlfir.declare %[[PRIV_ARG]] {{{.*}}} : (![[X_TYPE]]) -> ({{.*}})
+! CHECK: omp.parallel private(@[[X_PRIVATIZER]] %{{.*}}#0 -> %[[PRIV_ARG:.*]] : ![[PTR_TYPE]]) {
+! CHECK: %[[REG_DECL:.*]]:2 = hlfir.declare %[[PRIV_ARG]] {{{.*}}} : (![[PTR_TYPE]]) -> ({{.*}})
! CHECK: %[[CST:.*]] = arith.constant {{.*}}
! CHECK: hlfir.assign %[[CST]] to %[[REG_DECL]]#0 : {{.*}}
! CHECK: omp.terminator
diff --git a/flang/test/Lower/OpenMP/lastprivate-equivalence.f90 b/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
new file mode 100644
index 0000000000000..c9a8f5318e98b
--- /dev/null
+++ b/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
@@ -0,0 +1,46 @@
+! Test that EQUIVALENCE'd arrays are correctly privatized in OpenMP.
+! Variables in EQUIVALENCE are lowered with fir.ptr addresses (see
+! castAliasToPointer in ConvertVariable.cpp), but they are not Fortran
+! POINTERs. The privatizer must allocate storage for the actual array type.
+
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+
+! Verify the privatizer operates on a box (array) type, not a fir.ptr type,
+! and that it allocates the full array.
+!CHECK: omp.private {type = private} @[[A_PRIV:.*Ea_private.*]] : !fir.box<!fir.array<10xi32>> init {
+!CHECK: %[[SHAPE:.*]] = fir.shape %{{.*}} : (index) -> !fir.shape<1>
+!CHECK: %[[ALLOC:.*]] = fir.allocmem !fir.array<10xi32>
+!CHECK: omp.yield
+! Verify the dealloc region frees the allocated memory.
+!CHECK: } dealloc {
+!CHECK: fir.freemem %{{.*}} : !fir.heap<!fir.array<10xi32>>
+!CHECK: omp.yield
+!CHECK: }
+
+!CHECK-LABEL: func.func @_QPlastprivate_equivalence()
+!CHECK: %[[AGG:.*]] = fir.alloca !fir.array<40xi8>
+!CHECK: %[[A_PTR:.*]] = fir.convert %{{.*}} : (!fir.ref<i8>) -> !fir.ptr<!fir.array<10xi32>>
+!CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[A_PTR]](%{{.*}}) storage(%[[AGG]][0]) {uniq_name = "_QFlastprivate_equivalenceEa"}
+!CHECK: omp.parallel {
+!CHECK: omp.wsloop private(@[[A_PRIV]] %{{.*}}
+!CHECK: omp.loop_nest
+! Verify lastprivate writeback copies the private array to the original
+! EQUIVALENCE alias address.
+!CHECK: fir.if %{{.*}} {
+!CHECK: %[[PRIV_BOX:.*]] = fir.load %{{.*}} : !fir.ref<!fir.box<!fir.array<10xi32>>>
+!CHECK: hlfir.assign %[[PRIV_BOX]] to %[[A_DECL]]#0 : !fir.box<!fir.array<10xi32>>, !fir.ptr<!fir.array<10xi32>>
+!CHECK: }
+!CHECK: omp.yield
+!CHECK: }
+!CHECK: }
+!CHECK: omp.terminator
+!CHECK: }
+subroutine lastprivate_equivalence
+ integer :: a(10), b(1)
+ equivalence (a(1), b(1))
+ !$omp parallel do lastprivate(a)
+ do i = 1, 10
+ a(:) = i
+ end do
+ !$omp end parallel do
+end subroutine
>From 030caf006f94f350d252b23bb2a26d756be9c370 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Fri, 15 May 2026 09:20:56 -0500
Subject: [PATCH 2/2] Add CHARACTER test case
---
.../Lower/OpenMP/lastprivate-equivalence.f90 | 38 ++++++++++++++++---
1 file changed, 33 insertions(+), 5 deletions(-)
diff --git a/flang/test/Lower/OpenMP/lastprivate-equivalence.f90 b/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
index c9a8f5318e98b..829159c762ffe 100644
--- a/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
+++ b/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
@@ -1,11 +1,14 @@
-! Test that EQUIVALENCE'd arrays are correctly privatized in OpenMP.
-! Variables in EQUIVALENCE are lowered with fir.ptr addresses (see
-! castAliasToPointer in ConvertVariable.cpp), but they are not Fortran
-! POINTERs. The privatizer must allocate storage for the actual array type.
+! Test that EQUIVALENCE'd variables with lastprivate are correctly privatized.
+! EQUIVALENCE aliases use fir.ptr (via castAliasToPointer), but are not true
+! Fortran POINTERs. The privatizer must unwrap fir.ptr and allocate the
+! underlying data type.
!RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
-! Verify the privatizer operates on a box (array) type, not a fir.ptr type,
+! Privatizers appear at module scope; second subroutine's privatizer comes first.
+!CHECK: omp.private {type = private} @[[C1_PRIV:.*Ec1_private.*]] : !fir.char<1,10>
+
+! Verify the array privatizer operates on a box type, not a fir.ptr type,
! and that it allocates the full array.
!CHECK: omp.private {type = private} @[[A_PRIV:.*Ea_private.*]] : !fir.box<!fir.array<10xi32>> init {
!CHECK: %[[SHAPE:.*]] = fir.shape %{{.*}} : (index) -> !fir.shape<1>
@@ -44,3 +47,28 @@ subroutine lastprivate_equivalence
end do
!$omp end parallel do
end subroutine
+
+!CHECK-LABEL: func.func @_QPlastprivate_equiv_char()
+!CHECK: %[[CAGG:.*]] = fir.alloca !fir.array<10xi8>
+!CHECK: %[[C1_PTR:.*]] = fir.convert %{{.*}} : (!fir.ref<i8>) -> !fir.ptr<!fir.char<1,10>>
+!CHECK: %[[C1_DECL:.*]]:2 = hlfir.declare %[[C1_PTR]] typeparams %{{.*}} storage(%[[CAGG]][0]) {uniq_name = "_QFlastprivate_equiv_charEc1"}
+!CHECK: omp.parallel {
+!CHECK: omp.wsloop private(@[[C1_PRIV]] %{{.*}}
+!CHECK: omp.loop_nest
+!CHECK: fir.if %{{.*}} {
+!CHECK: hlfir.assign %{{.*}} to %[[C1_DECL]]#0 : !fir.ptr<!fir.char<1,10>>, !fir.ptr<!fir.char<1,10>>
+!CHECK: }
+!CHECK: omp.yield
+!CHECK: }
+!CHECK: }
+!CHECK: omp.terminator
+!CHECK: }
+subroutine lastprivate_equiv_char
+ character(10) :: c1, c2
+ equivalence (c1, c2)
+ !$omp parallel do lastprivate(c1)
+ do i = 1, 4
+ c1 = 'test'
+ end do
+ !$omp end parallel do
+end subroutine
More information about the flang-commits
mailing list