[flang-commits] [flang] [flang][acc] Preserve association status of privatized pointers (PR #228581)
via flang-commits
flang-commits at lists.llvm.org
Fri Oct 2 13:52:13 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
@llvm/pr-subscribers-openacc
Author: Razvan Lupusoru (razvanlupusoru)
<details>
<summary>Changes</summary>
Privatization allocated a target and copied it even when a Fortran pointer or allocatable was unassociated or unallocated. That reads through a null address. The private allocation now follows the original association status. A reduction stores its initial value only when that storage exists. The target is copied only when one exists.
Before:
```
%private = fir.allocmem f32
// Box %private into the private descriptor.
hlfir.assign %src to %private temporary_lhs : f32, !fir.heap<f32>
```
After:
```
%private = fir.if %is_associated -> !fir.heap<f32> {
%allocation = fir.allocmem f32
fir.result %allocation : !fir.heap<f32>
} else {
%null = fir.zero_bits !fir.heap<f32>
fir.result %null : !fir.heap<f32>
}
// Box %private into the private descriptor.
fir.if %is_associated {
hlfir.assign %src to %private temporary_lhs : f32, !fir.heap<f32>
}
```
---
Patch is 48.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/228581.diff
5 Files Affected:
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp (+83-19)
- (modified) flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir (+98)
- (modified) flang/test/Fir/OpenACC/recipe-populate-private.mlir (+41-17)
- (modified) flang/test/Lower/OpenACC/acc-private.f90 (+52-14)
- (modified) flang/test/Lower/OpenACC/acc-reduction.f90 (+93-39)
``````````diff
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 73be000f701b4..04352914be1ab 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -769,10 +769,17 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
fir::FirOpBuilder builder(mlirBuilder, mod);
hlfir::Entity inputVar = hlfir::Entity{var};
- bool preservePointerAllocation =
- fir::isPointerType(inputVar.getType()) && bounds.empty();
+ // Whole POINTER/ALLOCATABLE: allocate private storage only when the source
+ // is associated or allocated, so the private copy stays null otherwise.
+ // A reduction identity is stored only into that allocated storage.
+ // Reduction init yields only the private variable, so its destroy reloads
+ // that descriptor. Private and firstprivate also yield the allocation.
+ bool preserveNullAllocation =
+ bounds.empty() &&
+ (fir::isPointerType(inputVar.getType()) ||
+ fir::isAllocatableType(inputVar.getType()));
mlir::Type pointerAllocationType;
- if (preservePointerAllocation)
+ if (preserveNullAllocation)
pointerAllocationType =
fir::HeapType::get(inputVar.getElementOrSequenceType());
@@ -788,7 +795,8 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
mlir::Value cond =
fir::IsPresentOp::create(builder, loc, builder.getI1Type(), var);
llvm::SmallVector<mlir::Type> resultTypes{type};
- if (preservePointerAllocation)
+ // Reduction init cannot yield the allocation alongside the variable.
+ if (preserveNullAllocation && !initVal)
resultTypes.push_back(pointerAllocationType);
optIfOp = fir::IfOp::create(builder, loc, resultTypes, cond,
/*withElseRegion=*/true);
@@ -873,16 +881,30 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
// array POINTER and ALLOCATABLE always have dynamic size. Constant sections
// of POINTER/ALLOCATABLE can use alloca since only part of the data is
// privatized (it makes no sense to deallocate them).
- if (preservePointerAllocation) {
+ if (preserveNullAllocation) {
mlir::Value originalAddr =
hlfir::genVariableRawAddress(loc, builder, inputVar);
mlir::Value isAssociated = builder.genIsNotNullAddr(loc, originalAddr);
auto ifOp = fir::IfOp::create(
builder, loc, mlir::TypeRange{pointerAllocationType}, isAssociated,
/*withElseRegion=*/true);
+ ifOp.setUnlikelyIfWeights(/*unlikelyElse=*/true);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
mlir::Value privateAllocation = builder.createHeapTemporary(
loc, baseType, varName, tempExtents, typeParams, {placeholderAttr});
+ // The identity is stored only when storage was allocated. Storing it
+ // after the branch would write through the null result.
+ if (initVal) {
+ mlir::Value tempEntity = privateAllocation;
+ if (fir::hasDynamicSize(baseType))
+ tempEntity = fir::EmboxOp::create(
+ builder, loc, fir::BoxType::get(baseType), privateAllocation,
+ tempShape, /*slice=*/mlir::Value{}, typeParams);
+ hlfir::genNoAliasAssignment(loc, builder, hlfir::Entity{initVal},
+ hlfir::Entity{tempEntity},
+ /*emitWorkshareLoop=*/false,
+ /*temporaryLHS=*/true);
+ }
fir::ResultOp::create(builder, loc, privateAllocation);
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
fir::ResultOp::create(
@@ -894,7 +916,7 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
typeParams, {placeholderAttr});
}
needsDestroy = true;
- if (preservePointerAllocation) {
+ if (preserveNullAllocation && !initVal) {
assert(alloc.getType() == pointerAllocationType &&
"unexpected private pointer allocation type");
pointerAllocation = alloc;
@@ -904,7 +926,8 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
typeParams, {placeholderAttr});
}
// Step3: Assign the initial value to the privatized part if any.
- if (initVal) {
+ // Pointer and allocatable reductions assign inside the allocated branch.
+ if (initVal && !preserveNullAllocation) {
mlir::Value tempEntity = alloc;
if (fir::hasDynamicSize(baseType))
tempEntity =
@@ -1004,22 +1027,25 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
retVal = builder.createConvert(loc, type, retVal);
}
+ // Private and firstprivate destroy receives the allocation directly.
+ // A reduction yields only the private variable.
+ bool yieldAllocation = preserveNullAllocation && !initVal;
if (mayBeOptional) {
llvm::SmallVector<mlir::Value> thenResults{retVal};
- if (preservePointerAllocation)
+ if (yieldAllocation)
thenResults.push_back(pointerAllocation);
fir::ResultOp::create(builder, loc, thenResults);
builder.setInsertionPointToStart(&optIfOp->getElseRegion().front());
mlir::Value absent = fir::AbsentOp::create(builder, loc, type);
llvm::SmallVector<mlir::Value> elseResults{absent};
- if (preservePointerAllocation)
+ if (yieldAllocation)
elseResults.push_back(
builder.createNullConstant(loc, pointerAllocationType));
fir::ResultOp::create(builder, loc, elseResults);
retVal = optIfOp->getResult(0);
- if (preservePointerAllocation)
+ if (yieldAllocation)
destroyValues.push_back(optIfOp->getResult(1));
- } else if (preservePointerAllocation) {
+ } else if (yieldAllocation) {
destroyValues.push_back(pointerAllocation);
}
@@ -1070,27 +1096,41 @@ bool OpenACCMappableModel<Ty>::generateCopy(
hlfir::Entity source{src};
hlfir::Entity destination{dest};
- source = hlfir::derefPointersAndAllocatables(loc, builder, source);
- destination = hlfir::derefPointersAndAllocatables(loc, builder, destination);
-
// When optional: only copy when source is present (fir.is_present). When
// absent, destination is already null from init. When non-optional, copy
- // directly without the conditional.
+ // directly without the conditional. Presence is separate from association.
+ std::optional<fir::IfOp> presentIf;
if (auto fortranVarInfo =
mlir::dyn_cast_if_present<fir::OpenACCFortranVariableInfoAttr>(
varInfo)) {
if (fortranVarInfo.getMayBeOptional()) {
// When variable is optional: use fir.is_present to check. When
// non-optional, skip the conditional to avoid unnecessary branches.
- std::optional<fir::IfOp> optIfOp;
mlir::Value cond =
fir::IsPresentOp::create(builder, loc, builder.getI1Type(), src);
- optIfOp = fir::IfOp::create(builder, loc, mlir::TypeRange{}, cond,
- /*withElseRegion=*/false);
- builder.setInsertionPointToStart(&optIfOp->getThenRegion().front());
+ presentIf = fir::IfOp::create(builder, loc, mlir::TypeRange{}, cond,
+ /*withElseRegion=*/false);
+ builder.setInsertionPointToStart(&presentIf->getThenRegion().front());
}
}
+ // A POINTER or ALLOCATABLE may have a null base address, including when
+ // the recipe covers only a section of it. Test the base address before
+ // dereferencing, and copy only when the variable is associated or allocated.
+ std::optional<fir::IfOp> allocatedIf;
+ if (fir::isPointerType(source.getType()) ||
+ fir::isAllocatableType(source.getType())) {
+ mlir::Value srcAddr = hlfir::genVariableRawAddress(loc, builder, source);
+ mlir::Value isAllocated = builder.genIsNotNullAddr(loc, srcAddr);
+ allocatedIf = fir::IfOp::create(builder, loc, mlir::TypeRange{},
+ isAllocated, /*withElseRegion=*/false);
+ allocatedIf->setUnlikelyIfWeights(/*unlikelyElse=*/true);
+ builder.setInsertionPointToStart(&allocatedIf->getThenRegion().front());
+ }
+
+ source = hlfir::derefPointersAndAllocatables(loc, builder, source);
+ destination = hlfir::derefPointersAndAllocatables(loc, builder, destination);
+
if (!bounds.empty())
std::tie(source, destination) =
genArraySectionsInRecipe(builder, loc, bounds, source, destination);
@@ -1111,6 +1151,12 @@ bool OpenACCMappableModel<Ty>::generateCopy(
hlfir::AssignOp::create(builder, loc, source, destination, /*realloc=*/false,
/*keep_lhs_length_if_realloc=*/false,
/*temporary_lhs=*/true);
+ // Callers that share this builder append the recipe terminator in the
+ // copy block, after these conditions.
+ if (allocatedIf)
+ builder.setInsertionPointAfter(*allocatedIf);
+ if (presentIf)
+ builder.setInsertionPointAfter(*presentIf);
return true;
}
@@ -1344,6 +1390,24 @@ bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
fir::FreeMemOp::create(builder, loc, addr);
};
if (bounds.empty()) {
+ // Reduction init yields only the private descriptor. When that descriptor
+ // is a POINTER or ALLOCATABLE, its base address is null if the original
+ // was unassociated or unallocated, so free only in that case.
+ if (fir::isPointerType(inputVar.getType()) ||
+ fir::isAllocatableType(inputVar.getType())) {
+ mlir::Value addr = hlfir::genVariableRawAddress(loc, builder, inputVar);
+ mlir::Value isAllocated = builder.genIsNotNullAddr(loc, addr);
+ auto ifOp = fir::IfOp::create(builder, loc, isAllocated,
+ /*withElseRegion=*/false);
+ builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+ mlir::Type heapType =
+ fir::HeapType::get(fir::unwrapRefType(addr.getType()));
+ if (heapType != addr.getType())
+ addr = fir::ConvertOp::create(builder, loc, heapType, addr);
+ fir::FreeMemOp::create(builder, loc, addr);
+ builder.setInsertionPointAfter(ifOp);
+ return true;
+ }
genFreeRawAddress(inputVar);
return true;
}
diff --git a/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir b/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir
index ff582f93976f1..c3fd622409ea2 100644
--- a/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir
+++ b/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir
@@ -203,3 +203,101 @@ func.func @test_optional_ptr_f64() {
%1:2 = hlfir.declare %var uniq_name("load_hlfir") : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
return
}
+
+// -----
+
+// Whole scalar POINTER: copy the pointee only when base_addr is non-null.
+// CHECK: acc.firstprivate.recipe @firstprivate_box_ptr_scalar : !fir.ref<!fir.box<!fir.ptr<f64>>> init {
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.box<!fir.ptr<f64>>>, %[[DST:.*]]: !fir.ref<!fir.box<!fir.ptr<f64>>>):
+// CHECK: %[[BOX:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.box<!fir.ptr<f64>>>
+// CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.ptr<f64>>) -> !fir.ptr<f64>
+// CHECK-NOT: fir.load %[[ADDR]]
+// CHECK: %[[COND:.*]] = arith.cmpi ne
+// CHECK: fir.if %[[COND]] weights([1, 0]) {
+// CHECK: hlfir.assign {{.*}} temporary_lhs
+// CHECK: }
+// CHECK-NOT: hlfir.assign
+// CHECK: acc.terminator
+// CHECK: }
+
+func.func @test_box_ptr_scalar() {
+ %0 = fir.alloca !fir.box<!fir.ptr<f64>> {test.var = "box_ptr_scalar"}
+ %var = fir.alloca f32
+ %1:2 = hlfir.declare %var uniq_name("load_hlfir") : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+ return
+}
+
+// -----
+
+// Whole array POINTER: same association guard around the array assignment.
+// CHECK: acc.firstprivate.recipe @firstprivate_box_ptr_array : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> init {
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>):
+// CHECK: %[[BOX:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
+// CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
+// CHECK-NOT: fir.load %[[ADDR]]
+// CHECK: %[[COND:.*]] = arith.cmpi ne
+// CHECK: fir.if %[[COND]] weights([1, 0]) {
+// CHECK: hlfir.assign {{.*}} temporary_lhs
+// CHECK: }
+// CHECK-NOT: hlfir.assign
+// CHECK: acc.terminator
+// CHECK: }
+
+func.func @test_box_ptr_array() {
+ %0 = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>> {test.var = "box_ptr_array"}
+ %var = fir.alloca f32
+ %1:2 = hlfir.declare %var uniq_name("load_hlfir") : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+ return
+}
+
+// -----
+
+// Whole scalar ALLOCATABLE: copy only when allocated.
+// CHECK: acc.firstprivate.recipe @firstprivate_box_heap_scalar : !fir.ref<!fir.box<!fir.heap<f64>>> init {
+// CHECK: fir.if {{.*}} -> (!fir.heap<f64>) {
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.box<!fir.heap<f64>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>):
+// CHECK: %[[BOX:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.box<!fir.heap<f64>>>
+// CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<f64>>) -> !fir.heap<f64>
+// CHECK-NOT: fir.load %[[ADDR]]
+// CHECK: %[[COND:.*]] = arith.cmpi ne
+// CHECK: fir.if %[[COND]] weights([1, 0]) {
+// CHECK: hlfir.assign {{.*}} temporary_lhs
+// CHECK: }
+// CHECK-NOT: hlfir.assign
+// CHECK: acc.terminator
+// CHECK: }
+
+func.func @test_box_heap_scalar() {
+ %0 = fir.alloca !fir.box<!fir.heap<f64>> {test.var = "box_heap_scalar"}
+ %var = fir.alloca f32
+ %1:2 = hlfir.declare %var uniq_name("load_hlfir") : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+ return
+}
+
+// -----
+
+// Whole array ALLOCATABLE: same allocation guard around the array assignment.
+// CHECK: acc.firstprivate.recipe @firstprivate_box_heap_array : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> init {
+// CHECK: fir.if {{.*}} -> (!fir.heap<!fir.array<?xf32>>) {
+// CHECK: } copy {
+// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>):
+// CHECK: %[[BOX:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+// CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?xf32>>>) -> !fir.heap<!fir.array<?xf32>>
+// CHECK-NOT: fir.load %[[ADDR]]
+// CHECK: %[[COND:.*]] = arith.cmpi ne
+// CHECK: fir.if %[[COND]] weights([1, 0]) {
+// CHECK: hlfir.assign {{.*}} temporary_lhs
+// CHECK: }
+// CHECK-NOT: hlfir.assign
+// CHECK: acc.terminator
+// CHECK: }
+
+func.func @test_box_heap_array() {
+ %0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>> {test.var = "box_heap_array"}
+ %var = fir.alloca f32
+ %1:2 = hlfir.declare %var uniq_name("load_hlfir") : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+ return
+}
diff --git a/flang/test/Fir/OpenACC/recipe-populate-private.mlir b/flang/test/Fir/OpenACC/recipe-populate-private.mlir
index abd01fe526ff1..bf67e34089ef6 100644
--- a/flang/test/Fir/OpenACC/recipe-populate-private.mlir
+++ b/flang/test/Fir/OpenACC/recipe-populate-private.mlir
@@ -106,19 +106,27 @@ func.func @test_derived() {
// -----
-// Test box type with heap scalar (needs destroy)
+// Test box type with heap scalar (needs destroy). Unallocated allocatables
+// keep a null private allocation.
// CHECK: acc.private.recipe @private_box_heap_scalar : !fir.ref<!fir.box<!fir.heap<f64>>> init {
// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>):
-// CHECK: %[[SCALAR:.*]] = fir.allocmem f64
-// CHECK: %[[EMBOX:.*]] = fir.embox %[[SCALAR]] : (!fir.heap<f64>) -> !fir.box<!fir.heap<f64>>
+// CHECK: %[[PRIVATE_ALLOC:.*]] = fir.if {{.*}} -> (!fir.heap<f64>) {
+// CHECK: %[[SCALAR:.*]] = fir.allocmem f64
+// CHECK: fir.result %[[SCALAR]] : !fir.heap<f64>
+// CHECK: } else {
+// CHECK: %[[NULL_ALLOC:.*]] = fir.zero_bits !fir.heap<f64>
+// CHECK: fir.result %[[NULL_ALLOC]] : !fir.heap<f64>
+// CHECK: }
+// CHECK: %[[EMBOX:.*]] = fir.embox %[[PRIVATE_ALLOC]] : (!fir.heap<f64>) -> !fir.box<!fir.heap<f64>>
// CHECK: %[[BOXALLOC:.*]] = fir.alloca !fir.box<!fir.heap<f64>>
// CHECK: fir.store %[[EMBOX]] to %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.heap<f64>>>
-// CHECK: acc.yield %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.heap<f64>>>
+// CHECK: acc.yield %[[BOXALLOC]], %[[PRIVATE_ALLOC]] : !fir.ref<!fir.box<!fir.heap<f64>>>, !fir.heap<f64>
// CHECK: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>, %[[PRIVATE:.*]]: !fir.ref<!fir.box<!fir.heap<f64>>>):
-// CHECK: %[[BOX:.*]] = fir.load %[[PRIVATE]] : !fir.ref<!fir.box<!fir.heap<f64>>>
-// CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<f64>>) -> !fir.heap<f64>
-// CHECK: fir.freemem %[[ADDR]] : !fir.heap<f64>
+// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>, %[[PRIVATE_ALLOC:.*]]: !fir.heap<f64>):
+// CHECK-NOT: fir.box_addr
+// CHECK: fir.if {{.*}} {
+// CHECK: fir.freemem %[[PRIVATE_ALLOC]] : !fir.heap<f64>
+// CHECK: }
// CHECK: acc.terminator
// CHECK: }
@@ -163,16 +171,23 @@ func.func @test_box_ptr_scalar() {
// -----
-// Test box type with 1D heap array (needs destroy)
+// Test box type with 1D heap array (needs destroy). Unallocated allocatables
+// keep a null private allocation.
// CHECK: acc.private.recipe @private_box_heap_array_1d : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> init {
// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>):
+// CHECK: %[[PRIVATE_ALLOC:.*]] = fir.if {{.*}} -> (!fir.heap<!fir.array<?xf32>>) {
+// CHECK: fir.allocmem !fir.array<?xf32>
+// CHECK: } else {
+// CHECK: fir.zero_bits !fir.heap<!fir.array<?xf32>>
+// CHECK: }
// CHECK: %[[BOXALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>>
-// CHECK: acc.yield %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+// CHECK: acc.yield %[[BOXALLOC]], %[[PRIVATE_ALLOC]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, !fir.heap<!fir.array<?xf32>>
// CHECK: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, %[[PRIVATE:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>):
-// CHECK: %[[BOX:.*]] = fir.load %[[PRIVATE]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
-// CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?xf32>>>) -> !fir.heap<!fir.array<?xf32>>
-// CHECK: fir.freemem %[[ADDR]] : !fir.heap<!fir.array<?xf32>>
+// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, %[[PRIVATE_ALLOC:.*]]: !fir.heap<!fir.array<?xf32>>):
+// CHECK-NOT: fir.box_addr
+// CHECK: fir.if {{.*}} {
+// CHECK: fir.freemem %[[PRIVATE_ALLOC]] : !fir.heap<!fir.array<?xf32>>
+// CHECK: }
// CHECK: acc.terminator
// CHECK: }
@@ -185,13 +200,22 @@ func.func @test_box_heap_array_1d() {
// -----
-// Test box type with 2D heap array (needs destroy)
+// Test box type with 2D heap array (needs destroy). Unallocated allocatables
+// keep a null private allocation.
// CHECK: acc.private.recipe @private_box_heap_array_2d : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi64>>>> init {
// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi64>>>>):
+// CHECK: %[[PRIVATE_ALLOC:.*]] = fir.if {{.*}} -> (!fir.heap<!fir.array<?x?xi64>>) {
+// CHECK: fir.allocmem !fir.array<?x?xi64>
+// CHECK: } else {
+// CHECK: fir.zero_bits !fir.heap<!fir.array<?x?xi64>>
+// CHECK: }
// CHECK: %[[BOXALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x?xi64>>>
-// CHECK: acc.yield %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi64>>>>
+// CHECK: acc.yield %[[BOXALLOC]], %[[PRIVATE_ALLOC]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi64>>>>, !fir.heap<!fir.array<?x?xi64>>
// CHECK: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi64>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi64>>>>):
+// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!f...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/228581
More information about the flang-commits
mailing list