[Mlir-commits] [flang] [mlir] [flang][OpenACC] Changed memory management for private POINTERs. (PR #219052)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Aug 26 14:46:45 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-openacc

Author: Slava Zakharin (vzakhari)

<details>
<summary>Changes</summary>

The motivating example:
```
!$acc parallel loop private(ptr) present(target_data)
  do i = 1, 128
     ptr => target_data(i)%array
     ptr(...) = ...
```

The OpenACC privatization recipe was allocating private data
for `ptr`, storing it in `ptr` private descriptor and then
trying to deallocate `ptr->base_addr` at the end of the region.

`ptr->base_addr` is pointing to `target_data(i)%array` that is allocated
in device global memory and it is illegal to `free()` it in the device code.

The `destroy` part of the recipe can only `free()` the private data
that was originally allocated in the `init` part.

This approach works well for the two most common case.

Case 1: `ptr` is originally unassociated and it is used to point
to some target data (the example above).
Case 2: `ptr` is originally associated and it is used as a normal
private variable for storing something into it without reassociating
`ptr` to some other data.

This change, on the other hand, breaks the following case:
```
allocate(ptr(10))
!$acc parallel private(ptr)...
deallocate(ptr)
ptr => ...
```

The private memory for `ptr` data may be allocated from the host
using CUDA runtime or driver API, and it is illegal to deallocate
it via `free()` which flang-rt's implementation is using.
Moreover, flang-rt's implementation of `DEALLOCATE` assumes
there is an 8-byte footer after the data payload that it uses
to verify whether a `POINTER` deallocation is valid. OpenACC
privatization recipe uses `fir.allocmem` that does not create
the footer, so flang-rt may read past the allocated data
and crash the kernel.

The reference compiler implements `DEALLOCATE` as `free()`, so
the compute-sanitizer reports invalid `free()` errors, though,
w/o the sanitizer the kernel silently passes.

Flang's implementation either fails in `DEALLOCATE` runtime with
`DEALLOCATE of a pointer that is not the whole content of a pointer ALLOCATE`
error or crashes the kernel due to accessing past the allocated memory.

OpenACC spec does not explicitly specify what should happen in this case,
so I suggest that Flang's implementation always reports a runtime error
when program tries to deallocate the private data. This is left for
future improvement.

Assisted-by: Cursor


---

Patch is 56.18 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/219052.diff


15 Files Affected:

- (modified) flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h (+7-7) 
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp (+83-21) 
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp (+16-6) 
- (modified) flang/test/Fir/OpenACC/recipe-populate-private.mlir (+35-8) 
- (modified) flang/test/Lower/OpenACC/acc-private.f90 (+14-8) 
- (modified) flang/test/Lower/OpenACC/acc-reduction.f90 (+44-18) 
- (modified) flang/test/Transforms/OpenACC/acc-recipe-materialization-firstprivate.fir (+43) 
- (modified) flang/test/Transforms/OpenACC/acc-recipe-materialization-private.fir (+35) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td (+9-5) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td (+11-6) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCUtilsLoop.h (+4-3) 
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp (+33-15) 
- (modified) mlir/lib/Dialect/OpenACC/Transforms/ACCRecipeMaterialization.cpp (+20-15) 
- (modified) mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsLoop.cpp (+2-1) 
- (modified) mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp (+6-2) 


``````````diff
diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
index ae47805a1af42..4e4e0d5f2c6f2 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
@@ -91,16 +91,16 @@ struct OpenACCMappableModel
   genPrivateVariableInfo(mlir::Type type,
                          mlir::TypedValue<mlir::acc::MappableType> var) const;
 
-  mlir::Value generatePrivateInit(mlir::Type type, mlir::OpBuilder &builder,
-                                  mlir::Location loc,
-                                  mlir::TypedValue<mlir::acc::MappableType> var,
-                                  llvm::StringRef varName,
-                                  mlir::ValueRange extents, mlir::Value initVal,
-                                  mlir::acc::VariableInfoAttr varInfo,
-                                  bool &needsDestroy) const;
+  mlir::Value generatePrivateInit(
+      mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
+      mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
+      mlir::ValueRange extents, mlir::Value initVal,
+      mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy,
+      llvm::SmallVectorImpl<mlir::Value> &destroyValues) const;
 
   bool generatePrivateDestroy(mlir::Type type, mlir::OpBuilder &builder,
                               mlir::Location loc, mlir::Value privatized,
+                              mlir::ValueRange destroyValues,
                               mlir::ValueRange bounds,
                               mlir::acc::VariableInfoAttr varInfo) const;
 
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index e7e522e8e1570..0580eedcac51d 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -741,13 +741,22 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange bounds, mlir::Value initVal,
-    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const {
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy,
+    llvm::SmallVectorImpl<mlir::Value> &destroyValues) const {
   mlir::ModuleOp mod = mlirBuilder.getInsertionBlock()
                            ->getParent()
                            ->getParentOfType<mlir::ModuleOp>();
   assert(mod && "failed to retrieve ModuleOp");
   fir::FirOpBuilder builder(mlirBuilder, mod);
 
+  hlfir::Entity inputVar = hlfir::Entity{var};
+  bool preservePointerAllocation =
+      fir::isPointerType(inputVar.getType()) && bounds.empty();
+  mlir::Type pointerAllocationType;
+  if (preservePointerAllocation)
+    pointerAllocationType =
+        fir::HeapType::get(inputVar.getElementOrSequenceType());
+
   // 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;
@@ -759,13 +768,15 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
     if (mayBeOptional) {
       mlir::Value cond =
           fir::IsPresentOp::create(builder, loc, builder.getI1Type(), var);
-      optIfOp = fir::IfOp::create(builder, loc, mlir::TypeRange{type}, cond,
+      llvm::SmallVector<mlir::Type> resultTypes{type};
+      if (preservePointerAllocation)
+        resultTypes.push_back(pointerAllocationType);
+      optIfOp = fir::IfOp::create(builder, loc, resultTypes, cond,
                                   /*withElseRegion=*/true);
       builder.setInsertionPointToStart(&optIfOp->getThenRegion().front());
     }
   }
 
-  hlfir::Entity inputVar = hlfir::Entity{var};
   if (inputVar.isPolymorphic())
     TODO(loc, "OpenACC: polymorphic variable privatization");
   if (auto recType =
@@ -833,6 +844,7 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
       mlir::acc::VarNameAttr::get(builder.getContext(),
                                   mlir::acc::getVarNamePlaceholder()));
   mlir::Value alloc;
+  mlir::Value pointerAllocation;
   if (fir::hasDynamicSize(baseType) ||
       (isPointerOrAllocatable && bounds.empty())) {
     // Note: heap allocation is forced for whole pointers/allocatable so that
@@ -842,9 +854,32 @@ 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).
-    alloc = builder.createHeapTemporary(loc, baseType, varName, tempExtents,
-                                        typeParams, {placeholderAttr});
+    if (preservePointerAllocation) {
+      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);
+      builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+      mlir::Value privateAllocation = builder.createHeapTemporary(
+          loc, baseType, varName, tempExtents, typeParams, {placeholderAttr});
+      fir::ResultOp::create(builder, loc, privateAllocation);
+      builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
+      fir::ResultOp::create(
+          builder, loc, builder.createNullConstant(loc, pointerAllocationType));
+      builder.setInsertionPointAfter(ifOp);
+      alloc = ifOp.getResult(0);
+    } else {
+      alloc = builder.createHeapTemporary(loc, baseType, varName, tempExtents,
+                                          typeParams, {placeholderAttr});
+    }
     needsDestroy = true;
+    if (preservePointerAllocation) {
+      assert(alloc.getType() == pointerAllocationType &&
+             "unexpected private pointer allocation type");
+      pointerAllocation = alloc;
+    }
   } else {
     alloc = builder.createTemporary(loc, baseType, varName, tempExtents,
                                     typeParams, {placeholderAttr});
@@ -946,11 +981,22 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
   }
 
   if (mayBeOptional) {
-    fir::ResultOp::create(builder, loc, retVal);
+    llvm::SmallVector<mlir::Value> thenResults{retVal};
+    if (preservePointerAllocation)
+      thenResults.push_back(pointerAllocation);
+    fir::ResultOp::create(builder, loc, thenResults);
     builder.setInsertionPointToStart(&optIfOp->getElseRegion().front());
     mlir::Value absent = fir::AbsentOp::create(builder, loc, type);
-    fir::ResultOp::create(builder, loc, absent);
+    llvm::SmallVector<mlir::Value> elseResults{absent};
+    if (preservePointerAllocation)
+      elseResults.push_back(
+          builder.createNullConstant(loc, pointerAllocationType));
+    fir::ResultOp::create(builder, loc, elseResults);
     retVal = optIfOp->getResult(0);
+    if (preservePointerAllocation)
+      destroyValues.push_back(optIfOp->getResult(1));
+  } else if (preservePointerAllocation) {
+    destroyValues.push_back(pointerAllocation);
   }
 
   return retVal;
@@ -961,27 +1007,31 @@ OpenACCMappableModel<fir::BaseBoxType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy,
+    llvm::SmallVectorImpl<mlir::Value> &destroyValues) const;
 
 template mlir::Value
 OpenACCMappableModel<fir::ReferenceType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy,
+    llvm::SmallVectorImpl<mlir::Value> &destroyValues) const;
 
 template mlir::Value OpenACCMappableModel<fir::HeapType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy,
+    llvm::SmallVectorImpl<mlir::Value> &destroyValues) const;
 
 template mlir::Value
 OpenACCMappableModel<fir::PointerType>::generatePrivateInit(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
     mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
     mlir::ValueRange extents, mlir::Value initVal,
-    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const;
+    mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy,
+    llvm::SmallVectorImpl<mlir::Value> &destroyValues) const;
 
 template <typename Ty>
 bool OpenACCMappableModel<Ty>::generateCopy(
@@ -1242,13 +1292,25 @@ template bool OpenACCMappableModel<fir::HeapType>::generateCombiner(
 template <typename Ty>
 bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds,
-    mlir::acc::VariableInfoAttr varInfo) const {
+    mlir::Value privatized, mlir::ValueRange destroyValues,
+    mlir::ValueRange bounds, mlir::acc::VariableInfoAttr varInfo) const {
   hlfir::Entity inputVar = hlfir::Entity{privatized};
   mlir::ModuleOp mod =
       mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
   assert(mod && "failed to retrieve parent module");
   fir::FirOpBuilder builder(mlirBuilder, mod);
+  if (!destroyValues.empty()) {
+    assert(destroyValues.size() == 1 &&
+           "expected one private pointer allocation to destroy");
+    mlir::Value privateAllocation = destroyValues.front();
+    mlir::Value isAllocated = builder.genIsNotNullAddr(loc, privateAllocation);
+    auto ifOp =
+        fir::IfOp::create(builder, loc, isAllocated, /*withElseRegion=*/false);
+    builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+    fir::FreeMemOp::create(builder, loc, privateAllocation);
+    builder.setInsertionPointAfter(ifOp);
+    return true;
+  }
   auto genFreeRawAddress = [&](hlfir::Entity entity) {
     mlir::Value addr = hlfir::genVariableRawAddress(loc, builder, entity);
     mlir::Type heapType =
@@ -1278,20 +1340,20 @@ bool OpenACCMappableModel<Ty>::generatePrivateDestroy(
 
 template bool OpenACCMappableModel<fir::BaseBoxType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds,
-    mlir::acc::VariableInfoAttr varInfo) const;
+    mlir::Value privatized, mlir::ValueRange destroyValues,
+    mlir::ValueRange bounds, mlir::acc::VariableInfoAttr varInfo) const;
 template bool OpenACCMappableModel<fir::ReferenceType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds,
-    mlir::acc::VariableInfoAttr varInfo) const;
+    mlir::Value privatized, mlir::ValueRange destroyValues,
+    mlir::ValueRange bounds, mlir::acc::VariableInfoAttr varInfo) const;
 template bool OpenACCMappableModel<fir::HeapType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds,
-    mlir::acc::VariableInfoAttr varInfo) const;
+    mlir::Value privatized, mlir::ValueRange destroyValues,
+    mlir::ValueRange bounds, mlir::acc::VariableInfoAttr varInfo) const;
 template bool OpenACCMappableModel<fir::PointerType>::generatePrivateDestroy(
     mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
-    mlir::Value privatized, mlir::ValueRange bounds,
-    mlir::acc::VariableInfoAttr varInfo) const;
+    mlir::Value privatized, mlir::ValueRange destroyValues,
+    mlir::ValueRange bounds, mlir::acc::VariableInfoAttr varInfo) const;
 
 template <typename Ty>
 mlir::Value OpenACCPointerLikeModel<Ty>::genAllocate(
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index 4417b769fa314..77c9f2b2b9700 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -475,13 +475,16 @@ static RecipeOp genRecipeOp(
          "Expected that all variable types are considered mappable");
   auto initArg = mlir::cast<MappableValue>(initBlock->getArgument(0));
   bool needsDestroy = false;
+  llvm::SmallVector<mlir::Value> destroyValues;
   llvm::SmallVector<mlir::Value> initBounds =
       getRecipeBounds(builder, loc, dataOperationBounds,
                       initBlock->getArguments().drop_front(1));
   mlir::Value retVal = mappableTy.generatePrivateInit(
       builder, loc, initArg, initName, initBounds, initValue, varInfo,
-      needsDestroy);
-  mlir::acc::YieldOp::create(builder, loc, retVal);
+      needsDestroy, destroyValues);
+  llvm::SmallVector<mlir::Value> initResults{retVal};
+  initResults.append(destroyValues);
+  mlir::acc::YieldOp::create(builder, loc, initResults);
   // Create destroy region and generate destruction if requested.
   if (needsDestroy) {
     llvm::SmallVector<mlir::Type> destroyArgsTy;
@@ -491,6 +494,10 @@ static RecipeOp genRecipeOp(
     destroyArgsTy.push_back(ty);
     destroyArgsLoc.push_back(loc);
     destroyArgsLoc.push_back(loc);
+    for (mlir::Value destroyValue : destroyValues) {
+      destroyArgsTy.push_back(destroyValue.getType());
+      destroyArgsLoc.push_back(loc);
+    }
     // Append bounds arguments (if any) in the same order as init region
     if (argsTy.size() > 1) {
       destroyArgsTy.append(argsTy.begin() + 1, argsTy.end());
@@ -502,11 +509,14 @@ static RecipeOp genRecipeOp(
         destroyArgsTy, destroyArgsLoc);
     builder.setInsertionPointToEnd(destroyBlock);
 
-    llvm::SmallVector<mlir::Value> destroyBounds =
-        getRecipeBounds(builder, loc, dataOperationBounds,
-                        destroyBlock->getArguments().drop_front(2));
+    llvm::SmallVector<mlir::Value> destroyBounds = getRecipeBounds(
+        builder, loc, dataOperationBounds,
+        destroyBlock->getArguments().drop_front(2 + destroyValues.size()));
+    mlir::ValueRange destroyArgs =
+        destroyBlock->getArguments().slice(2, destroyValues.size());
     [[maybe_unused]] bool success = mappableTy.generatePrivateDestroy(
-        builder, loc, destroyBlock->getArgument(1), destroyBounds, varInfo);
+        builder, loc, destroyBlock->getArgument(1), destroyArgs, destroyBounds,
+        varInfo);
     assert(success && "failed to generate destroy region");
     mlir::acc::TerminatorOp::create(builder, loc);
   }
diff --git a/flang/test/Fir/OpenACC/recipe-populate-private.mlir b/flang/test/Fir/OpenACC/recipe-populate-private.mlir
index f5200f70a3ba8..c5e37db7d450b 100644
--- a/flang/test/Fir/OpenACC/recipe-populate-private.mlir
+++ b/flang/test/Fir/OpenACC/recipe-populate-private.mlir
@@ -115,7 +115,10 @@ func.func @test_derived() {
 // 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: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<f64>>>):
+// 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:   acc.terminator
 // CHECK: }
 
@@ -131,13 +134,23 @@ func.func @test_box_heap_scalar() {
 // Test box type with pointer scalar (needs destroy)
 // CHECK: acc.private.recipe @private_box_ptr_scalar : !fir.ref<!fir.box<!fir.ptr<i32>>> init {
 // CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.ptr<i32>>>):
-// CHECK:   %[[SCALAR:.*]] = fir.allocmem i32
-// CHECK:   %[[EMBOX:.*]] = fir.embox %[[SCALAR]] : (!fir.heap<i32>) -> !fir.box<!fir.ptr<i32>>
+// CHECK:   %[[PRIVATE_ALLOC:.*]] = fir.if {{.*}} -> (!fir.heap<i32>) {
+// CHECK:     %[[SCALAR:.*]] = fir.allocmem i32
+// CHECK:     fir.result %[[SCALAR]] : !fir.heap<i32>
+// CHECK:   } else {
+// CHECK:     %[[NULL_ALLOC:.*]] = fir.zero_bits !fir.heap<i32>
+// CHECK:     fir.result %[[NULL_ALLOC]] : !fir.heap<i32>
+// CHECK:   }
+// CHECK:   %[[EMBOX:.*]] = fir.embox %[[PRIVATE_ALLOC]] : (!fir.heap<i32>) -> !fir.box<!fir.ptr<i32>>
 // CHECK:   %[[BOXALLOC:.*]] = fir.alloca !fir.box<!fir.ptr<i32>>
 // CHECK:   fir.store %[[EMBOX]] to %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.ptr<i32>>>
-// CHECK:   acc.yield %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.ptr<i32>>>
+// CHECK:   acc.yield %[[BOXALLOC]], %[[PRIVATE_ALLOC]] : !fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.heap<i32>
 // CHECK: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.ptr<i32>>>, %{{.*}}: !fir.ref<!fir.box<!fir.ptr<i32>>>):
+// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.ptr<i32>>>, %{{.*}}: !fir.ref<!fir.box<!fir.ptr<i32>>>, %[[PRIVATE_ALLOC:.*]]: !fir.heap<i32>):
+// CHECK-NOT: fir.box_addr
+// CHECK:   fir.if {{.*}} {
+// CHECK:     fir.freemem %[[PRIVATE_ALLOC]] : !fir.heap<i32>
+// CHECK:   }
 // CHECK:   acc.terminator
 // CHECK: }
 
@@ -156,7 +169,10 @@ func.func @test_box_ptr_scalar() {
 // 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: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>):
+// 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:   acc.terminator
 // CHECK: }
 
@@ -191,10 +207,21 @@ func.func @test_box_heap_array_2d() {
 // Test box type with pointer array (needs destroy)
 // CHECK: acc.private.recipe @private_box_ptr_array : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> init {
 // CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>):
+// CHECK:   %[[PRIVATE_ALLOC:.*]] = fir.if {{.*}} -> (!fir.heap<!fir.array<?xf32>>) {
+// CHECK:     %[[ARRAY:.*]] = fir.allocmem !fir.array<?xf32>
+// CHECK:     fir.result %[[ARRAY]] : !fir.heap<!fir.array<?xf32>>
+// CHECK:   } else {
+// CHECK:     %[[NULL_ALLOC:.*]] = fir.zero_bits !fir.heap<!fir.array<?xf32>>
+// CHECK:     fir.result %[[NULL_ALLOC]] : !fir.heap<!fir.array<?xf32>>
+// CHECK:   }
 // CHECK:   %[[BOXALLOC:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?xf32>>>
-// CHECK:   acc.yield %[[BOXALLOC]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
+// CHECK:   acc.yield %[[BOXALLOC]], %[[PRIVATE_ALLOC]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, !fir.heap<!fir.array<?xf32>>
 // CHECK: } destroy {
-// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>):
+// CHECK: ^bb0(%{{.*}}: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, %{{.*}}: !fir.ref<!fir.box<!fir.ptr<!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: }
 
diff ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/219052


More information about the Mlir-commits mailing list