[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