[flang-commits] [flang] [flang][OpenMP] Fix EQUIVALENCE variable privatization in OpenMP (PR #197726)
via flang-commits
flang-commits at lists.llvm.org
Thu May 14 08:51:32 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-openmp
Author: Caroline Newcombe (cenewcombe)
<details>
<summary>Changes</summary>
Fixes #<!-- -->197553
EQUIVALENCE aliases are lowered with `fir.ptr` addresses (`castAliasToPointer` in ConvertVariable.cpp) to inform alias analysis. However, `privatizeSymbol()` in Utils.cpp treated all `fir::PointerType` values as true Fortran POINTERs, skipping the `unwrapRefType` that computes the correct allocation type. For arrays, this caused the privatizer to allocate pointer-sized storage instead of the full array, resulting in stack buffer overflows at runtime.
The fix adds a `!semantics::IsPointer()` check so that only true Fortran POINTERs preserve the `fir.ptr` wrapping. EQUIVALENCE aliases are correctly unwrapped to their underlying type.
**Changes:**
- flang/lib/Lower/Support/Utils.cpp: Gate the `PointerType` guard on `semantics::IsPointer` to distinguish true POINTERs from EQUIVALENCE aliases
- flang/test/Lower/OpenMP/DelayedPrivatization/equivalence.f90: Update existing test to match corrected output (alloc type is now `f32`, not `fir.ptr<f32>`)
- flang/test/Lower/OpenMP/lastprivate-equivalence.f90: New test for EQUIVALENCE'd array with lastprivate, verifying correct allocation, deallocation, and writeback
---
Full diff: https://github.com/llvm/llvm-project/pull/197726.diff
3 Files Affected:
- (modified) flang/lib/Lower/Support/Utils.cpp (+4-1)
- (modified) flang/test/Lower/OpenMP/DelayedPrivatization/equivalence.f90 (+10-13)
- (added) flang/test/Lower/OpenMP/lastprivate-equivalence.f90 (+46)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/197726
More information about the flang-commits
mailing list