[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