[flang-commits] [flang] [flang] Code generation for fir.pack/unpack_array. (PR #132080)

Slava Zakharin via flang-commits flang-commits at lists.llvm.org
Wed Mar 19 12:19:58 PDT 2025


https://github.com/vzakhari updated https://github.com/llvm/llvm-project/pull/132080

>From 81b3e3decebbdb8c141cbbaa78490b2d3bb6b04f Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Tue, 11 Mar 2025 12:52:54 -0700
Subject: [PATCH 1/2] [flang] Code generation for fir.pack/unpack_array.

The code generation relies on `ShallowCopyDirect` runtime
to copy data between the original and the temporary arrays
(both directions). The allocations are done by the compiler
generated code. The heap allocations could have been passed
to `ShallowCopy` runtime, but I decided to expose the allocations
so that the temporary descriptor passed to `ShallowCopyDirect`
has `nocapture` - maybe this will be better for LLVM optimizations.
---
 .../flang/Optimizer/CodeGen/CGPasses.td       |   11 +
 .../include/flang/Optimizer/CodeGen/CodeGen.h |    1 +
 flang/lib/Optimizer/CodeGen/CMakeLists.txt    |    1 +
 .../Optimizer/CodeGen/LowerRepackArrays.cpp   |  403 ++++++
 flang/lib/Optimizer/Passes/Pipelines.cpp      |    1 +
 flang/test/Driver/bbc-mlir-pass-pipeline.f90  |    1 +
 .../test/Driver/mlir-debug-pass-pipeline.f90  |    1 +
 flang/test/Driver/mlir-pass-pipeline.f90      |    1 +
 flang/test/Fir/basic-program.fir              |    1 +
 flang/test/Transforms/lower-repack-arrays.fir | 1124 +++++++++++++++++
 10 files changed, 1545 insertions(+)
 create mode 100644 flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp
 create mode 100644 flang/test/Transforms/lower-repack-arrays.fir

diff --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
index 2e097faec5403..df0ecf5540776 100644
--- a/flang/include/flang/Optimizer/CodeGen/CGPasses.td
+++ b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
@@ -99,4 +99,15 @@ def BoxedProcedurePass : Pass<"boxed-procedure", "mlir::ModuleOp"> {
   ];
 }
 
+def LowerRepackArraysPass : Pass<"lower-repack-arrays", "mlir::ModuleOp"> {
+  let summary = "Convert fir.pack/unpack_array to other FIR operations";
+  let description = [{
+    Convert fir.pack/unpack_array operations to other FIR operations
+    and Fortran runtime calls that implement the semantics
+    of packing/unpacking.
+  }];
+  let dependentDialects = ["fir::FIROpsDialect", "mlir::arith::ArithDialect",
+                           "mlir::func::FuncDialect"];
+}
+
 #endif // FORTRAN_OPTIMIZER_CODEGEN_FIR_PASSES
diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index 255b1950c8425..0398d0f248e08 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -26,6 +26,7 @@ struct NameUniquer;
 #define GEN_PASS_DECL_CODEGENREWRITE
 #define GEN_PASS_DECL_TARGETREWRITEPASS
 #define GEN_PASS_DECL_BOXEDPROCEDUREPASS
+#define GEN_PASS_DECL_LOWERREPACKARRAYSPASS
 #include "flang/Optimizer/CodeGen/CGPasses.h.inc"
 
 /// FIR to LLVM translation pass options.
diff --git a/flang/lib/Optimizer/CodeGen/CMakeLists.txt b/flang/lib/Optimizer/CodeGen/CMakeLists.txt
index 553c20bb85d38..f730c7fd03948 100644
--- a/flang/lib/Optimizer/CodeGen/CMakeLists.txt
+++ b/flang/lib/Optimizer/CodeGen/CMakeLists.txt
@@ -4,6 +4,7 @@ add_flang_library(FIRCodeGen
   CodeGen.cpp
   CodeGenOpenMP.cpp
   FIROpPatterns.cpp
+  LowerRepackArrays.cpp
   PreCGRewrite.cpp
   TBAABuilder.cpp
   Target.cpp
diff --git a/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp b/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp
new file mode 100644
index 0000000000000..c109dc4732ca5
--- /dev/null
+++ b/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp
@@ -0,0 +1,403 @@
+//===-- LowerRepackArrays.cpp
+//------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/CodeGen/CodeGen.h"
+
+#include "flang/Optimizer/Builder/BoxValue.h"
+#include "flang/Optimizer/Builder/Character.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Builder/MutableBox.h"
+#include "flang/Optimizer/Builder/Runtime/Allocatable.h"
+#include "flang/Optimizer/Builder/Runtime/Transformational.h"
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Support/DataLayout.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+
+namespace fir {
+#define GEN_PASS_DEF_LOWERREPACKARRAYSPASS
+#include "flang/Optimizer/CodeGen/CGPasses.h.inc"
+} // namespace fir
+
+#define DEBUG_TYPE "lower-repack-arrays"
+
+namespace {
+class RepackArrayConversion {
+public:
+  RepackArrayConversion(std::optional<mlir::DataLayout> dataLayout)
+      : dataLayout(dataLayout) {}
+
+protected:
+  std::optional<mlir::DataLayout> dataLayout;
+
+  static bool canAllocateTempOnStack(mlir::Value box);
+};
+
+class PackArrayConversion : public mlir::OpRewritePattern<fir::PackArrayOp>,
+                            RepackArrayConversion {
+public:
+  using OpRewritePattern::OpRewritePattern;
+
+  PackArrayConversion(mlir::MLIRContext *context,
+                      std::optional<mlir::DataLayout> dataLayout)
+      : OpRewritePattern(context), RepackArrayConversion(dataLayout) {}
+
+  mlir::LogicalResult
+  matchAndRewrite(fir::PackArrayOp op,
+                  mlir::PatternRewriter &rewriter) const override;
+
+private:
+  static constexpr llvm::StringRef bufferName = ".repacked";
+
+  static mlir::Value allocateTempBuffer(fir::FirOpBuilder &builder,
+                                        mlir::Location loc, bool useStack,
+                                        mlir::Value origBox,
+                                        llvm::ArrayRef<mlir::Value> extents,
+                                        llvm::ArrayRef<mlir::Value> typeParams);
+};
+
+class UnpackArrayConversion : public mlir::OpRewritePattern<fir::UnpackArrayOp>,
+                              RepackArrayConversion {
+public:
+  using OpRewritePattern::OpRewritePattern;
+
+  UnpackArrayConversion(mlir::MLIRContext *context,
+                        std::optional<mlir::DataLayout> dataLayout)
+      : OpRewritePattern(context), RepackArrayConversion(dataLayout) {}
+
+  mlir::LogicalResult
+  matchAndRewrite(fir::UnpackArrayOp op,
+                  mlir::PatternRewriter &rewriter) const override;
+};
+} // anonymous namespace
+
+bool RepackArrayConversion::canAllocateTempOnStack(mlir::Value box) {
+  return !fir::isPolymorphicType(box.getType());
+}
+
+mlir::LogicalResult
+PackArrayConversion::matchAndRewrite(fir::PackArrayOp op,
+                                     mlir::PatternRewriter &rewriter) const {
+  mlir::Location loc = op.getLoc();
+  fir::FirOpBuilder builder(rewriter, op.getOperation());
+  if (op.getMaxSize() || op.getMaxElementSize() || op.getMinStride())
+    TODO(loc, "fir.pack_array with constraints");
+  if (op.getHeuristics() != fir::PackArrayHeuristics::None)
+    TODO(loc, "fir.pack_array with heuristics");
+
+  mlir::Value box = op.getArray();
+  llvm::SmallVector<mlir::Value> typeParams(op.getTypeparams().begin(),
+                                            op.getTypeparams().end());
+  // TODO: set non-default lower bounds on fir.pack_array,
+  // so that we can preserve lower bounds in the temporary box.
+  fir::BoxValue boxValue(box, /*lbounds=*/{}, typeParams);
+  mlir::Type boxType = boxValue.getBoxTy();
+  unsigned rank = boxValue.rank();
+  mlir::Type indexType = builder.getIndexType();
+  mlir::Value zero = fir::factory::createZeroValue(builder, loc, indexType);
+
+  // Fetch the extents from the box, and see if the array
+  // is not empty.
+  // If the type params are not explicitly provided, then we must also
+  // fetch the type parameters from the box.
+  //
+  // bool isNotEmpty;
+  // vector<int64_t> extents;
+  // if (IsPresent(box) && !IsContiguous[UpTo](box[, 1])) {
+  //   isNotEmpty = box->base_addr != null;
+  //   extents = SHAPE(box);
+  // } else {
+  //   isNotEmpty = false;
+  //   extents = vector<int64_t>(rank, 0);
+  // }
+
+  unsigned numTypeParams = 0;
+  if (typeParams.size() == 0) {
+    if (auto recordType = mlir::dyn_cast<fir::RecordType>(boxValue.getEleTy()))
+      if (recordType.getNumLenParams() != 0)
+        TODO(loc,
+             "allocating temporary for a parameterized derived type array");
+
+    if (auto charType = mlir::dyn_cast<fir::CharacterType>(boxValue.getEleTy()))
+      if (charType.hasDynamicLen())
+        numTypeParams = 1;
+  }
+
+  // For now we have to always check if the box is present.
+  mlir::Type predicateType = builder.getI1Type();
+  auto isPresent =
+      builder.create<fir::IsPresentOp>(loc, predicateType, boxValue.getAddr());
+
+  // The results of the IfOp are:
+  //   (extent1, ..., extentN, typeParam1, ..., typeParamM, isNotEmpty)
+  // The number of results is rank + numTypeParams + 1.
+  llvm::SmallVector<mlir::Type> ifTypes(rank + numTypeParams, indexType);
+  ifTypes.push_back(predicateType);
+  llvm::SmallVector<mlir::Value> negativeResult(rank + numTypeParams, zero);
+  negativeResult.push_back(
+      fir::factory::createZeroValue(builder, loc, predicateType));
+  bool failedTypeParams = false;
+  llvm::SmallVector<mlir::Value> extentsAndPredicate =
+      builder
+          .genIfOp(loc, ifTypes, isPresent,
+                   /*withElseRegion=*/true)
+          .genThen([&]() {
+            // The box is present.
+            auto isContiguous = builder.create<fir::IsContiguousBoxOp>(
+                loc, box, op.getInnermost());
+            llvm::SmallVector<mlir::Value> extentsAndPredicate =
+                builder
+                    .genIfOp(loc, ifTypes, isContiguous,
+                             /*withElseRegion=*/true)
+                    .genThen([&]() {
+                      // Box is contiguous, return zero.
+                      builder.create<fir::ResultOp>(loc, negativeResult);
+                    })
+                    .genElse([&]() {
+                      // Get the extents.
+                      llvm::SmallVector<mlir::Value> results =
+                          fir::factory::readExtents(builder, loc, boxValue);
+
+                      // Get the type parameters from the box, if needed.
+                      llvm::SmallVector<mlir::Value> assumedTypeParams;
+                      if (numTypeParams != 0) {
+                        if (auto charType = mlir::dyn_cast<fir::CharacterType>(
+                                boxValue.getEleTy()))
+                          if (charType.hasDynamicLen()) {
+                            fir::factory::CharacterExprHelper charHelper(
+                                builder, loc);
+                            mlir::Value len = charHelper.readLengthFromBox(
+                                boxValue.getAddr(), charType);
+                            assumedTypeParams.push_back(
+                                builder.createConvert(loc, indexType, len));
+                          }
+
+                        if (numTypeParams != assumedTypeParams.size()) {
+                          failedTypeParams = true;
+                          assumedTypeParams.append(
+                              numTypeParams - assumedTypeParams.size(), zero);
+                        }
+                      }
+                      results.append(assumedTypeParams);
+
+                      auto dataAddr = builder.create<fir::BoxAddrOp>(
+                          loc, boxValue.getMemTy(), boxValue.getAddr());
+                      auto isNotEmpty = builder.create<fir::IsPresentOp>(
+                          loc, predicateType, dataAddr);
+                      results.push_back(isNotEmpty);
+                      builder.create<fir::ResultOp>(loc, results);
+                    })
+                    .getResults();
+
+            builder.create<fir::ResultOp>(loc, extentsAndPredicate);
+          })
+          .genElse([&]() {
+            // Box is absent, nothing to do.
+            builder.create<fir::ResultOp>(loc, negativeResult);
+          })
+          .getResults();
+
+  if (failedTypeParams)
+    return emitError(loc) << "failed to compute the type parameters for "
+                          << op.getOperation() << '\n';
+
+  // The last result is the isNotEmpty predicate value.
+  mlir::Value isNotEmpty = extentsAndPredicate.pop_back_val();
+  // If fir.pack_array does not specify type parameters, but they are needed
+  // for the type, then use the parameters fetched from the box.
+  if (typeParams.size() == 0 && numTypeParams != 0) {
+    assert(extentsAndPredicate.size() > numTypeParams);
+    typeParams.append(extentsAndPredicate.end() - numTypeParams,
+                      extentsAndPredicate.end());
+    extentsAndPredicate.pop_back_n(numTypeParams);
+  }
+  // The remaining resulst are the extents.
+  llvm::SmallVector<mlir::Value> extents = std::move(extentsAndPredicate);
+  assert(extents.size() == rank);
+
+  mlir::Value tempBox;
+  // Allocate memory for the temporary, if allocating on stack.
+  // We can do it unconditionally, even if size is zero.
+  if (op.getStack() && canAllocateTempOnStack(boxValue.getAddr())) {
+    tempBox = allocateTempBuffer(builder, loc, /*useStack=*/true,
+                                 boxValue.getAddr(), extents, typeParams);
+    if (!tempBox)
+      return rewriter.notifyMatchFailure(op,
+                                         "failed to produce stack allocation");
+  }
+
+  mlir::Value newResult =
+      builder.genIfOp(loc, {boxType}, isNotEmpty, /*withElseRegion=*/true)
+          .genThen([&]() {
+            // Do the heap allocation conditionally.
+            if (!tempBox)
+              tempBox =
+                  allocateTempBuffer(builder, loc, /*useStack=*/false,
+                                     boxValue.getAddr(), extents, typeParams);
+
+            // Do the copy, if needed, and return the new box (shaped same way
+            // as the original one).
+            if (!op.getNoCopy())
+              fir::runtime::genShallowCopy(builder, loc, tempBox,
+                                           boxValue.getAddr(),
+                                           /*resultIsAllocated=*/true);
+
+            // Set the lower bounds after the original box.
+            mlir::Value shape;
+            if (!boxValue.getLBounds().empty()) {
+              shape = builder.genShape(loc, boxValue.getLBounds(), extents);
+            }
+
+            // Rebox the temporary box to make its type the same as
+            // the original box's.
+            tempBox = builder.create<fir::ReboxOp>(loc, boxType, tempBox, shape,
+                                                   /*slice=*/nullptr);
+            builder.create<fir::ResultOp>(loc, tempBox);
+          })
+          .genElse([&]() {
+            // Return original box.
+            builder.create<fir::ResultOp>(loc, boxValue.getAddr());
+          })
+          .getResults()[0];
+
+  rewriter.replaceOp(op, newResult);
+  return mlir::success();
+}
+
+mlir::Value PackArrayConversion::allocateTempBuffer(
+    fir::FirOpBuilder &builder, mlir::Location loc, bool useStack,
+    mlir::Value origBox, llvm::ArrayRef<mlir::Value> extents,
+    llvm::ArrayRef<mlir::Value> typeParams) {
+  auto tempType = mlir::cast<fir::SequenceType>(
+      fir::extractSequenceType(origBox.getType()));
+  assert(tempType.getDimension() == extents.size() &&
+         "number of extents does not match the rank");
+
+  if (fir::isPolymorphicType(origBox.getType())) {
+    // Use runtime to allocate polymorphic temporary vector using the dynamic
+    // type of the original box and the provided numElements.
+    // TODO: try to generalize it with BufferizeHLFIR.cpp:createArrayTemp().
+
+    // We cannot allocate polymorphic entity on stack.
+    // Return null, and allow the caller to reissue the call.
+    if (useStack)
+      return nullptr;
+
+    mlir::Type indexType = builder.getIndexType();
+    mlir::Type boxHeapType = fir::HeapType::get(tempType);
+    mlir::Value boxAlloc = fir::factory::genNullBoxStorage(
+        builder, loc, fir::ClassType::get(boxHeapType));
+    fir::runtime::genAllocatableApplyMold(builder, loc, boxAlloc, origBox,
+                                          tempType.getDimension());
+    mlir::Value one = builder.createIntegerConstant(loc, indexType, 1);
+    unsigned dim = 0;
+    for (mlir::Value extent : extents) {
+      mlir::Value dimIndex =
+          builder.createIntegerConstant(loc, indexType, dim++);
+      fir::runtime::genAllocatableSetBounds(builder, loc, boxAlloc, dimIndex,
+                                            one, extent);
+    }
+
+    if (!typeParams.empty()) {
+      // We should call AllocatableSetDerivedLength() here.
+      TODO(loc,
+           "polymorphic type with length parameters in PackArrayConversion");
+    }
+
+    fir::runtime::genAllocatableAllocate(builder, loc, boxAlloc);
+    return builder.create<fir::LoadOp>(loc, boxAlloc);
+  }
+
+  // Allocate non-polymorphic temporary on stack or in heap.
+  mlir::Value newBuffer;
+  if (useStack)
+    newBuffer =
+        builder.createTemporary(loc, tempType, bufferName, extents, typeParams);
+  else
+    newBuffer = builder.createHeapTemporary(loc, tempType, bufferName, extents,
+                                            typeParams);
+
+  mlir::Type ptrType = newBuffer.getType();
+  mlir::Type tempBoxType = fir::BoxType::get(mlir::isa<fir::HeapType>(ptrType)
+                                                 ? ptrType
+                                                 : fir::unwrapRefType(ptrType));
+  mlir::Value shape = builder.genShape(loc, extents);
+  mlir::Value newBox =
+      builder.createBox(loc, tempBoxType, newBuffer, shape, /*slice=*/nullptr,
+                        typeParams, /*tdesc=*/nullptr);
+  return newBox;
+}
+
+mlir::LogicalResult
+UnpackArrayConversion::matchAndRewrite(fir::UnpackArrayOp op,
+                                       mlir::PatternRewriter &rewriter) const {
+  mlir::Location loc = op.getLoc();
+  fir::FirOpBuilder builder(rewriter, op.getOperation());
+  mlir::Type predicateType = builder.getI1Type();
+  mlir::Type indexType = builder.getIndexType();
+  mlir::Value tempBox = op.getTemp();
+  mlir::Value originalBox = op.getOriginal();
+
+  // For now we have to always check if the box is present.
+  auto isPresent =
+      builder.create<fir::IsPresentOp>(loc, predicateType, originalBox);
+
+  builder.genIfThen(loc, isPresent).genThen([&]() {
+    mlir::Type addrType =
+        fir::HeapType::get(fir::extractSequenceType(tempBox.getType()));
+    mlir::Value tempAddr =
+        builder.create<fir::BoxAddrOp>(loc, addrType, tempBox);
+    mlir::Value tempAddrAsIndex =
+        builder.createConvert(loc, indexType, tempAddr);
+    mlir::Value originalAddr =
+        builder.create<fir::BoxAddrOp>(loc, addrType, originalBox);
+    originalAddr = builder.createConvert(loc, indexType, originalAddr);
+
+    auto isNotSame = builder.create<mlir::arith::CmpIOp>(
+        loc, mlir::arith::CmpIPredicate::ne, tempAddrAsIndex, originalAddr);
+    builder.genIfThen(loc, isNotSame).genThen([&]() {});
+    // Copy from temporary to the original.
+    if (!op.getNoCopy())
+      fir::runtime::genShallowCopy(builder, loc, originalBox, tempBox,
+                                   /*resultIsAllocated=*/true);
+
+    // Deallocate, if it was allocated in heap.
+    if (!op.getStack())
+      builder.create<fir::FreeMemOp>(loc, tempAddr);
+  });
+  rewriter.eraseOp(op);
+  return mlir::success();
+}
+
+namespace {
+class LowerRepackArraysPass
+    : public fir::impl::LowerRepackArraysPassBase<LowerRepackArraysPass> {
+public:
+  using LowerRepackArraysPassBase<
+      LowerRepackArraysPass>::LowerRepackArraysPassBase;
+
+  void runOnOperation() override final {
+    auto *context = &getContext();
+    mlir::ModuleOp module = getOperation();
+    std::optional<mlir::DataLayout> dl = fir::support::getOrSetMLIRDataLayout(
+        module, /*allowDefaultLayout=*/false);
+    mlir::RewritePatternSet patterns(context);
+    patterns.insert<PackArrayConversion>(context, dl);
+    patterns.insert<UnpackArrayConversion>(context, dl);
+    mlir::GreedyRewriteConfig config;
+    config.enableRegionSimplification =
+        mlir::GreedySimplifyRegionLevel::Disabled;
+    (void)applyPatternsGreedily(module, std::move(patterns), config);
+  }
+};
+
+} // anonymous namespace
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 3aea021e596f6..6ec19556625bc 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -198,6 +198,7 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm,
   pm.addPass(fir::createPolymorphicOpConversion());
   pm.addPass(fir::createAssumedRankOpConversion());
 
+  pm.addPass(fir::createLowerRepackArraysPass());
   // Expand FIR operations that may use SCF dialect for their
   // implementation. This is a mandatory pass.
   pm.addPass(fir::createSimplifyFIROperations(
diff --git a/flang/test/Driver/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
index 276ef818622a1..137c19608c38f 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -47,6 +47,7 @@
 
 ! CHECK-NEXT: PolymorphicOpConversion
 ! CHECK-NEXT: AssumedRankOpConversion
+! CHECK-NEXT: LowerRepackArraysPass
 ! CHECK-NEXT: SimplifyFIROperations
 
 ! CHECK-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
diff --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90
index 70fa0cf5ae47c..42a71b2d6adc3 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -77,6 +77,7 @@
 
 ! ALL-NEXT: PolymorphicOpConversion
 ! ALL-NEXT: AssumedRankOpConversion
+! ALL-NEXT: LowerRepackArraysPass
 ! ALL-NEXT: SimplifyFIROperations
 
 ! ALL-NEXT: Pipeline Collection : ['fir.global', 'func.func', 'omp.declare_reduction', 'omp.private']
diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index 852764be1f136..45370895db397 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -101,6 +101,7 @@
 
 ! ALL-NEXT: PolymorphicOpConversion
 ! ALL-NEXT: AssumedRankOpConversion
+! ALL-NEXT: LowerRepackArraysPass
 ! ALL-NEXT: SimplifyFIROperations
 ! O2-NEXT:  AddAliasTags
 
diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
index 90bff80da1915..ded42886aad44 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -99,6 +99,7 @@ func.func @_QQmain() {
 
 // PASSES-NEXT: PolymorphicOpConversion
 // PASSES-NEXT: AssumedRankOpConversion
+// PASSES-NEXT: LowerRepackArraysPass
 // PASSES-NEXT: SimplifyFIROperations
 // PASSES-NEXT: AddAliasTags
 
diff --git a/flang/test/Transforms/lower-repack-arrays.fir b/flang/test/Transforms/lower-repack-arrays.fir
new file mode 100644
index 0000000000000..b09363e62f78b
--- /dev/null
+++ b/flang/test/Transforms/lower-repack-arrays.fir
@@ -0,0 +1,1124 @@
+// RUN: fir-opt --lower-repack-arrays %s | FileCheck %s
+
+// Test trivial type array repacking.
+// CHECK-LABEL:   func.func @_QPtest1(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+func.func @_QPtest1(%arg0: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_7:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           %[[VAL_8:.*]]:3 = fir.if %[[VAL_7]] -> (index, index, i1) {
+// CHECK:             %[[VAL_9:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:             %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
+// CHECK:               %[[VAL_14:.*]] = fir.is_present %[[VAL_13]] : (!fir.ref<!fir.array<?x?xf32>>) -> i1
+// CHECK:               fir.result %[[VAL_11]]#1, %[[VAL_12]]#1, %[[VAL_14]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_10]]#0, %[[VAL_10]]#1, %[[VAL_10]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_16:.*]] = fir.if %[[VAL_8]]#2 -> (!fir.box<!fir.array<?x?xf32>>) {
+// CHECK:             %[[VAL_18:.*]] = fir.allocmem !fir.array<?x?xf32>, %[[VAL_8]]#0, %[[VAL_8]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_19:.*]] = fir.shape %[[VAL_8]]#0, %[[VAL_8]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_20:.*]] = fir.embox %[[VAL_18]](%[[VAL_19]]) : (!fir.heap<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.box<!fir.heap<!fir.array<?x?xf32>>>
+// CHECK:             %[[VAL_21:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_22:.*]] = fir.convert %[[VAL_20]] : (!fir.box<!fir.heap<!fir.array<?x?xf32>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.rebox %[[VAL_20]] : (!fir.box<!fir.heap<!fir.array<?x?xf32>>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:             fir.result %[[VAL_25]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_26:.*]] = fir.declare %[[VAL_16]] dummy_scope %[[VAL_6]] {uniq_name = "_QFtest1Ex"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest1Ex"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_27:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           fir.if %[[VAL_27]] {
+// CHECK:             %[[VAL_28:.*]] = fir.box_addr %[[VAL_16]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_28]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_32:.*]] = arith.cmpi ne, %[[VAL_30]], %[[VAL_31]] : index
+// CHECK:             fir.if %[[VAL_32]] {
+// CHECK:               %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_34:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_16]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_28]] : !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.box<!fir.array<?x?xf32>>
+  return
+}
+
+// Test 'stack whole' repacking.
+// CHECK-LABEL:   func.func @_QPtest1_whole(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+func.func @_QPtest1_whole(%arg0: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_7:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           %[[VAL_8:.*]]:3 = fir.if %[[VAL_7]] -> (index, index, i1) {
+// CHECK:             %[[VAL_9:.*]] = fir.is_contiguous_box %[[VAL_0]] whole : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:             %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
+// CHECK:               %[[VAL_14:.*]] = fir.is_present %[[VAL_13]] : (!fir.ref<!fir.array<?x?xf32>>) -> i1
+// CHECK:               fir.result %[[VAL_11]]#1, %[[VAL_12]]#1, %[[VAL_14]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_10]]#0, %[[VAL_10]]#1, %[[VAL_10]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_16:.*]] = fir.alloca !fir.array<?x?xf32>, %[[VAL_8]]#0, %[[VAL_8]]#1 {bindc_name = ".repacked"}
+// CHECK:           %[[VAL_18:.*]] = fir.shape %[[VAL_8]]#0, %[[VAL_8]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:           %[[VAL_19:.*]] = fir.embox %[[VAL_16]](%[[VAL_18]]) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_20:.*]] = fir.if %[[VAL_8]]#2 -> (!fir.box<!fir.array<?x?xf32>>) {
+// CHECK:             %[[VAL_21:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_22:.*]] = fir.convert %[[VAL_19]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.rebox %[[VAL_19]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:             fir.result %[[VAL_25]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 stack whole : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_26:.*]] = fir.declare %[[VAL_20]] dummy_scope %[[VAL_6]] {uniq_name = "_QFtest1_wholeEx"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest1_wholeEx"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_27:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           fir.if %[[VAL_27]] {
+// CHECK:             %[[VAL_28:.*]] = fir.box_addr %[[VAL_20]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_28]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_32:.*]] = arith.cmpi ne, %[[VAL_30]], %[[VAL_31]] : index
+// CHECK:             fir.if %[[VAL_32]] {
+// CHECK:               %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_34:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_20]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 stack : !fir.box<!fir.array<?x?xf32>>
+  return
+}
+
+// Test unpacking with no_copy.
+// CHECK-LABEL:   func.func @_QPtest1_in(
+// CHECK-SAME:                           %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+func.func @_QPtest1_in(%arg0: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_3:.*]] = arith.constant false
+// CHECK:           %[[VAL_4:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_5:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_6:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           %[[VAL_7:.*]]:3 = fir.if %[[VAL_6]] -> (index, index, i1) {
+// CHECK:             %[[VAL_8:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:             %[[VAL_9:.*]]:3 = fir.if %[[VAL_8]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_4]], %[[VAL_4]], %[[VAL_3]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_10:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_4]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_2]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
+// CHECK:               %[[VAL_13:.*]] = fir.is_present %[[VAL_12]] : (!fir.ref<!fir.array<?x?xf32>>) -> i1
+// CHECK:               fir.result %[[VAL_10]]#1, %[[VAL_11]]#1, %[[VAL_13]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_9]]#0, %[[VAL_9]]#1, %[[VAL_9]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_4]], %[[VAL_4]], %[[VAL_3]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_15:.*]] = fir.if %[[VAL_7]]#2 -> (!fir.box<!fir.array<?x?xf32>>) {
+// CHECK:             %[[VAL_17:.*]] = fir.allocmem !fir.array<?x?xf32>, %[[VAL_7]]#0, %[[VAL_7]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_18:.*]] = fir.shape %[[VAL_7]]#0, %[[VAL_7]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_19:.*]] = fir.embox %[[VAL_17]](%[[VAL_18]]) : (!fir.heap<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.box<!fir.heap<!fir.array<?x?xf32>>>
+// CHECK:             %[[VAL_20:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_21:.*]] = fir.convert %[[VAL_19]] : (!fir.box<!fir.heap<!fir.array<?x?xf32>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_22:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_20]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_24:.*]] = fir.rebox %[[VAL_19]] : (!fir.box<!fir.heap<!fir.array<?x?xf32>>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:             fir.result %[[VAL_24]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_25:.*]] = fir.declare %[[VAL_15]] dummy_scope %[[VAL_5]] {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest1_inEx"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+  %2 = fir.declare %1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QFtest1_inEx"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_26:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           fir.if %[[VAL_26]] {
+// CHECK:             %[[VAL_27:.*]] = fir.box_addr %[[VAL_15]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_27]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_28:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_28]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_31:.*]] = arith.cmpi ne, %[[VAL_29]], %[[VAL_30]] : index
+// CHECK:             fir.if %[[VAL_31]] {
+// CHECK:               fir.freemem %[[VAL_27]] : !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap no_copy : !fir.box<!fir.array<?x?xf32>>
+  return
+}
+
+// Test packing with no_copy.
+// CHECK-LABEL:   func.func @_QPtest1_out(
+// CHECK-SAME:                            %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+func.func @_QPtest1_out(%arg0: !fir.box<!fir.array<?x?xf32>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_3:.*]] = arith.constant false
+// CHECK:           %[[VAL_4:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_5:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_6:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           %[[VAL_7:.*]]:3 = fir.if %[[VAL_6]] -> (index, index, i1) {
+// CHECK:             %[[VAL_8:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:             %[[VAL_9:.*]]:3 = fir.if %[[VAL_8]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_4]], %[[VAL_4]], %[[VAL_3]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_10:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_4]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_2]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
+// CHECK:               %[[VAL_13:.*]] = fir.is_present %[[VAL_12]] : (!fir.ref<!fir.array<?x?xf32>>) -> i1
+// CHECK:               fir.result %[[VAL_10]]#1, %[[VAL_11]]#1, %[[VAL_13]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_9]]#0, %[[VAL_9]]#1, %[[VAL_9]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_4]], %[[VAL_4]], %[[VAL_3]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_15:.*]] = fir.if %[[VAL_7]]#2 -> (!fir.box<!fir.array<?x?xf32>>) {
+// CHECK:             %[[VAL_17:.*]] = fir.allocmem !fir.array<?x?xf32>, %[[VAL_7]]#0, %[[VAL_7]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_18:.*]] = fir.shape %[[VAL_7]]#0, %[[VAL_7]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_19:.*]] = fir.embox %[[VAL_17]](%[[VAL_18]]) : (!fir.heap<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.box<!fir.heap<!fir.array<?x?xf32>>>
+// CHECK:             %[[VAL_20:.*]] = fir.rebox %[[VAL_19]] : (!fir.box<!fir.heap<!fir.array<?x?xf32>>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:             fir.result %[[VAL_20]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?xf32>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost no_copy : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_21:.*]] = fir.declare %[[VAL_15]] dummy_scope %[[VAL_5]] {fortran_attrs = #fir.var_attrs<intent_out>, uniq_name = "_QFtest1_outEx"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+  %2 = fir.declare %1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<intent_out>, uniq_name = "_QFtest1_outEx"} : (!fir.box<!fir.array<?x?xf32>>, !fir.dscope) -> !fir.box<!fir.array<?x?xf32>>
+// CHECK:           %[[VAL_22:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> i1
+// CHECK:           fir.if %[[VAL_22]] {
+// CHECK:             %[[VAL_23:.*]] = fir.box_addr %[[VAL_15]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_23]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_24:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             %[[VAL_26:.*]] = fir.convert %[[VAL_24]] : (!fir.heap<!fir.array<?x?xf32>>) -> index
+// CHECK:             %[[VAL_27:.*]] = arith.cmpi ne, %[[VAL_25]], %[[VAL_26]] : index
+// CHECK:             fir.if %[[VAL_27]] {
+// CHECK:               %[[VAL_28:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_29:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:               %[[VAL_30:.*]] = fir.convert %[[VAL_15]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.box<none>
+// CHECK:               %[[VAL_31:.*]] = fir.convert %[[VAL_28]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_29]], %[[VAL_30]], %[[VAL_31]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_23]] : !fir.heap<!fir.array<?x?xf32>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.box<!fir.array<?x?xf32>>
+  return
+}
+
+// Test character array with dynamic length and heap allocation
+// CHECK-LABEL:   func.func @_QPtest2(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "n"},
+// CHECK-SAME:                        %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest2(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}, %arg1: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_4:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_5:.*]] = arith.constant false
+// CHECK:           %[[VAL_6:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_7:.*]] = arith.constant 0 : i32
+// CHECK:           %[[VAL_8:.*]] = fir.dummy_scope : !fir.dscope
+  %c0_i32 = arith.constant 0 : i32
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_9:.*]] = fir.declare %[[VAL_0]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest2En"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+  %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "_QFtest2En"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+// CHECK:           %[[VAL_10:.*]] = fir.load %[[VAL_9]] : !fir.ref<i32>
+  %2 = fir.load %1 : !fir.ref<i32>
+// CHECK:           %[[VAL_11:.*]] = arith.cmpi sgt, %[[VAL_10]], %[[VAL_7]] : i32
+  %3 = arith.cmpi sgt, %2, %c0_i32 : i32
+// CHECK:           %[[VAL_12:.*]] = arith.select %[[VAL_11]], %[[VAL_10]], %[[VAL_7]] : i32
+  %4 = arith.select %3, %2, %c0_i32 : i32
+// CHECK:           %[[VAL_13:.*]] = fir.is_present %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           %[[VAL_14:.*]]:3 = fir.if %[[VAL_13]] -> (index, index, i1) {
+// CHECK:             %[[VAL_15:.*]] = fir.is_contiguous_box %[[VAL_1]] innermost : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:             %[[VAL_16:.*]]:3 = fir.if %[[VAL_15]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_17:.*]]:3 = fir.box_dims %[[VAL_1]], %[[VAL_6]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_18:.*]]:3 = fir.box_dims %[[VAL_1]], %[[VAL_4]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_19:.*]] = fir.box_addr %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:               %[[VAL_20:.*]] = fir.is_present %[[VAL_19]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:               fir.result %[[VAL_17]]#1, %[[VAL_18]]#1, %[[VAL_20]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_16]]#0, %[[VAL_16]]#1, %[[VAL_16]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_22:.*]] = fir.if %[[VAL_14]]#2 -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+// CHECK:             %[[VAL_24:.*]] = fir.allocmem !fir.array<?x?x!fir.char<1,?>>(%[[VAL_12]] : i32), %[[VAL_14]]#0, %[[VAL_14]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_25:.*]] = fir.shape %[[VAL_14]]#0, %[[VAL_14]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_26:.*]] = fir.embox %[[VAL_24]](%[[VAL_25]]) typeparams %[[VAL_12]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>, !fir.shape<2>, i32) -> !fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>
+// CHECK:             %[[VAL_27:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_28:.*]] = fir.convert %[[VAL_26]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_27]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_28]], %[[VAL_29]], %[[VAL_30]], %[[VAL_3]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_31:.*]] = fir.rebox %[[VAL_26]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             fir.result %[[VAL_31]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_1]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           }
+  %5 = fir.pack_array %arg1 heap innermost typeparams %4 : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i32) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_32:.*]] = fir.declare %[[VAL_22]] typeparams %[[VAL_12]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest2Ex"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i32, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  %6 = fir.declare %5 typeparams %4 dummy_scope %0 {uniq_name = "_QFtest2Ex"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i32, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_33:.*]] = fir.is_present %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           fir.if %[[VAL_33]] {
+// CHECK:             %[[VAL_34:.*]] = fir.box_addr %[[VAL_22]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_36:.*]] = fir.convert %[[VAL_34]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_35:.*]] = fir.box_addr %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_37:.*]] = fir.convert %[[VAL_35]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_38:.*]] = arith.cmpi ne, %[[VAL_36]], %[[VAL_37]] : index
+// CHECK:             fir.if %[[VAL_38]] {
+// CHECK:               %[[VAL_39:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_40:.*]] = fir.convert %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_41:.*]] = fir.convert %[[VAL_22]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_42:.*]] = fir.convert %[[VAL_39]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_34]] : !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %5 to %arg1 heap : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  return
+}
+
+// Test character array with dynamic length and stack allocation
+// CHECK-LABEL:   func.func @_QPtest2_stack(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "n"},
+// CHECK-SAME:                              %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest2_stack(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}, %arg1: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_4:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_5:.*]] = arith.constant false
+// CHECK:           %[[VAL_6:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_7:.*]] = arith.constant 0 : i32
+// CHECK:           %[[VAL_8:.*]] = fir.dummy_scope : !fir.dscope
+  %c0_i32 = arith.constant 0 : i32
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_9:.*]] = fir.declare %[[VAL_0]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest2_stackEn"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+  %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "_QFtest2_stackEn"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+// CHECK:           %[[VAL_10:.*]] = fir.load %[[VAL_9]] : !fir.ref<i32>
+  %2 = fir.load %1 : !fir.ref<i32>
+// CHECK:           %[[VAL_11:.*]] = arith.cmpi sgt, %[[VAL_10]], %[[VAL_7]] : i32
+  %3 = arith.cmpi sgt, %2, %c0_i32 : i32
+// CHECK:           %[[VAL_12:.*]] = arith.select %[[VAL_11]], %[[VAL_10]], %[[VAL_7]] : i32
+  %4 = arith.select %3, %2, %c0_i32 : i32
+// CHECK:           %[[VAL_13:.*]] = fir.is_present %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           %[[VAL_14:.*]]:3 = fir.if %[[VAL_13]] -> (index, index, i1) {
+// CHECK:             %[[VAL_15:.*]] = fir.is_contiguous_box %[[VAL_1]] innermost : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:             %[[VAL_16:.*]]:3 = fir.if %[[VAL_15]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_17:.*]]:3 = fir.box_dims %[[VAL_1]], %[[VAL_6]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_18:.*]]:3 = fir.box_dims %[[VAL_1]], %[[VAL_4]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_19:.*]] = fir.box_addr %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:               %[[VAL_20:.*]] = fir.is_present %[[VAL_19]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:               fir.result %[[VAL_17]]#1, %[[VAL_18]]#1, %[[VAL_20]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_16]]#0, %[[VAL_16]]#1, %[[VAL_16]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_22:.*]] = fir.alloca !fir.array<?x?x!fir.char<1,?>>(%[[VAL_12]] : i32), %[[VAL_14]]#0, %[[VAL_14]]#1 {bindc_name = ".repacked"}
+// CHECK:           %[[VAL_24:.*]] = fir.shape %[[VAL_14]]#0, %[[VAL_14]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:           %[[VAL_25:.*]] = fir.embox %[[VAL_22]](%[[VAL_24]]) typeparams %[[VAL_12]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>, !fir.shape<2>, i32) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_26:.*]] = fir.if %[[VAL_14]]#2 -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+// CHECK:             %[[VAL_27:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_28:.*]] = fir.convert %[[VAL_25]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_27]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_28]], %[[VAL_29]], %[[VAL_30]], %[[VAL_3]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_31:.*]] = fir.rebox %[[VAL_25]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             fir.result %[[VAL_31]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_1]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           }
+  %5 = fir.pack_array %arg1 stack innermost typeparams %4 : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i32) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_32:.*]] = fir.declare %[[VAL_26]] typeparams %[[VAL_12]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest2_stackEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i32, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  %6 = fir.declare %5 typeparams %4 dummy_scope %0 {uniq_name = "_QFtest2_stackEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, i32, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_33:.*]] = fir.is_present %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           fir.if %[[VAL_33]] {
+// CHECK:             %[[VAL_34:.*]] = fir.box_addr %[[VAL_26]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_36:.*]] = fir.convert %[[VAL_34]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_35:.*]] = fir.box_addr %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_37:.*]] = fir.convert %[[VAL_35]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_38:.*]] = arith.cmpi ne, %[[VAL_36]], %[[VAL_37]] : index
+// CHECK:             fir.if %[[VAL_38]] {
+// CHECK:               %[[VAL_39:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_40:.*]] = fir.convert %[[VAL_1]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_41:.*]] = fir.convert %[[VAL_26]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_42:.*]] = fir.convert %[[VAL_39]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %5 to %arg1 stack : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  return
+}
+
+// Test character array with assumed length and heap allocation.
+// CHECK-LABEL:   func.func @_QPtest3(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest3(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_7:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           %[[VAL_8:.*]]:4 = fir.if %[[VAL_7]] -> (index, index, index, i1) {
+// CHECK:             %[[VAL_9:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:             %[[VAL_10:.*]]:4 = fir.if %[[VAL_9]] -> (index, index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]] = fir.box_elesize %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:               %[[VAL_14:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:               %[[VAL_15:.*]] = fir.is_present %[[VAL_14]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:               fir.result %[[VAL_11]]#1, %[[VAL_12]]#1, %[[VAL_13]], %[[VAL_15]] : index, index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_10]]#0, %[[VAL_10]]#1, %[[VAL_10]]#2, %[[VAL_10]]#3 : index, index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_17:.*]] = fir.if %[[VAL_8]]#3 -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+// CHECK:             %[[VAL_19:.*]] = fir.allocmem !fir.array<?x?x!fir.char<1,?>>(%[[VAL_8]]#2 : index), %[[VAL_8]]#0, %[[VAL_8]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_20:.*]] = fir.shape %[[VAL_8]]#0, %[[VAL_8]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_21:.*]] = fir.embox %[[VAL_19]](%[[VAL_20]]) typeparams %[[VAL_8]]#2 : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>, !fir.shape<2>, index) -> !fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>
+// CHECK:             %[[VAL_22:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_21]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_22]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_26:.*]] = fir.rebox %[[VAL_21]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,?>>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             fir.result %[[VAL_26]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_27:.*]] = fir.declare %[[VAL_17]] dummy_scope %[[VAL_6]] {uniq_name = "_QFtest3Ex"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest3Ex"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_28:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           fir.if %[[VAL_28]] {
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_17]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_30:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_30]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_33:.*]] = arith.cmpi ne, %[[VAL_31]], %[[VAL_32]] : index
+// CHECK:             fir.if %[[VAL_33]] {
+// CHECK:               %[[VAL_34:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_17]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_37:.*]] = fir.convert %[[VAL_34]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_29]] : !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  return
+}
+
+// Test character array with assumed length and stack allocation.
+// CHECK-LABEL:   func.func @_QPtest3_stack(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest3_stack(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,?>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_7:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           %[[VAL_8:.*]]:4 = fir.if %[[VAL_7]] -> (index, index, index, i1) {
+// CHECK:             %[[VAL_9:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:             %[[VAL_10:.*]]:4 = fir.if %[[VAL_9]] -> (index, index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]] = fir.box_elesize %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:               %[[VAL_14:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.ref<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:               %[[VAL_15:.*]] = fir.is_present %[[VAL_14]] : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:               fir.result %[[VAL_11]]#1, %[[VAL_12]]#1, %[[VAL_13]], %[[VAL_15]] : index, index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_10]]#0, %[[VAL_10]]#1, %[[VAL_10]]#2, %[[VAL_10]]#3 : index, index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_17:.*]] = fir.alloca !fir.array<?x?x!fir.char<1,?>>(%[[VAL_8]]#2 : index), %[[VAL_8]]#0, %[[VAL_8]]#1 {bindc_name = ".repacked"}
+// CHECK:           %[[VAL_19:.*]] = fir.shape %[[VAL_8]]#0, %[[VAL_8]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:           %[[VAL_20:.*]] = fir.embox %[[VAL_17]](%[[VAL_19]]) typeparams %[[VAL_8]]#2 : (!fir.ref<!fir.array<?x?x!fir.char<1,?>>>, !fir.shape<2>, index) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_21:.*]] = fir.if %[[VAL_8]]#3 -> (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+// CHECK:             %[[VAL_22:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_20]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_22]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_26:.*]] = fir.rebox %[[VAL_20]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             fir.result %[[VAL_26]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 stack innermost : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_27:.*]] = fir.declare %[[VAL_21]] dummy_scope %[[VAL_6]] {uniq_name = "_QFtest3_stackEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest3_stackEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:           %[[VAL_28:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> i1
+// CHECK:           fir.if %[[VAL_28]] {
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_21]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_30:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,?>>>
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_30]] : (!fir.heap<!fir.array<?x?x!fir.char<1,?>>>) -> index
+// CHECK:             %[[VAL_33:.*]] = arith.cmpi ne, %[[VAL_31]], %[[VAL_32]] : index
+// CHECK:             fir.if %[[VAL_33]] {
+// CHECK:               %[[VAL_34:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_21]] : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_37:.*]] = fir.convert %[[VAL_34]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 stack : !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+  return
+}
+
+// Test character array with constant length and heap allocation.
+// CHECK-LABEL:   func.func @_QPtest4(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.char<1,10>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest4(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,10>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = arith.constant 10 : index
+// CHECK:           %[[VAL_7:.*]] = fir.dummy_scope : !fir.dscope
+  %c10 = arith.constant 10 : index
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_8:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:           %[[VAL_9:.*]]:3 = fir.if %[[VAL_8]] -> (index, index, i1) {
+// CHECK:             %[[VAL_10:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:             %[[VAL_11:.*]]:3 = fir.if %[[VAL_10]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_14:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.ref<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:               %[[VAL_15:.*]] = fir.is_present %[[VAL_14]] : (!fir.ref<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:               fir.result %[[VAL_12]]#1, %[[VAL_13]]#1, %[[VAL_15]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_11]]#0, %[[VAL_11]]#1, %[[VAL_11]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_17:.*]] = fir.if %[[VAL_9]]#2 -> (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) {
+// CHECK:             %[[VAL_19:.*]] = fir.allocmem !fir.array<?x?x!fir.char<1,10>>, %[[VAL_9]]#0, %[[VAL_9]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_20:.*]] = fir.shape %[[VAL_9]]#0, %[[VAL_9]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_21:.*]] = fir.embox %[[VAL_19]](%[[VAL_20]]) : (!fir.heap<!fir.array<?x?x!fir.char<1,10>>>, !fir.shape<2>) -> !fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,10>>>>
+// CHECK:             %[[VAL_22:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_21]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,10>>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_22]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_26:.*]] = fir.rebox %[[VAL_21]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.char<1,10>>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             fir.result %[[VAL_26]] : !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           %[[VAL_27:.*]] = fir.declare %[[VAL_17]] typeparams %[[VAL_6]] dummy_scope %[[VAL_7]] {uniq_name = "_QFtest4Ex"} : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+  %2 = fir.declare %1 typeparams %c10 dummy_scope %0 {uniq_name = "_QFtest4Ex"} : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           %[[VAL_28:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:           fir.if %[[VAL_28]] {
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_17]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?x!fir.char<1,10>>>) -> index
+// CHECK:             %[[VAL_30:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_30]] : (!fir.heap<!fir.array<?x?x!fir.char<1,10>>>) -> index
+// CHECK:             %[[VAL_33:.*]] = arith.cmpi ne, %[[VAL_31]], %[[VAL_32]] : index
+// CHECK:             fir.if %[[VAL_33]] {
+// CHECK:               %[[VAL_34:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_17]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_37:.*]] = fir.convert %[[VAL_34]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_29]] : !fir.heap<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+  return
+}
+
+// Test character array with constant length and stack allocation.
+// CHECK-LABEL:   func.func @_QPtest4_stack(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.char<1,10>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest4_stack(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,10>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = arith.constant 10 : index
+// CHECK:           %[[VAL_7:.*]] = fir.dummy_scope : !fir.dscope
+  %c10 = arith.constant 10 : index
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_8:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:           %[[VAL_9:.*]]:3 = fir.if %[[VAL_8]] -> (index, index, i1) {
+// CHECK:             %[[VAL_10:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:             %[[VAL_11:.*]]:3 = fir.if %[[VAL_10]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_14:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.ref<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:               %[[VAL_15:.*]] = fir.is_present %[[VAL_14]] : (!fir.ref<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:               fir.result %[[VAL_12]]#1, %[[VAL_13]]#1, %[[VAL_15]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_11]]#0, %[[VAL_11]]#1, %[[VAL_11]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_17:.*]] = fir.alloca !fir.array<?x?x!fir.char<1,10>>, %[[VAL_9]]#0, %[[VAL_9]]#1 {bindc_name = ".repacked"}
+// CHECK:           %[[VAL_19:.*]] = fir.shape %[[VAL_9]]#0, %[[VAL_9]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:           %[[VAL_20:.*]] = fir.embox %[[VAL_17]](%[[VAL_19]]) : (!fir.ref<!fir.array<?x?x!fir.char<1,10>>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           %[[VAL_21:.*]] = fir.if %[[VAL_9]]#2 -> (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) {
+// CHECK:             %[[VAL_22:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_20]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_22]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_26:.*]] = fir.rebox %[[VAL_20]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             fir.result %[[VAL_26]] : !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 stack innermost : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           %[[VAL_27:.*]] = fir.declare %[[VAL_21]] typeparams %[[VAL_6]] dummy_scope %[[VAL_7]] {uniq_name = "_QFtest4_stackEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+  %2 = fir.declare %1 typeparams %c10 dummy_scope %0 {uniq_name = "_QFtest4_stackEx"} : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>, index, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:           %[[VAL_28:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> i1
+// CHECK:           fir.if %[[VAL_28]] {
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_21]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?x!fir.char<1,10>>>) -> index
+// CHECK:             %[[VAL_30:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.heap<!fir.array<?x?x!fir.char<1,10>>>
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_30]] : (!fir.heap<!fir.array<?x?x!fir.char<1,10>>>) -> index
+// CHECK:             %[[VAL_33:.*]] = arith.cmpi ne, %[[VAL_31]], %[[VAL_32]] : index
+// CHECK:             fir.if %[[VAL_33]] {
+// CHECK:               %[[VAL_34:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_21]] : (!fir.box<!fir.array<?x?x!fir.char<1,10>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_37:.*]] = fir.convert %[[VAL_34]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 stack : !fir.box<!fir.array<?x?x!fir.char<1,10>>>
+  return
+}
+
+// Test derived type array with heap allocation.
+// CHECK-LABEL:   func.func @_QPtest5(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest5(%arg0: !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_7:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           %[[VAL_8:.*]]:3 = fir.if %[[VAL_7]] -> (index, index, i1) {
+// CHECK:             %[[VAL_9:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:             %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:               %[[VAL_14:.*]] = fir.is_present %[[VAL_13]] : (!fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:               fir.result %[[VAL_11]]#1, %[[VAL_12]]#1, %[[VAL_14]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_10]]#0, %[[VAL_10]]#1, %[[VAL_10]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_16:.*]] = fir.if %[[VAL_8]]#2 -> (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) {
+// CHECK:             %[[VAL_18:.*]] = fir.allocmem !fir.array<?x?x!fir.type<_QMmTt>>, %[[VAL_8]]#0, %[[VAL_8]]#1 {bindc_name = ".repacked", uniq_name = ""}
+// CHECK:             %[[VAL_19:.*]] = fir.shape %[[VAL_8]]#0, %[[VAL_8]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_20:.*]] = fir.embox %[[VAL_18]](%[[VAL_19]]) : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.shape<2>) -> !fir.box<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>
+// CHECK:             %[[VAL_21:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_22:.*]] = fir.convert %[[VAL_20]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.rebox %[[VAL_20]] : (!fir.box<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             fir.result %[[VAL_25]] : !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_26:.*]] = fir.declare %[[VAL_16]] dummy_scope %[[VAL_6]] {uniq_name = "_QFtest5Ex"} : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest5Ex"} : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_27:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           fir.if %[[VAL_27]] {
+// CHECK:             %[[VAL_28:.*]] = fir.box_addr %[[VAL_16]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_28]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_32:.*]] = arith.cmpi ne, %[[VAL_30]], %[[VAL_31]] : index
+// CHECK:             fir.if %[[VAL_32]] {
+// CHECK:               %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_34:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_16]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_28]] : !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+  return
+}
+
+// Test derived type array with stack allocation.
+// CHECK-LABEL:   func.func @_QPtest5_stack(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest5_stack(%arg0: !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_4:.*]] = arith.constant false
+// CHECK:           %[[VAL_5:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_6:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_7:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           %[[VAL_8:.*]]:3 = fir.if %[[VAL_7]] -> (index, index, i1) {
+// CHECK:             %[[VAL_9:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:             %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_11:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_5]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_12:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_3]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_13:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:               %[[VAL_14:.*]] = fir.is_present %[[VAL_13]] : (!fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:               fir.result %[[VAL_11]]#1, %[[VAL_12]]#1, %[[VAL_14]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_10]]#0, %[[VAL_10]]#1, %[[VAL_10]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_5]], %[[VAL_5]], %[[VAL_4]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_16:.*]] = fir.alloca !fir.array<?x?x!fir.type<_QMmTt>>, %[[VAL_8]]#0, %[[VAL_8]]#1 {bindc_name = ".repacked"}
+// CHECK:           %[[VAL_18:.*]] = fir.shape %[[VAL_8]]#0, %[[VAL_8]]#1 : (index, index) -> !fir.shape<2>
+// CHECK:           %[[VAL_19:.*]] = fir.embox %[[VAL_16]](%[[VAL_18]]) : (!fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_20:.*]] = fir.if %[[VAL_8]]#2 -> (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) {
+// CHECK:             %[[VAL_21:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_22:.*]] = fir.convert %[[VAL_19]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.rebox %[[VAL_19]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             fir.result %[[VAL_25]] : !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 stack innermost : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_26:.*]] = fir.declare %[[VAL_20]] dummy_scope %[[VAL_6]] {uniq_name = "_QFtest5_stackEx"} : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest5_stackEx"} : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_27:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           fir.if %[[VAL_27]] {
+// CHECK:             %[[VAL_28:.*]] = fir.box_addr %[[VAL_20]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_28]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_29:.*]] = fir.box_addr %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_32:.*]] = arith.cmpi ne, %[[VAL_30]], %[[VAL_31]] : index
+// CHECK:             fir.if %[[VAL_32]] {
+// CHECK:               %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_34:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_35:.*]] = fir.convert %[[VAL_20]] : (!fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 stack : !fir.box<!fir.array<?x?x!fir.type<_QMmTt>>>
+  return
+}
+
+// Test polymorphic type array with heap allocation.
+// CHECK-LABEL:   func.func @_QPtest6(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest6(%arg0: !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 2 : i32
+// CHECK:           %[[VAL_4:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_5:.*]] = arith.constant false
+// CHECK:           %[[VAL_6:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_7:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>
+// CHECK:           %[[VAL_8:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_9:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:             %[[VAL_11:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:             %[[VAL_12:.*]]:3 = fir.if %[[VAL_11]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_13:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_6]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_14:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_4]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_15:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:               %[[VAL_16:.*]] = fir.is_present %[[VAL_15]] : (!fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:               fir.result %[[VAL_13]]#1, %[[VAL_14]]#1, %[[VAL_16]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_12]]#0, %[[VAL_12]]#1, %[[VAL_12]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_18:.*]] = fir.if %[[VAL_10]]#2 -> (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) {
+// CHECK:             %[[VAL_20:.*]] = fir.zero_bits !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_21:.*]] = fir.shape %[[VAL_6]], %[[VAL_6]] : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_22:.*]] = fir.embox %[[VAL_20]](%[[VAL_21]]) : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.shape<2>) -> !fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>
+// CHECK:             fir.store %[[VAL_22]] to %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             fir.call @_FortranAAllocatableApplyMold(%[[VAL_23]], %[[VAL_24]], %[[VAL_3]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_26:.*]] = fir.convert %[[VAL_6]] : (index) -> i32
+// CHECK:             %[[VAL_27:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_28:.*]] = fir.convert %[[VAL_10]]#0 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_25]], %[[VAL_26]], %[[VAL_27]], %[[VAL_28]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_4]] : (index) -> i32
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_10]]#1 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_29]], %[[VAL_30]], %[[VAL_31]], %[[VAL_32]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_34:.*]] = fir.absent !fir.box<none>
+// CHECK:             %[[VAL_35:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             %[[VAL_37:.*]] = fir.call @_FortranAAllocatableAllocate(%[[VAL_35]], %[[VAL_5]], %[[VAL_34]], %[[VAL_36]], %[[VAL_2]]) : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
+// CHECK:             %[[VAL_38:.*]] = fir.load %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>
+// CHECK:             %[[VAL_39:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_40:.*]] = fir.convert %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_41:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_42:.*]] = fir.convert %[[VAL_39]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_43:.*]] = fir.rebox %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             fir.result %[[VAL_43]] : !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_44:.*]] = fir.declare %[[VAL_18]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest6Ex"} : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest6Ex"} : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_45:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           fir.if %[[VAL_45]] {
+// CHECK:             %[[VAL_46:.*]] = fir.box_addr %[[VAL_18]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_48:.*]] = fir.convert %[[VAL_46]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_47:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_49:.*]] = fir.convert %[[VAL_47]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_50:.*]] = arith.cmpi ne, %[[VAL_48]], %[[VAL_49]] : index
+// CHECK:             fir.if %[[VAL_50]] {
+// CHECK:               %[[VAL_51:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_52:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_53:.*]] = fir.convert %[[VAL_18]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_54:.*]] = fir.convert %[[VAL_51]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_46]] : !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+  return
+}
+
+// Test polymorphic type array with stack allocation.
+// CHECK-LABEL:   func.func @_QPtest6_stack(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+func.func @_QPtest6_stack(%arg0: !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 2 : i32
+// CHECK:           %[[VAL_4:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_5:.*]] = arith.constant false
+// CHECK:           %[[VAL_6:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_7:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>
+// CHECK:           %[[VAL_8:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_9:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:             %[[VAL_11:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:             %[[VAL_12:.*]]:3 = fir.if %[[VAL_11]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_13:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_6]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_14:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_4]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_15:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:               %[[VAL_16:.*]] = fir.is_present %[[VAL_15]] : (!fir.ref<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:               fir.result %[[VAL_13]]#1, %[[VAL_14]]#1, %[[VAL_16]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_12]]#0, %[[VAL_12]]#1, %[[VAL_12]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_18:.*]] = fir.if %[[VAL_10]]#2 -> (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) {
+// CHECK:             %[[VAL_20:.*]] = fir.zero_bits !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_21:.*]] = fir.shape %[[VAL_6]], %[[VAL_6]] : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_22:.*]] = fir.embox %[[VAL_20]](%[[VAL_21]]) : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.shape<2>) -> !fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>
+// CHECK:             fir.store %[[VAL_22]] to %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             fir.call @_FortranAAllocatableApplyMold(%[[VAL_23]], %[[VAL_24]], %[[VAL_3]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_26:.*]] = fir.convert %[[VAL_6]] : (index) -> i32
+// CHECK:             %[[VAL_27:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_28:.*]] = fir.convert %[[VAL_10]]#0 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_25]], %[[VAL_26]], %[[VAL_27]], %[[VAL_28]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_4]] : (index) -> i32
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_10]]#1 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_29]], %[[VAL_30]], %[[VAL_31]], %[[VAL_32]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_34:.*]] = fir.absent !fir.box<none>
+// CHECK:             %[[VAL_35:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             %[[VAL_37:.*]] = fir.call @_FortranAAllocatableAllocate(%[[VAL_35]], %[[VAL_5]], %[[VAL_34]], %[[VAL_36]], %[[VAL_2]]) : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
+// CHECK:             %[[VAL_38:.*]] = fir.load %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>>
+// CHECK:             %[[VAL_39:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_40:.*]] = fir.convert %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_41:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_42:.*]] = fir.convert %[[VAL_39]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_43:.*]] = fir.rebox %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>>) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             fir.result %[[VAL_43]] : !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 stack innermost : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_44:.*]] = fir.declare %[[VAL_18]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest6_stackEx"} : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest6_stackEx"} : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>, !fir.dscope) -> !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:           %[[VAL_45:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> i1
+// CHECK:           fir.if %[[VAL_45]] {
+// CHECK:             %[[VAL_46:.*]] = fir.box_addr %[[VAL_18]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_48:.*]] = fir.convert %[[VAL_46]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_47:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>
+// CHECK:             %[[VAL_49:.*]] = fir.convert %[[VAL_47]] : (!fir.heap<!fir.array<?x?x!fir.type<_QMmTt>>>) -> index
+// CHECK:             %[[VAL_50:.*]] = arith.cmpi ne, %[[VAL_48]], %[[VAL_49]] : index
+// CHECK:             fir.if %[[VAL_50]] {
+// CHECK:               %[[VAL_51:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_52:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_53:.*]] = fir.convert %[[VAL_18]] : (!fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>) -> !fir.box<none>
+// CHECK:               %[[VAL_54:.*]] = fir.convert %[[VAL_51]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 stack : !fir.class<!fir.array<?x?x!fir.type<_QMmTt>>>
+  return
+}
+
+// Test unlimited polymorphic type array with heap allocation.
+// CHECK-LABEL:   func.func @_QPtest7(
+// CHECK-SAME:                        %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.array<?x?xnone>> {fir.bindc_name = "x"}) {
+func.func @_QPtest7(%arg0: !fir.class<!fir.array<?x?xnone>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 2 : i32
+// CHECK:           %[[VAL_4:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_5:.*]] = arith.constant false
+// CHECK:           %[[VAL_6:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_7:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x?xnone>>>
+// CHECK:           %[[VAL_8:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_9:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> i1
+// CHECK:           %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:             %[[VAL_11:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.class<!fir.array<?x?xnone>>) -> i1
+// CHECK:             %[[VAL_12:.*]]:3 = fir.if %[[VAL_11]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_13:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_6]] : (!fir.class<!fir.array<?x?xnone>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_14:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_4]] : (!fir.class<!fir.array<?x?xnone>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_15:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.ref<!fir.array<?x?xnone>>
+// CHECK:               %[[VAL_16:.*]] = fir.is_present %[[VAL_15]] : (!fir.ref<!fir.array<?x?xnone>>) -> i1
+// CHECK:               fir.result %[[VAL_13]]#1, %[[VAL_14]]#1, %[[VAL_16]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_12]]#0, %[[VAL_12]]#1, %[[VAL_12]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_18:.*]] = fir.if %[[VAL_10]]#2 -> (!fir.class<!fir.array<?x?xnone>>) {
+// CHECK:             %[[VAL_20:.*]] = fir.zero_bits !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             %[[VAL_21:.*]] = fir.shape %[[VAL_6]], %[[VAL_6]] : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_22:.*]] = fir.embox %[[VAL_20]](%[[VAL_21]]) : (!fir.heap<!fir.array<?x?xnone>>, !fir.shape<2>) -> !fir.class<!fir.heap<!fir.array<?x?xnone>>>
+// CHECK:             fir.store %[[VAL_22]] to %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:             fir.call @_FortranAAllocatableApplyMold(%[[VAL_23]], %[[VAL_24]], %[[VAL_3]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_26:.*]] = fir.convert %[[VAL_6]] : (index) -> i32
+// CHECK:             %[[VAL_27:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_28:.*]] = fir.convert %[[VAL_10]]#0 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_25]], %[[VAL_26]], %[[VAL_27]], %[[VAL_28]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_4]] : (index) -> i32
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_10]]#1 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_29]], %[[VAL_30]], %[[VAL_31]], %[[VAL_32]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_34:.*]] = fir.absent !fir.box<none>
+// CHECK:             %[[VAL_35:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             %[[VAL_37:.*]] = fir.call @_FortranAAllocatableAllocate(%[[VAL_35]], %[[VAL_5]], %[[VAL_34]], %[[VAL_36]], %[[VAL_2]]) : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
+// CHECK:             %[[VAL_38:.*]] = fir.load %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>
+// CHECK:             %[[VAL_39:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_40:.*]] = fir.convert %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?xnone>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_41:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:             %[[VAL_42:.*]] = fir.convert %[[VAL_39]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_43:.*]] = fir.rebox %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?xnone>>>) -> !fir.class<!fir.array<?x?xnone>>
+// CHECK:             fir.result %[[VAL_43]] : !fir.class<!fir.array<?x?xnone>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.class<!fir.array<?x?xnone>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 heap innermost : (!fir.class<!fir.array<?x?xnone>>) -> !fir.class<!fir.array<?x?xnone>>
+// CHECK:           %[[VAL_44:.*]] = fir.declare %[[VAL_18]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest7Ex"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> !fir.class<!fir.array<?x?xnone>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest7Ex"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> !fir.class<!fir.array<?x?xnone>>
+// CHECK:           %[[VAL_45:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> i1
+// CHECK:           fir.if %[[VAL_45]] {
+// CHECK:             %[[VAL_46:.*]] = fir.box_addr %[[VAL_18]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             %[[VAL_48:.*]] = fir.convert %[[VAL_46]] : (!fir.heap<!fir.array<?x?xnone>>) -> index
+// CHECK:             %[[VAL_47:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             %[[VAL_49:.*]] = fir.convert %[[VAL_47]] : (!fir.heap<!fir.array<?x?xnone>>) -> index
+// CHECK:             %[[VAL_50:.*]] = arith.cmpi ne, %[[VAL_48]], %[[VAL_49]] : index
+// CHECK:             fir.if %[[VAL_50]] {
+// CHECK:               %[[VAL_51:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_52:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:               %[[VAL_53:.*]] = fir.convert %[[VAL_18]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:               %[[VAL_54:.*]] = fir.convert %[[VAL_51]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:               fir.freemem %[[VAL_46]] : !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             }
+// CHECK:           }
+  fir.unpack_array %1 to %arg0 heap : !fir.class<!fir.array<?x?xnone>>
+  return
+}
+
+// Test unlimited polymorphic type array with stack allocation.
+// CHECK-LABEL:   func.func @_QPtest7_stack(
+// CHECK-SAME:                              %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.class<!fir.array<?x?xnone>> {fir.bindc_name = "x"}) {
+func.func @_QPtest7_stack(%arg0: !fir.class<!fir.array<?x?xnone>> {fir.bindc_name = "x"}) {
+// CHECK:           %[[VAL_1:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_2:.*]] = arith.constant {{.*}} : i32
+// CHECK:           %[[VAL_3:.*]] = arith.constant 2 : i32
+// CHECK:           %[[VAL_4:.*]] = arith.constant 1 : index
+// CHECK:           %[[VAL_5:.*]] = arith.constant false
+// CHECK:           %[[VAL_6:.*]] = arith.constant 0 : index
+// CHECK:           %[[VAL_7:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?x?xnone>>>
+// CHECK:           %[[VAL_8:.*]] = fir.dummy_scope : !fir.dscope
+  %0 = fir.dummy_scope : !fir.dscope
+// CHECK:           %[[VAL_9:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> i1
+// CHECK:           %[[VAL_10:.*]]:3 = fir.if %[[VAL_9]] -> (index, index, i1) {
+// CHECK:             %[[VAL_11:.*]] = fir.is_contiguous_box %[[VAL_0]] innermost : (!fir.class<!fir.array<?x?xnone>>) -> i1
+// CHECK:             %[[VAL_12:.*]]:3 = fir.if %[[VAL_11]] -> (index, index, i1) {
+// CHECK:               fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:             } else {
+// CHECK:               %[[VAL_13:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_6]] : (!fir.class<!fir.array<?x?xnone>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_14:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_4]] : (!fir.class<!fir.array<?x?xnone>>, index) -> (index, index, index)
+// CHECK:               %[[VAL_15:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.ref<!fir.array<?x?xnone>>
+// CHECK:               %[[VAL_16:.*]] = fir.is_present %[[VAL_15]] : (!fir.ref<!fir.array<?x?xnone>>) -> i1
+// CHECK:               fir.result %[[VAL_13]]#1, %[[VAL_14]]#1, %[[VAL_16]] : index, index, i1
+// CHECK:             }
+// CHECK:             fir.result %[[VAL_12]]#0, %[[VAL_12]]#1, %[[VAL_12]]#2 : index, index, i1
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_6]], %[[VAL_6]], %[[VAL_5]] : index, index, i1
+// CHECK:           }
+// CHECK:           %[[VAL_18:.*]] = fir.if %[[VAL_10]]#2 -> (!fir.class<!fir.array<?x?xnone>>) {
+// CHECK:             %[[VAL_20:.*]] = fir.zero_bits !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             %[[VAL_21:.*]] = fir.shape %[[VAL_6]], %[[VAL_6]] : (index, index) -> !fir.shape<2>
+// CHECK:             %[[VAL_22:.*]] = fir.embox %[[VAL_20]](%[[VAL_21]]) : (!fir.heap<!fir.array<?x?xnone>>, !fir.shape<2>) -> !fir.class<!fir.heap<!fir.array<?x?xnone>>>
+// CHECK:             fir.store %[[VAL_22]] to %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>
+// CHECK:             %[[VAL_23:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:             fir.call @_FortranAAllocatableApplyMold(%[[VAL_23]], %[[VAL_24]], %[[VAL_3]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, i32) -> ()
+// CHECK:             %[[VAL_25:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_26:.*]] = fir.convert %[[VAL_6]] : (index) -> i32
+// CHECK:             %[[VAL_27:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_28:.*]] = fir.convert %[[VAL_10]]#0 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_25]], %[[VAL_26]], %[[VAL_27]], %[[VAL_28]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_4]] : (index) -> i32
+// CHECK:             %[[VAL_31:.*]] = fir.convert %[[VAL_4]] : (index) -> i64
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_10]]#1 : (index) -> i64
+// CHECK:             fir.call @_FortranAAllocatableSetBounds(%[[VAL_29]], %[[VAL_30]], %[[VAL_31]], %[[VAL_32]]) : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+// CHECK:             %[[VAL_33:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_34:.*]] = fir.absent !fir.box<none>
+// CHECK:             %[[VAL_35:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.box<none>>
+// CHECK:             %[[VAL_36:.*]] = fir.convert %[[VAL_33]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             %[[VAL_37:.*]] = fir.call @_FortranAAllocatableAllocate(%[[VAL_35]], %[[VAL_5]], %[[VAL_34]], %[[VAL_36]], %[[VAL_2]]) : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
+// CHECK:             %[[VAL_38:.*]] = fir.load %[[VAL_7]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>
+// CHECK:             %[[VAL_39:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:             %[[VAL_40:.*]] = fir.convert %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?xnone>>>) -> !fir.box<none>
+// CHECK:             %[[VAL_41:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:             %[[VAL_42:.*]] = fir.convert %[[VAL_39]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:             fir.call @_FortranAShallowCopyDirect(%[[VAL_40]], %[[VAL_41]], %[[VAL_42]], %[[VAL_2]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             %[[VAL_43:.*]] = fir.rebox %[[VAL_38]] : (!fir.class<!fir.heap<!fir.array<?x?xnone>>>) -> !fir.class<!fir.array<?x?xnone>>
+// CHECK:             fir.result %[[VAL_43]] : !fir.class<!fir.array<?x?xnone>>
+// CHECK:           } else {
+// CHECK:             fir.result %[[VAL_0]] : !fir.class<!fir.array<?x?xnone>>
+// CHECK:           }
+  %1 = fir.pack_array %arg0 stack innermost : (!fir.class<!fir.array<?x?xnone>>) -> !fir.class<!fir.array<?x?xnone>>
+// CHECK:           %[[VAL_44:.*]] = fir.declare %[[VAL_18]] dummy_scope %[[VAL_8]] {uniq_name = "_QFtest7Ex"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> !fir.class<!fir.array<?x?xnone>>
+  %2 = fir.declare %1 dummy_scope %0 {uniq_name = "_QFtest7Ex"} : (!fir.class<!fir.array<?x?xnone>>, !fir.dscope) -> !fir.class<!fir.array<?x?xnone>>
+// CHECK:           %[[VAL_45:.*]] = fir.is_present %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> i1
+// CHECK:           fir.if %[[VAL_45]] {
+// CHECK:             %[[VAL_46:.*]] = fir.box_addr %[[VAL_18]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             %[[VAL_48:.*]] = fir.convert %[[VAL_46]] : (!fir.heap<!fir.array<?x?xnone>>) -> index
+// CHECK:             %[[VAL_47:.*]] = fir.box_addr %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.heap<!fir.array<?x?xnone>>
+// CHECK:             %[[VAL_49:.*]] = fir.convert %[[VAL_47]] : (!fir.heap<!fir.array<?x?xnone>>) -> index
+// CHECK:             %[[VAL_50:.*]] = arith.cmpi ne, %[[VAL_48]], %[[VAL_49]] : index
+// CHECK:             fir.if %[[VAL_50]] {
+// CHECK:               %[[VAL_51:.*]] = fir.address_of(@{{_QQcl.*}}
+// CHECK:               %[[VAL_52:.*]] = fir.convert %[[VAL_0]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:               %[[VAL_53:.*]] = fir.convert %[[VAL_18]] : (!fir.class<!fir.array<?x?xnone>>) -> !fir.box<none>
+// CHECK:               %[[VAL_54:.*]] = fir.convert %[[VAL_51]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+// CHECK:               fir.call @_FortranAShallowCopyDirect(%[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_1]]) : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> ()
+// CHECK:             }
+// CHECK:           }
+// CHECK:           return
+// CHECK:         }
+  fir.unpack_array %1 to %arg0 stack : !fir.class<!fir.array<?x?xnone>>
+  return
+}

>From 95c96c1408f1336c36a288f408913bd89cb29952 Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Wed, 19 Mar 2025 12:19:05 -0700
Subject: [PATCH 2/2] Updated the header comment for LowerRepackArrays.cpp
 file.

---
 .../Optimizer/CodeGen/LowerRepackArrays.cpp   | 24 +++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp b/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp
index c109dc4732ca5..8e00fcf49dd88 100644
--- a/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp
+++ b/flang/lib/Optimizer/CodeGen/LowerRepackArrays.cpp
@@ -1,11 +1,31 @@
-//===-- LowerRepackArrays.cpp
-//------------------------------------------------===//
+//===-- LowerRepackArrays.cpp ---------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+/// \file
+/// This pass expands fir.pack_array and fir.unpack_array operations
+/// into sequences of other FIR operations and Fortran runtime calls.
+/// This pass is using structured control flow FIR operations such
+/// as fir.if, so its placement in the pipeline should guarantee
+/// further lowering of these operations.
+///
+/// A fir.pack_array operation is converted into a sequence of checks
+/// identifying whether an array needs to be copied into a contiguous
+/// temporary. When the checks pass, a new memory allocation is done
+/// for the temporary array (in either stack or heap memory).
+/// If `fir.pack_array` does not have no_copy attribute, then
+/// the original array is shallow-copied into the temporary.
+///
+/// A fir.unpack_array operations is converted into a check
+/// of whether the original and the temporary arrays are different
+/// memory. When the check passes, the temporary array might be
+/// shallow-copied into the original array, and then the temporary
+/// array is deallocated (if it was allocated in stack memory,
+/// then there is no explicit deallocation).
+//===----------------------------------------------------------------------===//
 
 #include "flang/Optimizer/CodeGen/CodeGen.h"
 



More information about the flang-commits mailing list