[flang-commits] [flang] 2c978fa - [flang][OpenMP] Avoid boxing constant-size trivial private arrays (#208315)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 15 06:23:01 PDT 2026
Author: Caroline Newcombe
Date: 2026-07-15T09:22:56-04:00
New Revision: 2c978fa06d19095c0003508b8dac084689b12e78
URL: https://github.com/llvm/llvm-project/commit/2c978fa06d19095c0003508b8dac084689b12e78
DIFF: https://github.com/llvm/llvm-project/commit/2c978fa06d19095c0003508b8dac084689b12e78.diff
LOG: [flang][OpenMP] Avoid boxing constant-size trivial private arrays (#208315)
Part of #208086. `privatizeSymbol` (used for both OpenMP privatization
and `do concurrent` locals) currently boxes every array as a workaround
for `fir.array` not being directly `alloca`-able in the OpenMP→LLVMIR
translation. This PR carves out the common case: a constant-shape array
of trivial intrinsic elements (e.g. `real(8) :: xx(3)`) is now
privatized unboxed, as a plain `fir.array`.
Added:
flang/test/Lower/OpenMP/private-array-boxing.f90
Modified:
flang/lib/Lower/Support/Utils.cpp
flang/test/Integration/OpenMP/copyprivate.f90
flang/test/Integration/OpenMP/private-global.f90
flang/test/Lower/OpenMP/DelayedPrivatization/target-teams-distribute-private-adjustable-array.f90
flang/test/Lower/OpenMP/copyprivate.f90
flang/test/Lower/OpenMP/delayed-privatization-array.f90
flang/test/Lower/OpenMP/lastprivate-equivalence.f90
flang/test/Lower/OpenMP/loop-directive.f90
flang/test/Lower/OpenMP/parallel-private-clause.f90
flang/test/Lower/OpenMP/private-commonblock.f90
flang/test/Lower/do_concurrent_local_assoc_entity.f90
Removed:
################################################################################
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index 6d38e1ff550a4..ef3ab0afa1f39 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -741,15 +741,25 @@ void privatizeSymbol(
TODO(symLoc, "create polymorphic host associated copy");
}
- // fir.array<> cannot be converted to any single llvm type and fir helpers
- // are not available in openmp to llvmir translation so we cannot generate
- // an alloca for a fir.array type there. Get around this by boxing all
- // arrays.
- if (mlir::isa<fir::SequenceType>(allocType)) {
- hlfir::Entity entity{hsb.getAddr()};
- entity = genVariableBox(symLoc, firOpBuilder, entity);
- privVal = entity.getBase();
- allocType = privVal.getType();
+ // fir.array<> has no single LLVM type and the openmp-to-llvmir translation
+ // cannot build an alloca for it, so arrays are boxed here. Exception: a
+ // constant-shape array of trivial elements (e.g. real(8)::xx(3)) maps to a
+ // fixed-size LLVM array the translation can stack-allocate, so it is left
+ // unboxed -- avoiding a per-thread heap allocation and letting alias analysis
+ // prove the private does not alias dummy arguments. Firstprivate,
+ // dynamic-extent, unknown-shape, character and derived-type arrays stay
+ // boxed, as do arrays whose HLFIR variable is already a descriptor and so
+ // never reach this branch as a fir.array (e.g. non-default lower bounds).
+ if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(allocType)) {
+ bool requiresBox = emitCopyRegion || seqTy.hasUnknownShape() ||
+ seqTy.hasDynamicExtents() ||
+ !fir::isa_trivial(seqTy.getEleTy());
+ if (requiresBox) {
+ hlfir::Entity entity{hsb.getAddr()};
+ entity = genVariableBox(symLoc, firOpBuilder, entity);
+ privVal = entity.getBase();
+ allocType = privVal.getType();
+ }
}
if (mlir::isa<fir::BaseBoxType>(privVal.getType())) {
diff --git a/flang/test/Integration/OpenMP/copyprivate.f90 b/flang/test/Integration/OpenMP/copyprivate.f90
index 7b4de32bb5cc7..a8146bbb72648 100644
--- a/flang/test/Integration/OpenMP/copyprivate.f90
+++ b/flang/test/Integration/OpenMP/copyprivate.f90
@@ -9,15 +9,15 @@
!RUN: %flang_fc1 -emit-llvm -fopenmp %s -o - | FileCheck %s
!CHECK-DAG: define internal void @_copy_ref_box_Uxi32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
-!CHECK-DAG: define internal void @_copy_ref_box_10xi32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
+!CHECK-DAG: define internal void @_copy_ref_10xi32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_i64(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_box_Uxi64(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_f32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
-!CHECK-DAG: define internal void @_copy_ref_box_2x3xf32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
+!CHECK-DAG: define internal void @_copy_ref_2x3xf32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_z32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
-!CHECK-DAG: define internal void @_copy_ref_box_10xz32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
+!CHECK-DAG: define internal void @_copy_ref_10xz32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_l32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
-!CHECK-DAG: define internal void @_copy_ref_box_5xl32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
+!CHECK-DAG: define internal void @_copy_ref_5xl32(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_c8x8(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_box_10xc8x8(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
!CHECK-DAG: define internal void @_copy_ref_c16x5(ptr {{[^%]*}}%{{.*}}, ptr {{[^%]*}}%{{.*}})
diff --git a/flang/test/Integration/OpenMP/private-global.f90 b/flang/test/Integration/OpenMP/private-global.f90
index ed11a95c4aeb1..56310a3ca21a4 100644
--- a/flang/test/Integration/OpenMP/private-global.f90
+++ b/flang/test/Integration/OpenMP/private-global.f90
@@ -17,34 +17,22 @@ program bug
! CHECK-LABEL: define internal void {{.*}}..omp_par(
! CHECK: omp.par.entry:
-! CHECK: %[[VAL_9:.*]] = alloca i32, align 4
-! CHECK: %[[VAL_10:.*]] = load i32, ptr %[[VAL_11:.*]], align 4
-! CHECK: store i32 %[[VAL_10]], ptr %[[VAL_9]], align 4
-! CHECK: %[[VAL_12:.*]] = load i32, ptr %[[VAL_9]], align 4
-! CHECK: %[[PRIV_BOX_ALLOC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
-! CHECK: %[[ELEMENTAL_TMP:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
-! CHECK: %[[ELEMENTAL_TMP_2:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
-! CHECK: %[[TABLE_BOX_ADDR:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
-! CHECK: %[[BOXED_FIFTY:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }, align 8
-! CHECK: %[[FIFTY:.*]] = alloca i32, i64 1, align 4
-! CHECK: %[[INTERMEDIATE:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
-! CHECK: %[[TABLE_BOX_ADDR2:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, i64 1, align 8
-! ...
-! check that we use the private copy of table for the assignment
+! The private copy of `table` is a constant-size array of trivial elements, so
+! it is privatized unboxed as a plain [10 x i32] on the stack -- no descriptor
+! and no heap allocation for the private itself.
+! CHECK: %[[PRIV_TABLE:.*]] = alloca [10 x i32], align 4
+
+! check that we use the private copy of table for the assignment: its data
+! pointer is placed in a descriptor which -- threaded through the store and
+! memcpy below -- is the destination handed to the runtime assign.
! CHECK: omp.par.region1:
-! CHECK: call void @llvm.memcpy.p0.p0.i32(ptr{{.*}}%[[INTERMEDIATE]], ptr{{.*}}%[[PRIV_BOX_ALLOC]], i32 {{4[48]}}, i1 false)
-! CHECK: store i32 50, ptr %[[FIFTY]], align 4
-! CHECK: %[[FIFTY_BOX_VAL:.*]] = insertvalue { ptr, i64, i32, i8, i8, i8, i8 } { ptr undef, i64 4, i32 20240719, i8 0, i8 9, i8 0, i8 0 }, ptr %[[FIFTY]], 0
-! CHECK: store { ptr, i64, i32, i8, i8, i8, i8 } %[[FIFTY_BOX_VAL]], ptr %[[BOXED_FIFTY]], align {{[48]}}
-! CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[TABLE_BOX_ADDR2]], ptr %[[INTERMEDIATE]], i32 {{4[48]}}, i1 false)
-! CHECK: call void @_FortranAAssign(ptr %[[TABLE_BOX_ADDR2]], ptr %[[BOXED_FIFTY]], ptr @{{.*}}, i32 9)
-! CHECK: call void @llvm.memcpy.p0.p0.i32(ptr{{.*}}%[[TABLE_BOX_ADDR]], ptr{{.*}}%[[PRIV_BOX_ALLOC]], i32 {{4[48]}}, i1 false)
-! CHECK: %[[PRIV_TABLE:.*]] = call ptr @malloc(i{{(32)|(64)}} 40)
-! ...
+! CHECK: %[[TABLE_DESC:.*]] = insertvalue {{.*}}, ptr %[[PRIV_TABLE]], 0
+! CHECK: store {{.*}} %[[TABLE_DESC]], ptr %[[DESC_MEM:.*]], align
+! CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[ASSIGN_DST:.*]], ptr %[[DESC_MEM]], i32 {{.*}}, i1 false)
+! CHECK: call void @_FortranAAssign(ptr %[[ASSIGN_DST]],
+
! check that we use the private copy of table for table/=50
! CHECK: omp.par.region3:
-! CHECK: %[[VAL_44:.*]] = sub nuw nsw i64 %{{.*}}, 1
-! CHECK: %[[VAL_45:.*]] = mul nuw nsw i64 %[[VAL_44]], 1
-! CHECK: %[[VAL_46:.*]] = mul nuw nsw i64 %[[VAL_45]], 1
-! CHECK: %[[VAL_47:.*]] = add nuw nsw i64 %[[VAL_46]], 0
-! CHECK: %[[VAL_48:.*]] = getelementptr nusw nuw i32, ptr %[[PRIV_TABLE]], i64 %[[VAL_47]]
+! CHECK: %[[ELT_PTR:.*]] = getelementptr {{.*}} i32, ptr %[[PRIV_TABLE]], i64 %{{.*}}
+! CHECK: %[[ELT:.*]] = load i32, ptr %[[ELT_PTR]]
+! CHECK: icmp ne i32 %[[ELT]], 50
diff --git a/flang/test/Lower/OpenMP/DelayedPrivatization/target-teams-distribute-private-adjustable-array.f90 b/flang/test/Lower/OpenMP/DelayedPrivatization/target-teams-distribute-private-adjustable-array.f90
index ac2b957c9d1c9..1396b97dd3ba4 100644
--- a/flang/test/Lower/OpenMP/DelayedPrivatization/target-teams-distribute-private-adjustable-array.f90
+++ b/flang/test/Lower/OpenMP/DelayedPrivatization/target-teams-distribute-private-adjustable-array.f90
@@ -1,5 +1,6 @@
-! Test that GPU delayed privatization allocates dynamic private arrays for
-! target teams distribute parallel do on the heap and emits cleanup.
+! Test GPU delayed privatization for target teams distribute parallel do:
+! a dynamic-extent private array is heap-allocated (with cleanup), while a
+! constant-size private array is privatized unboxed as a plain fir.array.
! RUN: %if amdgpu-registered-target %{ \
! RUN: %flang_fc1 -triple amdgcn-amd-amdhsa -emit-hlfir \
@@ -41,10 +42,10 @@ subroutine static_private_tmp(b)
! CHECK: warning: {{.*}}OpenMP private dynamic array 'tmp' on a GPU target may exceed stack memory; using device heap allocation instead, which can severely degrade performance
-! CHECK-LABEL: omp.private {type = private} @{{.*}}Etmp_private_box_64xf32 : !fir.box<!fir.array<64xf32>> init {
-! CHECK-NOT: fir.allocmem
-! CHECK: fir.alloca !fir.array<64xf32>
-! CHECK: omp.yield
+! A static (constant-size, trivial-element) private array is privatized unboxed
+! as a plain fir.array -- no descriptor and no init region, even on a GPU target.
+! The trailing end-of-line anchor confirms there is no `init {` region:
+! CHECK: omp.private {type = private} @{{.*}}Etmp_private_64xf32 : !fir.array<64xf32>{{$}}
! CHECK-LABEL: omp.private {type = private} @{{.*}}Etmp_private_heap_box_Uxf32 : !fir.box<!fir.array<?xf32>> init {
! CHECK: %[[DIMS:.*]]:3 = fir.box_dims
diff --git a/flang/test/Lower/OpenMP/copyprivate.f90 b/flang/test/Lower/OpenMP/copyprivate.f90
index f3e6a76d95acb..365c24f17622a 100644
--- a/flang/test/Lower/OpenMP/copyprivate.f90
+++ b/flang/test/Lower/OpenMP/copyprivate.f90
@@ -14,11 +14,11 @@
!CHECK-DAG: func private @_copy_ref_c16x8(%{{.*}}: !fir.ref<!fir.char<2,8>>, %{{.*}}: !fir.ref<!fir.char<2,8>>)
!CHECK-DAG: func private @_copy_ref_box_Uxi32(%{{.*}}: !fir.ref<!fir.box<!fir.array<?xi32>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<?xi32>>>)
-!CHECK-DAG: func private @_copy_ref_box_10xi32(%{{.*}}: !fir.ref<!fir.box<!fir.array<10xi32>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<10xi32>>>)
-!CHECK-DAG: func private @_copy_ref_box_3x4xi32(%{{.*}}: !fir.ref<!fir.box<!fir.array<3x4xi32>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<3x4xi32>>>)
-!CHECK-DAG: func private @_copy_ref_box_10xf32(%{{.*}}: !fir.ref<!fir.box<!fir.array<10xf32>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<10xf32>>>)
-!CHECK-DAG: func private @_copy_ref_box_3x4xz32(%{{.*}}: !fir.ref<!fir.box<!fir.array<3x4xcomplex<f32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<3x4xcomplex<f32>>>>)
-!CHECK-DAG: func private @_copy_ref_box_10xl32(%{{.*}}: !fir.ref<!fir.box<!fir.array<10x!fir.logical<4>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<10x!fir.logical<4>>>>)
+!CHECK-DAG: func private @_copy_ref_10xi32(%{{.*}}: !fir.ref<!fir.array<10xi32>>, %{{.*}}: !fir.ref<!fir.array<10xi32>>)
+!CHECK-DAG: func private @_copy_ref_3x4xi32(%{{.*}}: !fir.ref<!fir.array<3x4xi32>>, %{{.*}}: !fir.ref<!fir.array<3x4xi32>>)
+!CHECK-DAG: func private @_copy_ref_10xf32(%{{.*}}: !fir.ref<!fir.array<10xf32>>, %{{.*}}: !fir.ref<!fir.array<10xf32>>)
+!CHECK-DAG: func private @_copy_ref_3x4xz32(%{{.*}}: !fir.ref<!fir.array<3x4xcomplex<f32>>>, %{{.*}}: !fir.ref<!fir.array<3x4xcomplex<f32>>>)
+!CHECK-DAG: func private @_copy_ref_10xl32(%{{.*}}: !fir.ref<!fir.array<10x!fir.logical<4>>>, %{{.*}}: !fir.ref<!fir.array<10x!fir.logical<4>>>)
!CHECK-DAG: func private @_copy_ref_box_3xc8x8(%{{.*}}: !fir.ref<!fir.box<!fir.array<3x!fir.char<1,8>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<3x!fir.char<1,8>>>>)
!CHECK-DAG: func private @_copy_ref_box_3xc16x5(%{{.*}}: !fir.ref<!fir.box<!fir.array<3x!fir.char<2,5>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.array<3x!fir.char<2,5>>>>)
@@ -104,7 +104,7 @@ subroutine test_scalar()
!CHECK: %[[L1:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_arrayEl1"}
!CHECK: %[[S1:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFtest_arrayEs1"}
!CHECK: %[[S2:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFtest_arrayEs2"}
-!CHECK: omp.single copyprivate(%[[A]]#0 -> @_copy_ref_box_Uxi32 : {{.*}}, %[[I1]]#0 -> @_copy_ref_box_10xi32 : {{.*}}, %[[I2]]#0 -> @_copy_ref_box_3x4xi32 : {{.*}}, %[[I3]]#0 -> @_copy_ref_box_Uxi32 : {{.*}}, %[[R1]]#0 -> @_copy_ref_box_10xf32 : {{.*}}, %[[C1]]#0 -> @_copy_ref_box_3x4xz32 : {{.*}}, %[[L1]]#0 -> @_copy_ref_box_10xl32 : {{.*}}, %[[S1]]#0 -> @_copy_ref_box_3xc8x8 : {{.*}}, %[[S2]]#0 -> @_copy_ref_box_3xc16x5 : {{.*}})
+!CHECK: omp.single copyprivate(%[[A]]#0 -> @_copy_ref_box_Uxi32 : {{.*}}, %[[I1]]#0 -> @_copy_ref_10xi32 : {{.*}}, %[[I2]]#0 -> @_copy_ref_3x4xi32 : {{.*}}, %[[I3]]#0 -> @_copy_ref_box_Uxi32 : {{.*}}, %[[R1]]#0 -> @_copy_ref_10xf32 : {{.*}}, %[[C1]]#0 -> @_copy_ref_3x4xz32 : {{.*}}, %[[L1]]#0 -> @_copy_ref_10xl32 : {{.*}}, %[[S1]]#0 -> @_copy_ref_box_3xc8x8 : {{.*}}, %[[S2]]#0 -> @_copy_ref_box_3xc16x5 : {{.*}})
subroutine test_array(a, n)
integer :: a(:), n
integer :: i1(10), i2(3, 4), i3(n)
diff --git a/flang/test/Lower/OpenMP/delayed-privatization-array.f90 b/flang/test/Lower/OpenMP/delayed-privatization-array.f90
index 81df53dc51aca..43df825cac9f7 100644
--- a/flang/test/Lower/OpenMP/delayed-privatization-array.f90
+++ b/flang/test/Lower/OpenMP/delayed-privatization-array.f90
@@ -102,17 +102,8 @@ program main
!$omp end parallel
end program
+! A constant-size, default-lower-bound, trivial-element private array is
+! privatized unboxed as a plain fir.array with no init region. The trailing
+! end-of-line anchor confirms there is no `init {` region:
! ONE_DIM_DEFAULT_LB-LABEL: omp.private {type = private}
-! ONE_DIM_DEFAULT_LB-SAME: @[[PRIVATIZER_SYM:.*]] : [[BOX_TYPE:!fir.box<!fir.array<10xi32>>]] init {
-
-! ONE_DIM_DEFAULT_LB-NEXT: ^bb0(%[[PRIV_ARG:.*]]: [[TYPE:!fir.ref<!fir.box<!fir.array<10xi32>>>]], %[[PRIV_BOX_ALLOC:.*]]: [[TYPE]]):
-! ONE_DIM_DEFAULT_LB-NEXT: %[[C10:.*]] = arith.constant 10 : index
-! ONE_DIM_DEFAULT_LB-NEXT: %[[SHAPE:.*]] = fir.shape %[[C10]]
-! ONE_DIM_DEFAULT_LB-NEXT: %[[ARRAY_ALLOC:.*]] = fir.allocmem !fir.array<10xi32>
-! ONE_DIM_DEFAULT_LB-NEXT: %[[DECL:.*]]:2 = hlfir.declare %[[ARRAY_ALLOC]](%[[SHAPE]])
-! ONE_DIM_DEFAULT_LB-NEXT: %[[ONE:.*]] = arith.constant 1 : index
-! ONE_DIM_DEFAULT_LB-NEXT: %[[TEN:.*]] = arith.constant 10 : index
-! ONE_DIM_DEFAULT_LB-NEXT: %[[SHAPE_SHIFT:.*]] = fir.shape_shift %[[ONE]], %[[TEN]]
-! ONE_DIM_DEFAULT_LB-NEXT: %[[EMBOX:.*]] = fir.embox %[[DECL]]#0(%[[SHAPE_SHIFT]])
-! ONE_DIM_DEFAULT_LB-NEXT: fir.store %[[EMBOX]] to %[[PRIV_BOX_ALLOC]]
-! ONE_DIM_DEFAULT_LB-NEXT: omp.yield(%[[PRIV_BOX_ALLOC]] : [[TYPE]])
+! ONE_DIM_DEFAULT_LB-SAME: @{{.*}} : !fir.array<10xi32>{{$}}
diff --git a/flang/test/Lower/OpenMP/lastprivate-equivalence.f90 b/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
index 829159c762ffe..ddee17beb6378 100644
--- a/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
+++ b/flang/test/Lower/OpenMP/lastprivate-equivalence.f90
@@ -8,17 +8,11 @@
! 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>
-!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: }
+! The array privatizer operates on the unwrapped fir.ptr data type. A constant-
+! shape, trivial-element array privatizes unboxed as a plain fir.array (no
+! descriptor, no init/dealloc region). The end-of-line anchor confirms there is
+! no `init {` region.
+!CHECK: omp.private {type = private} @[[A_PRIV:.*Ea_private.*]] : !fir.array<10xi32>{{$}}
!CHECK-LABEL: func.func @_QPlastprivate_equivalence()
!CHECK: %[[AGG:.*]] = fir.alloca !fir.array<40xi8>
@@ -29,9 +23,9 @@
!CHECK: omp.loop_nest
! Verify lastprivate writeback copies the private array to the original
! EQUIVALENCE alias address.
+!CHECK: %[[A_PRIV_DECL:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "{{.*}}Ea"}
!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: hlfir.assign %[[A_PRIV_DECL]]#0 to %[[A_DECL]]#0 : !fir.ptr<!fir.array<10xi32>>, !fir.ptr<!fir.array<10xi32>>
!CHECK: }
!CHECK: omp.yield
!CHECK: }
diff --git a/flang/test/Lower/OpenMP/loop-directive.f90 b/flang/test/Lower/OpenMP/loop-directive.f90
index 05770c029286d..c74cc85db296a 100644
--- a/flang/test/Lower/OpenMP/loop-directive.f90
+++ b/flang/test/Lower/OpenMP/loop-directive.f90
@@ -150,7 +150,7 @@ subroutine test_standalone_bind_teams
num = N
! CHECK: omp.distribute
- ! CHECK-SAME: private(@{{.*}}Ea_private_box_100000xi32 {{[^,]*}},
+ ! CHECK-SAME: private(@{{.*}}Ea_private_100000xi32 {{[^,]*}},
! CHECK-SAME: @{{.*}}Ei_private_i32 {{.*}} : {{.*}}) {
! CHECK: omp.loop_nest {{.*}} {
! CHECK: }
@@ -170,7 +170,7 @@ subroutine test_standalone_bind_parallel
num = N
! CHECK: omp.wsloop
- ! CHECK-SAME: private(@{{.*}}Ea_private_box_100000xi32 {{[^,]*}},
+ ! CHECK-SAME: private(@{{.*}}Ea_private_100000xi32 {{[^,]*}},
! CHECK-SAME: @{{.*}}Ei_private_i32 {{.*}} : {{.*}}) {
! CHECK: omp.loop_nest {{.*}} {
! CHECK: }
diff --git a/flang/test/Lower/OpenMP/parallel-private-clause.f90 b/flang/test/Lower/OpenMP/parallel-private-clause.f90
index 12cbda1c3d572..5dffa4d1f54bf 100644
--- a/flang/test/Lower/OpenMP/parallel-private-clause.f90
+++ b/flang/test/Lower/OpenMP/parallel-private-clause.f90
@@ -33,11 +33,11 @@
!FIRDialect: omp.parallel private(@{{.*}} %[[ALPHA_DECL]]#0 -> %[[ALPHA_PVT:.*]], @{{.*}} %{{.*}} -> %[[ALPHA_ARRAY_PVT:.*]], @{{.*}} %{{.*}}#0 -> %[[BETA_PVT:.*]], @{{.*}} %{{.*}} -> %[[BETA_ARRAY_PVT:.*]], @{{.*}} %{{.*}}#0 -> %[[ARG1_PVT:.*]], @{{.*}} %{{.*}} -> %[[ARG2_PVT:.*]], @{{.*}} %{{.*}}#0 -> %[[ARG3_PVT:.*]], @{{.*}} %{{.*}} -> %[[ARG4_PVT:.*]] : {{.*}}) {
!FIRDialect-DAG: %[[ALPHA_PVT_DECL:.*]]:2 = hlfir.declare %[[ALPHA_PVT]] {uniq_name = "{{.*}}alpha"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-!FIRDialect-DAG: %[[ALPHA_ARRAY_PVT_DECL:.*]]:2 = hlfir.declare %[[ALPHA_ARRAY_PVT]] {uniq_name = "{{.*}}alpha_array"} :
+!FIRDialect-DAG: %[[ALPHA_ARRAY_PVT_DECL:.*]]:2 = hlfir.declare %[[ALPHA_ARRAY_PVT]](%{{.*}}) {uniq_name = "{{.*}}alpha_array"} :
!FIRDialect-DAG: %[[BETA_PVT_DECL:.*]]:2 = hlfir.declare %[[BETA_PVT]] typeparams {{.*}} {uniq_name = "{{.*}}beta"} : (!fir.ref<!fir.char<1,5>>, index) -> (!fir.ref<!fir.char<1,5>>, !fir.ref<!fir.char<1,5>>)
!FIRDialect-DAG: %[[BETA_ARRAY_PVT_DECL:.*]]:2 = hlfir.declare %[[BETA_ARRAY_PVT]] {uniq_name = "{{.*}}beta_array"} :
!FIRDialect-DAG: %[[ARG1_PVT_DECL:.*]]:2 = hlfir.declare %[[ARG1_PVT]] {uniq_name = "{{.*}}arg1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-!FIRDialect-DAG: %[[ARG2_PVT_DECL:.*]]:2 = hlfir.declare %[[ARG2_PVT]] {uniq_name = "{{.*}}arg2"} :
+!FIRDialect-DAG: %[[ARG2_PVT_DECL:.*]]:2 = hlfir.declare %[[ARG2_PVT]](%{{.*}}) {uniq_name = "{{.*}}arg2"} :
!FIRDialect-DAG: %[[ARG3_PVT_DECL:.*]]:2 = hlfir.declare %[[ARG3_PVT]] typeparams {{.*}} {uniq_name = "{{.*}}arg3"} : (!fir.ref<!fir.char<1,5>>, index) -> (!fir.ref<!fir.char<1,5>>, !fir.ref<!fir.char<1,5>>)
!FIRDialect-DAG: %[[ARG4_PVT_DECL:.*]]:2 = hlfir.declare %[[ARG4_PVT]] {uniq_name = "{{.*}}arg4"} :
!FIRDialect: omp.terminator
diff --git a/flang/test/Lower/OpenMP/private-array-boxing.f90 b/flang/test/Lower/OpenMP/private-array-boxing.f90
new file mode 100644
index 0000000000000..cf4a8aa0a2c64
--- /dev/null
+++ b/flang/test/Lower/OpenMP/private-array-boxing.f90
@@ -0,0 +1,48 @@
+! Test the array boxing decision made when privatizing a symbol for an OpenMP
+! data-sharing clause (privatizeSymbol in flang/lib/Lower/Support/Utils.cpp).
+!
+! A constant-shape array of trivial intrinsic elements is privatized *unboxed*
+! (as a plain fir.array), so the OpenMP-to-LLVMIR translation can allocate it
+! directly on the stack and no per-thread heap allocation / descriptor is
+! needed. Arrays that cannot be handled that way -- character arrays,
+! dynamic-extent arrays, firstprivate arrays (which need a copy region), and
+! arrays with non-default lower bounds (whose HLFIR variable is a box that
+! carries the lower bounds) -- keep being boxed.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+subroutine test_private_array_boxing(n)
+ integer :: n, i
+ real(8) :: trivial_const(3)
+ character(4) :: char_arr(2)
+ real(8) :: dyn_arr(n)
+ real(8) :: first_const(3)
+ real(8) :: nondef_lb(0:2)
+ first_const = 1.0d0
+!$omp parallel do private(trivial_const, char_arr, dyn_arr, nondef_lb) firstprivate(first_const)
+ do i = 1, n
+ trivial_const = 1.0d0
+ char_arr = "abcd"
+ dyn_arr = 1.0d0
+ nondef_lb = 1.0d0
+ end do
+end subroutine
+
+! A constant-shape array of trivial elements is privatized unboxed. The trailing
+! end-of-line anchor checks there is no `init {` region on this privatizer, i.e.
+! no per-thread initialization / heap allocation is generated for it:
+! CHECK-DAG: omp.private {type = private} @{{.*}}Etrivial_const_private_3xf64 : !fir.array<3xf64>{{$}}
+
+! A character array is boxed (character is not a trivial element type):
+! CHECK-DAG: omp.private {type = private} @{{.*}}Echar_arr_private_box{{.*}} : !fir.box<!fir.array<2x!fir.char<1,4>>>
+
+! A dynamic-extent array is boxed:
+! CHECK-DAG: omp.private {type = private} @{{.*}}Edyn_arr_private_box{{.*}} : !fir.box<!fir.array<?xf64>>
+
+! A firstprivate array is boxed (it needs the copy region):
+! CHECK-DAG: omp.private {type = firstprivate} @{{.*}}Efirst_const_firstprivate_box_3xf64 : !fir.box<!fir.array<3xf64>>
+
+! A constant-shape array with non-default lower bounds is boxed: its HLFIR
+! variable is a fir.box (which carries the lower bounds), so the constant-array
+! unboxing carve-out does not apply:
+! CHECK-DAG: omp.private {type = private} @{{.*}}Enondef_lb_private_box_3xf64 : !fir.box<!fir.array<3xf64>>
diff --git a/flang/test/Lower/OpenMP/private-commonblock.f90 b/flang/test/Lower/OpenMP/private-commonblock.f90
index df2702d93d3b8..d27e44ccb1a68 100644
--- a/flang/test/Lower/OpenMP/private-commonblock.f90
+++ b/flang/test/Lower/OpenMP/private-commonblock.f90
@@ -18,7 +18,6 @@ subroutine private_common
end subroutine
!CHECK: %[[D_BOX_ADDR:.*]] = fir.alloca !fir.box<!fir.array<5x!fir.char<1,5>>>
-!CHECK: %[[B_BOX_ADDR:.*]] = fir.alloca !fir.box<!fir.array<10xf32>>
!CHECK: %[[BLK_ADDR:.*]] = fir.address_of(@blk_) : !fir.ref<!fir.array<74xi8>>
!CHECK: %[[C0:.*]] = arith.constant 0 : index
!CHECK: %[[A_I8_REF:.*]] = fir.coordinate_of %[[BLK_ADDR]], %[[C0]] : (!fir.ref<!fir.array<74xi8>>, index) -> !fir.ref<i8>
@@ -46,24 +45,20 @@ subroutine private_common
!CHECK: %[[D_REF:.*]] = fir.convert %[[D_DECL]]#0 : (!fir.ref<!fir.array<5x!fir.char<1,5>>>) -> !fir.ref<!fir.char<1,5>>
!CHECK: %[[D_BOX:.*]] = fir.emboxchar %[[D_REF]], %[[TP5]] : (!fir.ref<!fir.char<1,5>>, index) -> !fir.boxchar<1>
!CHECK: fir.call @_QPsub1(%[[A_DECL]]#0, %[[B_DECL]]#0, %[[C_BOX]], %[[D_BOX]]) fastmath<contract> : (!fir.ref<i32>, !fir.ref<!fir.array<10xf32>>, !fir.boxchar<1>, !fir.boxchar<1>) -> ()
-!CHECK: %[[B_BOX:.*]] = fir.embox %[[B_DECL]]#0(%[[SH10]])
-!CHECK: fir.store %[[B_BOX]] to %[[B_BOX_ADDR]]
!CHECK: %[[D_BOX:.*]] = fir.embox %[[D_DECL]]#0(%[[SH5]])
!CHECK: fir.store %[[D_BOX]] to %[[D_BOX_ADDR]]
-!CHECK: omp.parallel private(@{{.*}} %{{.*}}#0 -> %[[A_PVT_REF:.*]], @{{.*}} %[[B_BOX_ADDR]] -> %[[B_PVT_REF:.*]], @{{.*}} %{{.*}}#0 -> %[[C_PVT_REF:.*]], @{{.*}} %[[D_BOX_ADDR]] -> %[[D_PVT_REF:.*]] : {{.*}}) {
+!CHECK: omp.parallel private(@{{.*}} %{{.*}}#0 -> %[[A_PVT_REF:.*]], @{{.*}} %{{.*}}#0 -> %[[B_PVT_REF:.*]], @{{.*}} %{{.*}}#0 -> %[[C_PVT_REF:.*]], @{{.*}} %[[D_BOX_ADDR]] -> %[[D_PVT_REF:.*]] : {{.*}}) {
!CHECK: %[[A_PVT_DECL:.*]]:2 = hlfir.declare %[[A_PVT_REF]] {uniq_name = "_QFprivate_clause_commonblockEa"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-!CHECK: %[[B_PVT_DECL:.*]]:2 = hlfir.declare %[[B_PVT_REF]] {uniq_name = "_QFprivate_clause_commonblockEb"} :
+!CHECK: %[[B_PVT_DECL:.*]]:2 = hlfir.declare %[[B_PVT_REF]](%{{.*}}) {uniq_name = "_QFprivate_clause_commonblockEb"} :
!CHECK: %[[C_PVT_DECL:.*]]:2 = hlfir.declare %[[C_PVT_REF]] typeparams %{{.*}} {uniq_name = "_QFprivate_clause_commonblockEc"} : (!fir.ref<!fir.char<1,5>>, index) -> (!fir.ref<!fir.char<1,5>>, !fir.ref<!fir.char<1,5>>)
!CHECK: %[[D_PVT_DECL:.*]]:2 = hlfir.declare %[[D_PVT_REF]]
-!CHECK: %[[B_LOADED:.*]] = fir.load %[[B_PVT_DECL]]#0
-!CHECK: %[[B_ADDR:.*]] = fir.box_addr %[[B_LOADED]]
!CHECK: %[[C_PVT_BOX:.*]] = fir.emboxchar %[[C_PVT_DECL]]#0, %{{.*}} : (!fir.ref<!fir.char<1,5>>, index) -> !fir.boxchar<1>
!CHECK: %[[D_LOADED:.*]] = fir.load %[[D_PVT_DECL]]#0
!CHECK: %[[D_ADDR:.*]] = fir.box_addr %[[D_LOADED]]
!CHECK: %[[D_PVT_REF:.*]] = fir.convert %[[D_ADDR]] : (!fir.ref<!fir.array<5x!fir.char<1,5>>>) -> !fir.ref<!fir.char<1,5>>
!CHECK: %[[D_PVT_BOX:.*]] = fir.emboxchar %[[D_PVT_REF]], %{{.*}} : (!fir.ref<!fir.char<1,5>>, index) -> !fir.boxchar<1>
-!CHECK: fir.call @_QPsub2(%[[A_PVT_DECL]]#0, %[[B_ADDR]], %[[C_PVT_BOX]], %[[D_PVT_BOX]]) fastmath<contract> : (!fir.ref<i32>, !fir.ref<!fir.array<10xf32>>, !fir.boxchar<1>, !fir.boxchar<1>) -> ()
+!CHECK: fir.call @_QPsub2(%[[A_PVT_DECL]]#0, %[[B_PVT_DECL]]#0, %[[C_PVT_BOX]], %[[D_PVT_BOX]]) fastmath<contract> : (!fir.ref<i32>, !fir.ref<!fir.array<10xf32>>, !fir.boxchar<1>, !fir.boxchar<1>) -> ()
!CHECK: omp.terminator
!CHECK: }
!CHECK: %[[C_BOX:.*]] = fir.emboxchar %[[C_DECL]]#0, %{{.*}} : (!fir.ref<!fir.char<1,5>>, index) -> !fir.boxchar<1>
diff --git a/flang/test/Lower/do_concurrent_local_assoc_entity.f90 b/flang/test/Lower/do_concurrent_local_assoc_entity.f90
index 67f080eb2c1c5..25424b0b7fa9b 100644
--- a/flang/test/Lower/do_concurrent_local_assoc_entity.f90
+++ b/flang/test/Lower/do_concurrent_local_assoc_entity.f90
@@ -12,27 +12,14 @@ subroutine local_assoc
end associate
end subroutine local_assoc
-! CHECK: fir.local {type = local} @[[LOCALIZER:.*local_assocEa.*]] : !fir.box<!fir.array<8xf32>> init {
-! CHECK-NEXT: ^{{.*}}(%{{.*}}: !{{.*}}, %[[LOCAL_ARG:.*]]: !{{.*}}):
-! CHECK-NEXT: %[[C8:.*]] = arith.constant 8 : index
-! CHECK-NEXT: %[[SHAPE:.*]] = fir.shape %[[C8]]
-! CHECK-NEXT: %[[TMP_ALLOC:.*]] = fir.allocmem !{{.*}} {bindc_name = ".tmp", {{.*}}}
-! CHECK: %[[TMP_DECL:.*]]:2 = hlfir.declare %[[TMP_ALLOC]](%[[SHAPE]])
-! CHECK: %[[C1:.*]] = arith.constant 1 : index
-! CHECK-NEXT: %[[C8:.*]] = arith.constant 8 : index
-! CHECK-NEXT: %[[SHAPE_SHIFT:.*]] = fir.shape_shift %[[C1]], %[[C8]]
-! CHECK-NEXT: %[[TMP_BOX:.*]] = fir.embox %[[TMP_DECL]]#0(%[[SHAPE_SHIFT]])
-! CHECK-NEXT: fir.store %[[TMP_BOX]] to %[[LOCAL_ARG]]
-! CHECK-NEXT: fir.yield(%[[LOCAL_ARG]] : !fir.ref<!fir.box<!fir.array<8xf32>>>)
-! CHECK-NEXT: }
+! A constant-shape, trivial-element local is localized unboxed as a plain
+! fir.array with no init region. The end-of-line anchor confirms there is no
+! `init {` region.
+! CHECK: fir.local {type = local} @[[LOCALIZER:.*local_assocEa.*]] : !fir.array<8xf32>{{$}}
! CHECK: func.func @_QPlocal_assoc()
-! CHECK: %[[BOX_REF:.*]] = fir.alloca !fir.box<!fir.array<8xf32>>
! CHECK: %[[ASSOC_DECL:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "{{.*}}local_assocEa"}
-! CHECK: %[[ASSOC_BOX:.*]] = fir.embox %[[ASSOC_DECL]]#0(%{{.*}})
-! CHECK: fir.store %[[ASSOC_BOX]] to %[[BOX_REF]]
-! CHECK: fir.do_concurrent.loop {{.*}} local(@[[LOCALIZER]] %[[BOX_REF]] -> %[[LOCAL_ARG:.*]] : !fir.ref<!fir.box<!fir.array<8xf32>>>) {
-! CHECK: %[[LOCAL_DECL:.*]]:2 = hlfir.declare %[[LOCAL_ARG]]
-! CHECK: %[[LOCAL_LD:.*]] = fir.load %[[LOCAL_DECL]]#0 : !fir.ref<!fir.box<!fir.array<8xf32>>>
-! CHECK: hlfir.designate %[[LOCAL_LD]] (%{{.*}})
+! CHECK: fir.do_concurrent.loop {{.*}} local(@[[LOCALIZER]] %[[ASSOC_DECL]]#0 -> %[[LOCAL_ARG:.*]] : !fir.ref<!fir.array<8xf32>>) {
+! CHECK: %[[LOCAL_DECL:.*]]:2 = hlfir.declare %[[LOCAL_ARG]](%{{.*}})
+! CHECK: hlfir.designate %[[LOCAL_DECL]]#0 (%{{.*}})
! CHECK: }
More information about the flang-commits
mailing list